/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.core.types;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.*;
import org.teiid.core.CorePlugin;
import org.teiid.core.types.basic.*;
import org.teiid.core.util.ArgCheck;
import org.teiid.core.util.HashCodeUtil;
import org.teiid.core.util.PropertiesUtils;
/**
* <p>
* This class manages data type, conversions between data types, and comparators
* for data types. In the future other data type information may be managed
* here.
* </p>
*
* <p>
* In general, methods are provided to refer to types either by Class, or by
* Class name. The benefit of the Class name option is that the user does not
* need to load the Class object, which may not be in the classpath. The
* advantage of the Class option is speed.
* </p>
*
* TODO: refactor the string/class/code into an enum
*/
public class DataTypeManager {
static final String ARRAY_SUFFIX = "[]"; //$NON-NLS-1$
public static final boolean USE_VALUE_CACHE = PropertiesUtils.getBooleanProperty(System.getProperties(), "org.teiid.useValueCache", false); //$NON-NLS-1$
private static final boolean COMPARABLE_LOBS = PropertiesUtils.getBooleanProperty(System.getProperties(), "org.teiid.comparableLobs", false); //$NON-NLS-1$
private static final boolean COMPARABLE_OBJECT = PropertiesUtils.getBooleanProperty(System.getProperties(), "org.teiid.comparableObject", false); //$NON-NLS-1$
public static final boolean PAD_SPACE = PropertiesUtils.getBooleanProperty(System.getProperties(), "org.teiid.padSpace", false); //$NON-NLS-1$
public static final String DEFAULT_COLLATION = "UCS-2"; //$NON-NLS-1$
public static final String COLLATION_LOCALE = System.getProperties().getProperty("org.teiid.collationLocale"); //$NON-NLS-1$
private static boolean valueCacheEnabled = USE_VALUE_CACHE;
private interface ValueCache<T> {
T getValue(T value);
}
private static class HashedValueCache<T> implements ValueCache<T> {
final Object[] cache;
HashedValueCache(int size) {
cache = new Object[1 << size];
}
@SuppressWarnings("unchecked")
public T getValue(T value) {
int index = hash(primaryHash(value)) & (cache.length - 1);
Object canonicalValue = get(index);
if (value.equals(canonicalValue)) {
return (T)canonicalValue;
}
set(index, value);
return value;
}
protected Object get(int index) {
return cache[index];
}
protected void set(int index, T value) {
cache[index] = value;
}
protected int primaryHash(T value) {
return value.hashCode();
}
/*
* The same power of 2 hash bucketing from the Java HashMap
*/
final static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^= (h >>> 7) ^ (h >>> 4);
}
}
public static class WeakReferenceHashedValueCache<T> extends HashedValueCache<T> {
public WeakReferenceHashedValueCache(int size) {
super(size);
}
public T getByHash(Object obj) {
int index = hash(obj.hashCode()) & (cache.length - 1);
return get(index);
}
@Override
protected T get(int index) {
WeakReference<T> ref = (WeakReference<T>) cache[index];
if (ref != null) {
T result = ref.get();
if (result == null) {
cache[index] = null;
}
return result;
}
return null;
}
@Override
protected void set(int index, T value) {
cache[index] = new WeakReference<T>(value);
}
}
private static Map<Class<?>, ValueCache<?>> valueMaps = new HashMap<Class<?>, ValueCache<?>>(128);
private static HashedValueCache<String> stringCache = new WeakReferenceHashedValueCache<String>(17) {
@Override
protected int primaryHash(String value) {
if (value.length() < 14) {
return value.hashCode();
}
return HashCodeUtil.expHashCode(value);
}
};
public static final int MAX_STRING_LENGTH = PropertiesUtils.getIntProperty(System.getProperties(), "org.teiid.maxStringLength", 4000); //$NON-NLS-1$
public static final int MAX_VARBINARY_BYTES = Math.max(nextPowOf2(2*MAX_STRING_LENGTH), 1<<13);
public static final int MAX_LOB_MEMORY_BYTES = Math.max(nextPowOf2(8*MAX_STRING_LENGTH), 1<<15);
public static int nextPowOf2(int val) {
int result = 1;
while (result < val) {
result <<= 1;
}
return result;
}
public static final class DataTypeAliases {
public static final String VARCHAR = "varchar"; //$NON-NLS-1$
public static final String TINYINT = "tinyint"; //$NON-NLS-1$
public static final String SMALLINT = "smallint"; //$NON-NLS-1$
public static final String BIGINT = "bigint"; //$NON-NLS-1$
public static final String REAL = "real"; //$NON-NLS-1$
public static final String DECIMAL = "decimal"; //$NON-NLS-1$
}
public static final class DefaultDataTypes {
public static final String STRING = "string"; //$NON-NLS-1$
public static final String BOOLEAN = "boolean"; //$NON-NLS-1$
public static final String BYTE = "byte"; //$NON-NLS-1$
public static final String SHORT = "short"; //$NON-NLS-1$
public static final String CHAR = "char"; //$NON-NLS-1$
public static final String INTEGER = "integer"; //$NON-NLS-1$
public static final String LONG = "long"; //$NON-NLS-1$
public static final String BIG_INTEGER = "biginteger"; //$NON-NLS-1$
public static final String FLOAT = "float"; //$NON-NLS-1$
public static final String DOUBLE = "double"; //$NON-NLS-1$
public static final String BIG_DECIMAL = "bigdecimal"; //$NON-NLS-1$
public static final String DATE = "date"; //$NON-NLS-1$
public static final String TIME = "time"; //$NON-NLS-1$
public static final String TIMESTAMP = "timestamp"; //$NON-NLS-1$
public static final String OBJECT = "object"; //$NON-NLS-1$
public static final String NULL = "null"; //$NON-NLS-1$
public static final String BLOB = "blob"; //$NON-NLS-1$
public static final String CLOB = "clob"; //$NON-NLS-1$
public static final String XML = "xml"; //$NON-NLS-1$
public static final String VARBINARY = "varbinary"; //$NON-NLS-1$
public static final String GEOMETRY = "geometry"; //$NON-NLS-1$
}
public static final class DefaultDataClasses {
public static final Class<String> STRING = String.class;
public static final Class<Boolean> BOOLEAN = Boolean.class;
public static final Class<Byte> BYTE = Byte.class;
public static final Class<Short> SHORT = Short.class;
public static final Class<Character> CHAR = Character.class;
public static final Class<Integer> INTEGER = Integer.class;
public static final Class<Long> LONG = Long.class;
public static final Class<BigInteger> BIG_INTEGER = BigInteger.class;
public static final Class<Float> FLOAT = Float.class;
public static final Class<Double> DOUBLE = Double.class;
public static final Class<BigDecimal> BIG_DECIMAL = BigDecimal.class;
public static final Class<java.sql.Date> DATE = java.sql.Date.class;
public static final Class<Time> TIME = Time.class;
public static final Class<Timestamp> TIMESTAMP = Timestamp.class;
public static final Class<Object> OBJECT = Object.class;
public static final Class<NullType> NULL = NullType.class;
public static final Class<BlobType> BLOB = BlobType.class;
public static final Class<ClobType> CLOB = ClobType.class;
public static final Class<XMLType> XML = XMLType.class;
public static final Class<BinaryType> VARBINARY = BinaryType.class;
public static final Class<GeometryType> GEOMETRY = GeometryType.class;
}
public static final class DefaultTypeCodes {
public static final int STRING = 0;
public static final int CHAR = 1;
public static final int BOOLEAN = 2;
public static final int BYTE = 3;
public static final int SHORT = 4;
public static final int INTEGER = 5;
public static final int LONG = 6;
public static final int BIGINTEGER = 7;
public static final int FLOAT = 8;
public static final int DOUBLE = 9;
public static final int BIGDECIMAL = 10;
public static final int DATE = 11;
public static final int TIME = 12;
public static final int TIMESTAMP = 13;
public static final int OBJECT = 14;
public static final int BLOB = 15;
public static final int CLOB = 16;
public static final int XML = 17;
public static final int NULL = 18;
public static final int VARBINARY = 19;
public static final int GEOMETRY = 20;
}
public static int MAX_TYPE_CODE = DefaultTypeCodes.GEOMETRY;
private static final Map<Class<?>, Integer> typeMap = new LinkedHashMap<Class<?>, Integer>(64);
private static final List<Class<?>> typeList;
static {
typeMap.put(DataTypeManager.DefaultDataClasses.STRING, DefaultTypeCodes.STRING);
typeMap.put(DataTypeManager.DefaultDataClasses.CHAR, DefaultTypeCodes.CHAR);
typeMap.put(DataTypeManager.DefaultDataClasses.BOOLEAN, DefaultTypeCodes.BOOLEAN);
typeMap.put(DataTypeManager.DefaultDataClasses.BYTE, DefaultTypeCodes.BYTE);
typeMap.put(DataTypeManager.DefaultDataClasses.SHORT, DefaultTypeCodes.SHORT);
typeMap.put(DataTypeManager.DefaultDataClasses.INTEGER, DefaultTypeCodes.INTEGER);
typeMap.put(DataTypeManager.DefaultDataClasses.LONG, DefaultTypeCodes.LONG);
typeMap.put(DataTypeManager.DefaultDataClasses.BIG_INTEGER, DefaultTypeCodes.BIGINTEGER);
typeMap.put(DataTypeManager.DefaultDataClasses.FLOAT, DefaultTypeCodes.FLOAT);
typeMap.put(DataTypeManager.DefaultDataClasses.DOUBLE, DefaultTypeCodes.DOUBLE);
typeMap.put(DataTypeManager.DefaultDataClasses.BIG_DECIMAL, DefaultTypeCodes.BIGDECIMAL);
typeMap.put(DataTypeManager.DefaultDataClasses.DATE, DefaultTypeCodes.DATE);
typeMap.put(DataTypeManager.DefaultDataClasses.TIME, DefaultTypeCodes.TIME);
typeMap.put(DataTypeManager.DefaultDataClasses.TIMESTAMP, DefaultTypeCodes.TIMESTAMP);
typeMap.put(DataTypeManager.DefaultDataClasses.OBJECT, DefaultTypeCodes.OBJECT);
typeMap.put(DataTypeManager.DefaultDataClasses.BLOB, DefaultTypeCodes.BLOB);
typeMap.put(DataTypeManager.DefaultDataClasses.CLOB, DefaultTypeCodes.CLOB);
typeMap.put(DataTypeManager.DefaultDataClasses.XML, DefaultTypeCodes.XML);
typeMap.put(DataTypeManager.DefaultDataClasses.NULL, DefaultTypeCodes.NULL);
typeMap.put(DataTypeManager.DefaultDataClasses.VARBINARY, DefaultTypeCodes.VARBINARY);
typeMap.put(DataTypeManager.DefaultDataClasses.GEOMETRY, DefaultTypeCodes.GEOMETRY);
typeList = new ArrayList<Class<?>>(typeMap.keySet());
}
public static int getTypeCode(Class<?> source) {
Integer result = typeMap.get(source);
if (result == null) {
return DefaultTypeCodes.OBJECT;
}
return result;
}
public static Class<?> getClass(int code) {
Class<?> result = typeList.get(code);
if (result == null) {
return DefaultDataClasses.OBJECT;
}
return result;
}
/**
* Doubly-nested map of String srcType --> Map of String targetType -->
* Transform
*/
private static Map<String, Map<String, Transform>> transforms = new HashMap<String, Map<String, Transform>>(128);
/** Utility to easily get Transform given srcType and targetType */
private static Transform getTransformFromMaps(String srcType,
String targetType) {
Map<String, Transform> innerMap = transforms.get(srcType);
boolean found = false;
if (innerMap != null) {
Transform result = innerMap.get(targetType);
if (result != null) {
return result;
}
found = true;
}
if (srcType.equals(targetType)) {
return null;
}
if (DataTypeManager.DefaultDataTypes.OBJECT.equals(targetType)) {
return AnyToObjectTransform.INSTANCE;
}
if (srcType.equals(DefaultDataTypes.NULL)) {
return NullToAnyTransform.INSTANCE;
}
if (srcType.equals(DefaultDataTypes.OBJECT)) {
return ObjectToAnyTransform.INSTANCE;
}
if (found) {
//built-in type
return null;
}
int sourceDims = 0;
while (isArrayType(srcType)) {
srcType = srcType.substring(0, srcType.length() - 2);
sourceDims++;
}
int targetDims = 0;
while(isArrayType(targetType)) {
targetType = targetType.substring(0, targetType.length() - 2);
targetDims++;
}
//go from typed[] to object[]
if (DataTypeManager.DefaultDataTypes.OBJECT.equals(targetType) && targetDims <= sourceDims) {
return AnyToObjectTransform.INSTANCE;
}
//go from object[] to typed[]
if (DataTypeManager.DefaultDataTypes.OBJECT.equals(srcType) && targetDims >= sourceDims) {
return ObjectToAnyTransform.INSTANCE;
}
//TODO: will eventually allow integer[] to long[], etc.
return null;
}
/** Base data type names and classes, Type name --> Type class */
private static Map<String, Class<?>> dataTypeNames = new LinkedHashMap<String, Class<?>>(128);
/** Base data type names and classes, Type class --> Type name */
private static Map<Class<?>, String> dataTypeClasses = new LinkedHashMap<Class<?>, String>(128);
private static Map<Class<?>, Class<?>> arrayTypes = new HashMap<Class<?>, Class<?>>(128);
private static Map<Class<?>, String> arrayTypeNames = new HashMap<Class<?>, String>(128);
/** a set of all type names roughly ordered based upon data width */
private static Set<String> DATA_TYPE_NAMES;
private static Set<Class<?>> DATA_TYPE_CLASSES = Collections.unmodifiableSet(dataTypeClasses.keySet());
// Static initializer - loads basic transforms types
static {
// Load default data types - not extensible yet
loadDataTypes();
// Load default transforms
loadBasicTransforms();
for (Map.Entry<String, Class<?>> entry : dataTypeNames.entrySet()) {
Class<?> arrayType = getArrayType(entry.getValue());
arrayTypes.put(entry.getValue(), arrayType);
arrayTypeNames.put(arrayType, getDataTypeName(arrayType));
}
}
/**
* Constructor is private so instance creation is controlled by the class.
*/
private DataTypeManager() {
}
/**
* Add a new data type. For now this consists just of the Class - in the
* future a data type will be a more complicated entity. This is
* package-level for now as it is just used to add the default data types.
*
* @param dataType
* New data type defined by Class
*/
static void addDataType(String typeName, Class<?> dataType) {
dataTypeNames.put(typeName, dataType);
dataTypeClasses.put(dataType, typeName);
}
/**
* Get a set of all data type names.
*
* @return Set of data type names (String)
*/
public static Set<String> getAllDataTypeNames() {
return DATA_TYPE_NAMES;
}
public static Set<Class<?>> getAllDataTypeClasses() {
return DATA_TYPE_CLASSES;
}
/**
* Get data type class.
* <br/>IMPORTANT: only valid for default runtime types
*
* @param name
* Data type name
* @return Data type class
*/
public static Class<?> getDataTypeClass(String name) {
if (name == null) {
return DefaultDataClasses.NULL;
}
// Hope this is the correct case (as it will be if using the constants
Class<?> dataTypeClass = dataTypeNames.get(name);
// If that fails, do a lower case to make sure we match
if (dataTypeClass == null) {
dataTypeClass = dataTypeNames.get(name.toLowerCase());
}
if (dataTypeClass == null) {
if (isArrayType(name)) {
return getArrayType(getDataTypeClass(name.substring(0, name.length() - 2)));
}
dataTypeClass = DefaultDataClasses.OBJECT;
}
return dataTypeClass;
}
public static boolean isArrayType(String name) {
return name.endsWith(ARRAY_SUFFIX);
}
public static String getDataTypeName(Class<?> typeClass) {
if (typeClass == null) {
return DefaultDataTypes.NULL;
}
String result = dataTypeClasses.get(typeClass);
if (result == null) {
if (typeClass.isArray() && !typeClass.getComponentType().isPrimitive()) {
result = arrayTypeNames.get(typeClass);
if (result == null) {
return getDataTypeName(typeClass.getComponentType()) + ARRAY_SUFFIX;
}
return result;
}
result = DefaultDataTypes.OBJECT;
}
return result;
}
/**
* Take an object and determine the MetaMatrix data type. In most cases,
* this is simply the class of the object. Some special cases are when the
* value is of type Object or Null.
*/
public static Class<?> determineDataTypeClass(Object value) {
// Handle null case
if (value == null) {
return DefaultDataClasses.NULL;
}
Class<?> clazz = value.getClass();
if (DATA_TYPE_CLASSES.contains(clazz)) {
return clazz;
}
clazz = convertToRuntimeType(value, true).getClass();
if (DATA_TYPE_CLASSES.contains(clazz)) {
return clazz;
}
return DefaultDataClasses.OBJECT;
}
/**
* Get a data value transformation between the sourceType and the
* targetType.
*
* @param sourceType
* Incoming value type
* @param targetType
* Outgoing value type
* @return A transform if one exists, null otherwise
*/
public static Transform getTransform(Class<?> sourceType, Class<?> targetType) {
if (sourceType == null || targetType == null) {
throw new IllegalArgumentException(CorePlugin.Util.getString(
"ERR.003.029.0002", sourceType, targetType)); //$NON-NLS-1$
}
return getTransformFromMaps(
DataTypeManager.getDataTypeName(sourceType), DataTypeManager
.getDataTypeName(targetType));
}
/**
* Get a data value transformation between the sourceType with given name
* and the targetType of given name. The Class for source and target type
* are not needed to do this lookup.
*
* @param sourceTypeName
* Incoming value type name
* @param targetTypeName
* Outgoing value type name
* @return A transform if one exists, null otherwise
*/
public static Transform getTransform(String sourceTypeName,
String targetTypeName) {
if (sourceTypeName == null || targetTypeName == null) {
throw new IllegalArgumentException(CorePlugin.Util.getString(
"ERR.003.029.0003", sourceTypeName, //$NON-NLS-1$
targetTypeName));
}
return getTransformFromMaps(sourceTypeName, targetTypeName);
}
/**
* Does a transformation exist between the source and target type?
*
* @param sourceType
* Incoming value type
* @param targetType
* Outgoing value type
* @return True if a transform exists
*/
public static boolean isTransformable(Class<?> sourceType, Class<?> targetType) {
return getTransform(sourceType, targetType) != null;
}
/**
* Does a transformation exist between the source and target type of given
* names? The Class for source and target type are not needed to do this
* lookup.
*
* @param sourceTypeName
* Incoming value type name
* @param targetTypeName
* Outgoing value type name
* @return True if a transform exists
*/
public static boolean isTransformable(String sourceTypeName,
String targetTypeName) {
if (sourceTypeName == null || targetTypeName == null) {
throw new IllegalArgumentException(CorePlugin.Util.getString(
"ERR.003.029.0003", sourceTypeName, //$NON-NLS-1$
targetTypeName));
}
return (getTransformFromMaps(sourceTypeName, targetTypeName) != null);
}
/**
* Add a new transform to the known transform types.
*
* @param transform
* New transform to add
*/
static void addTransform(Transform transform) {
ArgCheck.isNotNull(transform);
String sourceName = transform.getSourceTypeName();
String targetName = transform.getTargetTypeName();
Map<String, Transform> innerMap = transforms.get(sourceName);
if (innerMap == null) {
innerMap = new LinkedHashMap<String, Transform>();
transforms.put(sourceName, innerMap);
}
innerMap.put(targetName, transform);
}
public static void getImplicitConversions(String type, Collection<String> result) {
Map<String, Transform> innerMap = transforms.get(type);
if (innerMap != null) {
for (Map.Entry<String, Transform> entry : innerMap.entrySet()) {
if (!entry.getValue().isExplicit()) {
result.add(entry.getKey());
}
}
result.add(DefaultDataTypes.OBJECT);
return;
}
String previous = DataTypeManager.DefaultDataTypes.OBJECT;
result.add(previous);
while (isArrayType(type)) {
previous += ARRAY_SUFFIX;
result.add(previous);
type = getComponentType(type);
}
}
public static boolean isImplicitConversion(String srcType, String tgtType) {
Transform t = getTransform(srcType, tgtType);
if (t != null) {
return !t.isExplicit();
}
if (DefaultDataTypes.NULL.equals(srcType) && !DefaultDataTypes.NULL.equals(tgtType)) {
return true;
}
if (DefaultDataTypes.OBJECT.equals(tgtType) && !DefaultDataTypes.OBJECT.equals(srcType)) {
return true;
}
if (isArrayType(srcType) && isArrayType(tgtType)) {
return isImplicitConversion(getComponentType(srcType), getComponentType(tgtType));
}
return false;
}
public static String getComponentType(String srcType) {
return srcType.substring(0, srcType.length() - ARRAY_SUFFIX.length());
}
public static boolean isExplicitConversion(String srcType, String tgtType) {
Transform t = getTransform(srcType, tgtType);
if (t != null) {
return t.isExplicit();
}
return false;
}
/**
* Is the supplied class type a LOB based data type?
*
* @param type
* @return true if yes; false otherwise
*/
public static boolean isLOB(Class<?> type) {
return DataTypeManager.DefaultDataClasses.BLOB.equals(type)
|| DataTypeManager.DefaultDataClasses.CLOB.equals(type)
|| DataTypeManager.DefaultDataClasses.XML.equals(type)
|| DataTypeManager.DefaultDataClasses.GEOMETRY.equals(type);
}
public static boolean isLOB(String type) {
return DataTypeManager.DefaultDataTypes.BLOB.equals(type)
|| DataTypeManager.DefaultDataTypes.CLOB.equals(type)
|| DataTypeManager.DefaultDataTypes.XML.equals(type)
|| DataTypeManager.DefaultDataTypes.GEOMETRY.equals(type);
}
/**
* Load default data types.
*/
static void loadDataTypes() {
DataTypeManager.addDataType(DefaultDataTypes.BOOLEAN, DefaultDataClasses.BOOLEAN);
DataTypeManager.addDataType(DefaultDataTypes.BYTE, DefaultDataClasses.BYTE);
DataTypeManager.addDataType(DefaultDataTypes.SHORT, DefaultDataClasses.SHORT);
DataTypeManager.addDataType(DefaultDataTypes.CHAR, DefaultDataClasses.CHAR);
DataTypeManager.addDataType(DefaultDataTypes.INTEGER, DefaultDataClasses.INTEGER);
DataTypeManager.addDataType(DefaultDataTypes.LONG, DefaultDataClasses.LONG);
DataTypeManager.addDataType(DefaultDataTypes.BIG_INTEGER, DefaultDataClasses.BIG_INTEGER);
DataTypeManager.addDataType(DefaultDataTypes.FLOAT, DefaultDataClasses.FLOAT);
DataTypeManager.addDataType(DefaultDataTypes.DOUBLE, DefaultDataClasses.DOUBLE);
DataTypeManager.addDataType(DefaultDataTypes.BIG_DECIMAL, DefaultDataClasses.BIG_DECIMAL);
DataTypeManager.addDataType(DefaultDataTypes.DATE, DefaultDataClasses.DATE);
DataTypeManager.addDataType(DefaultDataTypes.TIME, DefaultDataClasses.TIME);
DataTypeManager.addDataType(DefaultDataTypes.TIMESTAMP, DefaultDataClasses.TIMESTAMP);
DataTypeManager.addDataType(DefaultDataTypes.STRING, DefaultDataClasses.STRING);
DataTypeManager.addDataType(DefaultDataTypes.CLOB, DefaultDataClasses.CLOB);
DataTypeManager.addDataType(DefaultDataTypes.XML, DefaultDataClasses.XML);
DataTypeManager.addDataType(DefaultDataTypes.OBJECT, DefaultDataClasses.OBJECT);
DataTypeManager.addDataType(DefaultDataTypes.NULL, DefaultDataClasses.NULL);
DataTypeManager.addDataType(DefaultDataTypes.BLOB, DefaultDataClasses.BLOB);
DataTypeManager.addDataType(DefaultDataTypes.VARBINARY, DefaultDataClasses.VARBINARY);
DataTypeManager.addDataType(DefaultDataTypes.GEOMETRY, DefaultDataClasses.GEOMETRY);
DATA_TYPE_NAMES = Collections.unmodifiableSet(new LinkedHashSet<String>(dataTypeNames.keySet()));
dataTypeNames.put(DataTypeAliases.BIGINT, DefaultDataClasses.LONG);
dataTypeNames.put(DataTypeAliases.DECIMAL, DefaultDataClasses.BIG_DECIMAL);
dataTypeNames.put(DataTypeAliases.REAL, DefaultDataClasses.FLOAT);
dataTypeNames.put(DataTypeAliases.SMALLINT, DefaultDataClasses.SHORT);
dataTypeNames.put(DataTypeAliases.TINYINT, DefaultDataClasses.BYTE);
dataTypeNames.put(DataTypeAliases.VARCHAR, DefaultDataClasses.STRING);
valueMaps.put(DefaultDataClasses.BOOLEAN, new ValueCache<Boolean>() {
@Override
public Boolean getValue(Boolean value) {
return Boolean.valueOf(value);
}
});
valueMaps.put(DefaultDataClasses.BYTE, new ValueCache<Byte>() {
@Override
public Byte getValue(Byte value) {
return Byte.valueOf(value);
}
});
if (USE_VALUE_CACHE) {
valueMaps.put(DefaultDataClasses.SHORT, new HashedValueCache<Short>(13));
valueMaps.put(DefaultDataClasses.CHAR, new HashedValueCache<Character>(13));
valueMaps.put(DefaultDataClasses.INTEGER, new HashedValueCache<Integer>(14));
valueMaps.put(DefaultDataClasses.LONG, new HashedValueCache<Long>(14));
valueMaps.put(DefaultDataClasses.BIG_INTEGER, new HashedValueCache<BigInteger>(15));
valueMaps.put(DefaultDataClasses.FLOAT, new HashedValueCache<Float>(14));
valueMaps.put(DefaultDataClasses.DOUBLE, new HashedValueCache<Double>(14));
valueMaps.put(DefaultDataClasses.DATE, new HashedValueCache<Date>(14));
valueMaps.put(DefaultDataClasses.TIME, new HashedValueCache<Time>(14));
valueMaps.put(DefaultDataClasses.TIMESTAMP, new HashedValueCache<Timestamp>(14));
valueMaps.put(DefaultDataClasses.BIG_DECIMAL, new WeakReferenceHashedValueCache<BigDecimal>(17));
valueMaps.put(DefaultDataClasses.STRING, stringCache);
valueMaps.put(DefaultDataClasses.VARBINARY, new WeakReferenceHashedValueCache<BinaryType>(17));
}
}
/**
* Load all basic {@link Transform}s into the DataTypeManager. This standard
* set is always installed but may be overridden.
*/
static void loadBasicTransforms() {
DataTypeManager.addTransform(new BooleanToNumberTransform(Byte.valueOf((byte)1), Byte.valueOf((byte)0)));
DataTypeManager.addTransform(new BooleanToNumberTransform(Short.valueOf((short)1), Short.valueOf((short)0)));
DataTypeManager.addTransform(new BooleanToNumberTransform(Integer.valueOf(1), Integer.valueOf(0)));
DataTypeManager.addTransform(new BooleanToNumberTransform(Long.valueOf(1), Long.valueOf(0)));
DataTypeManager.addTransform(new BooleanToNumberTransform(BigInteger.valueOf(1), BigInteger.valueOf(0)));
DataTypeManager.addTransform(new BooleanToNumberTransform(Float.valueOf(1), Float.valueOf(0)));
DataTypeManager.addTransform(new BooleanToNumberTransform(Double.valueOf(1), Double.valueOf(0)));
DataTypeManager.addTransform(new BooleanToNumberTransform(BigDecimal.valueOf(1), BigDecimal.valueOf(0)));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.BOOLEAN));
DataTypeManager.addTransform(new NumberToBooleanTransform(Byte.valueOf((byte)0)));
DataTypeManager.addTransform(new NumberToShortTransform(DefaultDataClasses.BYTE, false));
DataTypeManager.addTransform(new NumberToIntegerTransform(DefaultDataClasses.BYTE, false));
DataTypeManager.addTransform(new NumberToLongTransform(DefaultDataClasses.BYTE, false, false));
DataTypeManager.addTransform(new FixedNumberToBigIntegerTransform(DefaultDataClasses.BYTE));
DataTypeManager.addTransform(new NumberToFloatTransform(DefaultDataClasses.BYTE, false, false));
DataTypeManager.addTransform(new NumberToDoubleTransform(DefaultDataClasses.BYTE, false, false));
DataTypeManager.addTransform(new FixedNumberToBigDecimalTransform(DefaultDataClasses.BYTE));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.BYTE));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.CHAR));
DataTypeManager.addTransform(new NumberToBooleanTransform(Short.valueOf((short)0)));
DataTypeManager.addTransform(new NumberToByteTransform(DefaultDataClasses.SHORT));
DataTypeManager.addTransform(new NumberToIntegerTransform(DefaultDataClasses.SHORT, false));
DataTypeManager.addTransform(new NumberToLongTransform(DefaultDataClasses.SHORT, false, false));
DataTypeManager.addTransform(new FixedNumberToBigIntegerTransform(DefaultDataClasses.SHORT));
DataTypeManager.addTransform(new NumberToFloatTransform(DefaultDataClasses.SHORT, false, false));
DataTypeManager.addTransform(new NumberToDoubleTransform(DefaultDataClasses.SHORT, false, false));
DataTypeManager.addTransform(new FixedNumberToBigDecimalTransform(DefaultDataClasses.SHORT));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.SHORT));
DataTypeManager.addTransform(new NumberToBooleanTransform(Integer.valueOf(0)));
DataTypeManager.addTransform(new NumberToByteTransform(DefaultDataClasses.INTEGER));
DataTypeManager.addTransform(new NumberToShortTransform(DefaultDataClasses.INTEGER, true));
DataTypeManager.addTransform(new NumberToLongTransform(DefaultDataClasses.INTEGER, false, false));
DataTypeManager.addTransform(new FixedNumberToBigIntegerTransform(DefaultDataClasses.INTEGER));
DataTypeManager.addTransform(new NumberToFloatTransform(DefaultDataClasses.INTEGER, false, true)); //lossy, but not narrowing
DataTypeManager.addTransform(new NumberToDoubleTransform(DefaultDataClasses.INTEGER, false, false));
DataTypeManager.addTransform(new FixedNumberToBigDecimalTransform(DefaultDataClasses.INTEGER));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.INTEGER));
DataTypeManager.addTransform(new NumberToBooleanTransform(Long.valueOf(0)));
DataTypeManager.addTransform(new NumberToByteTransform(DefaultDataClasses.LONG));
DataTypeManager.addTransform(new NumberToShortTransform(DefaultDataClasses.LONG, true));
DataTypeManager.addTransform(new NumberToIntegerTransform(DefaultDataClasses.LONG, true));
DataTypeManager.addTransform(new FixedNumberToBigIntegerTransform(DefaultDataClasses.LONG));
DataTypeManager.addTransform(new NumberToFloatTransform(DefaultDataClasses.LONG, false, true)); //lossy, but not narrowing
DataTypeManager.addTransform(new NumberToDoubleTransform(DefaultDataClasses.LONG, false, true)); //lossy, but not narrowing
DataTypeManager.addTransform(new FixedNumberToBigDecimalTransform(DefaultDataClasses.LONG));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.LONG));
DataTypeManager.addTransform(new NumberToBooleanTransform(BigInteger.valueOf(0)));
DataTypeManager.addTransform(new NumberToByteTransform(DefaultDataClasses.BIG_INTEGER));
DataTypeManager.addTransform(new NumberToShortTransform(DefaultDataClasses.BIG_INTEGER, true));
DataTypeManager.addTransform(new NumberToIntegerTransform(DefaultDataClasses.BIG_INTEGER, true));
DataTypeManager.addTransform(new NumberToLongTransform(DefaultDataClasses.BIG_INTEGER, true, false));
DataTypeManager.addTransform(new NumberToFloatTransform(DefaultDataClasses.BIG_INTEGER, true, false));
DataTypeManager.addTransform(new NumberToDoubleTransform(DefaultDataClasses.BIG_INTEGER, true, false));
DataTypeManager.addTransform(new org.teiid.core.types.basic.BigIntegerToBigDecimalTransform());
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.BIG_INTEGER));
DataTypeManager.addTransform(new NumberToBooleanTransform(BigDecimal.valueOf(0)));
DataTypeManager.addTransform(new NumberToByteTransform(DefaultDataClasses.BIG_DECIMAL));
DataTypeManager.addTransform(new NumberToShortTransform(DefaultDataClasses.BIG_DECIMAL, true));
DataTypeManager.addTransform(new NumberToIntegerTransform(DefaultDataClasses.BIG_DECIMAL, true));
DataTypeManager.addTransform(new NumberToLongTransform(DefaultDataClasses.BIG_DECIMAL, true, false));
DataTypeManager.addTransform(new org.teiid.core.types.basic.BigDecimalToBigIntegerTransform());
DataTypeManager.addTransform(new NumberToFloatTransform(DefaultDataClasses.BIG_DECIMAL, true, false));
DataTypeManager.addTransform(new NumberToDoubleTransform(DefaultDataClasses.BIG_DECIMAL, true, false));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.BIG_DECIMAL));
DataTypeManager.addTransform(new NumberToBooleanTransform(Float.valueOf(0)));
DataTypeManager.addTransform(new NumberToByteTransform(DefaultDataClasses.FLOAT));
DataTypeManager.addTransform(new NumberToShortTransform(DefaultDataClasses.FLOAT, true));
DataTypeManager.addTransform(new NumberToIntegerTransform(DefaultDataClasses.FLOAT, true));
DataTypeManager.addTransform(new NumberToLongTransform(DefaultDataClasses.FLOAT, false, true)); //lossy, but not narrowing
DataTypeManager.addTransform(new FloatingNumberToBigIntegerTransform(DefaultDataClasses.FLOAT));
DataTypeManager.addTransform(new NumberToDoubleTransform(DefaultDataClasses.FLOAT, false, false));
DataTypeManager.addTransform(new FloatingNumberToBigDecimalTransform(DefaultDataClasses.FLOAT));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.FLOAT));
DataTypeManager.addTransform(new NumberToBooleanTransform(Double.valueOf(0)));
DataTypeManager.addTransform(new NumberToByteTransform(DefaultDataClasses.DOUBLE));
DataTypeManager.addTransform(new NumberToShortTransform(DefaultDataClasses.DOUBLE, true));
DataTypeManager.addTransform(new NumberToIntegerTransform(DefaultDataClasses.DOUBLE, true));
DataTypeManager.addTransform(new NumberToLongTransform( DefaultDataClasses.DOUBLE, false, true)); //lossy, but not narrowing
DataTypeManager.addTransform(new FloatingNumberToBigIntegerTransform(DefaultDataClasses.DOUBLE));
DataTypeManager.addTransform(new NumberToFloatTransform(DefaultDataClasses.DOUBLE, true, false));
DataTypeManager.addTransform(new FloatingNumberToBigDecimalTransform(DefaultDataClasses.DOUBLE));
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.DOUBLE));
DataTypeManager.addTransform(new org.teiid.core.types.basic.DateToTimestampTransform());
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.DATE));
DataTypeManager.addTransform(new org.teiid.core.types.basic.TimeToTimestampTransform());
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.TIME));
DataTypeManager.addTransform(new org.teiid.core.types.basic.TimestampToTimeTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.TimestampToDateTransform());
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.TIMESTAMP));
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToBooleanTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToByteTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToShortTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToIntegerTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToLongTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToBigIntegerTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToFloatTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToDoubleTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToBigDecimalTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToTimeTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToDateTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToTimestampTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToCharacterTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToClobTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.StringToSQLXMLTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.BinaryToBlobTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.ClobToStringTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.BlobToBinaryTransform());
DataTypeManager.addTransform(new org.teiid.core.types.basic.SQLXMLToStringTransform());
DataTypeManager.addTransform(new AnyToStringTransform(DefaultDataClasses.OBJECT) {
@Override
public boolean isExplicit() {
return true;
}
});
}
/**
* Convert the value to the probable runtime type.
* @param allConversions if false only lob conversions will be used
*/
public static Object convertToRuntimeType(Object value, boolean allConversions) {
if (value == null) {
return null;
}
Class<?> c = value.getClass();
if (DATA_TYPE_CLASSES.contains(c)) {
return value;
}
if (allConversions) {
if (c == char[].class) {
return new ClobType(ClobImpl.createClob((char[])value));
}
if (c == byte[].class) {
return new BinaryType((byte[])value);
}
if (java.util.Date.class.isAssignableFrom(c)) {
return new Timestamp(((java.util.Date)value).getTime());
}
if (Object[].class.isAssignableFrom(c)) {
return new ArrayImpl((Object[])value);
}
}
if (Clob.class.isAssignableFrom(c)) {
return new ClobType((Clob)value);
}
if (Blob.class.isAssignableFrom(c)) {
return new BlobType((Blob)value);
}
if (SQLXML.class.isAssignableFrom(c)) {
return new XMLType((SQLXML)value);
}
return value; // "object type"
}
public static Class<?> getRuntimeType(Class<?> c) {
if (c == null) {
return DefaultDataClasses.NULL;
}
if (DATA_TYPE_CLASSES.contains(c)) {
return c;
}
if (c == char[].class) {
return DefaultDataClasses.CLOB;
}
if (c == byte[].class) {
return DefaultDataClasses.VARBINARY;
}
if (java.util.Date.class.isAssignableFrom(c)) {
return DefaultDataClasses.DATE;
}
if (Clob.class.isAssignableFrom(c)) {
return DefaultDataClasses.CLOB;
}
if (Blob.class.isAssignableFrom(c)) {
return DefaultDataClasses.BLOB;
}
if (SQLXML.class.isAssignableFrom(c)) {
return DefaultDataClasses.XML;
}
if (c == ArrayImpl.class) {
return getArrayType(DefaultDataClasses.OBJECT);
}
if (c.isArray()) {
return getDataTypeClass(getDataTypeName(c));
}
return DefaultDataClasses.OBJECT; // "object type"
}
public static Object transformValue(Object value, Class<?> targetClass)
throws TransformationException {
if (value == null) {
return value;
}
return transformValue(value, value.getClass(), targetClass);
}
public static Object transformValue(Object value, Class<?> sourceType,
Class<?> targetClass) throws TransformationException {
if (value == null || sourceType == targetClass || DefaultDataClasses.OBJECT == targetClass) {
return value;
}
Transform transform = DataTypeManager.getTransform(sourceType,
targetClass);
if (transform == null) {
Object[] params = new Object[] { sourceType, targetClass, value};
throw new TransformationException(CorePlugin.Event.TEIID10076, CorePlugin.Util.gs(CorePlugin.Event.TEIID10076, params));
}
Object result = transform.transform(value, targetClass);
return getCanonicalValue(result);
}
public static boolean isNonComparable(String type) {
return (!COMPARABLE_OBJECT && DataTypeManager.DefaultDataTypes.OBJECT.equals(type))
|| (!COMPARABLE_LOBS && DataTypeManager.DefaultDataTypes.BLOB.equals(type))
|| (!COMPARABLE_LOBS && DataTypeManager.DefaultDataTypes.CLOB.equals(type))
|| DataTypeManager.DefaultDataTypes.GEOMETRY.equals(type)
|| DataTypeManager.DefaultDataTypes.XML.equals(type);
}
public static void setValueCacheEnabled(boolean enabled) {
valueCacheEnabled = enabled;
}
public static final boolean isValueCacheEnabled() {
return valueCacheEnabled;
}
@SuppressWarnings("unchecked")
public static final <T> T getCanonicalValue(T value) {
if (valueCacheEnabled) {
if (value == null) {
return null;
}
//TODO: this initial lookup is inefficient, since there are likely collisions
ValueCache valueCache = valueMaps.get(value.getClass());
if (valueCache != null) {
value = (T)valueCache.getValue(value);
}
}
return value;
}
public static final String getCanonicalString(String value) {
if (value == null) {
return null;
}
return stringCache.getValue(value);
}
public static boolean isHashable(Class<?> type) {
if (type == DataTypeManager.DefaultDataClasses.STRING
|| type == DataTypeManager.DefaultDataClasses.CHAR) {
return COLLATION_LOCALE == null;
}
if (type == DataTypeManager.DefaultDataClasses.STRING) {
return !PAD_SPACE;
}
return !(type == DataTypeManager.DefaultDataClasses.BIG_DECIMAL
|| type == DataTypeManager.DefaultDataClasses.BLOB
|| type == DataTypeManager.DefaultDataClasses.CLOB
|| type == DataTypeManager.DefaultDataClasses.OBJECT);
}
public static Class<?> getArrayType(Class<?> classType) {
Class<?> result = arrayTypes.get(classType);
if (result != null) {
return result;
}
return Array.newInstance(classType, 0).getClass();
}
}