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}