/* * $Id: ImportTextGLSD.cpp,v 1.6 2006/07/13 15:45:39 ozawa Exp $ * * Copyright 2006- ONGS Inc. All rights reserved. * * author: Masanori OZAWA (ozawa@ongs.co.jp) * version: $Revision: 1.6 $ * * 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/GLSDModuleChecker.h" #include "modules/GLSDDocumentModuleChecker.h" #include "modules/imports/ImportTextGLSD.h" #include "URI.h" #include "URLTool.h" #include "Utilities.h" #include "xmlloader.h" #include "XMLTool.h" using namespace XDTP; #define DEFAULT_MAX_NEST_LEVEL 6 #define MAX_NEST_LEVEL_KEY "text/glsd-document:max-nest-level" ImportTextGLSD::ImportTextGLSD() { } ImportTextGLSD::~ImportTextGLSD() { } /** * parseしたXML文章をXSL変換する直前に呼び出されます。 * * @param aDocument XML文章 */ void ImportTextGLSD::treatPreDocument(xmlDocPtr aDocument) { GLSDImportModuleAdapter::treatPreDocument(aDocument); int count; int nestcnt; int nestlevel = DEFAULT_MAX_NEST_LEVEL; XMLTool tool; xmlNodePtr root = xmlDocGetRootElement(aDocument); xmlNodeSetPtr nodes; xmlXPathObjectPtr obj; if (getOptionInt(MAX_NEST_LEVEL_KEY, nestcnt)) { nestlevel = nestcnt; } for (nestcnt = 0; nestcnt < nestlevel; nestcnt++) { obj = tool.getNodeList(root, "//import[@type=\"text/glsd-document\"]"); if (!obj) break; nodes = obj->nodesetval; // 置換対象が見付からない場合は処理を終了 if (0 == nodes->nodeNr) { xmlXPathFreeObject(obj); break; } for (count = 0; count < nodes->nodeNr; count++) { if (!transform(aDocument, nodes->nodeTab[count])) { THROW("ImportTextGLSD: transform failed."); } } xmlXPathFreeObject(obj); } } bool ImportTextGLSD::transform(xmlDocPtr aDocument, xmlNodePtr aNode) { Glib::ustring ref; Glib::ustring caption; Glib::ustring encoding; // encoding 属性は不要なため無視します if (!getImportAttributes(aNode, ref, caption, encoding)) { return false; } if ("html" == m_OutputType || "xhtml" == m_OutputType || "text" == m_OutputType) { return glsdInclude(aDocument, aNode, ref, caption, encoding); } // otherwise, do not operation. return true; } bool ImportTextGLSD::glsdInclude (xmlDocPtr aDocument, xmlNodePtr aNode, const Glib::ustring& aRef, const Glib::ustring& aCaption, const Glib::ustring& anEncoding) { // ファイルを読み込み、ノードを置換する bool bSuccess = false; std::string tmpfile; XMLTool tool; xmlDocPtr newdoc = NULL; xmlXPathObjectPtr obj = NULL; try { GLSDModuleChecker gmChecker; GLSDDocumentModuleChecker gmdChecker; Glib::RefPtr ioc = getIOChannel(aRef, tmpfile); ioc->set_encoding(); // binary mode newdoc = xmlReadFd(g_io_channel_unix_get_fd(ioc->gobj()), aRef.c_str(), NULL, 0); ioc->close(); // check if (!getXDTPTransform()->isXMLCheck()) { if (!gmChecker.checkFormat(newdoc) || !gmdChecker.checkFormat(newdoc)) { THROW("checkFormat failed. (" + aRef + ")"); } } // import の相対参照を絶対参照に変換 if (!absoluteImportRef(aRef, newdoc)) { THROW("Unable to fix the reference URL of 'import'."); } // id 属性値を一意に識別できるよう調整 if (!normalizeImportID(aNode, newdoc)) { THROW("Unable to normalize the attribute of id."); } // 置換ノードの取得 xmlNodePtr root = xmlDocGetRootElement(newdoc); xmlNodePtr newdocgroup = NULL; xmlNodePtr parent = aNode->parent; xmlNodeSetPtr nodes; obj = tool.getNodeList(root, "/document/docgroup"); if (obj) { nodes = obj->nodesetval; if (1 == nodes->nodeNr) { newdocgroup = xmlDocCopyNode(nodes->nodeTab[0], aDocument, 1); if (!newdocgroup) { THROW("xmlDocCopyNodeList failed. Out of memory?"); } } } // 置換 if (newdocgroup) { if (parent && 0 == strcmp("p", (const char*)parent->name)) { xmlFreeNode(xmlReplaceNode(parent, newdocgroup)); } else { xmlFreeNode(xmlReplaceNode(aNode, newdocgroup)); } } else { xmlUnlinkNode(aNode); xmlFreeNode(aNode); } bSuccess = true; } catch (Exception exception) { exception.print(stderr); } catch (Glib::Error error) { std::string msg = Glib::locale_from_utf8(error.what()); fprintf(stderr, "%s: %s (%s)" LINE_SEP, APP_NAME, msg.c_str(), aRef.c_str()); } catch (std::exception exception) { fprintf(stderr, "%s: %s (%s)" LINE_SEP, APP_NAME, exception.what(), aRef.c_str()); } catch (...) { fprintf(stderr, "%s: ImportTextSourcecode: unknown error occured. (%s)" LINE_SEP, APP_NAME, aRef.c_str()); } if (obj) { xmlXPathFreeObject(obj); } if (newdoc) { xmlFreeDoc(newdoc); } if (0 < tmpfile.length()) { remove(tmpfile.c_str()); } return bSuccess; } /** * import の相対参照を絶対参照に変換します。 * 基準となる参照位置を元に、基準となる絶対的な参照位置を決定します。 * 次に import タグ内の相対参照を、基準の絶対的な参照位置を元に修正します。 * XDTPは file スキーマ以外で相対参照を認めないため、file スキーマ以外の * 相対参照は修正しません。 * * @param aRef * 基準となる参照位置 * @param aDocument * 変換対象の import タグを保有するXMLドキュメント * @return 変換に成功した場合は true を戻します。 */ bool ImportTextGLSD::absoluteImportRef(const Glib::ustring& aRef, xmlDocPtr aDocument) { // 基準位置を決定 RefPtr uri = URI::parse(normalizeURL(aRef)); Glib::ustring basepath = uri->getPath(); Glib::ustring::size_type pos; // 基準を決めるにあたって、file スキーマ以外はホスト名の記述が必須。 if (!uri->isFileScheme() && 0 >= uri->getHostname().length()) { return false; } pos = basepath.rfind('/'); if (Glib::ustring::npos == pos || 1 == pos) { return false; } if (0 == pos) { basepath = ""; } else { basepath = basepath.substr(0, pos); if ('/' != basepath.at(0)) { basepath = "/" + basepath; } } // 相対参照を修正 bool result = true; int count; char buf[32]; Glib::ustring ref; Glib::ustring tmp1, tmp2; Glib::ustring path; Glib::ustring newref; RefPtr imref; XMLTool tool; xmlNodePtr root = xmlDocGetRootElement(aDocument); xmlNodePtr node; xmlNodeSetPtr nodes; xmlXPathObjectPtr obj = tool.getNodeList(root, "//import"); if (obj) { nodes = obj->nodesetval; for (count = 0; count < nodes->nodeNr; count++) { node = nodes->nodeTab[count]; if (!getImportAttributes(node, ref, tmp1, tmp2)) { result = false; break; } imref = URI::parse(ref); if (!imref->isFileScheme()) { if (0 < imref->getScheme().length()) { result = false; break; } else { path = ref; } } else { path = imref->getPath(); } if (0 >= path.length()) { result = false; break; } newref = uri->getScheme() + "://"; if (!uri->isFileScheme()) { if (0 < uri->getUsername().length()) { newref += uri->getUsername() + "@"; } newref += uri->getHostname(); if (0 < uri->getPort()) { bzero(buf, sizeof(buf)); snprintf(buf, sizeof(buf) -1, ":%d", uri->getPort()); newref += buf; } } if ('/' == path.at(0)) { newref += path; } else { newref += basepath + "/" + path; } xmlSetProp(node, (const xmlChar *)"ref", (const xmlChar *)newref.c_str()); } xmlXPathFreeObject(obj); } return result; } /** * id 属性値を一意に識別できるよう正規化します。 * * @param aParent * 元となるimportエレメント * @param aDocument * 変換対象の import タグを保有するXMLドキュメント * @return 変換に成功した場合は true を戻します。 */ bool ImportTextGLSD::normalizeImportID(xmlNodePtr aParent, xmlDocPtr aDocument) { // 元の id 属性値を取得 Glib::ustring id; const char *pId = (const char*)xmlGetProp(aParent, (const xmlChar*)"id"); id = Utilities::strTrim((pId ? pId : "")); if (pId) xmlFree((xmlChar*)pId); if (0 >= id.length()) { // 元となる id 属性値がない場合は何もしません。 return true; } id += "-"; // docgroup, import, bibitem 正規化 int count; Glib::ustring newid; XMLTool tool; xmlNodePtr root = xmlDocGetRootElement(aDocument); xmlNodePtr node; xmlNodeSetPtr nodes; xmlXPathObjectPtr obj = tool.getNodeList(root, "//*[@id]"); if (obj) { nodes = obj->nodesetval; for (count = 0; count < nodes->nodeNr; count++) { node = nodes->nodeTab[count]; // 対象エレメント以外は無視 if (0 != strcmp("docgroup", (const char*)node->name) && 0 != strcmp("import", (const char*)node->name) && 0 != strcmp("bibitem", (const char*)node->name)) { continue; } // 正規化 pId = (const char*)xmlGetProp(node, (const xmlChar*)"id"); newid = id + Utilities::strTrim((pId ? pId : "")); if (pId) xmlFree((xmlChar*)pId); xmlSetProp(node, (const xmlChar*)"id", (const xmlChar*)newid.c_str()); } xmlXPathFreeObject(obj); } // access 正規化 const char *pRef; Glib::ustring newref; obj = tool.getNodeList(root, "//access"); if (obj) { nodes = obj->nodesetval; for (count = 0; count < nodes->nodeNr; count++) { node = nodes->nodeTab[count]; pRef = (const char*)xmlGetProp(node, (const xmlChar*)"ref"); newref = Utilities::strTrim((pRef ? pRef : "")); if (pRef) xmlFree((xmlChar*)pRef); if (5 <= newref.length() && 0 == newref.substr(0, 5).compare("glsd:")) { // 正規化 newref = "glsd:" + id + newref.substr(5); xmlSetProp(node, (const xmlChar*)"ref", (const xmlChar*)newref.c_str()); } } xmlXPathFreeObject(obj); } return true; }