/*
 * 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.Type;
import blanco.ig.expander.Value;
import blanco.ig.expander.method.MethodExpander;
import blanco.wsdl.parser.BlancoWsdlParser;
import blanco.wsdl.valueobject.BlancoWsdlWebService;
import blanco.wsdl.valueobject.BlancoWsdlWebServiceOperation;

/**
 * WSDLからビジネスロジックのインタフェースを自動生成するクラス。
 * 
 * このクラスは、blancoSOAPのTERASOLUNA Rich対応拡張の一部となります。
 * 
 * @author IGA Tosiki
 * @version 1.0
 */
public class TerasolunaBLogicWsdl2BLogicInterface {

    /**
     * WSDLファイルの解析結果
     */
    private BlancoWsdlWebService fWsdl = null;

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

    /**
     * おのおののWSDLファイルを処理する。
     * 
     * WSDLファイルを入力として ビジネスロジックのインタフェースとなる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の記述に従い ビジネスロジックのインタフェース情報を展開する。
     * 
     * @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 ClassExpander classExpander = new ClassExpander(new Type(fWsdl
                .getPackage(), BlancoNameAdjuster.toClassName(operation
                .getName())
                + "BLogic")) {
            public boolean isInterface() {
                // インタフェースを生成する。
                return true;
            }

            protected void expandClassStruct() {
                addFileComment("TERASOLUNA Richが利用するビジネスロジックのインタフェースです<br>");
                addFileComment("※このクラスは blancoにより自動生成されています。");

                getJavaDoc().addLine(
                        "電文処理ID[" + operation.getName()
                                + "]のビジネスロジックインタフェースです。");
                getJavaDoc().addLine("");
                if (BlancoStringUtil.null2Blank(
                        telegramProcessDefInfo.getDescription()).length() > 0) {
                    // 「説明」が存在する場合にのみ出力します。
                    getJavaDoc()
                            .addLine(
                                    "説明: "
                                            + BlancoJavaSourceUtil
                                                    .escapeStringAsJavaDoc(telegramProcessDefInfo
                                                            .getDescription())
                                            + "<br>");
                }
                getJavaDoc().addLine(
                        "このインタフェースを実装して、["
                                + BlancoNameAdjuster.toClassName(operation
                                        .getName())
                                + "BLogicImpl]クラスを作成してください。<br>");
                getJavaDoc().addLine("このインタフェースは、DIコンテナから利用されます。<br>");
                getJavaDoc().addLine("");
                getJavaDoc().addLine(
                        "要求電文ID: " + operation.getInput().getName() + "<br>");
                getJavaDoc().addLine(
                        "応答電文ID: " + operation.getOutput().getName() + "<br>");
                getJavaDoc().addLine("<br>");
                getJavaDoc().addLine("<br>");

                // bean定義の具体的な記述内容を JavaDocに展開する。
                getJavaDoc().addLine("bean定義の具体例を下記に示します。<br>");
                getJavaDoc().addLine("<pre>");
                getJavaDoc().addLine(
                        BlancoJavaSourceUtil
                                .escapeStringAsJavaDoc("<bean id=\""
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogic\"")
                                + "<br>");
                getJavaDoc().addLine(
                        BlancoJavaSourceUtil
                                .escapeStringAsJavaDoc("    class=\""
                                        + fWsdl.getPackage()
                                        + "."
                                        + BlancoNameAdjuster
                                                .toClassName(operation
                                                        .getName())
                                        + "BLogicImpl\" singleton=\"false\">")
                                + "<br>");
                getJavaDoc().addLine(
                        BlancoJavaSourceUtil.escapeStringAsJavaDoc("</bean>"));
                getJavaDoc().addLine("</pre>");

                // 要求電文および応答電文のクラスをインポートする。
                addImport(new Type(operation.getInput().getTypeOfJava()));
                addImport(new Type(operation.getOutput().getTypeOfJava()));

                // executeメソッドを展開する。
                addMethod(new MethodExpander("execute") {
                    protected boolean isAbstract() {
                        // 抽象メソッドとする。
                        return true;
                    }

                    // メソッドのシグニチャをセットアップする。
                    public void setupSignature() {
                        getJavaDoc().addLine(
                                "電文処理ID[" + operation.getName()
                                        + "]のビジネスロジックを実行します。");
                        getJavaDoc().addLine("");
                        getJavaDoc().addLine("このメソッドを継承して、具体的なビジネスロジックを記述します。");
                        getJavaDoc().addLine(
                                "このメソッドは フレームワークからコントローラを経由して呼び出されます。");
                        getJavaDoc().addParameter(
                                "input",
                                "要求電文ID[" + operation.getInput().getName()
                                        + "]");
                        getJavaDoc().addReturn(
                                "応答電文ID[" + operation.getOutput().getName()
                                        + "]");
                        addArgument(new Value(new Type(operation.getInput()
                                .getTypeOfJava()), "input"));
                        setReturnType(new Type(operation.getOutput()
                                .getTypeOfJava()));
                    }

                    public void implement() {
                        // 抽象メソッドであるため本体は存在しません。
                    }
                });
            }
        };

        TerasolunaBLogicUtil.generateJavaSource(classExpander, directoryTarget);
    }
}
