/*
 * 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.service;

import java.io.File;
import java.util.List;

import blanco.cg.BlancoCgObjectFactory;
import blanco.cg.transformer.BlancoCgTransformerFactory;
import blanco.cg.valueobject.BlancoCgClass;
import blanco.cg.valueobject.BlancoCgField;
import blanco.cg.valueobject.BlancoCgMethod;
import blanco.cg.valueobject.BlancoCgSourceFile;
import blanco.commons.util.BlancoJavaSourceUtil;
import blanco.commons.util.BlancoStringUtil;
import blanco.service.valueobject.BlancoServiceStructure;

/**
 * サーブレットのソースコードを自動生成します。
 * 
 * @author IGA Tosiki
 */
class BlancoServiceXml2SourceFileServlet {
    /**
     * 内部的に利用するblancoCg用ファクトリ。
     */
    private BlancoCgObjectFactory fCgFactory = null;

    /**
     * 内部的に利用するblancoCg用ソースファイル情報。
     */
    private BlancoCgSourceFile fCgSourceFile = null;

    /**
     * 内部的に利用するblancoCg用クラス情報。
     */
    private BlancoCgClass fCgClass = null;

    /**
     * 収集された情報を元に、ソースコードを自動生成します。
     * 
     * @param argProcessStructure
     *            メタファイルから収集できた処理構造データ。
     * @param argDirectoryTarget
     *            ソースコードの出力先フォルダ。
     */
    public void process(final BlancoServiceStructure argProcessStructure,
            final File argDirectoryTarget) {

        // 従来と互換性を持たせるため、/mainサブフォルダに出力します。
        final File fileBlancoMain = new File(argDirectoryTarget
                .getAbsolutePath()
                + "/main");

        fCgFactory = BlancoCgObjectFactory.getInstance();
        fCgSourceFile = fCgFactory.createSourceFile(argProcessStructure
                .getPackage(), "このソースコードは blanco Frameworkによって自動生成されています。");
        fCgClass = fCgFactory.createClass(argProcessStructure.getName(),
                "サービス定義ID[" + argProcessStructure.getName() + "]のサーブレットクラス。");
        fCgSourceFile.getClassList().add(fCgClass);

        fCgClass.getExtendClassList().add(
                fCgFactory.createType("javax.servlet.http.HttpServlet"));
        if (BlancoStringUtil.null2Blank(argProcessStructure.getDescription())
                .length() > 0) {
            fCgClass.getLangDoc().getDescriptionList().add(
                    "説明:" + argProcessStructure.getDescription() + "<br>");
        }
        fCgClass.getLangDoc().getDescriptionList().add(
                "このクラスは、Javaで書かれたアプリケーションをアプリケーションサーバ上に常駐させるための仕組みの一部です。<br>");
        fCgClass
                .getLangDoc()
                .getDescriptionList()
                .add(
                        "このクラスはサーブレットです。アプリケーションサーバの起動時に呼び出されるサーブレットとして登録することによって常駐を実現します。<br>");
        fCgClass.getLangDoc().getDescriptionList().add("<br>");
        fCgClass
                .getLangDoc()
                .getDescriptionList()
                .add(
                        "アプリケーションサーバの web.xmlに下記の記述（またはそれに相当する内容）を記載することにより、常駐化することができます。<br>");
        fCgClass.getLangDoc().getDescriptionList().add("<pre>");
        fCgClass.getLangDoc().getDescriptionList().add(
                BlancoJavaSourceUtil.escapeStringAsJavaDoc("<servlet>"));
        fCgClass.getLangDoc().getDescriptionList().add(
                BlancoJavaSourceUtil.escapeStringAsJavaDoc("  <servlet-name>"
                        + argProcessStructure.getName() + "</servlet-name>"));
        fCgClass.getLangDoc().getDescriptionList().add(
                BlancoJavaSourceUtil.escapeStringAsJavaDoc("  <servlet-class>"
                        + argProcessStructure.getPackage() + "."
                        + argProcessStructure.getName() + "</servlet-class>"));
        fCgClass
                .getLangDoc()
                .getDescriptionList()
                .add(
                        BlancoJavaSourceUtil
                                .escapeStringAsJavaDoc("  <load-on-startup>1</load-on-startup>"));
        fCgClass.getLangDoc().getDescriptionList().add(
                BlancoJavaSourceUtil.escapeStringAsJavaDoc("</servlet>"));
        fCgClass.getLangDoc().getDescriptionList().add("</pre>");

        expandField(argProcessStructure);

        expandMethodInit(argProcessStructure);
        expandMethodDestory(argProcessStructure);

        BlancoCgTransformerFactory.getJavaSourceTransformer().transform(
                fCgSourceFile, fileBlancoMain);
    }

    /**
     * フィールドを展開します。
     * 
     * @param argProcessStructure
     *            メタファイルから収集できた処理構造データ。
     */
    private void expandField(final BlancoServiceStructure argProcessStructure) {

        {
            final BlancoCgField field = fCgFactory.createField("fLockObject",
                    "java.lang.Object", "起動したかどうかのフラグを保護するためのミューテックスオブジェクト。");
            fCgClass.getFieldList().add(field);

            field.setAccess("protected");
            field.setDefault("new Object()");
        }

        {
            final BlancoCgField field = fCgFactory.createField("fIsStarted",
                    "boolean", "この常駐が起動したかどうかを記憶するフラグ。");
            fCgClass.getFieldList().add(field);

            field.setAccess("protected");
            field.setDefault("false");
        }

        {
            final BlancoCgField field = fCgFactory.createField("fProcess",
                    argProcessStructure.getPackage() + ".Abstract"
                            + argProcessStructure.getName() + "Process",
                    "この常駐の実際の処理クラス。");
            fCgClass.getFieldList().add(field);

            field.setAccess("protected");
            field.setDefault("null");
        }
    }

    /**
     * init メソッドを展開します。
     * 
     * @param argProcessStructure
     *            メタファイルから収集できた処理構造データ。
     */
    private void expandMethodInit(
            final BlancoServiceStructure argProcessStructure) {

        final BlancoCgMethod method = fCgFactory.createMethod("init",
                "サービスのエントリポイント。");
        fCgClass.getMethodList().add(method);

        method.getThrowList().add(
                fCgFactory.createException("javax.servlet.ServletException",
                        "サーブレットに関する例外が発生した場合。"));

        final List listLine = method.getLineList();

        listLine.add("synchronized (fLockObject) {");
        listLine.add("// オブジェクトによるロックによって、再入を防止しています。");
        listLine.add("");
        listLine.add("System.out.println(\"" + argProcessStructure.getName()
                + ": init(): begin.\");");
        listLine.add("if (fIsStarted) {");
        listLine.add("System.out.println(\"" + argProcessStructure.getName()
                + ": init(): already started.\");");
        listLine.add("return;");
        listLine.add("}");
        listLine.add("");
        listLine.add("// 起動したことを示すフラグをtrueにセットします。");
        listLine
                .add("// このフラグをセットした後で例外が発生して初期化処理が中断されたとしても、もはや起動した状態であるとみなします。");
        listLine.add("fIsStarted = true;");
        listLine.add("");
        if (BlancoStringUtil.null2Blank(argProcessStructure.getDescription())
                .length() > 0) {
            listLine.add("System.out.println(\""
                    + argProcessStructure.getName()
                    + ":説明["
                    + BlancoJavaSourceUtil
                            .escapeStringAsJavaSource(argProcessStructure
                                    .getDescription()) + "]\");");
        }
        listLine.add("");
        listLine.add("// 実際に常駐させるクラスのインスタンスを作成します。");
        listLine.add("// ※ポイント");
        listLine.add("// 下記の行でコンパイルエラーが出る場合には、まだ "
                + argProcessStructure.getName() + "Process を作成していないことが考えられます。");
        listLine.add("// Abstract" + argProcessStructure.getName()
                + "Process クラスを継承して " + argProcessStructure.getPackage()
                + " パッケージに " + argProcessStructure.getName()
                + "Process クラスを作成することにより解決できることが多いです。");
        listLine.add("fProcess = new " + argProcessStructure.getName()
                + "Process();");
        listLine.add("new Thread(fProcess).start();");
        listLine.add("");
        listLine.add("System.out.println(\"" + argProcessStructure.getName()
                + ": init(): end.\");");
        listLine.add("}");
    }

    /**
     * exit メソッドを展開します。
     * 
     * @param argProcessStructure
     *            メタファイルから収集できた処理構造データ。
     */
    private void expandMethodDestory(
            final BlancoServiceStructure argProcessStructure) {

        final BlancoCgMethod method = fCgFactory.createMethod("destroy",
                "サービスの破棄通知。");
        fCgClass.getMethodList().add(method);

        final List listLine = method.getLineList();

        listLine.add("synchronized (fLockObject) {");
        listLine.add("// オブジェクトによるロックによって、再入を防止しています。");
        listLine.add("");
        listLine.add("System.out.println(\"" + argProcessStructure.getName()
                + ": destroy(): begin.\");");
        listLine.add("if (fIsStarted == false) {");
        listLine.add("System.out.println(\"" + argProcessStructure.getName()
                + ": destroy(): not started.\");");
        listLine.add("return;");
        listLine.add("}");
        listLine.add("");
        listLine.add("// 起動したことを示すフラグをfalseにセットします。");
        listLine.add("fIsStarted = false;");
        listLine.add("");
        listLine.add("");
        listLine.add("// 終了処理を行います。");
        listLine.add("fProcess.stop();");
        listLine.add("fProcess = null;");
        listLine.add("");
        listLine.add("System.out.println(\"" + argProcessStructure.getName()
                + ": destroy(): end.\");");
        listLine.add("}");
    }
}
