Added support for the Java 8 style 'from'/'to' method conventions
Also introduced a default ZonedDateTime-Calendar converter which is not covered by the default convention due to the 'from' method only being defined on GregorianCalendar. Issue: SPR-11259
This commit is contained in:
@@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils;
|
||||
public class DefaultConversionService extends GenericConversionService {
|
||||
|
||||
/** Java 8's java.time package available? */
|
||||
private static final boolean zoneIdAvailable =
|
||||
private static final boolean jsr310Available =
|
||||
ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader());
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ public class DefaultConversionService extends GenericConversionService {
|
||||
addCollectionConverters(converterRegistry);
|
||||
|
||||
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
|
||||
if (zoneIdAvailable) {
|
||||
ZoneIdConverterRegistrar.registerZoneIdConverters(converterRegistry);
|
||||
if (jsr310Available) {
|
||||
Jsr310ConverterRegistrar.registerZoneIdConverters(converterRegistry);
|
||||
}
|
||||
|
||||
converterRegistry.addConverter(new ObjectToObjectConverter());
|
||||
@@ -129,13 +129,13 @@ public class DefaultConversionService extends GenericConversionService {
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard-coded dependency on Java 8's {@link java.time.ZoneId}.
|
||||
* Inner class to avoid a hard-coded dependency on Java 8's {@code java.time} package.
|
||||
*/
|
||||
private static final class ZoneIdConverterRegistrar {
|
||||
private static final class Jsr310ConverterRegistrar {
|
||||
|
||||
public static void registerZoneIdConverters(ConverterRegistry converterRegistry) {
|
||||
converterRegistry.addConverter(new TimeZoneToZoneIdConverter());
|
||||
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
|
||||
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.io.StringWriter;
|
||||
@@ -21,10 +22,12 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Simply calls {@link Object#toString()} to convert any supported Object to a String.
|
||||
* Supports CharSequence, StringWriter, and any class with a String constructor or {@code valueOf(String)} method.
|
||||
* Supports CharSequence, StringWriter, and any class with a String constructor or
|
||||
* {@code valueOf(String)} method.
|
||||
*
|
||||
* <p>Used by the default ConversionService as a fallback if there are no other explicit
|
||||
* to-String converters registered.
|
||||
@@ -46,8 +49,10 @@ final class FallbackObjectToStringConverter implements ConditionalGenericConvert
|
||||
if (String.class.equals(sourceClass)) {
|
||||
return false;
|
||||
}
|
||||
return CharSequence.class.isAssignableFrom(sourceClass) || StringWriter.class.isAssignableFrom(sourceClass) ||
|
||||
ObjectToObjectConverter.hasValueOfMethodOrConstructor(sourceClass, String.class);
|
||||
return (CharSequence.class.isAssignableFrom(sourceClass) ||
|
||||
StringWriter.class.isAssignableFrom(sourceClass) ||
|
||||
ObjectToObjectConverter.getOfMethod(sourceClass, String.class) != null ||
|
||||
ClassUtils.getConstructorIfAvailable(sourceClass, String.class) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,8 +32,9 @@ import org.springframework.util.ReflectionUtils;
|
||||
* Generic converter that attempts to convert a source Object to a target type
|
||||
* by delegating to methods on the target type.
|
||||
*
|
||||
* <p>Calls a static {@code valueOf(sourceType)} or Java 8 style {@code of(sourceType)} method
|
||||
* on the target type to perform the conversion, if such a method exists. Otherwise, it calls
|
||||
* <p>Calls a static {@code valueOf(sourceType)} or Java 8 style {@code of|from(sourceType)}
|
||||
* method on the target type to perform the conversion, if such a method exists. Otherwise,
|
||||
* it checks for a {@code to[targetType.simpleName]} method on the source type calls
|
||||
* the target type's constructor that accepts a single {@code sourceType} argument, if such
|
||||
* a constructor exists. If neither strategy works, it throws a ConversionFailedException.
|
||||
*
|
||||
@@ -54,7 +55,9 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
|
||||
// no conversion required
|
||||
return false;
|
||||
}
|
||||
return hasValueOfMethodOrConstructor(targetType.getType(), sourceType.getType());
|
||||
return (String.class.equals(targetType.getType()) ?
|
||||
(ClassUtils.getConstructorIfAvailable(String.class, sourceType.getType()) != null) :
|
||||
hasToMethodOrOfMethodOrConstructor(targetType.getType(), sourceType.getType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,17 +67,22 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
Class<?> sourceClass = sourceType.getType();
|
||||
Class<?> targetClass = targetType.getType();
|
||||
Method method = getValueOfMethodOn(targetClass, sourceClass);
|
||||
try {
|
||||
if (method != null) {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(null, source);
|
||||
}
|
||||
else {
|
||||
Constructor<?> constructor = getConstructor(targetClass, sourceClass);
|
||||
if (constructor != null) {
|
||||
return constructor.newInstance(source);
|
||||
if (!String.class.equals(targetClass)) {
|
||||
Method method = getToMethod(targetClass, sourceClass);
|
||||
if (method != null) {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(source);
|
||||
}
|
||||
method = getOfMethod(targetClass, sourceClass);
|
||||
if (method != null) {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(null, source);
|
||||
}
|
||||
}
|
||||
Constructor<?> constructor = ClassUtils.getConstructorIfAvailable(targetClass, sourceClass);
|
||||
if (constructor != null) {
|
||||
return constructor.newInstance(source);
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
@@ -83,24 +91,31 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
|
||||
catch (Throwable ex) {
|
||||
throw new ConversionFailedException(sourceType, targetType, source, ex);
|
||||
}
|
||||
throw new IllegalStateException("No static valueOf(" + sourceClass.getName() +
|
||||
throw new IllegalStateException("No static valueOf/of/from(" + sourceClass.getName() +
|
||||
") method or Constructor(" + sourceClass.getName() + ") exists on " + targetClass.getName());
|
||||
}
|
||||
|
||||
static boolean hasValueOfMethodOrConstructor(Class<?> clazz, Class<?> sourceParameterType) {
|
||||
return getValueOfMethodOn(clazz, sourceParameterType) != null || getConstructor(clazz, sourceParameterType) != null;
|
||||
|
||||
private static boolean hasToMethodOrOfMethodOrConstructor(Class<?> targetClass, Class<?> sourceClass) {
|
||||
return (getToMethod(targetClass, sourceClass) != null ||
|
||||
getOfMethod(targetClass, sourceClass) != null ||
|
||||
ClassUtils.getConstructorIfAvailable(targetClass, sourceClass) != null);
|
||||
}
|
||||
|
||||
private static Method getValueOfMethodOn(Class<?> clazz, Class<?> sourceParameterType) {
|
||||
Method method = ClassUtils.getStaticMethod(clazz, "valueOf", sourceParameterType);
|
||||
private static Method getToMethod(Class<?> targetClass, Class<?> sourceClass) {
|
||||
Method method = ClassUtils.getMethodIfAvailable(sourceClass, "to" + targetClass.getSimpleName());
|
||||
return (method != null && targetClass.equals(method.getReturnType()) ? method : null);
|
||||
}
|
||||
|
||||
static Method getOfMethod(Class<?> targetClass, Class<?> sourceClass) {
|
||||
Method method = ClassUtils.getStaticMethod(targetClass, "valueOf", sourceClass);
|
||||
if (method == null) {
|
||||
method = ClassUtils.getStaticMethod(clazz, "of", sourceParameterType);
|
||||
method = ClassUtils.getStaticMethod(targetClass, "of", sourceClass);
|
||||
if (method == null) {
|
||||
method = ClassUtils.getStaticMethod(targetClass, "from", sourceClass);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private static Constructor<?> getConstructor(Class<?> clazz, Class<?> sourceParameterType) {
|
||||
return ClassUtils.getConstructorIfAvailable(clazz, sourceParameterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,17 +22,19 @@ import java.util.TimeZone;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* Simple Converter from Java 8's {@link java.time.ZoneId} to {@link java.util.TimeZone}.
|
||||
* Simple converter from Java 8's {@link java.time.ZoneId} to {@link java.util.TimeZone}.
|
||||
*
|
||||
* <p>Note that Spring's default ConversionService setup understands the 'of' convention that
|
||||
* the JSR-310 {@code java.time} package consistently uses. That convention is implemented
|
||||
* <p>Note that Spring's default ConversionService setup understands the 'from'/'to' convention
|
||||
* that the JSR-310 {@code java.time} package consistently uses. That convention is implemented
|
||||
* reflectively in {@link ObjectToObjectConverter}, not in specific JSR-310 converters.
|
||||
* It covers {@link java.util.TimeZone#toZoneId()} as well, and also
|
||||
* {@link java.util.Date#from(java.time.Instant)} and {@link java.util.Date#toInstant()}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see TimeZoneToZoneIdConverter
|
||||
* @see TimeZone#getTimeZone(java.time.ZoneId)
|
||||
*/
|
||||
class ZoneIdToTimeZoneConverter implements Converter<ZoneId, TimeZone> {
|
||||
final class ZoneIdToTimeZoneConverter implements Converter<ZoneId, TimeZone> {
|
||||
|
||||
@Override
|
||||
public TimeZone convert(ZoneId source) {
|
||||
|
||||
@@ -16,27 +16,30 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.TimeZone;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* Simple Converter from {@link java.util.TimeZone} to Java 8's {@link java.time.ZoneId}.
|
||||
* Simple converter from Java 8's {@link java.time.ZonedDateTime} to {@link java.util.Calendar}.
|
||||
*
|
||||
* <p>Note that Spring's default ConversionService setup understands the 'of' convention that
|
||||
* the JSR-310 {@code java.time} package consistently uses. That convention is implemented
|
||||
* <p>Note that Spring's default ConversionService setup understands the 'from'/'to' convention
|
||||
* that the JSR-310 {@code java.time} package consistently uses. That convention is implemented
|
||||
* reflectively in {@link ObjectToObjectConverter}, not in specific JSR-310 converters.
|
||||
* It covers {@link java.util.GregorianCalendar#toZonedDateTime()} as well, and also
|
||||
* {@link java.util.Date#from(java.time.Instant)} and {@link java.util.Date#toInstant()}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see ZoneIdToTimeZoneConverter
|
||||
* @since 4.0.1
|
||||
* @see java.util.GregorianCalendar#from(java.time.ZonedDateTime)
|
||||
*/
|
||||
class TimeZoneToZoneIdConverter implements Converter<TimeZone, ZoneId> {
|
||||
final class ZonedDateTimeToCalendarConverter implements Converter<ZonedDateTime, Calendar> {
|
||||
|
||||
@Override
|
||||
public ZoneId convert(TimeZone source) {
|
||||
return source.toZoneId();
|
||||
public Calendar convert(ZonedDateTime source) {
|
||||
return GregorianCalendar.from(source);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user