1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.apache.ibatis.type;
17  
18  import java.io.InputStream;
19  import java.io.Reader;
20  import java.lang.reflect.Constructor;
21  import java.lang.reflect.Modifier;
22  import java.lang.reflect.Type;
23  import java.math.BigDecimal;
24  import java.math.BigInteger;
25  import java.time.Instant;
26  import java.time.LocalDate;
27  import java.time.LocalDateTime;
28  import java.time.LocalTime;
29  import java.time.Month;
30  import java.time.OffsetDateTime;
31  import java.time.OffsetTime;
32  import java.time.Year;
33  import java.time.YearMonth;
34  import java.time.ZonedDateTime;
35  import java.time.chrono.JapaneseDate;
36  import java.util.Collection;
37  import java.util.Collections;
38  import java.util.Date;
39  import java.util.EnumMap;
40  import java.util.HashMap;
41  import java.util.Map;
42  import java.util.Map.Entry;
43  import java.util.Set;
44  import java.util.concurrent.ConcurrentHashMap;
45  
46  import org.apache.ibatis.binding.MapperMethod.ParamMap;
47  import org.apache.ibatis.io.ResolverUtil;
48  import org.apache.ibatis.io.Resources;
49  import org.apache.ibatis.session.Configuration;
50  
51  
52  
53  
54  
55  public final class TypeHandlerRegistry {
56  
57    private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
58    private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
59    private final TypeHandler<Object> unknownTypeHandler;
60    private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
61  
62    private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
63  
64    private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
65  
66    
67  
68  
69    public TypeHandlerRegistry() {
70      this(new Configuration());
71    }
72  
73    
74  
75  
76  
77  
78  
79    public TypeHandlerRegistry(Configuration configuration) {
80      this.unknownTypeHandler = new UnknownTypeHandler(configuration);
81  
82      register(Boolean.class, new BooleanTypeHandler());
83      register(boolean.class, new BooleanTypeHandler());
84      register(JdbcType.BOOLEAN, new BooleanTypeHandler());
85      register(JdbcType.BIT, new BooleanTypeHandler());
86  
87      register(Byte.class, new ByteTypeHandler());
88      register(byte.class, new ByteTypeHandler());
89      register(JdbcType.TINYINT, new ByteTypeHandler());
90  
91      register(Short.class, new ShortTypeHandler());
92      register(short.class, new ShortTypeHandler());
93      register(JdbcType.SMALLINT, new ShortTypeHandler());
94  
95      register(Integer.class, new IntegerTypeHandler());
96      register(int.class, new IntegerTypeHandler());
97      register(JdbcType.INTEGER, new IntegerTypeHandler());
98  
99      register(Long.class, new LongTypeHandler());
100     register(long.class, new LongTypeHandler());
101 
102     register(Float.class, new FloatTypeHandler());
103     register(float.class, new FloatTypeHandler());
104     register(JdbcType.FLOAT, new FloatTypeHandler());
105 
106     register(Double.class, new DoubleTypeHandler());
107     register(double.class, new DoubleTypeHandler());
108     register(JdbcType.DOUBLE, new DoubleTypeHandler());
109 
110     register(Reader.class, new ClobReaderTypeHandler());
111     register(String.class, new StringTypeHandler());
112     register(String.class, JdbcType.CHAR, new StringTypeHandler());
113     register(String.class, JdbcType.CLOB, new ClobTypeHandler());
114     register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
115     register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
116     register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
117     register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
118     register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
119     register(JdbcType.CHAR, new StringTypeHandler());
120     register(JdbcType.VARCHAR, new StringTypeHandler());
121     register(JdbcType.CLOB, new ClobTypeHandler());
122     register(JdbcType.LONGVARCHAR, new StringTypeHandler());
123     register(JdbcType.NVARCHAR, new NStringTypeHandler());
124     register(JdbcType.NCHAR, new NStringTypeHandler());
125     register(JdbcType.NCLOB, new NClobTypeHandler());
126 
127     register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
128     register(JdbcType.ARRAY, new ArrayTypeHandler());
129 
130     register(BigInteger.class, new BigIntegerTypeHandler());
131     register(JdbcType.BIGINT, new LongTypeHandler());
132 
133     register(BigDecimal.class, new BigDecimalTypeHandler());
134     register(JdbcType.REAL, new BigDecimalTypeHandler());
135     register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
136     register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
137 
138     register(InputStream.class, new BlobInputStreamTypeHandler());
139     register(Byte[].class, new ByteObjectArrayTypeHandler());
140     register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
141     register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
142     register(byte[].class, new ByteArrayTypeHandler());
143     register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
144     register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
145     register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
146     register(JdbcType.BLOB, new BlobTypeHandler());
147 
148     register(Object.class, unknownTypeHandler);
149     register(Object.class, JdbcType.OTHER, unknownTypeHandler);
150     register(JdbcType.OTHER, unknownTypeHandler);
151 
152     register(Date.class, new DateTypeHandler());
153     register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
154     register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
155     register(JdbcType.TIMESTAMP, new DateTypeHandler());
156     register(JdbcType.DATE, new DateOnlyTypeHandler());
157     register(JdbcType.TIME, new TimeOnlyTypeHandler());
158 
159     register(java.sql.Date.class, new SqlDateTypeHandler());
160     register(java.sql.Time.class, new SqlTimeTypeHandler());
161     register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
162 
163     register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
164 
165     register(Instant.class, new InstantTypeHandler());
166     register(LocalDateTime.class, new LocalDateTimeTypeHandler());
167     register(LocalDate.class, new LocalDateTypeHandler());
168     register(LocalTime.class, new LocalTimeTypeHandler());
169     register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
170     register(OffsetTime.class, new OffsetTimeTypeHandler());
171     register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
172     register(Month.class, new MonthTypeHandler());
173     register(Year.class, new YearTypeHandler());
174     register(YearMonth.class, new YearMonthTypeHandler());
175     register(JapaneseDate.class, new JapaneseDateTypeHandler());
176 
177     
178     register(Character.class, new CharacterTypeHandler());
179     register(char.class, new CharacterTypeHandler());
180   }
181 
182   
183 
184 
185 
186 
187 
188   public void setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler) {
189     this.defaultEnumTypeHandler = typeHandler;
190   }
191 
192   public boolean hasTypeHandler(Class<?> javaType) {
193     return hasTypeHandler(javaType, null);
194   }
195 
196   public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
197     return hasTypeHandler(javaTypeReference, null);
198   }
199 
200   public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
201     return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
202   }
203 
204   public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
205     return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
206   }
207 
208   public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
209     return allTypeHandlersMap.get(handlerType);
210   }
211 
212   public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
213     return getTypeHandler((Type) type, null);
214   }
215 
216   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
217     return getTypeHandler(javaTypeReference, null);
218   }
219 
220   public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
221     return jdbcTypeHandlerMap.get(jdbcType);
222   }
223 
224   public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
225     return getTypeHandler((Type) type, jdbcType);
226   }
227 
228   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
229     return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
230   }
231 
232   @SuppressWarnings("unchecked")
233   private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
234     if (ParamMap.class.equals(type)) {
235       return null;
236     }
237     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
238     TypeHandler<?> handler = null;
239     if (jdbcHandlerMap != null) {
240       handler = jdbcHandlerMap.get(jdbcType);
241       if (handler == null) {
242         handler = jdbcHandlerMap.get(null);
243       }
244       if (handler == null) {
245         
246         handler = pickSoleHandler(jdbcHandlerMap);
247       }
248     }
249     
250     return (TypeHandler<T>) handler;
251   }
252 
253   private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
254     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
255     if (jdbcHandlerMap != null) {
256       return NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap) ? null : jdbcHandlerMap;
257     }
258     if (type instanceof Class) {
259       Class<?> clazz = (Class<?>) type;
260       if (Enum.class.isAssignableFrom(clazz)) {
261         Class<?> enumClass = clazz.isAnonymousClass() ? clazz.getSuperclass() : clazz;
262         jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(enumClass, enumClass);
263         if (jdbcHandlerMap == null) {
264           register(enumClass, getInstance(enumClass, defaultEnumTypeHandler));
265           return typeHandlerMap.get(enumClass);
266         }
267       } else {
268         jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
269       }
270     }
271     typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
272     return jdbcHandlerMap;
273   }
274 
275   private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
276     for (Class<?> iface : clazz.getInterfaces()) {
277       Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(iface);
278       if (jdbcHandlerMap == null) {
279         jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
280       }
281       if (jdbcHandlerMap != null) {
282         
283         HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<>();
284         for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
285           
286           newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
287         }
288         return newMap;
289       }
290     }
291     return null;
292   }
293 
294   private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
295     Class<?> superclass = clazz.getSuperclass();
296     if (superclass == null || Object.class.equals(superclass)) {
297       return null;
298     }
299     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);
300     if (jdbcHandlerMap != null) {
301       return jdbcHandlerMap;
302     } else {
303       return getJdbcHandlerMapForSuperclass(superclass);
304     }
305   }
306 
307   private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
308     TypeHandler<?> soleHandler = null;
309     for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
310       if (soleHandler == null) {
311         soleHandler = handler;
312       } else if (!handler.getClass().equals(soleHandler.getClass())) {
313         
314         return null;
315       }
316     }
317     return soleHandler;
318   }
319 
320   public TypeHandler<Object> getUnknownTypeHandler() {
321     return unknownTypeHandler;
322   }
323 
324   public void register(JdbcType jdbcType, TypeHandler<?> handler) {
325     jdbcTypeHandlerMap.put(jdbcType, handler);
326   }
327 
328   
329   
330   
331 
332   
333 
334   @SuppressWarnings("unchecked")
335   public <T> void register(TypeHandler<T> typeHandler) {
336     boolean mappedTypeFound = false;
337     MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
338     if (mappedTypes != null) {
339       for (Class<?> handledType : mappedTypes.value()) {
340         register(handledType, typeHandler);
341         mappedTypeFound = true;
342       }
343     }
344     
345     if (!mappedTypeFound && typeHandler instanceof TypeReference) {
346       try {
347         TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
348         register(typeReference.getRawType(), typeHandler);
349         mappedTypeFound = true;
350       } catch (Throwable t) {
351         
352       }
353     }
354     if (!mappedTypeFound) {
355       register((Class<T>) null, typeHandler);
356     }
357   }
358 
359   
360 
361   public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
362     register((Type) javaType, typeHandler);
363   }
364 
365   private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
366     MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
367     if (mappedJdbcTypes != null) {
368       for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
369         register(javaType, handledJdbcType, typeHandler);
370       }
371       if (mappedJdbcTypes.includeNullJdbcType()) {
372         register(javaType, null, typeHandler);
373       }
374     } else {
375       register(javaType, null, typeHandler);
376     }
377   }
378 
379   public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
380     register(javaTypeReference.getRawType(), handler);
381   }
382 
383   
384 
385   
386   @SuppressWarnings("cast")
387   public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
388     register((Type) type, jdbcType, handler);
389   }
390 
391   private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
392     if (javaType != null) {
393       Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
394       if (map == null || map == NULL_TYPE_HANDLER_MAP) {
395         map = new HashMap<>();
396       }
397       map.put(jdbcType, handler);
398       typeHandlerMap.put(javaType, map);
399     }
400     allTypeHandlersMap.put(handler.getClass(), handler);
401   }
402 
403   
404   
405   
406 
407   
408 
409   public void register(Class<?> typeHandlerClass) {
410     boolean mappedTypeFound = false;
411     MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
412     if (mappedTypes != null) {
413       for (Class<?> javaTypeClass : mappedTypes.value()) {
414         register(javaTypeClass, typeHandlerClass);
415         mappedTypeFound = true;
416       }
417     }
418     if (!mappedTypeFound) {
419       register(getInstance(null, typeHandlerClass));
420     }
421   }
422 
423   
424 
425   public void register(String javaTypeClassName, String typeHandlerClassName) throws ClassNotFoundException {
426     register(Resources.classForName(javaTypeClassName), Resources.classForName(typeHandlerClassName));
427   }
428 
429   public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
430     register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
431   }
432 
433   
434 
435   public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
436     register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
437   }
438 
439   
440 
441   @SuppressWarnings("unchecked")
442   public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
443     if (javaTypeClass != null) {
444       try {
445         Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
446         return (TypeHandler<T>) c.newInstance(javaTypeClass);
447       } catch (NoSuchMethodException ignored) {
448         
449       } catch (Exception e) {
450         throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
451       }
452     }
453     try {
454       Constructor<?> c = typeHandlerClass.getConstructor();
455       return (TypeHandler<T>) c.newInstance();
456     } catch (Exception e) {
457       throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
458     }
459   }
460 
461   
462 
463   public void register(String packageName) {
464     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
465     resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
466     Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
467     for (Class<?> type : handlerSet) {
468       
469       if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
470         register(type);
471       }
472     }
473   }
474 
475   
476 
477   
478 
479 
480 
481 
482 
483   public Collection<TypeHandler<?>> getTypeHandlers() {
484     return Collections.unmodifiableCollection(allTypeHandlersMap.values());
485   }
486 
487 }