/*
 * blanco Framework
 * Copyright (C) 2004-2006 IGA Tosiki
 * 
 * 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.
 */
package blanco.dbmetadata;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;

import blanco.dbmetadata.valueobject.BlancoDbMetaDataColumnStructure;

/**
 * データベースからメタ情報を取得するユーティリティ。
 * 
 * 基本的なメソッドが含まれます。
 * 
 * @author IGA Tosiki
 */
public class BlancoDbMetaDataUtil {
    /**
     * 指定された情報をもとにデータベースに接続します。
     * 
     * @param argJdbcDriverName
     *            JDBCドライバ名。
     * @param argJdbcUrl
     *            JDBC接続先URL。
     * @param argJdbcUser
     *            JDBC接続ユーザ名。
     * @param argJdbcPassword
     *            JDBC接続パスワード。
     * @return データベース接続。
     * @throws IllegalArgumentException
     *             接続時に例外が発生した場合。
     */
    public static Connection connect(final String argJdbcDriverName,
            final String argJdbcUrl, final String argJdbcUser,
            final String argJdbcPassword) {
        Connection conn = null;

        try {
            Class.forName(argJdbcDriverName);
        } catch (ClassNotFoundException ex) {
            throw new IllegalArgumentException("データベース接続: JDBCドライバクラス["
                    + argJdbcDriverName + "]ロードに失敗しました: " + ex.toString());
        }

        try {
            conn = DriverManager.getConnection(argJdbcUrl, argJdbcUser,
                    argJdbcPassword);
        } catch (SQLException ex) {
            throw new IllegalArgumentException("データベース接続: JDBC接続に失敗しました: "
                    + ex.toString());
        }

        return conn;
    }

    /**
     * JDBCデータタイプをJavaの文字列に読み替えます。
     * 
     * [java.sql.Types.]を前方に付与するとJava JDBCの定数になります。
     * 
     * @param argJdbcDataType
     * @return
     */
    public final static String convertJdbcDataTypeToString(
            final int argJdbcDataType) {
        switch (argJdbcDataType) {
        case Types.BIT:
            return "BIT";
        case Types.TINYINT:
            return "TINYINT";
        case Types.SMALLINT:
            return "SMALLINT";
        case Types.INTEGER:
            return "INTEGER";
        case Types.BIGINT:
            return "BIGINT";
        case Types.FLOAT:
            return "FLOAT";
        case Types.REAL:
            return "REAL";
        case Types.DOUBLE:
            return "DOUBLE";
        case Types.NUMERIC:
            return "NUMERIC";
        case Types.DECIMAL:
            return "DECIMAL";
        case Types.CHAR:
            return "CHAR";
        case Types.VARCHAR:
            return "VARCHAR";
        case Types.LONGVARCHAR:
            return "LONGVARCHAR";
        case Types.DATE:
            return "DATE";
        case Types.TIME:
            return "TIME";
        case Types.TIMESTAMP:
            return "TIMESTAMP";
        case Types.BINARY:
            return "BINARY";
        case Types.VARBINARY:
            return "VARBINARY";
        case Types.LONGVARBINARY:
            return "LONGVARBINARY";
        case Types.NULL:
            return "NULL";
        case Types.OTHER:
            return "OTHER";
        case Types.JAVA_OBJECT:
            return "JAVA_OBJECT";
        case Types.DISTINCT:
            return "DISTINCT";
        case Types.STRUCT:
            return "STRUCT";
        case Types.ARRAY:
            return "ARRAY";
        case Types.BLOB:
            return "BLOB";
        case Types.CLOB:
            return "CLOB";
        case Types.REF:
            return "REF";
        case Types.DATALINK:
            return "DATALINK";
        case Types.BOOLEAN:
            return "BOOLEAN";
        default:
            // 合致しなかった場合には 空白を戻します。
            return "";
        }
    }

    /**
     * JDBCデータタイプ文字列をJavaのTypesに読み替えます。
     * 
     * @param argJdbcDataType
     *            [java.sql.Types.]を前方に付与するとJava JDBCの定数になる値。
     * @return java.sql.Typesの値。ヒットしなかった場合には Integer.MIN_VALUE。
     */
    public final static int convertJdbcDataType2Int(final String argJdbcDataType) {
        if (argJdbcDataType.equals("BIT")) {
            return Types.BIT;
        }
        if (argJdbcDataType.equals("TINYINT")) {
            return Types.TINYINT;
        }
        if (argJdbcDataType.equals("SMALLINT")) {
            return Types.SMALLINT;
        }
        if (argJdbcDataType.equals("INTEGER")) {
            return Types.INTEGER;
        }
        if (argJdbcDataType.equals("BIGINT")) {
            return Types.BIGINT;
        }
        if (argJdbcDataType.equals("FLOAT")) {
            return Types.FLOAT;
        }
        if (argJdbcDataType.equals("REAL")) {
            return Types.REAL;
        }
        if (argJdbcDataType.equals("DOUBLE")) {
            return Types.DOUBLE;
        }
        if (argJdbcDataType.equals("NUMERIC")) {
            return Types.NUMERIC;
        }
        if (argJdbcDataType.equals("DECIMAL")) {
            return Types.DECIMAL;
        }
        if (argJdbcDataType.equals("CHAR")) {
            return Types.CHAR;
        }
        if (argJdbcDataType.equals("VARCHAR")) {
            return Types.VARCHAR;
        }
        if (argJdbcDataType.equals("LONGVARCHAR")) {
            return Types.LONGVARCHAR;
        }
        if (argJdbcDataType.equals("DATE")) {
            return Types.DATE;
        }
        if (argJdbcDataType.equals("TIME")) {
            return Types.TIME;
        }
        if (argJdbcDataType.equals("TIMESTAMP")) {
            return Types.TIMESTAMP;
        }
        if (argJdbcDataType.equals("BINARY")) {
            return Types.BINARY;
        }
        if (argJdbcDataType.equals("VARBINARY")) {
            return Types.VARBINARY;
        }
        if (argJdbcDataType.equals("LONGVARBINARY")) {
            return Types.LONGVARBINARY;
        }
        if (argJdbcDataType.equals("NULL")) {
            return Types.NULL;
        }
        if (argJdbcDataType.equals("OTHER")) {
            return Types.OTHER;
        }
        if (argJdbcDataType.equals("JAVA_OBJECT")) {
            return Types.JAVA_OBJECT;
        }
        if (argJdbcDataType.equals("DISTINCT")) {
            return Types.DISTINCT;
        }
        if (argJdbcDataType.equals("STRUCT")) {
            return Types.STRUCT;
        }
        if (argJdbcDataType.equals("ARRAY")) {
            return Types.ARRAY;
        }
        if (argJdbcDataType.equals("BLOB")) {
            return Types.BLOB;
        }
        if (argJdbcDataType.equals("CLOB")) {
            return Types.CLOB;
        }
        if (argJdbcDataType.equals("REF")) {
            return Types.REF;
        }
        if (argJdbcDataType.equals("DATALINK")) {
            return Types.DATALINK;
        }
        if (argJdbcDataType.equals("BOOLEAN")) {
            return Types.BOOLEAN;
        }

        // 確定しなかった場合。
        return Integer.MIN_VALUE;
    }

    /**
     * JDBC の nullableをJavaの文字列に読み替えます。
     * 
     * [java.sql.ResultSetMetaData.]を前方に付与するとJava JDBCの定数になります。
     * 
     * @param argJdbcDataType
     * @return
     */
    public final static String convertJdbcNullableToString(
            final int argJdbcDataType) {
        switch (argJdbcDataType) {
        case ResultSetMetaData.columnNoNulls:
            return "columnNoNulls";
        case ResultSetMetaData.columnNullable:
            return "columnNullable";
        case ResultSetMetaData.columnNullableUnknown:
            return "columnNullableUnknown";
        default:
            // 合致しなかった場合には 空白を戻します。
            return "";
        }
    }

    /**
     * データベース上のタイプ名からデータタイプを引き当てます。
     * 
     * ORACLEにおいて
     * TIMESTAMP型に対してサイズ指定をおこなった場合にOTHERになってしまいます。その際に名称からタイプ名を引き当てる処理をおこないます。
     * 
     * OTHER や 不定 の場合にのみ、この処理を実施するようにしてください。
     * 
     * @param columnStructure
     * @return trueなら引き当ての結果更新された。 falseなら何も実施されていない。
     */
    public static final boolean mapTypeName2DataType(
            final BlancoDbMetaDataColumnStructure columnStructure) {

        // データベース上の型名を取得します。
        String wrkTypeName = columnStructure.getTypeName().toUpperCase();

        // ()によるサイズ指定を切り取ります。
        int indexOfTrim = wrkTypeName.indexOf('(');
        if (indexOfTrim > 0) {
            wrkTypeName = wrkTypeName.substring(0, indexOfTrim);
        }

        // JDBCタイプ名の文字列を使って、型名引き当てを実施します。
        final int jdbcDataType = convertJdbcDataType2Int(wrkTypeName);
        if (jdbcDataType != Integer.MIN_VALUE) {
            // 導出されたデータタイプを正として扱います。
            columnStructure.setDataType(jdbcDataType);
            return true;
        }

        return false;
    }
}
