File indexing completed on 2024-05-12 05:10:15
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "xslthandler.h" 0026 #include "../tellico_debug.h" 0027 #include "../utils/string_utils.h" 0028 0029 #include <QUrl> 0030 0031 #include <QDomDocument> 0032 #include <QTextCodec> 0033 #include <QVector> 0034 0035 extern "C" { 0036 #include <libxslt/xslt.h> 0037 #include <libxslt/transform.h> 0038 #include <libxslt/xsltutils.h> 0039 #include <libxslt/extensions.h> 0040 0041 #include <libexslt/exslt.h> 0042 } 0043 0044 // I don't want any network I/O at all 0045 static const int xml_options = XML_PARSE_NONET | XML_PARSE_NOCDATA; 0046 static const int xslt_options = xml_options; 0047 0048 /* some functions to pass to the XSLT libs */ 0049 static int writeToQString(void* context, const char* buffer, int len) { 0050 QString* t = static_cast<QString*>(context); 0051 *t += QString::fromUtf8(buffer, len); 0052 return len; 0053 } 0054 0055 static int closeQString(void* context) { 0056 QString* t = static_cast<QString*>(context); 0057 *t += QLatin1String("\n"); 0058 return 0; 0059 } 0060 0061 using Tellico::XSLTHandler; 0062 0063 XSLTHandler::XMLOutputBuffer::XMLOutputBuffer() { 0064 m_buf = xmlOutputBufferCreateIO((xmlOutputWriteCallback)writeToQString, 0065 (xmlOutputCloseCallback)closeQString, 0066 &m_res, nullptr); 0067 if(m_buf) { 0068 m_buf->written = 0; 0069 } else { 0070 myWarning() << "error writing output buffer!"; 0071 } 0072 } 0073 0074 XSLTHandler::XMLOutputBuffer::~XMLOutputBuffer() { 0075 if(m_buf) { 0076 xmlOutputBufferClose(m_buf); //also flushes 0077 m_buf = nullptr; 0078 } 0079 } 0080 0081 int XSLTHandler::s_initCount = 0; 0082 0083 XSLTHandler::XSLTHandler(const QByteArray& xsltFile_) : 0084 m_stylesheet(nullptr) { 0085 init(); 0086 QByteArray file = QUrl::toPercentEncoding(QString::fromLocal8Bit(xsltFile_)); 0087 if(!file.isEmpty()) { 0088 xmlDocPtr xsltDoc = xmlReadFile(file.constData(), nullptr, xslt_options); 0089 m_stylesheet = xsltParseStylesheetDoc(xsltDoc); 0090 if(!m_stylesheet) { 0091 myDebug() << "null stylesheet pointer for " << xsltFile_; 0092 } 0093 } else { 0094 myDebug() << "XSLTHandler(QByteArray) - empty file name"; 0095 } 0096 } 0097 0098 XSLTHandler::XSLTHandler(const QUrl& xsltURL_) : 0099 m_stylesheet(nullptr) { 0100 init(); 0101 if(xsltURL_.isValid() && xsltURL_.isLocalFile()) { 0102 xmlDocPtr xsltDoc = xmlReadFile(xsltURL_.toLocalFile().toUtf8().constData(), nullptr, xslt_options); 0103 m_stylesheet = xsltParseStylesheetDoc(xsltDoc); 0104 if(!m_stylesheet) { 0105 myDebug() << "null stylesheet pointer for " << xsltURL_.path(); 0106 } 0107 } else { 0108 myDebug() << "XSLTHandler(QUrl) - invalid: " << xsltURL_; 0109 } 0110 } 0111 0112 XSLTHandler::XSLTHandler(const QDomDocument& xsltDoc_, const QByteArray& xsltFile_, bool translate_) : 0113 m_stylesheet(nullptr) { 0114 init(); 0115 QByteArray file = QUrl::toPercentEncoding(QString::fromLocal8Bit(xsltFile_)); 0116 if(!xsltDoc_.isNull() && !file.isEmpty()) { 0117 setXSLTDoc(xsltDoc_, file, translate_); 0118 } 0119 } 0120 0121 XSLTHandler::~XSLTHandler() { 0122 if(m_stylesheet) { 0123 xsltFreeStylesheet(m_stylesheet); 0124 } 0125 0126 --s_initCount; 0127 if(s_initCount == 0) { 0128 xsltUnregisterExtModule(EXSLT_STRINGS_NAMESPACE); 0129 xsltUnregisterExtModule(EXSLT_DYNAMIC_NAMESPACE); 0130 xsltCleanupGlobals(); 0131 xmlCleanupParser(); 0132 } 0133 } 0134 0135 void XSLTHandler::init() { 0136 if(s_initCount == 0) { 0137 xmlLoadExtDtdDefaultValue = 0; 0138 0139 // register all exslt extensions 0140 exsltRegisterAll(); 0141 } 0142 ++s_initCount; 0143 0144 m_params.clear(); 0145 } 0146 0147 bool XSLTHandler::isValid() const { 0148 return (m_stylesheet != nullptr); 0149 } 0150 0151 void XSLTHandler::setXSLTDoc(const QDomDocument& dom_, const QByteArray& xsltFile_, bool translate_) { 0152 bool utf8 = true; // XML defaults to utf-8 0153 0154 // need to find out if utf-8 or not 0155 const QDomNodeList children = dom_.childNodes(); 0156 for(int j = 0; j < children.count(); ++j) { 0157 if(children.item(j).isProcessingInstruction()) { 0158 QDomProcessingInstruction pi = children.item(j).toProcessingInstruction(); 0159 if(pi.data().toLower().contains(QLatin1String("encoding"))) { 0160 if(!pi.data().toLower().contains(QLatin1String("utf-8"))) { 0161 utf8 = false; 0162 // } else { 0163 // myDebug() << "PI = " << pi.data(); 0164 } 0165 break; 0166 } 0167 } 0168 } 0169 0170 QString s; 0171 if(translate_) { 0172 s = Tellico::i18nReplace(dom_.toString(0 /* indent */)); 0173 } else { 0174 s = dom_.toString(); 0175 } 0176 0177 xmlDocPtr xsltDoc; 0178 if(utf8) { 0179 xsltDoc = xmlReadDoc(reinterpret_cast<xmlChar*>(s.toUtf8().data()), xsltFile_.data(), nullptr, xslt_options); 0180 } else { 0181 xsltDoc = xmlReadDoc(reinterpret_cast<xmlChar*>(s.toLocal8Bit().data()), xsltFile_.data(), nullptr, xslt_options); 0182 } 0183 0184 if(m_stylesheet) { 0185 xsltFreeStylesheet(m_stylesheet); 0186 } 0187 m_stylesheet = xsltParseStylesheetDoc(xsltDoc); 0188 if(!m_stylesheet) { 0189 myDebug() << "null stylesheet pointer for " << xsltFile_; 0190 } 0191 // xmlFreeDoc(xsltDoc); // this causes a crash for some reason 0192 } 0193 0194 void XSLTHandler::addStringParam(const QByteArray& name_, const QByteArray& value_) { 0195 QByteArray value = value_; 0196 if(value.contains('\'')) { 0197 if(value.contains('"')) { 0198 myWarning() << "String param contains both ' and \"" << value_; 0199 value.replace('"', "'"); 0200 } 0201 addParam(name_, QByteArray("\"") + value + QByteArray("\"")); 0202 } else { 0203 addParam(name_, QByteArray("'") + value + QByteArray("'")); 0204 } 0205 } 0206 0207 void XSLTHandler::addParam(const QByteArray& name_, const QByteArray& value_) { 0208 m_params.insert(name_, value_); 0209 // myDebug() << name_ << ":" << value_; 0210 } 0211 0212 void XSLTHandler::removeParam(const QByteArray& name_) { 0213 m_params.remove(name_); 0214 } 0215 0216 const QByteArray& XSLTHandler::param(const QByteArray& name_) { 0217 return m_params[name_]; 0218 } 0219 0220 QString XSLTHandler::applyStylesheet(const QString& text_) { 0221 if(!m_stylesheet) { 0222 myDebug() << "null stylesheet pointer!"; 0223 return QString(); 0224 } 0225 if(text_.isEmpty()) { 0226 myDebug() << "XSLTHandler::applyStylesheet() - empty input"; 0227 return QString(); 0228 } 0229 0230 xmlDocPtr docIn; 0231 docIn = xmlReadDoc(reinterpret_cast<xmlChar*>(text_.toUtf8().data()), nullptr, nullptr, xml_options); 0232 0233 return process(docIn); 0234 } 0235 0236 QString XSLTHandler::process(xmlDocPtr docIn) { 0237 if(!docIn) { 0238 myDebug() << "XSLTHandler::applyStylesheet() - error parsing input string!"; 0239 return QString(); 0240 } 0241 0242 QVector<const char*> params(2*m_params.count() + 1); 0243 params[0] = nullptr; 0244 QHash<QByteArray, QByteArray>::ConstIterator it = m_params.constBegin(); 0245 QHash<QByteArray, QByteArray>::ConstIterator end = m_params.constEnd(); 0246 for(int i = 0; it != end; ++it) { 0247 params[i ] = qstrdup(it.key().constData()); 0248 params[i+1] = qstrdup(it.value().constData()); 0249 params[i+2] = nullptr; 0250 i += 2; 0251 } 0252 // returns NULL on error 0253 xmlDocPtr docOut; 0254 docOut = xsltApplyStylesheet(m_stylesheet, docIn, params.data()); 0255 for(int i = 0; i < 2*m_params.count(); ++i) { 0256 delete[] params[i]; 0257 } 0258 0259 xmlFreeDoc(docIn); 0260 docIn = nullptr; 0261 0262 if(!docOut) { 0263 myDebug() << "error applying stylesheet!"; 0264 return QString(); 0265 } 0266 0267 XMLOutputBuffer output; 0268 if(output.isValid()) { 0269 int num_bytes = xsltSaveResultTo(output.buffer(), docOut, m_stylesheet); 0270 if(num_bytes == -1) { 0271 myDebug() << "error saving output buffer!"; 0272 } 0273 } 0274 0275 xmlFreeDoc(docOut); 0276 docOut = nullptr; 0277 0278 return output.result(); 0279 } 0280 0281 //static 0282 QDomDocument& XSLTHandler::setLocaleEncoding(QDomDocument& dom_) { 0283 const QDomNodeList children = dom_.documentElement().childNodes(); 0284 for(int j = 0; j < children.count(); ++j) { 0285 if(children.item(j).isElement() && children.item(j).nodeName() == QLatin1String("xsl:output")) { 0286 QDomElement e = children.item(j).toElement(); 0287 const QString encoding = QLatin1String(QTextCodec::codecForLocale()->name()); 0288 e.setAttribute(QStringLiteral("encoding"), encoding); 0289 break; 0290 } 0291 } 0292 return dom_; 0293 }