/*
 * blanco Framework
 * Copyright(C) 2006-2008 NTT DATA BUSINESS BRAINS CORPORATION.
 * 
 * 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.soap.terasoluna.rich;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.transform.TransformerException;

import blanco.commons.util.BlancoJavaSourceUtil;
import blanco.commons.util.BlancoNameAdjuster;
import blanco.commons.util.BlancoStringUtil;
import blanco.ig.expander.ClassExpander;
import blanco.ig.expander.Scope;
import blanco.ig.expander.Type;
import blanco.ig.expander.Value;
import blanco.ig.expander.field.FieldExpander;
import blanco.ig.expander.method.MethodExpander;
import blanco.wsdl.parser.BlancoWsdlParser;
import blanco.wsdl.valueobject.BlancoWsdlWebService;
import blanco.wsdl.valueobject.BlancoWsdlWebServiceOperation;

/**
 * WSDLからControllerを自動生成するクラス。
 * 
 * このクラスは、blancoSOAPのTERASOLUNA Rich対応拡張の一部となります。
 * なお、Controllerクラスは、他のクラスと異なり、名前変形しない点に注意すること。
 * 
 * @author IGA Tosiki
 * @version 1.0
 */
public class TerasolunaBLogicWsdl2Controller {
    /**
     * WSDLファイルの解析結果
     */
    private BlancoWsdlWebService fWsdl = null;

    /**
     * 電文処理定義書情報へのマップ。
     */
    private HashMap fMapTelegramProcessDef = null;

    /**
     * おのおののWSDLファイルを処理する。
     * 
     * WSDLファイルを入力として ControllerのためのJavaソースコードを生成する。
     * 
     * @param fileSource
     *            WSDLファイル。
     * @param mapTelegramProcessDef
     *            電文処理定義のMAP
     * @param directoryTarget
     *            Javaソースコードの出力先ディレクトリ。
     * @throws IOException
     *             入出力例外が発生した場合。
     * @throws TransformerException
     *             XML変換例外が発生した場合。
     */
    public void process(final File fileSource,
            final HashMap mapTelegramProcessDef, final File directoryTarget)
            throws IOException, TransformerException {
        fWsdl = new BlancoWsdlParser().parse(fileSource);
        fMapTelegramProcessDef = mapTelegramProcessDef;

        // WSDL内のおのおののoperationを展開する。
        final ArrayList listOperation = fWsdl.getOperationList();
        for (int indexOperation = 0; indexOperation < listOperation.size(); indexOperation++) {
            final BlancoWsdlWebServiceOperation operation = (BlancoWsdlWebServiceOperation) listOperation
                    .get(indexOperation);

            expandOperation(operation, directoryTarget);
        }
    }

    /**
     * WSDLのoperationの記述に従い Controller情報を展開する。
     * 
     * @param operation
     *            WSDL内のオペレーション
     * @param directoryTarget
     *            Javaソースコード出力先ディレクトリ。
     * @throws IOException
     *             入出力例外が発生した場合。
     */
    private void expandOperation(final BlancoWsdlWebServiceOperation operation,
            final File directoryTarget) throws IOException {
        // 電文処理定義書のマップから、電文定義書の情報をバリューオブジェクトのもと情報として取り出す。
        final BlancoWsdlWebServiceOperation telegramProcessDefInfo = TerasolunaBLogicUtil
                .getTelegramProcessorDef(operation.getName(),
                        fMapTelegramProcessDef);

        final String targetBLogicPackage = fWsdl.getPackage();
        // コントローラ名は原則名前変形しないのですが、やむを得ず先頭だけは大文字化します。
        final String targetControllerName = BlancoNameAdjuster
                .toUpperCaseTitle(operation.getName());

        // ※Controllerの名前そのものは、名前変形しない点に注意!
        final ClassExpander classExpander = new ClassExpander(new Type(
                targetBLogicPackage, targetControllerName + "Controller")) {
            protected void expandClassStruct() {
                final Type typeController = new Type(
                        "jp.terasoluna.fw.web.rich.springmvc.controller.TerasolunaController");
                typeController.setGenerics(BlancoNameAdjuster
                        .toClassName(operation.getInput().getName())
                        + ", "
                        + BlancoNameAdjuster.toClassName(operation.getOutput()
                                .getName()));
                setSuperClass(typeController);

                addFileComment("Rich版のコントローラを担うクラスです。 <br>");
                addFileComment("※このクラスは blancoにより自動生成されています。");

                getJavaDoc().addLine(
                        "電文処理ID[" + operation.getName()
                                + "]のコントローラクラス(Controller)です。");
                getJavaDoc().addLine("");
                if (BlancoStringUtil.null2Blank(
                        telegramProcessDefInfo.getDescription()).length() > 0) {
                    // 「説明」が存在する場合にのみ出力します。
                    getJavaDoc()
                            .addLine(
                                    "説明: "
                                            + BlancoJavaSourceUtil
                                                    .escapeStringAsJavaDoc(telegramProcessDefInfo
                                                            .getDescription())
                                            + "<br>");
                }
                getJavaDoc().addLine(
                        "このクラスはフレームワーク(TERASOLUNA)からのみ呼び出されます。<br>");
                getJavaDoc().addLine("※業務からはこのクラスは直接呼び出さないでください。<br>");

                getJavaDoc().addLine("<br>");
                getJavaDoc().addLine("Controller定義の具体例を下記に示します。<br>");
                getJavaDoc().addLine("<pre>");

                // bean定義の具体的な記述内容を JavaDocに展開する。
                // コントローラ名は名前変形しない点に注意。
                getJavaDoc().addLine(
                        BlancoJavaSourceUtil
                                .escapeStringAsJavaDoc("<bean name=\"/"
                                        + operation.getName() + "Controller\"")
                                + "<br>");
                getJavaDoc()
                        .addLine(
                                BlancoJavaSourceUtil
                                        .escapeStringAsJavaDoc("    class=\""
                                                + targetBLogicPackage + "."
                                                + targetControllerName
                                                + "Controller\"")
                                        + "<br>");
                getJavaDoc()
                        .addLine(
                                BlancoJavaSourceUtil
                                        .escapeStringAsJavaDoc("    parent=\"pojoXmlRequestCastorViewController\" singleton=\"false\">")
                                        + "<br>");
                getJavaDoc().addLine(
                        BlancoJavaSourceUtil
                                .escapeStringAsJavaDoc("    <property name=\""
                                        + BlancoNameAdjuster
                                                .toParameterName(operation
                                                        .getName())
                                        + "BLogic\" ref=\""
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogic\" />")
                                + "<br>");
                getJavaDoc().addLine(
                        BlancoJavaSourceUtil.escapeStringAsJavaDoc("</bean>"));
                getJavaDoc().addLine("</pre>");

                // ビジネスロジックのインスタンスを記憶するフィールド。
                final FieldExpander blogic = new FieldExpander(new Type(
                        targetBLogicPackage, BlancoNameAdjuster
                                .toClassName(operation.getName())
                                + "BLogic"), "f"
                        + BlancoNameAdjuster.toClassName(operation.getName())
                        + "BLogic");
                blogic.getJavaDoc().addLine("ビジネスロジックのインスタンスを記憶するフィールド変数。");
                addField(blogic);

                addMethod(new MethodExpander("set"
                        + BlancoNameAdjuster.toClassName(operation.getName())
                        + "BLogic") {
                    protected Scope getScope() {
                        return Scope.PUBLIC;
                    }

                    public void setupSignature() {
                        getJavaDoc().addLine(
                                "電文処理ID[" + operation.getName()
                                        + "]のビジネスロジックをセットします。");
                        getJavaDoc().addLine("");
                        getJavaDoc().addLine(
                                "ビジネスロジック["
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogicImpl]をセットします。<BR>");
                        getJavaDoc().addLine("このメソッドはTERASOLUNAから呼び出されます。<BR>");
                        getJavaDoc().addParameter(
                                "arg"
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName()) + "BLogic",
                                "BLogicオブジェクト");
                        addArgument(new Value(new Type(targetBLogicPackage,
                                BlancoNameAdjuster.toClassName(operation
                                        .getName())
                                        + "BLogic"), "arg"
                                + BlancoNameAdjuster.toClassName(operation
                                        .getName()) + "BLogic"));
                    }

                    public void implement() {
                        getData().addLine(
                                "f"
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogic"
                                        + " = arg"
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName()) + "BLogic"
                                        + ";");
                    }
                });

                addMethod(new MethodExpander("executeService") {
                    protected Scope getScope() {
                        return Scope.PUBLIC;
                    }

                    public void setupSignature() {
                        getJavaDoc().addLine(
                                "電文処理ID[" + operation.getName()
                                        + "]のビジネスロジックへのコントローラです。");
                        getJavaDoc().addLine("");
                        getJavaDoc().addLine(
                                "このメソッドから実際のビジネスロジックが記述された["
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogicImpl]を呼び出します。<BR>");
                        getJavaDoc().addLine("このメソッドはTERASOLUNAから呼び出されます。<BR>");
                        getJavaDoc().addParameter(
                                "input",
                                "要求電文[" + operation.getInput().getName()
                                        + "]オブジェクト。");
                        addArgument(new Value(new Type(operation.getInput()
                                .getTypeOfJava()), "input"));
                        getJavaDoc().addReturn(
                                "応答電文[" + operation.getOutput().getName()
                                        + "]オブジェクト。");
                        setReturnType(new Type(operation.getOutput()
                                .getTypeOfJava()));
                    }

                    public void implement() {

                        getData().addLine(
                                "// 電文処理ID["
                                        + operation.getName()
                                        + "]のビジネスロジック実装["
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogicImpl]を実行。");

                        getData().addLine(
                                "return f"
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogic.execute(input);");
                    }
                });
            }
        };

        TerasolunaBLogicUtil.generateJavaSource(classExpander, directoryTarget);
    }
}
