1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtDebug>
41
42#include "qxmlformatter.h"
43#include "qxpathhelper_p.h"
44#include "qxmlserializer_p.h"
45
46QT_BEGIN_NAMESPACE
47
48using namespace QPatternist;
49
50class QXmlFormatterPrivate : public QXmlSerializerPrivate
51{
52public:
53 inline QXmlFormatterPrivate(const QXmlQuery &q,
54 QIODevice *const outputDevice);
55
56 int indentationDepth;
57 int currentDepth;
58 QString characterBuffer;
59 QString indentString;
60
61 /**
62 * Whether we /have/ sent nodes like processing instructions and comments
63 * to QXmlSerializer.
64 */
65 QStack<bool> canIndent;
66};
67
68QXmlFormatterPrivate::QXmlFormatterPrivate(const QXmlQuery &query,
69 QIODevice *const outputDevice) : QXmlSerializerPrivate(query, outputDevice)
70 , indentationDepth(4)
71 , currentDepth(0)
72{
73 indentString.reserve(asize: 30);
74 indentString.resize(size: 1);
75 indentString[0] = QLatin1Char('\n');
76 canIndent.push(t: false);
77}
78
79/*!
80 \class QXmlFormatter
81 \brief The QXmlFormatter class is an implementation of QXmlSerializer for transforming XQuery output into formatted XML.
82 \reentrant
83 \since 4.4
84 \ingroup xml-tools
85 \inmodule QtXmlPatterns
86
87 QXmlFormatter is a subclass of QXmlSerializer that formats the XML
88 output to make it easier for humans to read.
89
90 QXmlSerializer outputs XML without adding unnecessary whitespace.
91 In particular, it does not add \e {newlines} and indentation.
92 To make the XML output easier to read, QXmlFormatter adds \e{newlines}
93 and indentation by adding, removing, and modifying
94 \l{XQuery Sequence}{sequence nodes} that only consist of whitespace.
95 It also modifies whitespace in other places where it is not
96 significant; e.g., between attributes and in the document prologue.
97
98 For example, where the base class QXmlSerializer would
99 output this:
100
101 \quotefile patternist/notIndented.xml
102
103 QXmlFormatter outputs this:
104
105 \quotefile patternist/indented.xml
106
107 If you just want to serialize your XML in a human-readable
108 format, use QXmlFormatter as it is. The default indentation
109 level is 4 spaces, but you can set your own indentation value
110 setIndentationDepth().
111
112 The \e{newlines} and indentation added by QXmlFormatter are
113 suitable for common formats, such as XHTML, SVG, or Docbook,
114 where whitespace is not significant. However, if your XML will
115 be used as input where whitespace is significant, then you must
116 write your own subclass of QXmlSerializer or QAbstractXmlReceiver.
117
118 Note that using QXmlFormatter instead of QXmlSerializer will
119 increase computational overhead and document storage size due
120 to the insertion of whitespace.
121
122 Note also that the indentation style used by QXmlFormatter
123 remains loosely defined and may change in future versions of
124 Qt. If a specific indentation style is required then either
125 use the base class QXmlSerializer directly, or write your own
126 subclass of QXmlSerializer or QAbstractXmlReceiver.
127 Alternatively, you can subclass QXmlFormatter and reimplement
128 the callbacks there.
129
130 \snippet code/src_xmlpatterns_api_qxmlformatter.cpp 0
131*/
132
133/*!
134 Constructs a formatter that uses the name pool and message
135 handler in \a query, and writes the result to \a outputDevice
136 as formatted XML.
137
138 \a outputDevice is passed directly to QXmlSerializer's constructor.
139
140 \sa QXmlSerializer
141 */
142QXmlFormatter::QXmlFormatter(const QXmlQuery &query,
143 QIODevice *outputDevice) : QXmlSerializer(new QXmlFormatterPrivate(query, outputDevice))
144{
145}
146
147/*!
148 \internal
149 */
150void QXmlFormatter::startFormattingContent()
151{
152 Q_D(QXmlFormatter);
153
154 if(QPatternist::XPathHelper::isWhitespaceOnly(string: d->characterBuffer))
155 {
156 if(d->canIndent.top())
157 QXmlSerializer::characters(value: QStringRef(&d->indentString));
158 }
159 else
160 {
161 if(!d->characterBuffer.isEmpty()) /* Significant data, we don't touch it. */
162 QXmlSerializer::characters(value: QStringRef(&d->characterBuffer));
163 }
164
165 d->characterBuffer.clear();
166}
167
168/*!
169 \reimp
170 */
171void QXmlFormatter::startElement(const QXmlName &name)
172{
173 Q_D(QXmlFormatter);
174 startFormattingContent();
175 ++d->currentDepth;
176 d->indentString.append(s: QString(d->indentationDepth, QLatin1Char(' ')));
177 d->canIndent.push(t: true);
178
179 QXmlSerializer::startElement(name);
180}
181
182/*!
183 \reimp
184 */
185void QXmlFormatter::endElement()
186{
187 Q_D(QXmlFormatter);
188 --d->currentDepth;
189 d->indentString.chop(n: d->indentationDepth);
190
191 if(!d->hasClosedElement.top().second)
192 d->canIndent.top() = false;
193
194 startFormattingContent();
195
196 d->canIndent.pop();
197 d->canIndent.top() = true;
198 QXmlSerializer::endElement();
199}
200
201/*!
202 \reimp
203 */
204void QXmlFormatter::attribute(const QXmlName &name,
205 const QStringRef &value)
206{
207 QXmlSerializer::attribute(name, value);
208}
209
210/*!
211 \reimp
212 */
213void QXmlFormatter::comment(const QString &value)
214{
215 Q_D(QXmlFormatter);
216 startFormattingContent();
217 QXmlSerializer::comment(value);
218 d->canIndent.top() = true;
219}
220
221/*!
222 \reimp
223 */
224void QXmlFormatter::characters(const QStringRef &value)
225{
226 Q_D(QXmlFormatter);
227 d->isPreviousAtomic = false;
228 d->characterBuffer += value.toString();
229}
230
231/*!
232 \reimp
233 */
234void QXmlFormatter::processingInstruction(const QXmlName &name,
235 const QString &value)
236{
237 Q_D(QXmlFormatter);
238 startFormattingContent();
239 QXmlSerializer::processingInstruction(name, value);
240 d->canIndent.top() = true;
241}
242
243/*!
244 \reimp
245 */
246void QXmlFormatter::atomicValue(const QVariant &value)
247{
248 Q_D(QXmlFormatter);
249 d->canIndent.top() = false;
250 QXmlSerializer::atomicValue(value);
251}
252
253/*!
254 \reimp
255 */
256void QXmlFormatter::startDocument()
257{
258 QXmlSerializer::startDocument();
259}
260
261/*!
262 \reimp
263 */
264void QXmlFormatter::endDocument()
265{
266 QXmlSerializer::endDocument();
267}
268
269/*!
270 \reimp
271 */
272void QXmlFormatter::startOfSequence()
273{
274 QXmlSerializer::startOfSequence();
275}
276
277/*!
278 \reimp
279 */
280void QXmlFormatter::endOfSequence()
281{
282 Q_D(QXmlFormatter);
283
284 /* Flush any buffered content. */
285 if(!d->characterBuffer.isEmpty())
286 QXmlSerializer::characters(value: QStringRef(&d->characterBuffer));
287
288 d->write(c: '\n');
289 QXmlSerializer::endOfSequence();
290}
291
292/*!
293 \internal
294 */
295void QXmlFormatter::item(const QPatternist::Item &item)
296{
297 Q_D(QXmlFormatter);
298
299 if(item.isAtomicValue())
300 {
301 if(QPatternist::XPathHelper::isWhitespaceOnly(string: item.stringValue()))
302 return;
303 else
304 {
305 d->canIndent.top() = false;
306 startFormattingContent();
307 }
308 }
309
310 QXmlSerializer::item(item);
311}
312
313/*!
314 Returns the number of spaces QXmlFormatter will output for each
315 indentation level. The default is four.
316
317 \sa setIndentationDepth()
318 */
319int QXmlFormatter::indentationDepth() const
320{
321 Q_D(const QXmlFormatter);
322 return d->indentationDepth;
323}
324
325/*!
326 Sets \a depth to be the number of spaces QXmlFormatter will
327 output for level of indentation. The default is four.
328
329 \sa indentationDepth()
330 */
331void QXmlFormatter::setIndentationDepth(int depth)
332{
333 Q_D(QXmlFormatter);
334 d->indentationDepth = depth;
335}
336
337QT_END_NAMESPACE
338

source code of qtxmlpatterns/src/xmlpatterns/api/qxmlformatter.cpp