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