/*
* $Id: GLSDImportModuleAdapter.cpp,v 1.19 2006/07/13 13:31:18 ozawa Exp $
*
* Copyright 2005- ONGS Inc. All rights reserved.
*
* author: Masanori OZAWA (ozawa@ongs.co.jp)
* version: $Revision: 1.19 $
*
* 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 "modules/imports/GLSDImportModuleAdapter.h"
#include "URI.h"
#include "URLTool.h"
#include "Utilities.h"
#include "XMLTool.h"
#define TIP_TAG "___XDTP_GLSD_IMPORT_TAG___"
using namespace XDTP;
GLSDImportModuleAdapter::GLSDImportModuleAdapter()
{
m_OutputType = DEFAULT_OUTPUT_TYPE;
m_OutputEncoding = DEFAULT_OUTPUT_ENCODING;
m_pXDTPTransform = NULL;
}
GLSDImportModuleAdapter::~GLSDImportModuleAdapter()
{
}
/**
* 現在のXDTPTransformモジュールを取得します。
*
* @return 現在のXDTPTransformモジュール
*/
const XDTPTransform * GLSDImportModuleAdapter::getXDTPTransform()
{
return m_pXDTPTransform;
}
/**
* 処理を開始する前に出力形式を設定します。
*
* @param aType 出力形式
*/
void GLSDImportModuleAdapter::setOutputType(const Glib::ustring& aType)
{
m_OutputType = aType;
}
/**
* 処理を開始する前にオプションを設定します。
*
* @param anOptions オプション
*/
void GLSDImportModuleAdapter::setOption(const StringList& anOptions)
{
m_OptionList = anOptions;
}
/**
* 処理を開始する前に出力ファイル名を設定します。
*
* @param anOutputFile 出力ファイル名
*/
void GLSDImportModuleAdapter::setOutputFile(const Glib::ustring& anOutputFile)
{
m_OutputFile = anOutputFile;
}
/**
* 処理を開始する前に変換元XMLファイル名を設定します。
*
* @param aXMLFile 変換元XMLファイル名
*/
void GLSDImportModuleAdapter::setXMLFile(const Glib::ustring& aXMLFile)
{
m_XMLFile = aXMLFile;
}
/**
* 入力ファイルのエンコードを指定します。
* XMLファイルはこの限りではありません。
*
* @param anInputEncoding 入力ファイルのエンコード
*/
void GLSDImportModuleAdapter::setInputEncoding(const Glib::ustring& anInputEncoding)
{
m_InputEncoding = anInputEncoding;
}
/**
* 出力ファイルのエンコードを指定します。
*
* @param anOutputEncoding 出力ファイルのエンコード
*/
void GLSDImportModuleAdapter::setOutputEncoding(const Glib::ustring& anOutputEncoding)
{
m_OutputEncoding = anOutputEncoding;
}
/**
* 現在のXDTPTransformモジュールを指定します。
*
* @param aXDTPTransform 現在のXDTPTransformモジュール
*/
void GLSDImportModuleAdapter::setXDTPTransform(const XDTPTransform *aXDTPTransform)
{
m_pXDTPTransform = aXDTPTransform;
}
/**
* XMLファイルのエンコーディングを指定します。
*
* @param anEncoding XMLファイルのエンコーディング
*/
void GLSDImportModuleAdapter::setXMLEncoding(const std::string& anEncoding)
{
m_XMLEncoding = anEncoding;
}
/**
* ファイルをparseする直前に呼び出されます。
*
* @param aFile parse対象のファイル名
*/
void GLSDImportModuleAdapter::treatPreFile(const Glib::ustring& aFile)
{
// no operation
}
/**
* parseしたXML文章をXSL変換する直前に呼び出されます。
*
* @param aDocument XML文章
*/
void GLSDImportModuleAdapter::treatPreDocument(xmlDocPtr aDocument)
{
// no operation
}
/**
* XSL変換後のXML文章を保存する直前に呼び出されます。
*
* @param aDocument XML文章
*/
void GLSDImportModuleAdapter::treatPostDocument(xmlDocPtr aDocument)
{
// no operation
}
/**
* XML文章を保存した直後に呼び出されます。
*
* @param aFile 保存したファイル名
*/
void GLSDImportModuleAdapter::treatPostFile(const Glib::ustring& aFile)
{
// no operation
}
/**
* ファイルからインポート処理対象の部分を抽出し、置換処理を
* 行う際に呼び出されます。
*
* @param aRef インポート先のファイル
* @param aCaption キャプション
* @param anEncoding インポート先のファイルのエンコード
* @param aNewData 置換すべき文字列を応答します。
* @return 処理結果を応答します。
*/
TIP_STATUS GLSDImportModuleAdapter::transformImportPointCallback
(const Glib::ustring& aRef, const Glib::ustring& aCaption,
const Glib::ustring& anEncoding, Glib::ustring& aNewData)
{
return TIP_SKIP;
}
/**
* インポート処理対象を識別するノードを構築します。
*
* @param aType 処理対象の種類
* @param aRef インポート先のファイル
* @param aCaption キャプション
* @param anEncoding インポート先のファイルのエンコード
* @return インポート処理対象を識別するノード
*/
xmlNodePtr GLSDImportModuleAdapter::createNewImportPointNode
(const Glib::ustring& aType, const Glib::ustring& aRef,
const Glib::ustring& aCaption, const Glib::ustring& anEncoding)
{
Glib::ustring comment = TIP_TAG LINE_SEP;
comment += "type=" + aType + LINE_SEP;
comment += "ref=" + aRef + LINE_SEP;
comment += "caption=" + aCaption + LINE_SEP;
comment += "encoding=" + anEncoding + LINE_SEP;
return xmlNewComment((const xmlChar*)comment.c_str());
}
/**
* ファイルからインポート処理対象の部分を抽出し、置換処理を行います。
*
* @param aFile 処理対象のファイル
* @param aType 処理対象の種類
* @return 成功した場合は true を戻します。
*/
bool GLSDImportModuleAdapter::transformImportPoint
(const Glib::ustring& aFile, const Glib::ustring& aType)
{
bool result = false;
// 一時ファイルを構築
std::string tmpfile = "_xdtp_temp.XXXXXX";
int fd = Glib::mkstemp(tmpfile);
if (0 > fd) {
fprintf(stderr, "%s: Unable to create temp file." LINE_SEP, APP_NAME);
return false;
}
close(fd);
// インポート置換処理
try {
Glib::ustring line, data, newdata;
Glib::ustring type, ref, caption, encoding;
Glib::ustring::size_type pos, posRet;
Glib::RefPtr<Glib::IOChannel> iioc =
Glib::IOChannel::create_from_file(aFile.c_str(), "r");
Glib::RefPtr<Glib::IOChannel> oioc =
Glib::IOChannel::create_from_file(tmpfile.c_str(), "w");
iioc->set_encoding(m_OutputEncoding);
oioc->set_encoding(m_OutputEncoding);
while (iioc->read_line(line) == Glib::IO_STATUS_NORMAL) {
pos = line.find("<!--" TIP_TAG);
if (Glib::ustring::npos != pos) {
if (0 < pos) {
oioc->write(line.substr(0, pos));
}
data = line.substr(pos);
pos = Glib::ustring::npos;
while (iioc->read_line(line) == Glib::IO_STATUS_NORMAL) {
pos = line.find("-->");
if (Glib::ustring::npos != pos) {
data += line.substr(0, pos +3);
break;
}
data += line;
}
if (Glib::ustring::npos == pos)
THROW("Unable to find the terminate position of import.");
line = line.substr(pos +3);
pos = data.find("type=");
posRet = data.find(LINE_SEP, pos);
if (Glib::ustring::npos == pos ||
Glib::ustring::npos == posRet) {
THROW("Internal error!");
}
pos += 5;
type = data.substr(pos, posRet - pos);
pos = data.find("ref=");
posRet = data.find(LINE_SEP, pos);
if (Glib::ustring::npos == pos ||
Glib::ustring::npos == posRet) {
THROW("Internal error!");
}
pos += 4;
ref = data.substr(pos, posRet - pos);
pos = data.find("caption=");
posRet = data.find(LINE_SEP, pos);
if (Glib::ustring::npos == pos ||
Glib::ustring::npos == posRet) {
THROW("Internal error!");
}
pos += 8;
caption = data.substr(pos, posRet - pos);
pos = data.find("encoding=");
posRet = data.find(LINE_SEP, pos);
if (Glib::ustring::npos == pos ||
Glib::ustring::npos == posRet) {
THROW("Internal error!");
}
pos += 9;
encoding = data.substr(pos, posRet - pos);
if (type == aType) {
newdata = "";
switch (transformImportPointCallback(ref, caption, encoding, newdata)) {
case TIP_SUCCESS:
oioc->write(newdata);
break;
case TIP_SKIP:
oioc->write(data);
break;
default:
case TIP_FAILED:
THROW("Unable to transform the import point.");
break;
}
}
else {
oioc->write(data);
}
}
oioc->write(line);
}
iioc->close();
oioc->close();
result = true;
}
catch (Exception exception) {
exception.print(stderr);
}
catch (Glib::Error error) {
std::string msg = Glib::locale_from_utf8(error.what());
fprintf(stderr, "%s: %s" LINE_SEP, APP_NAME, msg.c_str());
}
catch (std::exception exception) {
fprintf(stderr, "%s: %s" LINE_SEP, APP_NAME, exception.what());
}
catch (...) {
fprintf(stderr, "%s: ImportTextSourcecode: unknown error occured." LINE_SEP, APP_NAME);
}
if (result) {
result = Utilities::move(tmpfile, aFile);
}
if (!result) {
remove(tmpfile.c_str());
}
return result;
}
/**
* 任意タイプのスタイルシートを取得する。
*
* @param aDocument
* XHTMLドキュメント
* @param aType
* スタイルシートのタイプ
* 常に小文字で指定する必要があります。
* @return 取得したスタイルシート
*/
xmlNodePtr GLSDImportModuleAdapter::getStyleSheet
(xmlDocPtr aDocument, const Glib::ustring& aType)
{
if (0 >= aType.length()) {
THROW("bad arguments.");
}
// スタイルシートのエレメントを取得
int count;
XMLTool tool;
xmlNodePtr comment = NULL;
xmlNodePtr node;
xmlNodePtr root = xmlDocGetRootElement(aDocument);
xmlNodeSetPtr nodes;
xmlXPathObjectPtr obj = tool.getNodeList(root, "/html/head/style");
const char *pTmp;
Glib::ustring value;
if (obj) {
nodes = obj->nodesetval;
for (count = 0; count < nodes->nodeNr; count++) {
node = nodes->nodeTab[count];
pTmp = (const char*)xmlGetProp(node, (const xmlChar*)"type");
value = (pTmp ? pTmp : "");
if (pTmp) xmlFree((xmlChar*)pTmp);
value = value.lowercase();
if (value == aType) {
break;
}
}
if (count < nodes->nodeNr) {
// スタイルシートのコメント部を取得
for (node = node->children; node; node = node->next) {
if (tool.isComment(node)) {
comment = node;
break;
}
}
}
xmlXPathFreeObject(obj);
}
// 取得できない場合は新規に作成する
if (!comment) {
xmlNodePtr style = NULL;
obj = tool.getNodeList(root, "/html/head/style");
if (obj) {
nodes = obj->nodesetval;
for (count = 0; count < nodes->nodeNr; count++) {
pTmp = (const char*)xmlGetProp(nodes->nodeTab[count], (const xmlChar*)"type");
value = (pTmp ? pTmp : "");
if (pTmp) xmlFree((xmlChar*)pTmp);
value = value.lowercase();
if (value == aType) {
style = nodes->nodeTab[count];
break;
}
}
xmlXPathFreeObject(obj);
}
if (!style) {
obj = tool.getNodeList(root, "/html/head");
if (!obj) return NULL;
nodes = obj->nodesetval;
node = nodes->nodeTab[0];
xmlXPathFreeObject(obj);
style = xmlNewNode(NULL, (const xmlChar*)"style");
xmlSetProp(style, (const xmlChar*)"type", (const xmlChar*)aType.c_str());
xmlAddChild(node, style);
}
if (!style) THROW("Out of memory!");
comment = xmlNewComment((const xmlChar*)"");
if (!comment) THROW("Out of memory!");
xmlAddChild(style, comment);
}
return comment;
}
/**
* Importタグの各種属性値を取得します。
*
* @param aNode Importノード
* @param aRef リソースのURL
* @param aCaption キャプション
* @param anEncoding リソースのエンコーディング
* @return 取得に成功した場合は true を戻す。
*/
bool GLSDImportModuleAdapter::getImportAttributes(xmlNodePtr aNode,
Glib::ustring& aRef, Glib::ustring& aCaption, Glib::ustring& anEncoding)
{
const char *pRef = (const char*)xmlGetProp(aNode, (const xmlChar*)"ref");
if (!pRef) return false;
const char *pCaption = (const char*)xmlGetProp(aNode, (const xmlChar*)"caption");
const char *pEncoding = (const char*)xmlGetProp(aNode, (const xmlChar*)"encoding");
aRef = Utilities::strTrim(pRef);
aCaption = Utilities::strTrim((pCaption ? pCaption : ""));
anEncoding = Utilities::strTrim((pEncoding ? pEncoding : ""));
xmlFree((xmlChar*)pRef);
if (pCaption) xmlFree((xmlChar*)pCaption);
if (pEncoding) xmlFree((xmlChar*)pEncoding);
if (getenv(XDTP_URI_TRANSITION)) {
// 誤ったURIを修正する
RefPtr<URI> uri = URI::parse(aRef);
aRef = uri->toString().c_str();
}
return true;
}
/**
* URIで参照するリソースへのIOChannelを取得します。
* 取得できない場合は XDTP::Exception を throw します。
*
* @param aRef URI
* @param aTmpFile 一時ファイルを生成した場合は、一時ファイルへのパスを応答します。
* 生成しない場合は、空文字列を応答します。
* @return リソースへのIOChannel
*/
Glib::RefPtr<Glib::IOChannel> GLSDImportModuleAdapter::getIOChannel
(const Glib::ustring& aRef, std::string& aTmpFile)
{
Glib::RefPtr<Glib::IOChannel> result;
try {
std::string target;
RefPtr<URI> uri = URI::parse(normalizeURL(aRef));
if (0 >= uri->getScheme().length() || uri->isFileScheme()) {
target = uri->getPath();
}
else {
aTmpFile = "_xdtp_temp.XXXXXX";
int fd = Glib::mkstemp(aTmpFile);
if (0 > fd) {
aTmpFile = "";
THROW("Unable to create temp file.");
}
if (!URLTool::getResource2File(aRef, fd)) {
close(fd);
THROW("Unable to read the resource. " + aRef);
}
close(fd);
target = aTmpFile;
}
result = Glib::IOChannel::create_from_file(target.c_str(), "r");
}
catch (Exception exception) {
THROW(exception.what() + " (" + aRef + ")");
}
catch (Glib::Error error) {
std::string msg = Glib::locale_from_utf8(error.what());
THROW(msg + " (" + aRef + ")");
}
catch (std::exception exception) {
std::string msg = exception.what();
THROW(msg + " (" + aRef + ")");
}
catch (...) {
THROW("GLSDImportModuleAdapter: Unknown error occured. (" + aRef + ")");
}
return result;
}
/**
* ローカルファイルを参照するURLを絶対パスのURLに展開する。
*/
Glib::ustring GLSDImportModuleAdapter::normalizeURL(const Glib::ustring& url)
{
bool froceFileScheme = false;
RefPtr<URI> uri = URI::parse(url);
Glib::ustring result = url;
if (0 >= uri->getScheme().length()) {
if (0 >= url.length() ||
(2 <= url.length() && "//" == url.substr(0, 2)))
{
THROW("Bad URI. (" + url + ")");
}
froceFileScheme = true;
}
if (froceFileScheme || uri->isFileScheme()) {
if (0 >= uri->getPath().length()) {
// パスが存在しないURLはエラーとする。
if (7 < url.length() && "file://" == url.substr(0, 7).lowercase()) {
THROW("Bad URL. (" + url + ")" LINE_SEP
"\tThis problem may be able to be solved by setting up" LINE_SEP
"\tenvironment variable XDTP_URI_TRANSITION.");
}
THROW("Bad URL. (" + url + ")");
}
else if (!Glib::path_is_absolute(uri->getPath())) {
result = uri->getPath();
Glib::ustring::size_type pos = m_XMLFile.rfind(FILE_SEP);
if (Glib::ustring::npos != pos) {
result = m_XMLFile.substr(0, pos) + FILE_SEP + result;
#ifdef WIN32
result = Utilities.strReplaceAll(result, FILE_SEP, "/");
#endif
}
if (!Glib::path_is_absolute(result)) {
result = Glib::build_filename(Glib::get_current_dir(), result);
}
result = "file:" + result;
}
}
return result;
}
/**
* ファイルの読み込み用エンコードを適切に設定する。
*
* @param anIOC エンコードを設定するIOChannel
* @param aDocument XMLドキュメント
* @param anEncoding 優先的に設定するエンコード。不明な場合は空文字列を指定する。
*/
void GLSDImportModuleAdapter::setEncoding(Glib::RefPtr<Glib::IOChannel>& anIOC,
const Glib::ustring& anEncoding)
{
if (!anEncoding.empty()) {
anIOC->set_encoding(anEncoding);
}
else if (!m_InputEncoding.empty()) {
anIOC->set_encoding(m_InputEncoding);
}
else if (!m_XMLEncoding.empty()) {
anIOC->set_encoding(m_XMLEncoding);
}
else {
std::string encoding;
Glib::get_charset(encoding);
anIOC->set_encoding(encoding.c_str());
}
}
/**
* XMLドキュメント用に特殊文字をエスケープします。
*
* @param aString 特殊文字を含む文字列
* @return エスケープ後の文字列
*/
Glib::ustring GLSDImportModuleAdapter::escapeString(const Glib::ustring& aString)
{
// エスケープ処理
Glib::ustring result =aString;
result = Utilities::strReplaceAll(result, "&", "&"); // &
result = Utilities::strReplaceAll(result, "<", "<"); // <
result = Utilities::strReplaceAll(result, ">", ">"); // >
result = Utilities::strReplaceAll(result, " ", " "); // スペース
result = Utilities::strReplaceAll(result, "\\", "\"); // バックスラッシュ
return result;
}
/**
* Importタグをテキスト形式で保持します。
*/
bool GLSDImportModuleAdapter::preserveImport(xmlDocPtr aDocument, xmlNodePtr aNode,
const Glib::ustring& aRef,
const Glib::ustring& aCaption,
const Glib::ustring& anEncoding)
{
const char *pType = (const char*)xmlGetProp(aNode, (const xmlChar*)"type");
Glib::ustring type = (pType ? pType : "");
Glib::ustring text = "<import type=\"" + type + "\" ref=\"" + aRef + "\" ";
text += "caption=\"" + aCaption + "\" encoding=\"" + anEncoding + "\">";
if (pType) xmlFree((xmlChar*)pType);
xmlNodePtr import = xmlNewText((const xmlChar*)text.c_str());
if (!import) return false;
xmlFreeNode(xmlReplaceNode(aNode, import));
return true;
}
/**
* 指定のキーで識別されるオプションの値を取得します。
*
* @param aKey オプションを一意に識別する値
* @param aValue 取得したオプションの値を格納する領域
* @return オプションが見付かり正しく取得できた場合は true を戻します。
*/
bool GLSDImportModuleAdapter::getOptionInt(const Glib::ustring& aKey, int &aValue)
{
long val = 0;
char *endptr = NULL;
Glib::ustring tmp;
if (getOptionString(aKey, tmp) && 0 < tmp.length()) {
val = strtol(tmp.c_str(), &endptr, 10);
if ('\0' == *endptr) {
aValue = (int)val;
return true;
}
}
return false;
}
bool GLSDImportModuleAdapter::getOptionString(const Glib::ustring& aKey, Glib::ustring& aValue)
{
bool result = false;
Glib::ustring key = aKey + "=";
StringList::const_iterator iter = m_OptionList.begin();
StringList::const_iterator end = m_OptionList.end();
for (; iter != end; iter++) {
if (key.length() <= iter->length() &&
key.compare(iter->substr(0, key.length())) == 0) {
aValue = iter->substr(key.length());
result = true;
break;
}
}
return result;
}
syntax highlighted by Code2HTML, v. 0.9.1