/*
* Copyright 2015, The Querydsl Team (http://www.querydsl.com/team)
*
* 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 com.querydsl.sql;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.primitives.Primitives;
import com.querydsl.core.util.ReflectionUtils;
import com.querydsl.sql.types.*;
/**
* {@code JavaTypeMapping} provides a mapping from Class to Type instances
*
* @author tiwe
*
*/
class JavaTypeMapping {
private static final Type<Object> DEFAULT = new ObjectType();
private static final Map<Class<?>,Type<?>> defaultTypes = new HashMap<Class<?>,Type<?>>();
static {
registerDefault(new BigIntegerType());
registerDefault(new BigDecimalType());
registerDefault(new BlobType());
registerDefault(new BooleanType());
registerDefault(new BytesType());
registerDefault(new ByteType());
registerDefault(new CharacterType());
registerDefault(new CalendarType());
registerDefault(new ClobType());
registerDefault(new CurrencyType());
registerDefault(new DateType());
registerDefault(new DoubleType());
registerDefault(new FloatType());
registerDefault(new IntegerType());
registerDefault(new LocaleType());
registerDefault(new LongType());
registerDefault(new ObjectType());
registerDefault(new ShortType());
registerDefault(new StringType());
registerDefault(new TimestampType());
registerDefault(new TimeType());
registerDefault(new URLType());
registerDefault(new UtilDateType());
registerDefault(new UtilUUIDType(false));
// Joda time types
registerDefault(new DateTimeType());
registerDefault(new LocalDateTimeType());
registerDefault(new LocalDateType());
registerDefault(new LocalTimeType());
// initialize java time api (JSR 310) converters only if java 8 is available
try {
Class.forName("java.time.Instant");
registerDefault((Type<?>) Class.forName("com.querydsl.sql.types.JSR310InstantType").newInstance());
registerDefault((Type<?>) Class.forName("com.querydsl.sql.types.JSR310LocalDateTimeType").newInstance());
registerDefault((Type<?>) Class.forName("com.querydsl.sql.types.JSR310LocalDateType").newInstance());
registerDefault((Type<?>) Class.forName("com.querydsl.sql.types.JSR310LocalTimeType").newInstance());
registerDefault((Type<?>) Class.forName("com.querydsl.sql.types.JSR310OffsetDateTimeType").newInstance());
registerDefault((Type<?>) Class.forName("com.querydsl.sql.types.JSR310OffsetTimeType").newInstance());
registerDefault((Type<?>) Class.forName("com.querydsl.sql.types.JSR310ZonedDateTimeType").newInstance());
} catch (ClassNotFoundException e) {
// converters for JSR 310 are not loaded
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static void registerDefault(Type<?> type) {
defaultTypes.put(type.getReturnedClass(), type);
Class<?> primitive = Primitives.unwrap(type.getReturnedClass());
if (primitive != null) {
defaultTypes.put(primitive, type);
}
}
private final Map<Class<?>,Type<?>> typeByClass = new HashMap<Class<?>,Type<?>>();
private final Map<Class<?>,Type<?>> resolvedTypesByClass = new HashMap<Class<?>,Type<?>>();
private final Map<String, Map<String,Type<?>>> typeByColumn = new HashMap<String,Map<String,Type<?>>>();
@Nullable
public Type<?> getType(String table, String column) {
Map<String,Type<?>> columns = typeByColumn.get(table);
if (columns != null) {
return columns.get(column);
} else {
return null;
}
}
@SuppressWarnings("unchecked")
public <T> Type<T> getType(Class<T> clazz) {
Type<?> resolvedType = resolvedTypesByClass.get(clazz);
if (resolvedType == null) {
resolvedType = findType(clazz);
if (resolvedType != null) {
resolvedTypesByClass.put(clazz, resolvedType);
} else {
return (Type) DEFAULT;
}
}
return (Type<T>) resolvedType;
}
@Nullable
private Type<?> findType(Class<?> clazz) {
//Look for a registered type in the class hierarchy
Class<?> cl = clazz;
do {
if (typeByClass.containsKey(cl)) {
return typeByClass.get(cl);
} else if (defaultTypes.containsKey(cl)) {
return defaultTypes.get(cl);
}
cl = cl.getSuperclass();
} while (!cl.equals(Object.class));
//Look for a registered type in any implemented interfaces
Set<Class<?>> interfaces = ReflectionUtils.getImplementedInterfaces(clazz);
for (Class<?> itf : interfaces) {
if (typeByClass.containsKey(itf)) {
return typeByClass.get(itf);
} else if (defaultTypes.containsKey(itf)) {
return defaultTypes.get(itf);
}
}
return null;
}
public void register(Type<?> type) {
typeByClass.put(type.getReturnedClass(), type);
Class<?> primitive = Primitives.unwrap(type.getReturnedClass());
if (primitive != null) {
typeByClass.put(primitive, type);
}
// Clear previous resolved types, so they won't impact future lookups
resolvedTypesByClass.clear();
}
public void setType(String table, String column, Type<?> type) {
Map<String,Type<?>> columns = typeByColumn.get(table);
if (columns == null) {
columns = new HashMap<String, Type<?>>();
typeByColumn.put(table, columns);
}
columns.put(column, type);
}
}