/* 
 * $Id: XDTPTransform.cpp,v 1.21 2006/06/29 07:26:42 ozawa Exp $
 *
 * Copyright 2005- ONGS Inc. All rights reserved.
 * 
 * author: Masanori OZAWA (ozawa@ongs.co.jp)
 * version: $Revision: 1.21 $
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY ONGS INC ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL ONGS INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the ONGS Inc.
 * 
 */

#include "xdtp.h"
#include "XDTPTransform.h"

#include "Exception.h"
#include "Utilities.h"
#include "xmlloader.h"
#include "xmlsaver.h"
#include "XMLTool.h"

#include "modules/GLSDModule.h"
#include "modules/GLSDModuleChecker.h"
#include "modules/GLSDDocumentModule.h"
#include "modules/GLSDDocumentModuleChecker.h"
#include "modules/GLSDBanBunModule.h"
#include "modules/GLSDBanBunModuleChecker.h"

#include <sys/stat.h>

#define RESOURCE_TAG			"res:"

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
// objectwrapperの解放処理
namespace XDTP {
static void objectwrapper_ustring_destractor(void *value)
{
    Glib::ustring *string = (Glib::ustring*)value;
    if (string) delete string;
}

static void objectwrapper_mc_destractor(void *value)
{
    XDTP_MC *mc = (XDTP_MC*)value;
    if (mc) delete mc;
}

static void objectwrapper_module_destractor(void *value)
{
    XDTPModule *module = (XDTPModule*)value;
    if (module) delete module;
}

static void objectwrapper_checker_destractor(void *value)
{
    XDTPChecker *checker = (XDTPChecker*)value;
    if (checker) delete checker;
}
};

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
// 雑多な処理
namespace XDTP {
static Glib::ustring getOptionValue(const Glib::ustring& key,
									 const StringList& options)
{
	int length = key.length();
	Glib::ustring result;
    StringList::const_iterator iter = options.begin();
    StringList::const_iterator end = options.end();
    
    for (; iter != end; iter++) {
    	if (strncmp(key.c_str(), (*iter).c_str(), length) == 0) {
    		result = (*iter).substr(length);
    		if (0 < result.length() && '=' == result.at(0)) {
    			result = result.substr(1);
    			break;
    		}
    		else {
    			result.clear();
    		}
    	}
    }
    
    return result;
}

static mode_t getFileMask(const Glib::ustring& file)
{
	struct stat sb;
	
	if (!stat(file.c_str(), &sb)) {
		return (sb.st_mode & 0777);
	}
	
	return 0600;
}
};

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
using namespace XDTP;

XDTPTransform::XDTPTransform()
{
    m_XSL4OutputPos = -1;
    m_bSkipXMLCheck = false;
    m_nVerbose = 0;
    m_OutputType = DEFAULT_OUTPUT_TYPE;
    m_OutputEncoding = DEFAULT_OUTPUT_ENCODING;
    m_OutputMask = 0;
    
    XDTPObjectWrapper::setDestractor(OBJECT_TYPE_USTRING, objectwrapper_ustring_destractor);
    XDTPObjectWrapper::setDestractor(OBJECT_TYPE_XSL, objectwrapper_null_destractor);
    XDTPObjectWrapper::setDestractor(OBJECT_TYPE_MC, objectwrapper_mc_destractor);
    XDTPObjectWrapper::setDestractor(OBJECT_TYPE_XDTP_EXT_MODULE, objectwrapper_null_destractor);
    XDTPObjectWrapper::setDestractor(OBJECT_TYPE_XDTP_EXT_CHECKER, objectwrapper_null_destractor);
    XDTPObjectWrapper::setDestractor(OBJECT_TYPE_XDTP_LCL_MODULE, objectwrapper_module_destractor);
    XDTPObjectWrapper::setDestractor(OBJECT_TYPE_XDTP_LCL_CHECKER, objectwrapper_checker_destractor);
}

XDTPTransform::~XDTPTransform()
{
    uninitialize();
}

/**
 * 初期化処理を行います。
 * 初期化後に set/get 系のメソッドを使用することは避けてください。
 * 変換処理中に set/get 系メソッドを使用することはできません。
 * 
 * @return 初期化に成功した場合は true を戻します。
 */
bool XDTPTransform::initialize()
{
	bool result = true;

    uninitialize();
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // モジュールのロード
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: load modules." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

    if (!loadModules(m_libraries, m_modules, m_checkers)) {
        return false;
    }

    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // モジュールの初期化
    int count = 0;
    const int size = m_modules.size();
    XDTPModule *module = NULL;
    
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: initiailze modules." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

	try {
	    for (; count < size; count++) {
	        module = (XDTPModule*)m_modules.get(count);
            module->setXDTPTransform(this);
	        module->setOutputType(m_OutputType);
	        module->setOption(m_OptionList);
	        module->setInputEncoding(m_InputEncoding);
	        module->setOutputEncoding(m_OutputEncoding);
	    }
	}
	catch (Exception exception) {
		uninitialize();
		throw exception;
	}
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // 出力ファイルのアクセス権の決定
    char *tmp;
    Glib::ustring mask = getOptionValue(OUTPUT_FILE_MASK_KEY, getOptionList());
    
    m_OutputMask = 0;
    if (0 < mask.length()) {
    	m_OutputMask = strtol(mask.c_str(), &tmp, 8);
    	if ('\0' != *tmp || 0777 < m_OutputMask) {
    		result = false;
    		m_OutputMask = 0;
    	}
    }
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: output file mask: %03o" LINE_SEP, APP_NAME, m_OutputMask);
        fflush(stdout);
    }
    
    return result;
}

/**
 * オブジェクトを解放する準備を行います。
 * 変換処理後、オブジェクトを利用することがない場合や、状態を変更する
 * 場合は、事前に呼び出す必要があります。
 */
void XDTPTransform::uninitialize()
{
    unloadModules(m_libraries, m_modules, m_checkers);
}

/**
 * 変換処理を実行します。
 * 
 * @param anInput 変換元XMLファイル
 * @param anOutput 出力先ファイル(既存のファイルは上書きします)
 * @return 変換に成功した場合は true を戻します。
 */
bool XDTPTransform::transform(const Glib::ustring& anInput, const Glib::ustring& anOutput)
{
    bool result = true;
    
    XMLTool xmltool;
    
    // エラーメッセージをクリア
    m_ErrorMsg.clear();
    
    if (0 < m_nVerbose) {
        fprintf(stdout, "%s: transform: %s" LINE_SEP, APP_NAME, anInput.c_str());
        fflush(stdout);
    }
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // モジュールに入出力ファイルを通知する
    int count = 0;
    const int modules_size = m_modules.size();
    XDTPModule *module = NULL;
    
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: set input/output file to modules." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

	try {
	    for (; count < modules_size; count++) {
	        module = (XDTPModule*)m_modules.get(count);
	        module->setOutputFile(anOutput);
	        module->setXMLFile(anInput);
	    }
	}
	catch (Exception exception) {
		setErrorMsg(exception.what());
		return false;
	}
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // 入力ファイルを複製
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: backup the input file." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

    std::string tmpfile = "_xdtp_temp.XXXXXX";
    int fd = Glib::mkstemp(tmpfile);
    
    if (0 > fd) {
        setErrorMsg("Unable to create temp file.");
        return false;
    }
    
    if (!Utilities::copy(anInput, fd)) {
        setErrorMsg("Unable to backup the input file. (" + anInput + ")");
        close(fd);
        remove(tmpfile.c_str());
        return false;
    }
    close(fd);
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // parse前処理
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: treatPreFile." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

	try {
	    for (count = 0; count < modules_size; count++) {
	        module = (XDTPModule*)m_modules.get(count);
	        module->treatPreFile(tmpfile);
	    }
	}
	catch (Exception exception) {
		setErrorMsg(exception.what());
        remove(tmpfile.c_str());
		return false;
	}
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // parse
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: parse the XML file." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

    xmlDocPtr document = XMLLoader::load(tmpfile);
    
    if (!document) {
        setErrorMsg("Unable to parse the input xml file.");
        remove(tmpfile.c_str());
        return false;
    }
    remove(tmpfile.c_str());
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // check
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: check the XML file." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

    if (!m_bSkipXMLCheck) {
        const int checkers_size = m_checkers.size();
        XDTPChecker *checker = NULL;
        
        for (count = 0; count < checkers_size; count++) {
            checker = (XDTPChecker*)m_checkers.get(count);
            if (!checker->checkFormat(document)) {
                setErrorMsg("checkFormat failed.");
                xmlFreeDoc(document);
                return false;
            }
        }
    }
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // parse後処理
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: treatPreDocument." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

	try {
	    for (count = 0; count < modules_size; count++) {
	        module = (XDTPModule*)m_modules.get(count);
	        module->treatPreDocument(document);
	    }
	}
	catch (Exception exception) {
		setErrorMsg(exception.what());
        xmlFreeDoc(document);
		return false;
	}
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // xslt
    {
        if (1 < m_nVerbose) {
            fprintf(stdout, "%s: transform: do xslt." LINE_SEP, APP_NAME);
            fflush(stdout);
        }

        int count = 0;
        const int size = m_XSLObjects.size();
        xmlDocPtr doctmp = NULL;
        xsltStylesheetPtr xsl = NULL;
        
        for (; document && count < size; count++) {
            doctmp = NULL;
            xsl = parseXSL(count);
            
            if (xsl) {
                doctmp = xmltool.transform(document, xsl);
                xmlFreeDoc(document);
                xsltFreeStylesheet(xsl);
                document = doctmp;
            }
            
            if (!doctmp) {
                setErrorMsg("XSLT failed.");
            }
        }
    }
    
    if (!document) {
        return false;
    }
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // xslt後の処理
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: treatPostDocument." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

	try {
	    for (count = 0; count < modules_size; count++) {
	        module = (XDTPModule*)m_modules.get(count);
	        module->treatPostDocument(document);
	    }
	}
	catch (Exception exception) {
		setErrorMsg(exception.what());
        xmlFreeDoc(document);
		return false;
	}
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // 保存
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: save file." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

    tmpfile = "_xdtp_temp.XXXXXX";
    fd = Glib::mkstemp(tmpfile);
    
    if (0 > fd) {
        setErrorMsg("Unable to create temp file.");
        xmlFreeDoc(document);
        return false;
    }
    
    close(fd);
    
    xsltStylesheetPtr xsl = NULL;
    if (0 < m_XSLObjects.size()) {
        // 出力形式を保持するXSLファイルを決定
        if (0 < m_XSL4OutputPos) xsl = parseXSL(m_XSL4OutputPos);
        if (!xsl) xsl = parseXSL(m_XSLObjects.size() -1);

        // XSLT変換処理後は、保存にスタイルシート情報が必要。
        if (NULL == xsl) {
            setErrorMsg("Unable to load xsl file for output.");
            xmlFreeDoc(document);
            remove(tmpfile.c_str());
            return false;
        }
    }
    if (!XMLSaver::save(tmpfile, document, xsl, m_OutputEncoding)) {
        setErrorMsg("Unable to save xml file. (" + tmpfile + ")");
        if (xsl) xsltFreeStylesheet(xsl);
        xmlFreeDoc(document);
        remove(tmpfile.c_str());
        return false;
    }

    if (xsl) xsltFreeStylesheet(xsl);
    xmlFreeDoc(document);
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // 保存後の処理
    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: treatPostFile." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

	try {
	    for (count = 0; count < modules_size; count++) {
	        module = (XDTPModule*)m_modules.get(count);
	        module->treatPostFile(tmpfile);
	    }
	}
	catch (Exception exception) {
		setErrorMsg(exception.what());
        remove(tmpfile.c_str());
		return false;
	}
    
    //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    // 出力
    mode_t outputMask = m_OutputMask;

    if (1 < m_nVerbose) {
        fprintf(stdout, "%s: transform: output file." LINE_SEP, APP_NAME);
        fflush(stdout);
    }

	// 標準出力
    if (0 >= anOutput.length() || anOutput == "-") {
        if (!Utilities::printFile(tmpfile)) {
            setErrorMsg("Unable to print out.");
            result = false;
        }
        remove(tmpfile.c_str());
    }
    // リネーム
    else {
        if (!Utilities::move(tmpfile, anOutput)) {
            setErrorMsg("Unable to move file. (" + tmpfile + " -> " + anOutput + ")");
            remove(tmpfile.c_str());
            result = false;
        }
        else {
        	/*
        	 * パーミッションを変更
        	 * 変更元ファイルは、mkstempの仕様により、通常は600である。
        	 * リネーム時に同名のファイルが存在する場合は、その限りではない。
        	 */
        	if (!outputMask) {
        		// 変換元ファイルのパーミッションを引き継ぎます。
        		outputMask = getFileMask(anInput);
        	}
        	if (chmod(anOutput.c_str(), outputMask)) {
		        setErrorMsg(strerror(errno));
		        //result = false; 現在はエラーとしません。 2006/03/02 ozawa
        	}
        }
    }
    
    return result;
}

/**
 * 変換処理時に発生したエラーメッセージを出力します。
 * エラーが発生しなかった場合は、何も表示しません。
 * 
 * @param out 出力先
 */
void XDTPTransform::showErrorMsg(FILE* out) const
{
    if (0 < m_ErrorMsg.length()) {
    	fprintf(out, "%s: %s" LINE_SEP, APP_NAME, m_ErrorMsg.c_str());
    }
}

/**
 * エラーメッセージを設定します。
 * 
 * @param aMsg エラーメッセージ
 */
void XDTPTransform::setErrorMsg(const Glib::ustring& aMsg)
{
    m_ErrorMsg = aMsg;
}

/**
 * XML文章の正当性チェックをスキップします。
 * 
 * @param bCheck スキップする場合は true を指定します。
 */
void XDTPTransform::skipXMLCheck(bool bCheck)
{
    m_bSkipXMLCheck = bCheck;
}

/**
 * XML文章の正当性チェックをスキップするか調べます。
 * 
 * @return スキップする場合は true を戻します。
 */
bool XDTPTransform::isXMLCheck() const
{
    return m_bSkipXMLCheck;
}

/**
 * 変換用XSLファイルを追加します。
 * 
 * @param useOutputType 出力形式を保持し、これを利用する場合は true を指定します。
 */
void XDTPTransform::addXSLFile(const Glib::ustring& aPath, bool useOutputType)
{
    Glib::ustring *path = new Glib::ustring;
    
    if (!path) THROW("Out of memory!");

    *path = aPath;
    m_XSLObjects.add(OBJECT_TYPE_USTRING, path);
    if (useOutputType) {
        m_XSL4OutputPos = m_XSLObjects.size() -1;
    }
}

/**
 * 変換用XSLオブジェクトを追加します。
 * 
 * @param aXSL XSLオブジェクト
 * @param useOutputType オブジェクトが出力形式を保持し、これを利用する場合は true を指定します。
 */
void XDTPTransform::addXSLObject(xsltStylesheetPtr aXSL, bool useOutputType)
{
    m_XSLObjects.add(OBJECT_TYPE_XSL, aXSL);
    if (useOutputType) {
        m_XSL4OutputPos = m_XSLObjects.size() -1;
    }
}

/**
 * 変換用XSLファイルの一覧をクリアします。
 */
void XDTPTransform::clearXSLFile()
{
    m_XSLObjects.clear();
    m_XSL4OutputPos = -1;
}

/**
 * 変換用XSLファイルの一覧を取得します。
 * 
 * @deprecated
 * @return 変換用XSLファイルの一覧
 */
const StringList& XDTPTransform::getXSLFileList()
{
    int type;
    void *value;

    for (int count = 0; count < m_XSLObjects.size(); count++) {
        value = m_XSLObjects.get(count, &type);
        if (OBJECT_TYPE_USTRING == type) {
            __m_XSLFileList.push_back(*((Glib::ustring*)value));
        }
    }
    
    return __m_XSLFileList;
}

/**
 * 変換用XSLファイルおよびオブジェクトの一覧を取得します。
 * 
 * @return 変換用XSLファイルおよびオブジェクトの一覧
 */
const XDTPObjectWrapper& XDTPTransform::getXSLList() const
{
    return m_XSLObjects;
}

/**
 * 変換用XDTPモジュールを追加します。
 * 
 * @param aPath XDTPモジュールへのパス
 */
void XDTPTransform::addXDTPModule(const Glib::ustring& aPath)
{
    Glib::ustring *path = new Glib::ustring;
    
    if (!path) THROW("Out of memory!");

    *path = aPath;
    m_XDTPModules.add(OBJECT_TYPE_USTRING, path);
}

/**
 * 変換用XDTPモジュールを追加します。
 * 
 * @param aModule XDTPモジュール
 * @param aChecker XDTPチェッカ。使用しない場合は NULL を指定できます。
 */
void XDTPTransform::addXDTPModule(XDTPModule *aModule, XDTPChecker *aChecker)
{
    if (!aModule) THROW("XDTPTransform::addXDTPModule: bad arguments.");

    XDTP_MC *mc = new XDTP_MC;

    if (!mc) THROW("Out of memory!");
    
    mc->module = aModule;
    mc->checker = aChecker;
    
    m_XDTPModules.add(OBJECT_TYPE_MC, mc);
}

/**
 * 変換用XDTPモジュールをクリアします。
 */
void XDTPTransform::clearXDTPModule()
{
    m_XDTPModules.clear();
}

/**
 * 変換用XDTPモジュールの一覧を取得します。
 * 
 * @return 変換用XDTPモジュールの一覧
 */
const StringList& XDTPTransform::getXDTPModuleList()
{
    int type;
    void *value;

    for (int count = 0; count < m_XDTPModules.size(); count++) {
        value = m_XDTPModules.get(count, &type);
        if (OBJECT_TYPE_USTRING == type) {
            __m_XDTPModuleList.push_back(*((Glib::ustring*)value));
        }
    }
    
    return __m_XDTPModuleList;
}

/**
 * 変換用XDTPモジュールの一覧を取得します。
 * 
 * @return 変換用XDTPモジュールの一覧
 */
const XDTPObjectWrapper& XDTPTransform::getXDTPMCList() const
{
    return m_XDTPModules;
}

/**
 * 変換用XDTPモジュールへのオプションを追加します。
 * 
 * @param anOption 追加オプション
 */
void XDTPTransform::addOption(const Glib::ustring& anOption)
{
    m_OptionList.push_back(anOption);
}

/**
 * 変換用XDTPモジュールへのオプションをクリアします。
 */
void XDTPTransform::clearOption()
{
    m_OptionList.clear();
}

/**
 * 変換用XDTPモジュールへのオプションの一覧を取得します。
 * 
 * @return 変換用XDTPモジュールへのオプションの一覧
 */
const StringList& XDTPTransform::getOptionList() const
{
    return m_OptionList;
}

/**
 * 出力形式を指定します。
 * 
 * @param aType 出力形式
 */
void XDTPTransform::setOutputType(const Glib::ustring& aType)
{
    m_OutputType = aType.lowercase();
}

/**
 * 出力形式を取得します。
 * 
 * @return 出力形式
 */
const Glib::ustring& XDTPTransform::getOutputType() const
{
    return m_OutputType;
}

/**
 * メッセージの表示レベルを指定します。
 * 
 * @param aMode メッセージの表示レベル
 */
void XDTPTransform::setVerboseMode(int aMode)
{
    m_nVerbose = aMode;
}

/**
 * メッセージの表示レベルを取得します。
 */
int XDTPTransform::getVerboseMode() const
{
    return m_nVerbose;
}

/**
 * 入力ファイルのエンコードを指定します。
 * 
 * @param anEncoding 入力ファイルのエンコード
 */
void XDTPTransform::setInputEncoding(const Glib::ustring& anEncoding)
{
    if ("getlocale" == anEncoding.lowercase()) {
        std::string encoding;
        Glib::get_charset(encoding);
        m_InputEncoding = encoding;
    }
    else {
        m_InputEncoding = anEncoding;
    }
}

/**
 * 入力ファイルのエンコードを取得します。
 * 
 * @return 入力ファイルのエンコード
 */
const Glib::ustring& XDTPTransform::getInputEncoding() const
{
    return m_InputEncoding;
}

/**
 * 出力ファイルのエンコードを指定します。
 * 
 * @param anEncoding 出力ファイルのエンコード
 */
void XDTPTransform::setOutputEncoding(const Glib::ustring& anEncoding)
{
    if ("getlocale" == anEncoding.lowercase()) {
        std::string encoding;
        Glib::get_charset(encoding);
        m_OutputEncoding = encoding;
    }
    else {
        m_OutputEncoding = anEncoding;
    }
}

/**
 * 出力ファイルのエンコードを取得します。
 * 
 * @return 出力ファイルのエンコード
 */
const Glib::ustring& XDTPTransform::getOutputEncoding() const
{
    return m_OutputEncoding;
}

bool XDTPTransform::loadModules(LibraryList& libraries,
                                XDTPObjectWrapper& modules, XDTPObjectWrapper& checkers)
{
    bool result = true;
    int count = 0;
    int type = 0;
    const int size = m_XDTPModules.size();
    void *value;
    XDTP_MC *mc;
    XDTPModuleLibrary *lib = NULL;
    XDTPModule *module = NULL;
    XDTPChecker *checker = NULL;
    Glib::ustring path;
    
    for (; count < size; count++) {
        value = m_XDTPModules.get(count, &type);
        
        if (type == OBJECT_TYPE_MC) {
            mc = (XDTP_MC*)value;
            modules.add(OBJECT_TYPE_XDTP_EXT_MODULE, mc->module);
            if (mc->checker) checkers.add(OBJECT_TYPE_XDTP_EXT_CHECKER, mc->checker);
            
            continue;
        }
        
        if (type != OBJECT_TYPE_USTRING) continue;
        
        path = *((Glib::ustring*)value);
        
        // 内部モジュールのロード
        if (4 < path.length() && path.substr(0, 4).lowercase() == RESOURCE_TAG) {
            path = path.substr(4);
            
            result &= loadLocalModule(path, modules, checkers);
            
            continue;
        }
        
        // 外部モジュールのロード
        lib = XDTPModuleLoader::loadModuleLibrary(path);
        
        if (!lib) {
            setErrorMsg("Unable to load module. (" + path + ")");
            result = false;
        }
        else {
            module = XDTPModuleLoader::buildModule(lib);
            checker = XDTPModuleLoader::buildChecker(lib);
            
            if (!module) {
                XDTPModuleLoader::unloadModuleLibrary(lib);
                setErrorMsg("Unable to build module. (" + path + ")");
                result = false;
            }
            else {
                libraries.push_back(lib);
                modules.add(OBJECT_TYPE_XDTP_EXT_MODULE, module);
                if (checker) checkers.add(OBJECT_TYPE_XDTP_EXT_CHECKER, checker);
            }
        }
    }
    
    if (!result) {
        unloadModules(libraries, modules, checkers);
    }
    
    return result;
}

void XDTPTransform::unloadModules(LibraryList& libraries,
                                  XDTPObjectWrapper& modules, XDTPObjectWrapper& checkers)
{
    LibraryList::iterator iter = libraries.begin();
    LibraryList::iterator end = libraries.end();
    for (; iter != end; iter++) {
        XDTPModuleLoader::unloadModuleLibrary(*iter);
    }
    libraries.clear();
    
    modules.clear();
    checkers.clear();
}

bool XDTPTransform::loadLocalModule(const Glib::ustring& module,
                                    XDTPObjectWrapper& modules, XDTPObjectWrapper& checkers)
{
    if (module.lowercase() == "glsdmodule") {
        modules.add(OBJECT_TYPE_XDTP_LCL_MODULE, new GLSDModule());
        checkers.add(OBJECT_TYPE_XDTP_LCL_CHECKER, new GLSDModuleChecker());
    }
    else if (module.lowercase() == "glsddocumentmodule") {
        modules.add(OBJECT_TYPE_XDTP_LCL_MODULE, new GLSDDocumentModule());
        checkers.add(OBJECT_TYPE_XDTP_LCL_CHECKER, new GLSDDocumentModuleChecker());
    }
    else if (module.lowercase() == "glsdbanbunmodule") {
        modules.add(OBJECT_TYPE_XDTP_LCL_MODULE, new GLSDBanBunModule());
        checkers.add(OBJECT_TYPE_XDTP_LCL_CHECKER, new GLSDBanBunModuleChecker());
    }
    else {
        setErrorMsg("Unknown module name. (" + module + ")");
        return false;
    }

    return true;
}

xsltStylesheetPtr XDTPTransform::parseXSL(int pos)
{
    int type;
    void *value = m_XSLObjects.get(pos, &type);
    
    switch (type) {
    case OBJECT_TYPE_USTRING:
        return xsltParseStylesheetFile((const xmlChar *)((Glib::ustring*)value)->c_str());
    case OBJECT_TYPE_XSL:
        return (xsltStylesheetPtr)value;
    default:
        THROW("XDTPTransform::parseXSL: Internal error! (Unknown object type)");
    }
    
    return NULL;
}


syntax highlighted by Code2HTML, v. 0.9.1