View Javadoc
1   /**
2    *    Copyright 2009-2015 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.type;
17  
18  import java.lang.reflect.Constructor;
19  import java.lang.reflect.Modifier;
20  import java.lang.reflect.Type;
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Date;
26  import java.util.EnumMap;
27  import java.util.HashMap;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.ibatis.io.ResolverUtil;
32  
33  /**
34   * @author Clinton Begin
35   */
36  public final class TypeHandlerRegistry {
37  
38    private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
39    private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
40    private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
41    private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
42  
43    public TypeHandlerRegistry() {
44      register(Boolean.class, new BooleanTypeHandler());
45      register(boolean.class, new BooleanTypeHandler());
46      register(JdbcType.BOOLEAN, new BooleanTypeHandler());
47      register(JdbcType.BIT, new BooleanTypeHandler());
48  
49      register(Byte.class, new ByteTypeHandler());
50      register(byte.class, new ByteTypeHandler());
51      register(JdbcType.TINYINT, new ByteTypeHandler());
52  
53      register(Short.class, new ShortTypeHandler());
54      register(short.class, new ShortTypeHandler());
55      register(JdbcType.SMALLINT, new ShortTypeHandler());
56  
57      register(Integer.class, new IntegerTypeHandler());
58      register(int.class, new IntegerTypeHandler());
59      register(JdbcType.INTEGER, new IntegerTypeHandler());
60  
61      register(Long.class, new LongTypeHandler());
62      register(long.class, new LongTypeHandler());
63  
64      register(Float.class, new FloatTypeHandler());
65      register(float.class, new FloatTypeHandler());
66      register(JdbcType.FLOAT, new FloatTypeHandler());
67  
68      register(Double.class, new DoubleTypeHandler());
69      register(double.class, new DoubleTypeHandler());
70      register(JdbcType.DOUBLE, new DoubleTypeHandler());
71  
72      register(String.class, new StringTypeHandler());
73      register(String.class, JdbcType.CHAR, new StringTypeHandler());
74      register(String.class, JdbcType.CLOB, new ClobTypeHandler());
75      register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
76      register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
77      register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
78      register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
79      register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
80      register(JdbcType.CHAR, new StringTypeHandler());
81      register(JdbcType.VARCHAR, new StringTypeHandler());
82      register(JdbcType.CLOB, new ClobTypeHandler());
83      register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
84      register(JdbcType.NVARCHAR, new NStringTypeHandler());
85      register(JdbcType.NCHAR, new NStringTypeHandler());
86      register(JdbcType.NCLOB, new NClobTypeHandler());
87  
88      register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
89      register(JdbcType.ARRAY, new ArrayTypeHandler());
90  
91      register(BigInteger.class, new BigIntegerTypeHandler());
92      register(JdbcType.BIGINT, new LongTypeHandler());
93  
94      register(BigDecimal.class, new BigDecimalTypeHandler());
95      register(JdbcType.REAL, new BigDecimalTypeHandler());
96      register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
97      register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
98  
99      register(Byte[].class, new ByteObjectArrayTypeHandler());
100     register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
101     register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
102     register(byte[].class, new ByteArrayTypeHandler());
103     register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
104     register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
105     register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
106     register(JdbcType.BLOB, new BlobTypeHandler());
107 
108     register(Object.class, UNKNOWN_TYPE_HANDLER);
109     register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
110     register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
111 
112     register(Date.class, new DateTypeHandler());
113     register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
114     register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
115     register(JdbcType.TIMESTAMP, new DateTypeHandler());
116     register(JdbcType.DATE, new DateOnlyTypeHandler());
117     register(JdbcType.TIME, new TimeOnlyTypeHandler());
118 
119     register(java.sql.Date.class, new SqlDateTypeHandler());
120     register(java.sql.Time.class, new SqlTimeTypeHandler());
121     register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
122 
123     // issue #273
124     register(Character.class, new CharacterTypeHandler());
125     register(char.class, new CharacterTypeHandler());
126   }
127 
128   public boolean hasTypeHandler(Class<?> javaType) {
129     return hasTypeHandler(javaType, null);
130   }
131 
132   public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
133     return hasTypeHandler(javaTypeReference, null);
134   }
135 
136   public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
137     return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
138   }
139 
140   public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
141     return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
142   }
143 
144   public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
145     return ALL_TYPE_HANDLERS_MAP.get(handlerType);
146   }
147 
148   public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
149     return getTypeHandler((Type) type, null);
150   }
151 
152   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
153     return getTypeHandler(javaTypeReference, null);
154   }
155 
156   public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
157     return JDBC_TYPE_HANDLER_MAP.get(jdbcType);
158   }
159 
160   public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
161     return getTypeHandler((Type) type, jdbcType);
162   }
163 
164   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
165     return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
166   }
167 
168   @SuppressWarnings("unchecked")
169   private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
170     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
171     TypeHandler<?> handler = null;
172     if (jdbcHandlerMap != null) {
173       handler = jdbcHandlerMap.get(jdbcType);
174       if (handler == null) {
175         handler = jdbcHandlerMap.get(null);
176       }
177     }
178     if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
179       handler = new EnumTypeHandler((Class<?>) type);
180     }
181     // type drives generics here
182     return (TypeHandler<T>) handler;
183   }
184 
185   public TypeHandler<Object> getUnknownTypeHandler() {
186     return UNKNOWN_TYPE_HANDLER;
187   }
188 
189   public void register(JdbcType jdbcType, TypeHandler<?> handler) {
190     JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
191   }
192 
193   //
194   // REGISTER INSTANCE
195   //
196 
197   // Only handler
198 
199   @SuppressWarnings("unchecked")
200   public <T> void register(TypeHandler<T> typeHandler) {
201     boolean mappedTypeFound = false;
202     MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
203     if (mappedTypes != null) {
204       for (Class<?> handledType : mappedTypes.value()) {
205         register(handledType, typeHandler);
206         mappedTypeFound = true;
207       }
208     }
209     // @since 3.1.0 - try to auto-discover the mapped type
210     if (!mappedTypeFound && typeHandler instanceof TypeReference) {
211       try {
212         TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
213         register(typeReference.getRawType(), typeHandler);
214         mappedTypeFound = true;
215       } catch (Throwable t) {
216         // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
217       }
218     }
219     if (!mappedTypeFound) {
220       register((Class<T>) null, typeHandler);
221     }
222   }
223 
224   // java type + handler
225 
226   public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
227     register((Type) javaType, typeHandler);
228   }
229 
230   private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
231     MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
232     if (mappedJdbcTypes != null) {
233       for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
234         register(javaType, handledJdbcType, typeHandler);
235       }
236       if (mappedJdbcTypes.includeNullJdbcType()) {
237         register(javaType, null, typeHandler);
238       }
239     } else {
240       register(javaType, null, typeHandler);
241     }
242   }
243 
244   public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
245     register(javaTypeReference.getRawType(), handler);
246   }
247 
248   // java type + jdbc type + handler
249 
250   public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
251     register((Type) type, jdbcType, handler);
252   }
253 
254   private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
255     if (javaType != null) {
256       Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
257       if (map == null) {
258         map = new HashMap<JdbcType, TypeHandler<?>>();
259         TYPE_HANDLER_MAP.put(javaType, map);
260       }
261       map.put(jdbcType, handler);
262     }
263     ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
264   }
265 
266   //
267   // REGISTER CLASS
268   //
269 
270   // Only handler type
271 
272   public void register(Class<?> typeHandlerClass) {
273     boolean mappedTypeFound = false;
274     MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
275     if (mappedTypes != null) {
276       for (Class<?> javaTypeClass : mappedTypes.value()) {
277         register(javaTypeClass, typeHandlerClass);
278         mappedTypeFound = true;
279       }
280     }
281     if (!mappedTypeFound) {
282       register(getInstance(null, typeHandlerClass));
283     }
284   }
285 
286   // java type + handler type
287 
288   public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
289     register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
290   }
291 
292   // java type + jdbc type + handler type
293 
294   public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
295     register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
296   }
297 
298   // Construct a handler (used also from Builders)
299 
300   @SuppressWarnings("unchecked")
301   public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
302     if (javaTypeClass != null) {
303       try {
304         Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
305         return (TypeHandler<T>) c.newInstance(javaTypeClass);
306       } catch (NoSuchMethodException ignored) {
307         // ignored
308       } catch (Exception e) {
309         throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
310       }
311     }
312     try {
313       Constructor<?> c = typeHandlerClass.getConstructor();
314       return (TypeHandler<T>) c.newInstance();
315     } catch (Exception e) {
316       throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
317     }
318   }
319 
320   // scan
321 
322   public void register(String packageName) {
323     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
324     resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
325     Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
326     for (Class<?> type : handlerSet) {
327       //Ignore inner classes and interfaces (including package-info.java) and abstract classes
328       if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
329         register(type);
330       }
331     }
332   }
333   
334   // get information
335   
336   /**
337    * @since 3.2.2
338    */
339   public Collection<TypeHandler<?>> getTypeHandlers() {
340     return Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values());
341   }
342   
343 }