001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.convert.support;
018
019import java.nio.charset.Charset;
020import java.util.Currency;
021import java.util.Locale;
022import java.util.UUID;
023
024import org.springframework.core.convert.ConversionService;
025import org.springframework.core.convert.converter.ConverterRegistry;
026import org.springframework.util.ClassUtils;
027
028/**
029 * A specialization of {@link GenericConversionService} configured by default
030 * with converters appropriate for most environments.
031 *
032 * <p>Designed for direct instantiation but also exposes the static
033 * {@link #addDefaultConverters(ConverterRegistry)} utility method for ad-hoc
034 * use against any {@code ConverterRegistry} instance.
035 *
036 * @author Chris Beams
037 * @author Juergen Hoeller
038 * @author Stephane Nicoll
039 * @since 3.1
040 */
041public class DefaultConversionService extends GenericConversionService {
042
043        /** Java 8's java.util.Optional class available? */
044        private static final boolean javaUtilOptionalClassAvailable =
045                        ClassUtils.isPresent("java.util.Optional", DefaultConversionService.class.getClassLoader());
046
047        /** Java 8's java.time package available? */
048        private static final boolean jsr310Available =
049                        ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader());
050
051        /** Java 8's java.util.stream.Stream class available? */
052        private static final boolean streamAvailable = ClassUtils.isPresent(
053                        "java.util.stream.Stream", DefaultConversionService.class.getClassLoader());
054
055        private static volatile DefaultConversionService sharedInstance;
056
057
058        /**
059         * Create a new {@code DefaultConversionService} with the set of
060         * {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
061         */
062        public DefaultConversionService() {
063                addDefaultConverters(this);
064        }
065
066
067        /**
068         * Return a shared default {@code ConversionService} instance,
069         * lazily building it once needed.
070         * <p><b>NOTE:</b> We highly recommend constructing individual
071         * {@code ConversionService} instances for customization purposes.
072         * This accessor is only meant as a fallback for code paths which
073         * need simple type coercion but cannot access a longer-lived
074         * {@code ConversionService} instance any other way.
075         * @return the shared {@code ConversionService} instance (never {@code null})
076         * @since 4.3.5
077         */
078        public static ConversionService getSharedInstance() {
079                if (sharedInstance == null) {
080                        synchronized (DefaultConversionService.class) {
081                                if (sharedInstance == null) {
082                                        sharedInstance = new DefaultConversionService();
083                                }
084                        }
085                }
086                return sharedInstance;
087        }
088
089        /**
090         * Add converters appropriate for most environments.
091         * @param converterRegistry the registry of converters to add to
092         * (must also be castable to ConversionService, e.g. being a {@link ConfigurableConversionService})
093         * @throws ClassCastException if the given ConverterRegistry could not be cast to a ConversionService
094         */
095        public static void addDefaultConverters(ConverterRegistry converterRegistry) {
096                addScalarConverters(converterRegistry);
097                addCollectionConverters(converterRegistry);
098
099                converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
100                if (jsr310Available) {
101                        Jsr310ConverterRegistrar.registerJsr310Converters(converterRegistry);
102                }
103
104                converterRegistry.addConverter(new ObjectToObjectConverter());
105                converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
106                converterRegistry.addConverter(new FallbackObjectToStringConverter());
107                if (javaUtilOptionalClassAvailable) {
108                        converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
109                }
110        }
111
112        /**
113         * Add common collection converters.
114         * @param converterRegistry the registry of converters to add to
115         * (must also be castable to ConversionService, e.g. being a {@link ConfigurableConversionService})
116         * @throws ClassCastException if the given ConverterRegistry could not be cast to a ConversionService
117         * @since 4.2.3
118         */
119        public static void addCollectionConverters(ConverterRegistry converterRegistry) {
120                ConversionService conversionService = (ConversionService) converterRegistry;
121
122                converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
123                converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
124
125                converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
126                converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
127                converterRegistry.addConverter(new MapToMapConverter(conversionService));
128
129                converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
130                converterRegistry.addConverter(new StringToArrayConverter(conversionService));
131
132                converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
133                converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
134
135                converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
136                converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
137
138                converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
139                converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
140
141                if (streamAvailable) {
142                        converterRegistry.addConverter(new StreamConverter(conversionService));
143                }
144        }
145
146        private static void addScalarConverters(ConverterRegistry converterRegistry) {
147                converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
148
149                converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
150                converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
151
152                converterRegistry.addConverter(new StringToCharacterConverter());
153                converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
154
155                converterRegistry.addConverter(new NumberToCharacterConverter());
156                converterRegistry.addConverterFactory(new CharacterToNumberFactory());
157
158                converterRegistry.addConverter(new StringToBooleanConverter());
159                converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
160
161                converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
162                converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
163
164                converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
165                converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
166
167                converterRegistry.addConverter(new StringToLocaleConverter());
168                converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
169
170                converterRegistry.addConverter(new StringToCharsetConverter());
171                converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
172
173                converterRegistry.addConverter(new StringToCurrencyConverter());
174                converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
175
176                converterRegistry.addConverter(new StringToPropertiesConverter());
177                converterRegistry.addConverter(new PropertiesToStringConverter());
178
179                converterRegistry.addConverter(new StringToUUIDConverter());
180                converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
181        }
182
183
184        /**
185         * Inner class to avoid a hard-coded dependency on Java 8's {@code java.time} package.
186         */
187        private static final class Jsr310ConverterRegistrar {
188
189                public static void registerJsr310Converters(ConverterRegistry converterRegistry) {
190                        converterRegistry.addConverter(new StringToTimeZoneConverter());
191                        converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
192                        converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
193                }
194        }
195
196}