/*
 * 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.db.expander.query.caller;

import java.sql.Types;
import java.util.List;

import blanco.cg.BlancoCgObjectFactory;
import blanco.cg.valueobject.BlancoCgClass;
import blanco.cg.valueobject.BlancoCgMethod;
import blanco.cg.valueobject.BlancoCgSourceFile;
import blanco.db.common.expander.BlancoDbAbstractMethod;
import blanco.db.common.valueobject.BlancoDbSetting;
import blanco.db.common.valueobject.BlancoDbSqlInfoStructure;
import blanco.db.util.BlancoDbCgUtilDotNet;
import blanco.db.util.BlancoDbMappingUtilDotNet;
import blanco.dbmetadata.valueobject.BlancoDbMetaDataColumnStructure;

/**
 * 個別のメソッドを展開するためのクラス。
 * 
 * @author tosiki iga
 */
public class PrepareCallMethod2DotNet extends BlancoDbAbstractMethod {
    public PrepareCallMethod2DotNet(final BlancoDbSetting argDbSetting,
            final BlancoDbSqlInfoStructure argSqlInfo,
            final BlancoCgObjectFactory argCgFactory,
            final BlancoCgSourceFile argCgSourceFile,
            final BlancoCgClass argCgClass) {
        super(argDbSetting, argSqlInfo, argCgFactory, argCgSourceFile,
                argCgClass);
    }

    public void expand() {
        final BlancoCgMethod cgMethod = fCgFactory.createMethod("PrepareCall",
                "クエリのプリコンパイルを実施します。");
        fCgClass.getMethodList().add(cgMethod);

        cgMethod.getParameterList()
                .add(
                        fCgFactory.createParameter("query", "string",
                                "利用したいクエリ(SQL文)"));

        BlancoDbCgUtilDotNet.addExceptionToMethodSqlException(fCgFactory,
                cgMethod);

        final List listDesc = cgMethod.getLangDoc().getDescriptionList();
        listDesc.add("動的に内容が変化するようなSQLを実行する必要がある場合にのみ、こちらのメソッドを利用します。");
        listDesc
                .add("※SQL文そのものをパラメータとして与えることができて自由度が高い一方、SQLインジェクションと呼ばれるセキュリティホールが発生する危険がある点に注意した上で利用してください。");
        listDesc.add("内部的には SqlCommandオブジェクトを新規作成します。");

        final List listLine = cgMethod.getLineList();

        if (fDbSetting.getLogging()) {
            listLine.add("if (fLog.isDebugEnabled()) {");
            listLine.add("fLog.debug(\"" + cgMethod.getName()
                    + ": query = \" + query);");
            listLine.add("}");
            listLine.add("");
        }

        listLine.add("Close();");

        listLine.add("fStatement = new SqlCommand();");
        listLine.add("fStatement.Connection = fConnection;");
        listLine.add("fStatement.Transaction = fTransaction;");
        listLine.add("fStatement.CommandText = query;");

        if (fDbSetting.getStatementTimeout() >= 0) {
            listLine.add("// ステートメントタイムアウト値についてデフォルト値をセットします。");
            listLine.add("fStatement.CommandTimeout = "
                    + fDbSetting.getStatementTimeout() + ";");
        }

        // ポイント: fStatement.CommandType = CommandType.StoredProcedure
        // を指定すると希望する動作にはなりません。

        for (int indexParameter = 0; indexParameter < fSqlInfo
                .getOutParameterList().size(); indexParameter++) {
            // 現状、とりあえず与えられた順序で登場と仮定が加わっています。
            final BlancoDbMetaDataColumnStructure columnStructure = (BlancoDbMetaDataColumnStructure) fSqlInfo
                    .getOutParameterList().get(indexParameter);

            listLine.add("");
            listLine.add("// SQL出力パラメータ[" + columnStructure.getName()
                    + "]を設定します。");

            final String stmtLine = "SqlParameter sqlOutParameter"
                    + (indexParameter + 1)
                    + " = new SqlParameter(\"@"
                    + columnStructure.getName()
                    + "\", SqlDbType."
                    + BlancoDbMappingUtilDotNet
                            .getDotNetSqlDbType(columnStructure) + ");";
            listLine.add(stmtLine);
            listLine.add("sqlOutParameter" + (indexParameter + 1)
                    + ".Direction = ParameterDirection.Output;");

            if (columnStructure.getDataType() == Types.VARCHAR) {
                listLine.add("// string型は期待するサイズを8000とします。");
                listLine.add("sqlOutParameter" + (indexParameter + 1)
                        + ".Size = 8000;");
            }

            listLine.add("fStatement.Parameters.Add(sqlOutParameter"
                    + (indexParameter + 1) + ");");
        }
    }
}