/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* 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 org.seasar.extension.jdbc.types;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.seasar.extension.jdbc.ValueType;
import org.seasar.framework.util.ConstructorUtil;
import org.seasar.framework.util.Disposable;
import org.seasar.framework.util.DisposableUtil;
import org.seasar.framework.util.MapUtil;
import org.seasar.framework.util.MethodUtil;
import org.seasar.framework.util.ModifierUtil;
/**
* 値タイプのファクトリです。
*
* @author higa
*
*/
public class ValueTypes {
/**
* String用の値タイプです。
*/
public final static ValueType STRING = new StringType();
/**
* <code>CLOB</code>用の値タイプです。
*/
public final static ValueType CLOB = new StringClobType();
/**
* WaveDashを変換するString用の値タイプです。
*/
public final static ValueType WAVE_DASH_STRING = new WaveDashStringType();
/**
* WaveDashを変換する<code>CLOB</code>用の値タイプです。
*/
public final static ValueType WAVE_DASH_CLOB = new WaveDashStringClobType();
/**
* Character用の値タイプです。
*/
public final static ValueType CHARACTER = new CharacterType();
/**
* Byte用の値タイプです。
*/
public final static ValueType BYTE = new ByteType();
/**
* Short用の値タイプです。
*/
public final static ValueType SHORT = new ShortType();
/**
* Integer用の値タイプです。
*/
public final static ValueType INTEGER = new IntegerType();
/**
* Long用の値タイプです。
*/
public final static ValueType LONG = new LongType();
/**
* Float用の値タイプです。
*/
public final static ValueType FLOAT = new FloatType();
/**
* Double用の値タイプです。
*/
public final static ValueType DOUBLE = new DoubleType();
/**
* BigDecimal用の値タイプです。
*/
public final static ValueType BIGDECIMAL = new BigDecimalType();
/**
* BigInteger用の値タイプです。
*/
public final static ValueType BIGINTEGER = new BigIntegerType();
/**
* Time用の値タイプです。
*/
public final static ValueType TIME = new TimeType();
/**
* java.sql.Date用の値タイプです。
*/
public final static ValueType SQLDATE = new SqlDateType();
/**
* Timestamp用の値タイプです。
*/
public final static ValueType TIMESTAMP = new TimestampType();
/**
* java.sql.Dateと互換性をもつjava.util.Date用の値タイプです。
*/
public final static ValueType DATE_SQLDATE = new DateSqlDateType();
/**
* Timeと互換性をもつjava.util.Date用の値タイプです。
*/
public final static ValueType DATE_TIME = new DateTimeType();
/**
* Timestampと互換性をもつjava.util.Date用の値タイプです。
*/
public final static ValueType DATE_TIMESTAMP = new DateTimestampType();
/**
* java.sql.Dateと互換性をもつCalendar用の値タイプです。
*/
public final static ValueType CALENDAR_SQLDATE = new CalendarSqlDateType();
/**
* Timeと互換性をもつCalendar用の値タイプです。
*/
public final static ValueType CALENDAR_TIME = new CalendarTimeType();
/**
* Timestampと互換性をもつCalendar用の値タイプです。
*/
public final static ValueType CALENDAR_TIMESTAMP = new CalendarTimestampType();
/**
* Binary用の値タイプです。
*/
public final static ValueType BINARY = new BinaryType();
/**
* BinaryStream用の値タイプです。
*/
public final static ValueType BINARY_STREAM = new BinaryStreamType();
/**
* バイト配列用の値タイプです。
*/
public final static ValueType BYTE_ARRAY = new BytesType(
BytesType.BYTES_TRAIT);
/**
* BLOB用の値タイプです。
*/
public final static ValueType BLOB = new BytesType(BytesType.BLOB_TRAIT);
/**
* オブジェクトをシリアライズしたバイト配列用の値タイプです。
*/
public final static ValueType SERIALIZABLE_BYTE_ARRAY = new SerializableType(
BytesType.BYTES_TRAIT);
/**
* オブジェクトをシリアライズしたBLOB用の値タイプです。
*/
public final static ValueType SERIALIZABLE_BLOB = new SerializableType(
BytesType.BLOB_TRAIT);
/**
* Boolean用の値タイプです。
*/
public final static ValueType BOOLEAN = new BooleanType();
/**
* JavaのBooleanの値をデータベースのIntegerに変換する値タイプです。
*/
public final static ValueType BOOLEAN_INTEGER = new BooleanIntegerType();
/**
* PostgreSQLの結果セット用の値タイプです。
*/
public final static ValueType POSTGRE_RESULT_SET = new PostgreResultSetType();
/**
* オラクルの結果セット用の値タイプです。
*/
public final static ValueType ORACLE_RESULT_SET = new OracleResultSetType();
/**
* 汎用的な値タイプです。
*/
public final static ValueType OBJECT = new ObjectType();
private static final ValueType NULL = new NullType();
private static final Class BYTE_ARRAY_CLASS = new byte[0].getClass();
private static Map types = new HashMap();
private static Method isEnumMethod;
private static Constructor enumDefaultValueTypeConstructor;
private static Constructor enumOrdinalValueTypeConstructor;
private static Constructor enumStringValueTypeConstructor;
private static volatile boolean initialized;
private static Map valueTypeCache = MapUtil.createHashMap(50);
static {
registerValueType(String.class, STRING);
registerValueType(char.class, CHARACTER);
registerValueType(Character.class, CHARACTER);
registerValueType(byte.class, BYTE);
registerValueType(Byte.class, BYTE);
registerValueType(short.class, SHORT);
registerValueType(Short.class, SHORT);
registerValueType(int.class, INTEGER);
registerValueType(Integer.class, INTEGER);
registerValueType(long.class, LONG);
registerValueType(Long.class, LONG);
registerValueType(float.class, FLOAT);
registerValueType(Float.class, FLOAT);
registerValueType(double.class, DOUBLE);
registerValueType(Double.class, DOUBLE);
registerValueType(BigInteger.class, BIGINTEGER);
registerValueType(BigDecimal.class, BIGDECIMAL);
registerValueType(java.sql.Date.class, SQLDATE);
registerValueType(java.sql.Time.class, TIME);
registerValueType(java.util.Date.class, TIMESTAMP);
registerValueType(Timestamp.class, TIMESTAMP);
registerValueType(Calendar.class, TIMESTAMP);
registerValueType(BYTE_ARRAY_CLASS, BINARY);
registerValueType(InputStream.class, BINARY_STREAM);
registerValueType(boolean.class, BOOLEAN);
registerValueType(Boolean.class, BOOLEAN);
// registerValueType(Object.class, OBJECT);
try {
isEnumMethod = Class.class.getMethod("isEnum", null);
setEnumDefaultValueType(Class
.forName("org.seasar.extension.jdbc.types.EnumOrdinalType"));
setEnumOrdinalValueType(Class
.forName("org.seasar.extension.jdbc.types.EnumOrdinalType"));
setEnumStringValueType(Class
.forName("org.seasar.extension.jdbc.types.EnumType"));
} catch (Throwable ignore) {
isEnumMethod = null;
enumStringValueTypeConstructor = null;
enumOrdinalValueTypeConstructor = null;
}
initialize();
}
/**
* インスタンスを構築します。
*/
protected ValueTypes() {
}
/**
* 初期化を行ないます。
*/
public static void initialize() {
DisposableUtil.add(new Disposable() {
public void dispose() {
clear();
}
});
initialized = true;
}
/**
* キャッシュをクリアします。
*/
public static void clear() {
valueTypeCache.clear();
initialized = false;
}
/**
* クラスに対する{@link ValueType}を登録します。
*
* @param clazz
* @param valueType
*/
public static void registerValueType(Class clazz, ValueType valueType) {
types.put(clazz, valueType);
}
/**
* クラスに対する{@link ValueType}の登録を解除します。
*
* @param clazz
*/
public static void unregisterValueType(Class clazz) {
types.remove(clazz);
}
/**
* enum型に対するデフォルトの{@link ValueType}を設定します。
*
* @param enumDefaultValueTypeClass
* enum型に対するデフォルトの{@link ValueType}
* @throws NoSuchMethodException
* 指定のクラスにClassを唯一の引数とするコンストラクタがない場合
*/
public static void setEnumDefaultValueType(Class enumDefaultValueTypeClass)
throws NoSuchMethodException {
enumDefaultValueTypeConstructor = enumDefaultValueTypeClass
.getConstructor(new Class[] { Class.class });
}
/**
* enum型の{@link Enum#ordinal() 序数}に対する{@link ValueType}を設定します。
*
* @param enumOrdinalValueTypeClass
* enum型の{@link Enum#ordinal() 序数}に対する{@link ValueType}
* @throws NoSuchMethodException
* 指定のクラスにClassを唯一の引数とするコンストラクタがない場合
*/
public static void setEnumOrdinalValueType(Class enumOrdinalValueTypeClass)
throws NoSuchMethodException {
enumOrdinalValueTypeConstructor = enumOrdinalValueTypeClass
.getConstructor(new Class[] { Class.class });
}
/**
* enum型の{@link Enum#name() 名前}に対する{@link ValueType}を設定します。
*
* @param enumStringValueTypeClass
* enum型の{@link Enum#name() 名前}に対する{@link ValueType}
* @throws NoSuchMethodException
* 指定のクラスにClassを唯一の引数とするコンストラクタがない場合
*/
public static void setEnumStringValueType(Class enumStringValueTypeClass)
throws NoSuchMethodException {
enumStringValueTypeConstructor = enumStringValueTypeClass
.getConstructor(new Class[] { Class.class });
}
/**
* {@link ValueType}を返します。
*
* @param obj
* @return {@link ValueType}
*/
public static ValueType getValueType(Object obj) {
if (obj == null) {
return OBJECT;
}
return getValueType(obj.getClass());
}
/**
* {@link ValueType}を返します。
*
* @param clazz
* @return {@link ValueType}
*/
public static ValueType getValueType(Class clazz) {
if (clazz == null) {
return OBJECT;
}
for (Class c = clazz; c != null && c != Object.class; c = c
.getSuperclass()) {
ValueType valueType = getValueType0(c);
if (valueType != null) {
return valueType;
}
}
ValueType valueType = getCachedValueType(clazz);
if (valueType != null) {
return valueType;
}
return OBJECT;
}
private static ValueType getValueType0(Class clazz) {
return (ValueType) types.get(clazz);
}
private static boolean hasCachedValueType(Class clazz) {
return getCachedValueType(clazz) != null;
}
private static ValueType getCachedValueType(Class clazz) {
if (!initialized) {
initialize();
}
if (Map.class.isAssignableFrom(clazz)) {
return null;
}
ValueType valueType = (ValueType) valueTypeCache.get(clazz.getName());
if (valueType == NULL) {
return null;
}
if (valueType != null) {
return valueType;
}
Class normalizedEnumClass = normalizeEnum(clazz);
if (normalizedEnumClass != null) {
valueType = getEnumDefaultValueType(normalizedEnumClass);
valueTypeCache.put(normalizedEnumClass.getName(), valueType);
return valueType;
}
valueType = createUserDefineValueType(clazz);
if (valueType != null) {
valueTypeCache.put(clazz.getName(), valueType);
return valueType;
}
valueTypeCache.put(clazz.getName(), NULL);
return null;
}
private static Class normalizeEnum(Class clazz) {
if (isEnumMethod == null || enumStringValueTypeConstructor == null) {
return null;
}
for (Class c = clazz; c != null; c = c.getSuperclass()) {
if (MethodUtil.invoke(isEnumMethod, c, null).equals(Boolean.TRUE)) {
return c;
}
}
return null;
}
/**
* enum型に対するデフォルトの{@link ValueType}を作成して返します。
*
* @param clazz
* enum型のクラス
* @return enum型用の{@link ValueType}
*/
public static ValueType getEnumDefaultValueType(Class clazz) {
return (ValueType) ConstructorUtil.newInstance(
enumDefaultValueTypeConstructor, new Class[] { clazz });
}
/**
* enum型の{@link Enum#name() 名前}に対する{@link ValueType}を作成して返します。
*
* @param clazz
* enum型のクラス
* @return enum型の{@link Enum#name() 名前}用の{@link ValueType}
*/
public static ValueType getEnumStringValueType(Class clazz) {
return (ValueType) ConstructorUtil.newInstance(
enumStringValueTypeConstructor, new Class[] { clazz });
}
/**
* enum型の{@link Enum#ordinal() 序数}に対する{@link ValueType}を作成して返します。
*
* @param clazz
* enum型のクラス
* @return enum型の{@link Enum#ordinal() 序数}用の{@link ValueType}
*/
public static ValueType getEnumOrdinalValueType(Class clazz) {
return (ValueType) ConstructorUtil.newInstance(
enumOrdinalValueTypeConstructor, new Class[] { clazz });
}
/**
* ユーザ定義型用の{@link ValueType}を作成して返します。
* <p>
* <code>clazz</code>がユーザ定義型でなければ<code>null</code>を返します。
* </p>
*
* @param clazz
* ユーザ定義型
* @return ユーザ定義型用の{@link ValueType}
*/
public static ValueType createUserDefineValueType(Class clazz) {
List valueOfMethods = new ArrayList();
Method valueMethod = null;
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; ++i) {
Method method = methods[i];
if (MethodUtil.isBridgeMethod(method)
|| MethodUtil.isSyntheticMethod(method)) {
continue;
}
int mod = method.getModifiers();
if (method.getName().equals("valueOf")
&& method.getParameterTypes().length == 1
&& method.getReturnType() == clazz
&& ModifierUtil.isPublic(mod) && ModifierUtil.isStatic(mod)) {
valueOfMethods.add(method);
} else if (method.getName().equals("value")
&& method.getParameterTypes().length == 0
&& ModifierUtil.isPublic(mod)
&& !ModifierUtil.isStatic(mod)) {
valueMethod = method;
}
}
if (valueMethod == null) {
return null;
}
for (int i = 0; i < valueOfMethods.size(); ++i) {
Method valueOfMethod = (Method) valueOfMethods.get(i);
if (valueOfMethod.getParameterTypes()[0] == valueMethod
.getReturnType()) {
Class baseClass = valueMethod.getReturnType();
ValueType baseValueType = getValueType0(baseClass);
if (baseValueType == null) {
return null;
}
return new UserDefineType(baseValueType, valueOfMethod,
valueMethod);
}
}
return null;
}
/**
* sqltypeに応じた {@link Class}を返します。
*
* @param sqltype
* @return {@link Class}
*/
public static Class getType(int sqltype) {
switch (sqltype) {
case Types.TINYINT:
return Byte.class;
case Types.SMALLINT:
return Short.class;
case Types.INTEGER:
return Integer.class;
case Types.BIGINT:
return Long.class;
case Types.REAL:
case Types.FLOAT:
return Float.class;
case Types.DOUBLE:
return Double.class;
case Types.DECIMAL:
case Types.NUMERIC:
return BigDecimal.class;
case Types.DATE:
return Timestamp.class;
case Types.TIME:
return java.sql.Time.class;
case Types.TIMESTAMP:
return Timestamp.class;
case Types.BINARY:
case Types.BLOB:
case Types.VARBINARY:
case Types.LONGVARBINARY:
return BYTE_ARRAY_CLASS;
case Types.CHAR:
case Types.LONGVARCHAR:
case Types.VARCHAR:
return String.class;
case Types.BOOLEAN:
return Boolean.class;
default:
return Object.class;
}
}
/**
* {@link ValueType}を返します。
*
* @param sqltype
* @return {@link ValueType}
*/
public static ValueType getValueType(int sqltype) {
return getValueType(getType(sqltype));
}
/**
* 単純な型かどうかを返します。
*
* @param clazz
* クラス
* @return 単純な型かどうか
*/
public static boolean isSimpleType(Class clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
return types.containsKey(clazz) || hasCachedValueType(clazz);
}
private static class NullType implements ValueType {
public void bindValue(CallableStatement cs, String parameterName,
Object value) throws SQLException {
throw new SQLException("not supported");
}
public void bindValue(PreparedStatement ps, int index, Object value)
throws SQLException {
throw new SQLException("not supported");
}
public Object getValue(CallableStatement cs, int index)
throws SQLException {
throw new SQLException("not supported");
}
public Object getValue(CallableStatement cs, String parameterName)
throws SQLException {
throw new SQLException("not supported");
}
public Object getValue(ResultSet resultSet, int index)
throws SQLException {
throw new SQLException("not supported");
}
public Object getValue(ResultSet resultSet, String columnName)
throws SQLException {
throw new SQLException("not supported");
}
public void registerOutParameter(CallableStatement cs, int index)
throws SQLException {
throw new SQLException("not supported");
}
public void registerOutParameter(CallableStatement cs,
String parameterName) throws SQLException {
throw new SQLException("not supported");
}
public String toText(Object value) {
throw new UnsupportedOperationException("toText");
}
public int getSqlType() {
return Types.NULL;
}
}
}