Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
Expand All @@ -21,6 +21,7 @@
* Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
*/
public interface ConversionService {
Expand Down Expand Up @@ -54,6 +55,28 @@ public interface ConversionService {
*/
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

/**
* Returns true if conversion between the sourceType and targetType can be bypassed.
* More precisely this method will return true if objects of sourceType can be
* converted to the targetType by returning the source object unchanged.
* @param sourceType context about the source type to convert from (may be null if source is null)
* @param targetType context about the target type to convert to (required)
* @return true if conversion can be bypassed
* @throws IllegalArgumentException if targetType is null
*/
boolean canBypassConvert(Class<?> sourceType, Class<?> targetType);

/**
* Returns true if conversion between the sourceType and targetType can be bypassed.
* More precisely this method will return true if objects of sourceType can be
* converted to the targetType by returning the source object unchanged.
* @param sourceType context about the source type to convert from (may be null if source is null)
* @param targetType context about the target type to convert to (required)
* @return true if conversion can be bypassed
* @throws IllegalArgumentException if targetType is null
*/
boolean canBypassConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

/**
* Convert the source to targetType.
* @param source the source object to convert (may be null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Map;

import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

Expand Down Expand Up @@ -249,6 +250,23 @@ public TypeDescriptor narrow(Object value) {
this.mapKeyTypeDescriptor, this.mapValueTypeDescriptor, this.annotations);
}

/**
* Cast this {@link TypeDescriptor} to a superclass or implemented interface
* preserving annotations and nested type context.
*
* @param superType the super type to cast to (can be {@code null}
* @return a new TypeDescriptor for the up-cast type
* @throws IllegalArgumentException if this type is not assignable to the super-type
*/
public TypeDescriptor upcast(Class<?> superType) {
if (superType == null) {
return null;
}
Assert.isAssignable(superType, getType());
return new TypeDescriptor(superType, this.elementTypeDescriptor,
this.mapKeyTypeDescriptor, this.mapValueTypeDescriptor, this.annotations);
}

/**
* Returns the name of this type: the fully qualified class name.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2012 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;

/**
* Allows a {@link Converter}, {@link GenericConverter} or {@link ConverterFactory} to
* conditionally execute based on attributes of the {@code source} and {@code target}
* {@link TypeDescriptor}.
*
* <p>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
*
* {@code true} if the target field has also been annotated with {@code @DateTimeFormat}.
*
* <p>As another example, when converting from a String field to an {@code Account} field, an
* implementation might return {@code true} if the target Account class defines a
* {@code public static findAccount(String)} method.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.2
* @see Converter
* @see GenericConverter
* @see ConverterFactory
* @see ConditionalGenericConverter
*/
public interface ConditionalConversion {

/**
* Should the converter from {@code sourceType} to {@code 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);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
Expand All @@ -18,34 +18,19 @@

import org.springframework.core.convert.TypeDescriptor;


/**
* A generic converter that conditionally executes.
*
* <p>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.
*
* <p>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
* <code>true</code> if the target field has also been annotated with <code>@DateTimeFormat</code>.
*
* <p>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
* <code>public static findAccount(String)</code> method.
* A {@link GenericConverter} that may conditionally execute based on attributes of the
* {@code source} and {@code target} {@link TypeDescriptor}. See
* {@link ConditionalConversion} for details.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
* @see GenericConverter
* @see ConditionalConversion
*/
public interface ConditionalGenericConverter extends GenericConverter {

/**
* 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);
public interface ConditionalGenericConverter extends GenericConverter,
ConditionalConversion {

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
Expand All @@ -20,10 +20,13 @@
* A converter converts a source object of type S to a target of type T.
* Implementations of this interface are thread-safe and can be shared.
*
* <p>Implementations may additionally implement {@link ConditionalConversion}.
*
* @author Keith Donald
* @since 3.0
* @see ConditionalConversion
* @param <S> The source type
* @param <T> The target type
* @since 3.0
*/
public interface Converter<S, T> {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
Expand All @@ -19,8 +19,11 @@
/**
* A factory for "ranged" converters that can convert objects from S to subtypes of R.
*
* <p>Implementations may additionally implement {@link ConditionalConversion}.
*
* @author Keith Donald
* @since 3.0
* @see ConditionalConversion
* @param <S> The source type converters created by this factory can convert from
* @param <R> The target range (or base) type converters created by this factory can convert to;
* for example {@link Number} for a set of number subtypes.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
Expand Down Expand Up @@ -34,18 +34,24 @@
* <p>This interface should generally not be used when the simpler {@link Converter} or
* {@link ConverterFactory} interfaces are sufficient.
*
* <p>Implementations may additionally implement {@link ConditionalConversion}.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
* @see TypeDescriptor
* @see Converter
* @see ConverterFactory
* @see ConditionalConversion
*/
public interface GenericConverter {

/**
* Return the source and target types which this converter can convert between.
* <p>Each entry is a convertible source-to-target type pair.
* Return the source and target types which this converter can convert between. Each
* entry is a convertible source-to-target type pair.
* <p>
* For {@link ConditionalConversion conditional} converters this method may return
* {@code null} to indicate all source-to-target pairs should be considered. *
*/
Set<ConvertiblePair> getConvertibleTypes();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
Expand All @@ -18,6 +18,7 @@

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.springframework.core.convert.ConversionService;
Expand All @@ -26,18 +27,22 @@
import org.springframework.util.ObjectUtils;

/**
* Converts an Array to another Array.
* First adapts the source array to a List, then delegates to {@link CollectionToArrayConverter} to perform the target array conversion.
* Converts an Array to another Array. First adapts the source array to a List, then
* delegates to {@link CollectionToArrayConverter} to perform the target array conversion.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
*/
final class ArrayToArrayConverter implements ConditionalGenericConverter {

private final CollectionToArrayConverter helperConverter;

private final ConversionService conversionService;

public ArrayToArrayConverter(ConversionService conversionService) {
this.helperConverter = new CollectionToArrayConverter(conversionService);
this.conversionService = conversionService;
}

public Set<ConvertiblePair> getConvertibleTypes() {
Expand All @@ -48,8 +53,14 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.matches(sourceType, targetType);
}

public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType);
public Object convert(Object source, TypeDescriptor sourceType,
TypeDescriptor targetType) {
if (conversionService.canBypassConvert(sourceType.getElementTypeDescriptor(),
targetType.getElementTypeDescriptor())) {
return source;
}
List<Object> sourceList = Arrays.asList(ObjectUtils.toObjectArray(source));
return this.helperConverter.convert(sourceList, sourceType, targetType);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static void addDefaultConverters(ConverterRegistry converterRegistry) {
// internal helpers

private static void addScalarConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

Expand All @@ -74,7 +75,7 @@ private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new CharacterToNumberFactory());

converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(Enum.class, String.class, new EnumToStringConverter());
converterRegistry.addConverter(Enum.class, String.class, new EnumToStringConverter(conversionService));

converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,37 @@

package org.springframework.core.convert.support;

import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalConversion;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
* Simply calls {@link Enum#name()} to convert a source Enum to a String.
* Calls {@link Enum#name()} to convert a source Enum to a String. This converter will
* not match enums with interfaces that can be converterd.
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
*/
final class EnumToStringConverter implements Converter<Enum<?>, String> {
final class EnumToStringConverter implements Converter<Enum<?>, String>, ConditionalConversion {

private final ConversionService conversionService;

public EnumToStringConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}

public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
for (Class<?> interfaceType : ClassUtils.getAllInterfacesForClass(sourceType.getType())) {
if (conversionService.canConvert(TypeDescriptor.valueOf(interfaceType),
targetType)) {
return false;
}
}
return true;
}

public String convert(Enum<?> source) {
return source.name();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
Expand Down Expand Up @@ -41,6 +41,9 @@ public Set<ConvertiblePair> getConvertibleTypes() {

public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> sourceClass = sourceType.getObjectType();
if (String.class.equals(sourceClass)) {
return false;
}
return CharSequence.class.isAssignableFrom(sourceClass) || StringWriter.class.isAssignableFrom(sourceClass) ||
ObjectToObjectConverter.hasValueOfMethodOrConstructor(sourceClass, String.class);
}
Expand Down
Loading