/*
* $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<Glib::IOChannel> 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 = 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<URI> 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;
}
syntax highlighted by Code2HTML, v. 0.9.1