Comprehensive update to the framework's TimeZone handling, including a new TimeZoneAwareLocaleContext and a LocaleContextResolver for Spring MVC
A few noteworthy minor changes: LocaleContext.getLocale() may return null in special cases (not by default), which our own accessing classes are able to handle now. If there is a non-null TimeZone user setting, we're exposing it to all collaborating libraries, in particular to JSTL, Velocity and JasperReports. Our JSR-310 and Joda-Time support falls back to checking the general LocaleContext TimeZone now, adapting it to their time zone types, if no more specific setting has been provided. Our DefaultConversionService has TimeZone<->ZoneId converters registered. And finally, we're using a custom parseTimeZoneString method now that doesn't accept the TimeZone.getTimeZone(String) GMT fallback for an invalid time zone id anymore. Issue: SPR-1528
This commit is contained in:
@@ -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.
|
||||
@@ -21,6 +21,7 @@ import java.util.UUID;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A specialization of {@link GenericConversionService} configured by default with
|
||||
@@ -31,10 +32,16 @@ import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
* {@code ConverterRegistry} instance.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DefaultConversionService extends GenericConversionService {
|
||||
|
||||
/** Java 8's java.time package available? */
|
||||
private static final boolean zoneIdAvailable =
|
||||
ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader());
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultConversionService} with the set of
|
||||
* {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
|
||||
@@ -43,46 +50,55 @@ public class DefaultConversionService extends GenericConversionService {
|
||||
addDefaultConverters(this);
|
||||
}
|
||||
|
||||
|
||||
// static utility methods
|
||||
|
||||
/**
|
||||
* Add converters appropriate for most environments.
|
||||
* @param converterRegistry the registry of converters to add to (must also be castable to ConversionService)
|
||||
* @throws ClassCastException if the converterRegistry could not be cast to a ConversionService
|
||||
* @param converterRegistry the registry of converters to add to (must also be castable to ConversionService,
|
||||
* e.g. being a {@link ConfigurableConversionService})
|
||||
* @throws ClassCastException if the given ConverterRegistry could not be cast to a ConversionService
|
||||
*/
|
||||
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
|
||||
addScalarConverters(converterRegistry);
|
||||
addCollectionConverters(converterRegistry);
|
||||
addBinaryConverters(converterRegistry);
|
||||
addFallbackConverters(converterRegistry);
|
||||
|
||||
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
|
||||
if (zoneIdAvailable) {
|
||||
ZoneIdConverterRegistrar.registerZoneIdConverters(converterRegistry);
|
||||
}
|
||||
|
||||
converterRegistry.addConverter(new ObjectToObjectConverter());
|
||||
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
|
||||
converterRegistry.addConverter(new FallbackObjectToStringConverter());
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private static void addScalarConverters(ConverterRegistry converterRegistry) {
|
||||
ConversionService conversionService = (ConversionService) converterRegistry;
|
||||
converterRegistry.addConverter(new StringToBooleanConverter());
|
||||
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
|
||||
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
|
||||
|
||||
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
|
||||
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
|
||||
|
||||
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
|
||||
|
||||
converterRegistry.addConverter(new StringToCharacterConverter());
|
||||
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
|
||||
|
||||
converterRegistry.addConverter(new NumberToCharacterConverter());
|
||||
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
|
||||
|
||||
converterRegistry.addConverter(new StringToBooleanConverter());
|
||||
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
|
||||
|
||||
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
|
||||
converterRegistry.addConverter(Enum.class, String.class, new EnumToStringConverter(conversionService));
|
||||
converterRegistry.addConverter(Enum.class, String.class,
|
||||
new EnumToStringConverter((ConversionService) converterRegistry));
|
||||
|
||||
converterRegistry.addConverter(new StringToLocaleConverter());
|
||||
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
|
||||
|
||||
converterRegistry.addConverter(new PropertiesToStringConverter());
|
||||
converterRegistry.addConverter(new StringToPropertiesConverter());
|
||||
converterRegistry.addConverter(new PropertiesToStringConverter());
|
||||
|
||||
converterRegistry.addConverter(new StringToUUIDConverter());
|
||||
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
|
||||
@@ -90,6 +106,7 @@ public class DefaultConversionService extends GenericConversionService {
|
||||
|
||||
private static void addCollectionConverters(ConverterRegistry converterRegistry) {
|
||||
ConversionService conversionService = (ConversionService) converterRegistry;
|
||||
|
||||
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
|
||||
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
|
||||
|
||||
@@ -110,16 +127,16 @@ public class DefaultConversionService extends GenericConversionService {
|
||||
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
|
||||
}
|
||||
|
||||
private static void addBinaryConverters(ConverterRegistry converterRegistry) {
|
||||
ConversionService conversionService = (ConversionService) converterRegistry;
|
||||
converterRegistry.addConverter(new ByteBufferConverter(conversionService));
|
||||
}
|
||||
|
||||
private static void addFallbackConverters(ConverterRegistry converterRegistry) {
|
||||
ConversionService conversionService = (ConversionService) converterRegistry;
|
||||
converterRegistry.addConverter(new ObjectToObjectConverter());
|
||||
converterRegistry.addConverter(new IdToEntityConverter(conversionService));
|
||||
converterRegistry.addConverter(new FallbackObjectToStringConverter());
|
||||
/**
|
||||
* Inner class to avoid a hard-coded dependency on Java 8's {@link java.time.ZoneId}.
|
||||
*/
|
||||
private static final class ZoneIdConverterRegistrar {
|
||||
|
||||
public static void registerZoneIdConverters(ConverterRegistry converterRegistry) {
|
||||
converterRegistry.addConverter(new TimeZoneToZoneIdConverter());
|
||||
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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 java.time.ZoneId;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* Simple Converter from {@link java.util.TimeZone} to Java 8's {@link java.time.ZoneId}.
|
||||
*
|
||||
* <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
|
||||
* reflectively in {@link ObjectToObjectConverter}, not in specific JSR-310 converters.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see ZoneIdToTimeZoneConverter
|
||||
*/
|
||||
class TimeZoneToZoneIdConverter implements Converter<TimeZone, ZoneId> {
|
||||
|
||||
@Override
|
||||
public ZoneId convert(TimeZone source) {
|
||||
return source.toZoneId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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 java.time.ZoneId;
|
||||
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}.
|
||||
*
|
||||
* <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
|
||||
* reflectively in {@link ObjectToObjectConverter}, not in specific JSR-310 converters.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see TimeZoneToZoneIdConverter
|
||||
*/
|
||||
class ZoneIdToTimeZoneConverter implements Converter<ZoneId, TimeZone> {
|
||||
|
||||
@Override
|
||||
public TimeZone convert(ZoneId source) {
|
||||
return TimeZone.getTimeZone(source);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
@@ -28,6 +28,7 @@ import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
@@ -674,10 +675,11 @@ public abstract class StringUtils {
|
||||
/**
|
||||
* Parse the given {@code localeString} value into a {@link Locale}.
|
||||
* <p>This is the inverse operation of {@link Locale#toString Locale's toString}.
|
||||
* @param localeString the locale string, following {@code Locale's}
|
||||
* @param localeString the locale String, following {@code Locale's}
|
||||
* {@code toString()} format ("en", "en_UK", etc);
|
||||
* also accepts spaces as separators, as an alternative to underscores
|
||||
* @return a corresponding {@code Locale} instance
|
||||
* @throws IllegalArgumentException in case of an invalid locale specification
|
||||
*/
|
||||
public static Locale parseLocaleString(String localeString) {
|
||||
String[] parts = tokenizeToStringArray(localeString, "_ ", false, false);
|
||||
@@ -719,6 +721,22 @@ public abstract class StringUtils {
|
||||
return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given {@code timeZoneString} value into a {@link TimeZone}.
|
||||
* @param timeZoneString the time zone String, following {@link TimeZone#getTimeZone(String)}
|
||||
* but throwing {@link IllegalArgumentException} in case of an invalid time zone specification
|
||||
* @return a corresponding {@link TimeZone} instance
|
||||
* @throws IllegalArgumentException in case of an invalid time zone specification
|
||||
*/
|
||||
public static TimeZone parseTimeZoneString(String timeZoneString) {
|
||||
TimeZone timeZone = TimeZone.getTimeZone(timeZoneString);
|
||||
if ("GMT".equals(timeZone.getID()) && !timeZoneString.startsWith("GMT")) {
|
||||
// We don't want that GMT fallback...
|
||||
throw new IllegalArgumentException("Invalid time zone specification '" + timeZoneString + "'");
|
||||
}
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Convenience methods for working with String arrays
|
||||
|
||||
Reference in New Issue
Block a user