/*
 * blancoDb
 * Copyright (C) 2004-2006 Yasuo Nakanishi
 * 
 * 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.iterator;

import blanco.cg.BlancoCgObjectFactory;
import blanco.cg.valueobject.BlancoCgSourceFile;
import blanco.commons.util.BlancoNameAdjuster;
import blanco.db.common.expander.BlancoDbAbstractClass;
import blanco.db.common.stringgroup.BlancoDbSqlInfoScrollStringGroup;
import blanco.db.common.util.BlancoDbUtil;
import blanco.db.common.valueobject.BlancoDbSetting;
import blanco.db.common.valueobject.BlancoDbSqlInfoStructure;
import blanco.db.expander.query.Finalize;
import blanco.db.expander.query.GetQueryMethod;
import blanco.db.expander.query.GetStatementMethod;
import blanco.db.expander.query.PrepareStatementMethod;
import blanco.db.expander.query.PrepareStatementMethod2;
import blanco.db.expander.query.QueryConstructor;
import blanco.db.expander.query.field.ConnectionField;
import blanco.db.expander.query.field.LogField;
import blanco.db.expander.query.field.LogSqlDynamicSqlField;
import blanco.db.expander.query.field.LogSqlInParamField;
import blanco.db.expander.query.field.ResultSetField;
import blanco.db.expander.query.field.StatementField;
import blanco.db.resourcebundle.BlancoDbResourceBundle;
import blanco.dbmetadata.valueobject.BlancoDbMetaDataColumnStructure;

/**
 * 個別のクラスを展開するためのクラス。
 * 
 * @author Yasuo Nakanishi
 */
public class QueryIteratorClass extends BlancoDbAbstractClass {
    private final BlancoDbResourceBundle fBundle = new BlancoDbResourceBundle();

    public QueryIteratorClass(final BlancoDbSetting argDbSetting,
            final BlancoDbSqlInfoStructure argSqlInfo,
            final BlancoCgObjectFactory argCgFactory) {
        super(argDbSetting, argSqlInfo, argCgFactory);
    }

    public BlancoCgSourceFile expand() {
        final String className = BlancoNameAdjuster.toClassName(fSqlInfo
                .getName()) + "Iterator";

        fCgSourceFile = fCgFactory.createSourceFile(
                BlancoDbUtil.getBasePackage(fSqlInfo, fDbSetting) + ".query",
                "This code is generated by blanco Framework.");
        fCgClass = fCgFactory.createClass(className, "[" + fSqlInfo.getName()
                + "] " + fSqlInfo.getDescription() + " (QueryIterator)");
        fCgSourceFile.getClassList().add(fCgClass);

        // アノテーションを付与します。
        fCgClass.getAnnotationList().add("BlancoGeneratedBy(name = \"blancoDb\")");
        fCgSourceFile.getImportList().add("blanco.fw.BlancoGeneratedBy");

        fCgClass.getLangDoc().getDescriptionList()
                .add("検索型SQL文をラッピングして各種アクセサを提供します。<br>");
        if (fSqlInfo.getSingle()) {
            fCgClass.getLangDoc().getDescriptionList()
                    .add("シングル属性: 有効 (期待する処理件数は1件)<br>");
        }
        fCgClass.getLangDoc()
                .getDescriptionList()
                .add("スクロール属性: "
                        + new BlancoDbSqlInfoScrollStringGroup()
                                .convertToString(fSqlInfo.getScroll()) + "<br>");
        if (fSqlInfo.getUpdatable()) {
            fCgClass.getLangDoc().getDescriptionList().add("更新可能属性: 有効<br>");
        }

        // BlancoDbUtilは常にインポートします。
        fCgSourceFile.getImportList().add(
                BlancoDbUtil.getRuntimePackage(fDbSetting)
                        + ".util.BlancoDbUtil");

        new ConnectionField(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();
        new StatementField(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass, false).expand();
        new ResultSetField(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        new QueryConstructor(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        new GetQueryMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        new PrepareStatementMethod(fDbSetting, fSqlInfo, fCgFactory,
                fCgSourceFile, fCgClass).expand();
        new PrepareStatementMethod2(fDbSetting, fSqlInfo, fCgFactory,
                fCgSourceFile, fCgClass).expand();

        // パラメータがある場合にのみ bindメソッドを生成します。
        if (fSqlInfo.getInParameterList().size() > 0) {
            new SetInputParameterMethod(fDbSetting, fSqlInfo, fCgFactory,
                    fCgSourceFile, fCgClass, false).expand();
        }

        new ExecuteQueryMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        new NextMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        // カーソル属性がtrueの際には previousメソッドなどの
        // スクロールカーソル関連メソッドを追加生成
        // 前提: カーソル属性がtrueの場合にはシングル属性はfalse
        if (fSqlInfo.getScroll() == BlancoDbSqlInfoScrollStringGroup.TYPE_SCROLL_INSENSITIVE
                || fSqlInfo.getScroll() == BlancoDbSqlInfoScrollStringGroup.TYPE_SCROLL_SENSITIVE
                || fSqlInfo.getScroll() == BlancoDbSqlInfoScrollStringGroup.NOT_DEFINED) {
            // TODO 1.6.4との互換性確保のため BlancoDbSqlInfoScrollStringGroup.NOT_DEFINED
            // の場合にもスクロール関連メソッドを生成しています。

            new PreviousMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                    fCgClass).expand();
            new FirstMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                    fCgClass).expand();
            new LastMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                    fCgClass).expand();

            new AbsoluteMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                    fCgClass).expand();
            new RelativeMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                    fCgClass).expand();
        }

        new GetRowMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        if (fBundle.getExpanderDisableGetStatement().equals("true") == false) {
            // 1.6.8以前と互換性を持たせる必要がある場合にのみ getStatementを生成しません。
            new GetStatementMethod(fDbSetting, fSqlInfo, fCgFactory,
                    fCgSourceFile, fCgClass, false).expand();
        }

        new GetResultSetMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        if (fSqlInfo.getSingle()) {
            new GetSingleRowMethod(fDbSetting, fSqlInfo, fCgFactory,
                    fCgSourceFile, fCgClass).expand();
        }

        if (fSqlInfo.getSingle() == false) {
            new GetListMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                    fCgClass).expand();
        }

        // 更新可能属性の場合にのみの生成です。
        if (fSqlInfo.getUpdatable()) {
            // 検索結果の列の数だけ、updateメソッドを生成
            boolean isAllFieldReadOnly = true;
            for (int index = 0; index < fSqlInfo.getResultSetColumnList()
                    .size(); index++) {
                final BlancoDbMetaDataColumnStructure columnStructure = (BlancoDbMetaDataColumnStructure) fSqlInfo
                        .getResultSetColumnList().get(index);

                // ResultSetMetaDataが Writableであれば それ以上は条件を加えずにメソッドを生成します。
                if (columnStructure.getWritable()) {
                    new UpdateObjectMethod(fDbSetting, fSqlInfo, fCgFactory,
                            fCgSourceFile, fCgClass, columnStructure).expand();
                    isAllFieldReadOnly = false;
                }
            }
            if (isAllFieldReadOnly == false) {
                new UpdateRowMethod(fDbSetting, fSqlInfo, fCgFactory,
                        fCgSourceFile, fCgClass).expand();
            }
        }

        new CloseMethod(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                fCgClass).expand();

        new Finalize(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile, fCgClass)
                .expand();

		if (fDbSetting.getLoggingsql()) {
			// 標準出力に出力。
			new LogSqlInParamField(fDbSetting, fSqlInfo, fCgFactory,
					fCgSourceFile, fCgClass).expand();
			if (fSqlInfo.getDynamicSql()) {
				new LogSqlDynamicSqlField(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile, fCgClass).expand();
			}
		}

        if (fDbSetting.getLogging()) {
            fCgSourceFile.getImportList().add(
                    "org.apache.commons.logging.LogFactory");
            new LogField(fDbSetting, fSqlInfo, fCgFactory, fCgSourceFile,
                    fCgClass).expand();
        }

        return fCgSourceFile;
    }
}