/*
 * BlancoDbDefaultGenerator.
 * もっとも典型的なBlancoDbのGenerator
 * 
 * Copyright (C) 2005 tosiki.iga@nifty.ne.jp
 */

package blanco.db.helper;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import blanco.db.BlancoDb;
import blanco.db.BlancoDbConstants;
import blanco.db.collector.BlancoDbDatabaseConnection;
import blanco.db.conf.BlancoDbDatabaseConnectionSettingDef;
import blanco.db.conf.BlancoDbMetadata;
import blanco.db.conf.BlancoDbSetting;
import blanco.db.conf.ExecuteQuery;
import blanco.db.conf.SelectQuery;
import blanco.db.exception.BlancoDbException;
import blanco.ig.generator.GenerationMode;

/**
 * blancoDbを利用する際のもっとも基本的なGeneratorを示します。
 */
public abstract class BlancoDbDefaultGenerator implements IBlancoDbProgress {
    private boolean _isSupportOldVersionTableGateway = false;

    public void setSupportOldVersionTableGateway(boolean arg) {
        _isSupportOldVersionTableGateway = arg;
    }

    /**
     * 最も一般的な blancoDbのGenerator処理を行います。
     * 
     * @param jdbcDriver
     *            JDBCドライバクラス名
     * @param jdbcUrl
     *            JDBC接続URI
     * @param jdbcUsername
     *            JDBCユーザ名
     * @param jdbcPassword
     *            JDBCパスワード
     * @param blancoSqlDirectory
     *            blancoSqlが格納されたディレクトリ.null指定の際には処理をスキップします.
     * @param rootPackage
     *            blancoに設定するルートパッケージ
     * @return 正常終了ならtrue, 異常終了ならfalse
     * @throws BlancoDbException
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     * @throws SQLException
     */
    public void process(BlancoDbDatabaseConnectionSettingDef connDef,
            File blancoSqlDirectory, String rootPackage,
            String blancoTargetSourceDirectory) throws SQLException,
            SAXException, IOException, ParserConfigurationException,
            BlancoDbException {
        System.out.println(BlancoDbConstants.PRODUCT_NAME + " ("
                + BlancoDbConstants.VERSION + ") ソースコード生成: 開始.");

        BlancoDbSetting setting = new BlancoDbSetting();
        setting
                .setSupportOldVersionTableGateway(_isSupportOldVersionTableGateway);

        setting.setJdbc(connDef);
        setting.setMode(GenerationMode.OPERATION);
        if (blancoSqlDirectory != null) {
            // 指定がある場合にのみ SQL定義ファイル格納ディレクトリをセットします。
            setting.setQueryDirectory(blancoSqlDirectory.getAbsolutePath());
        }
        if (blancoTargetSourceDirectory != null) {
            setting.setWorkDirectory(blancoTargetSourceDirectory);
        }

        BlancoDbDatabaseConnection dbInfoCollector = new BlancoDbDatabaseConnection();
        try {
            if (dbInfoCollector.connect(connDef) == false) {
                System.err.println("データベース接続に失敗しました。処理中断します。");
                return;
            }
            dbInfoCollector.getDatabaseVersionInfo();

            BlancoDb blancoDb = new BlancoDb(setting);
            setting.setRootNameSpace(rootPackage);

            if (blancoSqlDirectory != null) {
                // 指定がある場合にのみ SQL定義ファイル格納ディレクトリを処理します。
                File[] fileSettingXml = blancoSqlDirectory.listFiles();
                for (int index = 0; index < fileSettingXml.length; index++) {
                    if (fileSettingXml[index].getName().endsWith(".xml") == false) {
                        // ファイルの拡張子が xml であるもののみ処理します。
                        continue;
                    }
                    if (progress(index + 1, fileSettingXml.length,
                            fileSettingXml[index].getName()) == false) {
                        break;
                    }

                    // メタ情報は毎回 破棄します。
                    BlancoDbMetadata metadata = new BlancoDbMetadata();
                    parseAndProcessBlancoDbSettingFile(fileSettingXml[index],
                            metadata);

                    // 生成はファイル毎に行います。
                    blancoDb.generate(metadata, dbInfoCollector);
                    if (_isSupportOldVersionTableGateway) {
                        blancoDb
                                .generateTableGateway(metadata, dbInfoCollector);
                    }
                }
            }

        } finally {
            dbInfoCollector.close();
            System.out.println("ソースコード生成: 終了.");
        }
    }

    /**
     * BlancoDb SQL定義ファイルをパース処理して、必要があればメタ情報に追加します。 このメソッドは 外部から呼び出される場合があります。
     * 
     * @param fileSettingXml
     *            入力となるblancoDb設定ファイル
     * @param metadata
     *            出力となるメタデータ
     */
    public void parseAndProcessBlancoDbSettingFile(File fileSettingXml,
            BlancoDbMetadata metadata) {
        System.out.println("ファイル[" + fileSettingXml.getAbsolutePath()
                + "]を処理します");

        DocumentBuilderFactory documentFactory = DocumentBuilderFactory
                .newInstance();
        DocumentBuilder documentBuilder = null;
        Document document = null;
        try {
            documentBuilder = documentFactory.newDocumentBuilder();
        } catch (ParserConfigurationException ex1) {
            System.err.println("ドキュメント作成時に例外が発生しました.:" + ex1.toString());
            ex1.printStackTrace();
            return;
        }
        try {
            document = documentBuilder.parse(fileSettingXml);
        } catch (SAXException e) {
            System.err.println("ドキュメントパース時に例外が発生しました:" + e.toString());
            e.printStackTrace();
            return;
        } catch (IOException e) {
            System.err.println("ドキュメントパース時に例外が発生しました:" + e.toString());
            e.printStackTrace();
            return;
        }
        Element blancoDbElement = document.getDocumentElement();
        if (blancoDbElement == null) {
            System.out.println("blanco-dbルートノードが見つかりません");
            return;
        }
        NodeList rootNodeList = blancoDbElement.getChildNodes();
        if (rootNodeList == null) {
            return;
        }
        int length = rootNodeList.getLength();
        for (int indexNodeList = 0; indexNodeList < length; indexNodeList++) {
            Node node = rootNodeList.item(indexNodeList);
            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            Element eleCommandQuery = (Element) node;
            String nodeName = eleCommandQuery.getNodeName();
            if (nodeName.equals(BlancoDbConstants.QUERY_ITERATOR)) {
                System.out.println("検索型 クエリ名["
                        + eleCommandQuery.getAttribute("name") + "]");
                metadata.addSelectQuery(new SelectQuery(eleCommandQuery
                        .getAttribute("name"), fileSettingXml.getName()));
            } else if (nodeName.equals(BlancoDbConstants.QUERY_INVOKER)) {
                System.out.println("実行型 クエリ名["
                        + eleCommandQuery.getAttribute("name") + "]");
                metadata.addExecuteQuery(new ExecuteQuery(eleCommandQuery
                        .getAttribute("name"), fileSettingXml.getName()));
            }
        }
    }
}