1
/*
2
 * This file is part of the API Extractor project.
3
 *
4
 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
5
 *
6
 * Contact: PySide team <contact@pyside.org>
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * version 2 as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
 * 02110-1301 USA
21
 *
22
 */
23
#include "docparser.h"
24
#include <QtCore/QDebug>
25
#include <QtXmlPatterns/QXmlQuery>
26
#include <QBuffer>
27
28
#include <cstdlib>
29
#include <libxslt/xsltutils.h>
30
#include <libxslt/transform.h>
31
32
DocParser::DocParser()
33
{
34
    xmlSubstituteEntitiesDefault(1);
35
}
36
37
DocParser::~DocParser()
38
{
39
}
40
41
QString DocParser::getDocumentation(QXmlQuery& xquery, const QString& query,
42
                                    const DocModificationList& mods) const
43
{
44
    QString doc = execXQuery(xquery, query);
45
    return applyDocModifications(mods, doc);
46
}
47
48
QString DocParser::execXQuery(QXmlQuery& xquery, const QString& query) const
49
{
50
    QString escapedQuery(query);
51
    // XQuery can't have invalid XML characters
52
    escapedQuery.replace("&", "&amp;").replace("<", "&lt;");
53
    xquery.setQuery(escapedQuery);
54
    if (!xquery.isValid()) {
55
        qWarning() << "Bad XQuery: " << escapedQuery;
56
        return QString();
57
    }
58
59
    QString result;
60
    xquery.evaluateTo(&result);
61
    return result;
62
}
63
64
namespace
65
{
66
67
struct XslResources
68
{
69
    xmlDocPtr xmlDoc;
70
    xsltStylesheetPtr xslt;
71
    xmlDocPtr xslResult;
72
73
    XslResources() : xmlDoc(0), xslt(0), xslResult(0) {}
74
75
    ~XslResources()
76
    {
77
        if (xslt)
78
            xsltFreeStylesheet(xslt);
79
80
        if (xslResult)
81
            xmlFreeDoc(xslResult);
82
83
        if (xmlDoc)
84
            xmlFreeDoc(xmlDoc);
85
86
        xsltCleanupGlobals();
87
        xmlCleanupParser();
88
    }
89
};
90
91
} // namespace
92
93
QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const
94
{
95
    if (mods.isEmpty())
96
        return xml;
97
98
    bool hasXPathBasedModification = false;
99
    foreach (DocModification mod, mods) {
100
        if (mod.mode() == DocModification::XPathReplace) {
101
            hasXPathBasedModification = true;
102
            break;
103
        }
104
    }
105
106
    if (!hasXPathBasedModification)
107
        return xml;
108
109
    QString xsl = QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
110
                                "<xsl:transform version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n"
111
                                "<xsl:template match=\"/\">\n"
112
                                "    <xsl:apply-templates />\n"
113
                                "</xsl:template>\n"
114
                                "<xsl:template match=\"*\">\n"
115
                                "<xsl:copy>\n"
116
                                "    <xsl:copy-of select=\"@*\"/>\n"
117
                                "    <xsl:apply-templates/>\n"
118
                                "</xsl:copy>\n"
119
                                "</xsl:template>\n"
120
                               );
121
    foreach (DocModification mod, mods) {
122
        if (mod.mode() == DocModification::XPathReplace) {
123
            xsl += QLatin1String("<xsl:template match=\"")
124
                   + mod.xpath().replace("\"", "&quot;") + QLatin1String("\">")
125
                   + mod.code() + QLatin1String("</xsl:template>\n");
126
        }
127
    }
128
    xsl += QLatin1String("</xsl:transform>");
129
130
    XslResources res;
131
    // Read XML data
132
    QByteArray xmlData = xml.toUtf8();
133
    res.xmlDoc = xmlParseMemory(xmlData.constData(), xmlData.size());
134
    if (!res.xmlDoc)
135
        return xml;
136
137
    // Read XSL data as a XML file
138
    QByteArray xslData = xsl.toUtf8();
139
    // xsltFreeStylesheet will delete this pointer
140
    xmlDocPtr xslDoc = xmlParseMemory(xslData.constData(), xslData.size());
141
    if (!xslDoc)
142
        return xml;
143
144
    // Parse XSL data
145
    res.xslt = xsltParseStylesheetDoc(xslDoc);
146
    if (!res.xslt)
147
        return xml;
148
149
    // Apply XSL
150
    res.xslResult = xsltApplyStylesheet(res.xslt, res.xmlDoc, 0);
151
    xmlChar* buffer = 0;
152
    int bufferSize;
153
    QString result;
154
    if (!xsltSaveResultToString(&buffer, &bufferSize, res.xslResult, res.xslt)) {
155
        result = QString::fromUtf8(reinterpret_cast<char*>(buffer), bufferSize);
156
        std::free(buffer);
157
    } else {
158
        result = xml;
159
    }
160
161
    Q_ASSERT(result != xml);
162
    return result;
163
}