You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/translators/xslthandler.cpp

268 lines
8.0 KiB

/***************************************************************************
copyright : (C) 2003-2006 by Robby Stephenson
email : robby@periapsis.org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of version 2 of the GNU General Public License as *
* published by the Free Software Foundation; *
* *
***************************************************************************/
#include "xslthandler.h"
#include "../latin1literal.h"
#include "../tellico_debug.h"
#include "../tellico_utils.h"
#include <tqdom.h>
#include <tqtextcodec.h>
#include <kurl.h>
extern "C" {
#include <libxslt/xslt.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#include <libxslt/extensions.h>
#include <libexslt/exslt.h>
}
// I don't want any network I/O at all
static const int xml_options = XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_NOCDATA;
static const int xslt_options = xml_options;
/* some functions to pass to the XSLT libs */
static int writeToTQString(void* context, const char* buffer, int len) {
TQString* t = static_cast<TQString*>(context);
*t += TQString::fromUtf8(buffer, len);
return len;
}
static void closeTQString(void* context) {
TQString* t = static_cast<TQString*>(context);
*t += TQString::fromLatin1("\n");
}
using Tellico::XSLTHandler;
XSLTHandler::XMLOutputBuffer::XMLOutputBuffer() : m_res(TQString()) {
m_buf = xmlOutputBufferCreateIO((xmlOutputWriteCallback)writeToTQString,
(xmlOutputCloseCallback)closeTQString,
&m_res, 0);
if(m_buf) {
m_buf->written = 0;
} else {
myDebug() << "XMLOutputBuffer::XMLOutputBuffer() - error writing output buffer!" << endl;
}
}
XSLTHandler::XMLOutputBuffer::~XMLOutputBuffer() {
if(m_buf) {
xmlOutputBufferClose(m_buf); //also flushes
m_buf = 0;
}
}
int XSLTHandler::s_initCount = 0;
XSLTHandler::XSLTHandler(const TQCString& xsltFile_) :
m_stylesheet(0),
m_docIn(0),
m_docOut(0) {
init();
TQString file = KURL::encode_string(TQString::fromLocal8Bit(xsltFile_));
if(!file.isEmpty()) {
xmlDocPtr xsltDoc = xmlReadFile(file.utf8(), NULL, xslt_options);
m_stylesheet = xsltParseStylesheetDoc(xsltDoc);
if(!m_stylesheet) {
myDebug() << "XSLTHandler::applyStylesheet() - null stylesheet pointer for " << xsltFile_ << endl;
}
}
}
XSLTHandler::XSLTHandler(const KURL& xsltURL_) :
m_stylesheet(0),
m_docIn(0),
m_docOut(0) {
init();
if(xsltURL_.isValid() && xsltURL_.isLocalFile()) {
xmlDocPtr xsltDoc = xmlReadFile(xsltURL_.encodedPathAndQuery().utf8(), NULL, xslt_options);
m_stylesheet = xsltParseStylesheetDoc(xsltDoc);
if(!m_stylesheet) {
myDebug() << "XSLTHandler::applyStylesheet() - null stylesheet pointer for " << xsltURL_.path() << endl;
}
}
}
XSLTHandler::XSLTHandler(const TQDomDocument& xsltDoc_, const TQCString& xsltFile_, bool translate_) :
m_stylesheet(0),
m_docIn(0),
m_docOut(0) {
init();
TQString file = KURL::encode_string(TQString::fromLocal8Bit(xsltFile_));
if(!xsltDoc_.isNull() && !file.isEmpty()) {
setXSLTDoc(xsltDoc_, file.utf8(), translate_);
}
}
XSLTHandler::~XSLTHandler() {
if(m_stylesheet) {
xsltFreeStylesheet(m_stylesheet);
}
if(m_docIn) {
xmlFreeDoc(m_docIn);
}
if(m_docOut) {
xmlFreeDoc(m_docOut);
}
--s_initCount;
if(s_initCount == 0) {
xsltUnregisterExtModule(EXSLT_STRINGS_NAMESPACE);
xsltUnregisterExtModule(EXSLT_DYNAMIC_NAMESPACE);
xsltCleanupGlobals();
xmlCleanupParser();
}
}
void XSLTHandler::init() {
if(s_initCount == 0) {
xmlSubstituteEntitiesDefault(1);
xmlLoadExtDtdDefaultValue = 0;
// register all exslt extensions
exsltRegisterAll();
}
++s_initCount;
m_params.clear();
}
void XSLTHandler::setXSLTDoc(const TQDomDocument& dom_, const TQCString& xsltFile_, bool translate_) {
bool utf8 = true; // XML defaults to utf-8
// need to find out if utf-8 or not
const TQDomNodeList childs = dom_.childNodes();
for(uint j = 0; j < childs.count(); ++j) {
if(childs.item(j).isProcessingInstruction()) {
TQDomProcessingInstruction pi = childs.item(j).toProcessingInstruction();
if(pi.data().lower().contains(TQString::fromLatin1("encoding"))) {
if(!pi.data().lower().contains(TQString::fromLatin1("utf-8"))) {
utf8 = false;
// } else {
// myDebug() << "XSLTHandler::setXSLTDoc() - PI = " << pi.data() << endl;
}
break;
}
}
}
TQString s;
if(translate_) {
s = Tellico::i18nReplace(dom_.toString(0 /* indent */));
} else {
s = dom_.toString();
}
xmlDocPtr xsltDoc;
if(utf8) {
xsltDoc = xmlReadDoc(reinterpret_cast<xmlChar*>(s.utf8().data()), xsltFile_.data(), NULL, xslt_options);
} else {
xsltDoc = xmlReadDoc(reinterpret_cast<xmlChar*>(s.local8Bit().data()), xsltFile_.data(), NULL, xslt_options);
}
if(m_stylesheet) {
xsltFreeStylesheet(m_stylesheet);
}
m_stylesheet = xsltParseStylesheetDoc(xsltDoc);
if(!m_stylesheet) {
myDebug() << "XSLTHandler::applyStylesheet() - null stylesheet pointer for " << xsltFile_ << endl;
}
// xmlFreeDoc(xsltDoc); // this causes a crash for some reason
}
void XSLTHandler::addStringParam(const TQCString& name_, const TQCString& value_) {
TQCString value = value_;
value.replace('\'', "&apos;");
addParam(name_, TQCString("'") + value + TQCString("'"));
}
void XSLTHandler::addParam(const TQCString& name_, const TQCString& value_) {
m_params.insert(name_, value_);
// myDebug() << "XSLTHandler::addParam() - " << name_ << ":" << value_ << endl;
}
void XSLTHandler::removeParam(const TQCString& name_) {
m_params.remove(name_);
}
const TQCString& XSLTHandler::param(const TQCString& name_) {
return m_params[name_];
}
TQString XSLTHandler::applyStylesheet(const TQString& text_) {
if(!m_stylesheet) {
myDebug() << "XSLTHandler::applyStylesheet() - null stylesheet pointer!" << endl;
return TQString();
}
m_docIn = xmlReadDoc(reinterpret_cast<xmlChar*>(text_.utf8().data()), NULL, NULL, xml_options);
return process();
}
TQString XSLTHandler::process() {
if(!m_docIn) {
myDebug() << "XSLTHandler::process() - error parsing input string!" << endl;
return TQString();
}
TQMemArray<const char*> params(2*m_params.count() + 1);
params[0] = NULL;
TQMap<TQCString, TQCString>::ConstIterator it = m_params.constBegin();
TQMap<TQCString, TQCString>::ConstIterator end = m_params.constEnd();
for(uint i = 0; it != end; ++it) {
params[i ] = tqstrdup(it.key());
params[i+1] = tqstrdup(it.data());
params[i+2] = NULL;
i += 2;
}
// returns NULL on error
m_docOut = xsltApplyStylesheet(m_stylesheet, m_docIn, params.data());
for(uint i = 0; i < 2*m_params.count(); ++i) {
delete[] params[i];
}
if(!m_docOut) {
myDebug() << "XSLTHandler::applyStylesheet() - error applying stylesheet!" << endl;
return TQString();
}
XMLOutputBuffer output;
if(output.isValid()) {
int num_bytes = xsltSaveResultTo(output.buffer(), m_docOut, m_stylesheet);
if(num_bytes == -1) {
myDebug() << "XSLTHandler::applyStylesheet() - error saving output buffer!" << endl;
}
}
return output.result();
}
//static
TQDomDocument& XSLTHandler::setLocaleEncoding(TQDomDocument& dom_) {
const TQDomNodeList childs = dom_.documentElement().childNodes();
for(unsigned j = 0; j < childs.count(); ++j) {
if(childs.item(j).isElement() && childs.item(j).nodeName() == Latin1Literal("xsl:output")) {
TQDomElement e = childs.item(j).toElement();
const TQString encoding = TQString::fromLatin1(TQTextCodec::codecForLocale()->name());
e.setAttribute(TQString::fromLatin1("encoding"), encoding);
break;
}
}
return dom_;
}