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