a50b161cca5c20ca09e89ce0a50c9b38d7536f82
[profile/ivi/qtxmlpatterns.git] / src / xmlpatterns / api / qxmlserializer.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 "qdynamiccontext_p.h"
43 #include "qpatternistlocale_p.h"
44 #include "qitem_p.h"
45 #include "qxmlquery_p.h"
46 #include "qxmlserializer_p.h"
47 #include "qxmlserializer.h"
48
49 QT_BEGIN_NAMESPACE
50
51 using namespace QPatternist;
52
53 QXmlSerializerPrivate::QXmlSerializerPrivate(const QXmlQuery &query,
54                                              QIODevice *outputDevice)
55     : isPreviousAtomic(false),
56       state(QXmlSerializer::BeforeDocumentElement),
57       np(query.namePool().d),
58       device(outputDevice),
59       codec(QTextCodec::codecForMib(106)), /* UTF-8 */
60       query(query)
61 {
62     hasClosedElement.reserve(EstimatedTreeDepth);
63     namespaces.reserve(EstimatedTreeDepth);
64     nameCache.reserve(EstimatedNameCount);
65
66     hasClosedElement.push(qMakePair(QXmlName(), true));
67
68     /*
69       We push the empty namespace such that first of all
70       namespaceBinding() won't assert on an empty QStack,
71       and such that the empty namespace is in-scope and
72       that the code doesn't attempt to declare it.
73
74       We push the XML namespace. Although we won't receive
75       declarations for it, we may output attributes by that
76       name.
77     */
78     QVector<QXmlName> defNss;
79     defNss.resize(2);
80     defNss[0] = QXmlName(StandardNamespaces::empty,
81                          StandardLocalNames::empty,
82                          StandardPrefixes::empty);
83     defNss[1] = QXmlName(StandardNamespaces::xml,
84                          StandardLocalNames::empty,
85                          StandardPrefixes::xml);
86
87     namespaces.push(defNss);
88
89     /* If we don't set this flag, QTextCodec will generate a BOM. */
90     converterState.flags = QTextCodec::IgnoreHeader;
91 }
92
93 /*!
94   \class QXmlSerializer
95   \brief The QXmlSerializer class is an implementation of QAbstractXmlReceiver for transforming XQuery output into unformatted XML.
96
97   \reentrant
98   \since 4.4
99   \ingroup xml-tools
100
101   QXmlSerializer translates an \l {XQuery Sequence} {XQuery sequence}, usually
102   the output of an QXmlQuery, into XML. Consider the example:
103
104   \snippet doc/src/snippets/code/src_xmlpatterns_api_qxmlserializer.cpp 0
105
106   First it constructs a \l {QXmlQuery} {query} that gets the
107   first paragraph from document \c index.html. Then it constructs
108   an instance of this class with the \l {QXmlQuery} {query} and
109   \l {QIODevice} {myOutputDevice}. Finally, it
110   \l {QXmlQuery::evaluateTo()} {evaluates} the
111   \l {QXmlQuery} {query}, producing an ordered sequence of calls
112   to the serializer's callback functions. The sequence of callbacks
113   transforms the query output to XML and writes it to
114   \l {QIODevice} {myOutputDevice}.
115
116   QXmlSerializer will:
117
118   \list
119   \o Declare namespaces when needed,
120
121   \o Use appropriate escaping, when characters can't be
122   represented in the XML,
123
124   \o Handle line endings appropriately,
125
126   \o Report errors, when it can't serialize the content, e.g.,
127   when asked to serialize an attribute that is a top-level node,
128   or when more than one top-level element is encountered.
129
130   \endlist
131
132   If an error occurs during serialization, result is undefined
133   unless the serializer is driven through a call to
134   QXmlQuery::evaluateTo().
135
136   If the generated XML should be indented and formatted for reading,
137   use QXmlFormatter.
138
139   \sa {http://www.w3.org/TR/xslt-xquery-serialization/}{XSLT 2.0 and XQuery 1.0 Serialization}
140
141   \sa QXmlFormatter
142  */
143
144 /*!
145   Constructs a serializer that uses the name pool and message
146   handler in \a query, and writes the output to \a outputDevice.
147
148   \a outputDevice must be a valid, non-null device that is open in
149   write mode, otherwise behavior is undefined.
150
151   \a outputDevice must not be opened with QIODevice::Text because it
152   will cause the output to be incorrect. This class will ensure line
153   endings are serialized as according with the XML specification.
154   QXmlSerializer does not take ownership of \a outputDevice.
155  */
156 QXmlSerializer::QXmlSerializer(const QXmlQuery &query,
157                                QIODevice *outputDevice) : QAbstractXmlReceiver(new QXmlSerializerPrivate(query, outputDevice))
158 {
159     if(!outputDevice)
160     {
161         qWarning("outputDevice cannot be null.");
162         return;
163     }
164
165     if(!outputDevice->isWritable())
166     {
167         qWarning("outputDevice must be opened in write mode.");
168         return;
169     }
170 }
171
172 /*!
173   \internal
174  */
175 QXmlSerializer::QXmlSerializer(QAbstractXmlReceiverPrivate *d) : QAbstractXmlReceiver(d)
176 {
177 }
178
179 /*!
180   \internal
181  */
182 bool QXmlSerializer::atDocumentRoot() const
183 {
184     Q_D(const QXmlSerializer);
185     return d->state == BeforeDocumentElement ||
186            (d->state == InsideDocumentElement && d->hasClosedElement.size() == 1);
187 }
188
189 /*!
190   \internal
191  */
192 void QXmlSerializer::startContent()
193 {
194     Q_D(QXmlSerializer);
195     if (!d->hasClosedElement.top().second) {
196         d->write('>');
197         d->hasClosedElement.top().second = true;
198     }
199 }
200
201 /*!
202   \internal
203  */
204 void QXmlSerializer::writeEscaped(const QString &toEscape)
205 {
206     if(toEscape.isEmpty()) /* Early exit. */
207         return;
208
209     QString result;
210     result.reserve(int(toEscape.length() * 1.1));
211     const int length = toEscape.length();
212
213     for(int i = 0; i < length; ++i)
214     {
215         const QChar c(toEscape.at(i));
216
217         if(c == QLatin1Char('<'))
218             result += QLatin1String("&lt;");
219         else if(c == QLatin1Char('>'))
220             result += QLatin1String("&gt;");
221         else if(c == QLatin1Char('&'))
222             result += QLatin1String("&amp;");
223         else
224             result += toEscape.at(i);
225     }
226
227     write(result);
228 }
229
230 /*!
231   \internal
232  */
233 void QXmlSerializer::writeEscapedAttribute(const QString &toEscape)
234 {
235     if(toEscape.isEmpty()) /* Early exit. */
236         return;
237
238     QString result;
239     result.reserve(int(toEscape.length() * 1.1));
240     const int length = toEscape.length();
241
242     for(int i = 0; i < length; ++i)
243     {
244         const QChar c(toEscape.at(i));
245
246         if(c == QLatin1Char('<'))
247             result += QLatin1String("&lt;");
248         else if(c == QLatin1Char('>'))
249             result += QLatin1String("&gt;");
250         else if(c == QLatin1Char('&'))
251             result += QLatin1String("&amp;");
252         else if(c == QLatin1Char('"'))
253             result += QLatin1String("&quot;");
254         else
255             result += toEscape.at(i);
256     }
257
258     write(result);
259 }
260
261 /*!
262   \internal
263  */
264 void QXmlSerializer::write(const QString &content)
265 {
266     Q_D(QXmlSerializer);
267     d->device->write(d->codec->fromUnicode(content.constData(), content.length(), &d->converterState));
268 }
269
270 /*!
271   \internal
272  */
273 void QXmlSerializer::write(const QXmlName &name)
274 {
275     Q_D(QXmlSerializer);
276     const QByteArray &cell = d->nameCache[name.code()];
277
278     if(cell.isNull())
279     {
280         QByteArray &mutableCell = d->nameCache[name.code()];
281
282         const QString content(d->np->toLexical(name));
283         mutableCell = d->codec->fromUnicode(content.constData(),
284                                             content.length(),
285                                             &d->converterState);
286         d->device->write(mutableCell);
287     }
288     else
289         d->device->write(cell);
290 }
291
292 /*!
293   \internal
294  */
295 void QXmlSerializer::write(const char *const chars)
296 {
297     Q_D(QXmlSerializer);
298     d->device->write(chars);
299 }
300
301 /*!
302   \reimp
303  */
304 void QXmlSerializer::startElement(const QXmlName &name)
305 {
306     Q_D(QXmlSerializer);
307     Q_ASSERT(d->device);
308     Q_ASSERT(d->device->isWritable());
309     Q_ASSERT(d->codec);
310     Q_ASSERT(!name.isNull());
311
312     d->namespaces.push(QVector<QXmlName>());
313
314     if(atDocumentRoot())
315     {
316         if(d->state == BeforeDocumentElement)
317             d->state = InsideDocumentElement;
318         else if(d->state != InsideDocumentElement)
319         {
320             d->query.d->staticContext()->error(QtXmlPatterns::tr(
321                "Element %1 can't be serialized because it appears outside "
322                "the document element.").arg(formatKeyword(d->np, name)),
323                                                ReportContext::SENR0001,
324                                                d->query.d->expression().data());
325         }
326     }
327
328     startContent();
329     d->write('<');
330     write(name);
331
332     /* Ensure that the namespace URI used in the name gets outputted. */
333     namespaceBinding(name);
334
335     d->hasClosedElement.push(qMakePair(name, false));
336     d->isPreviousAtomic = false;
337 }
338
339 /*!
340   \reimp
341  */
342 void QXmlSerializer::endElement()
343 {
344     Q_D(QXmlSerializer);
345     const QPair<QXmlName, bool> e(d->hasClosedElement.pop());
346     d->namespaces.pop();
347
348     if(e.second)
349     {
350         write("</");
351         write(e.first);
352         d->write('>');
353     }
354     else
355         write("/>");
356
357     d->isPreviousAtomic = false;
358 }
359
360 /*!
361   \reimp
362  */
363 void QXmlSerializer::attribute(const QXmlName &name,
364                                const QStringRef &value)
365 {
366     Q_D(QXmlSerializer);
367     Q_ASSERT(!name.isNull());
368
369     /* Ensure that the namespace URI used in the name gets outputted. */
370     {
371         /* Since attributes doesn't pick up the default namespace, a
372          * namespace declaration would cause trouble if we output it. */
373         if(name.prefix() != StandardPrefixes::empty)
374             namespaceBinding(name);
375     }
376
377     if(atDocumentRoot())
378     {
379         Q_UNUSED(d);
380         d->query.d->staticContext()->error(QtXmlPatterns::tr(
381            "Attribute %1 can't be serialized because it appears at "
382            "the top level.").arg(formatKeyword(d->np, name)),
383                                            ReportContext::SENR0001,
384                                            d->query.d->expression().data());
385     }
386     else
387     {
388         d->write(' ');
389         write(name);
390         write("=\"");
391         writeEscapedAttribute(value.toString());
392         d->write('"');
393     }
394 }
395
396 /*!
397   \internal
398  */
399 bool QXmlSerializer::isBindingInScope(const QXmlName nb) const
400 {
401     Q_D(const QXmlSerializer);
402     const int levelLen = d->namespaces.size();
403
404     if(nb.prefix() == StandardPrefixes::empty)
405     {
406         for(int lvl = levelLen - 1; lvl >= 0; --lvl)
407         {
408             const QVector<QXmlName> &scope = d->namespaces.at(lvl);
409             const int vectorLen = scope.size();
410
411             for(int s = vectorLen - 1; s >= 0; --s)
412             {
413                 const QXmlName &nsb = scope.at(s);
414
415                 if(nsb.prefix() == StandardPrefixes::empty)
416                     return nsb.namespaceURI() == nb.namespaceURI();
417             }
418         }
419     }
420     else
421     {
422         for(int lvl = 0; lvl < levelLen; ++lvl)
423         {
424             const QVector<QXmlName> &scope = d->namespaces.at(lvl);
425             const int vectorLen = scope.size();
426
427             for(int s = 0; s < vectorLen; ++s)
428             {
429                 const QXmlName &n = scope.at(s);
430                 if (n.prefix() == nb.prefix() &&
431                     n.namespaceURI() == nb.namespaceURI())
432                     return true;
433             }
434         }
435     }
436
437     return false;
438 }
439
440 /*!
441  \reimp
442  */
443 void QXmlSerializer::namespaceBinding(const QXmlName &nb)
444 {
445     /*
446      * Writes out \a nb.
447      *
448      * Namespace bindings aren't looked up in a cache, because
449      * we typically receive very few.
450      */
451
452     Q_D(QXmlSerializer);
453     Q_ASSERT_X(!nb.isNull(), Q_FUNC_INFO,
454                "It makes no sense to pass a null QXmlName.");
455
456     Q_ASSERT_X((nb.namespaceURI() != StandardNamespaces::empty) ||
457                (nb.prefix() == StandardPrefixes::empty),
458                Q_FUNC_INFO,
459                "Undeclarations of prefixes aren't allowed in XML 1.0 "
460                "and aren't supposed to be received.");
461
462     if(nb.namespaceURI() == QPatternist::StandardNamespaces::StopNamespaceInheritance)
463         return;
464
465     if(isBindingInScope(nb))
466         return;
467
468     d->namespaces.top().append(nb);
469
470     if(nb.prefix() == StandardPrefixes::empty)
471         write(" xmlns");
472     else
473     {
474         write(" xmlns:");
475         write(d->np->stringForPrefix(nb.prefix()));
476     }
477
478     write("=\"");
479     writeEscapedAttribute(d->np->stringForNamespace(nb.namespaceURI()));
480     d->write('"');
481 }
482
483 /*!
484  \reimp
485  */
486 void QXmlSerializer::comment(const QString &value)
487 {
488     Q_D(QXmlSerializer);
489     Q_ASSERT_X(!value.contains(QLatin1String("--")),
490                Q_FUNC_INFO,
491                "Invalid input; it's the caller's responsibility to ensure "
492                "the input is correct.");
493
494     startContent();
495     write("<!--");
496     write(value);
497     write("-->");
498     d->isPreviousAtomic = false;
499 }
500
501 /*!
502  \reimp
503  */
504 void QXmlSerializer::characters(const QStringRef &value)
505 {
506     Q_D(QXmlSerializer);
507     d->isPreviousAtomic = false;
508     startContent();
509     writeEscaped(value.toString());
510 }
511
512 /*!
513  \reimp
514  */
515 void QXmlSerializer::processingInstruction(const QXmlName &name,
516                                            const QString &value)
517 {
518     Q_D(QXmlSerializer);
519     Q_ASSERT_X(!value.contains(QLatin1String("?>")),
520                Q_FUNC_INFO,
521                "Invalid input; it's the caller's responsibility to ensure "
522                "the input is correct.");
523
524     startContent();
525     write("<?");
526     write(name);
527     d->write(' ');
528     write(value);
529     write("?>");
530
531     d->isPreviousAtomic = false;
532 }
533
534 /*!
535   \internal
536  */
537 void QXmlSerializer::item(const QPatternist::Item &outputItem)
538 {
539     Q_D(QXmlSerializer);
540
541     if(outputItem.isAtomicValue())
542     {
543         if(d->isPreviousAtomic)
544         {
545             startContent();
546             d->write(' ');
547             writeEscaped(outputItem.stringValue());
548         }
549         else
550         {
551             d->isPreviousAtomic = true;
552             const QString value(outputItem.stringValue());
553
554             if(!value.isEmpty())
555             {
556                 startContent();
557                 writeEscaped(value);
558             }
559         }
560     }
561     else
562     {
563         startContent();
564         Q_ASSERT(outputItem.isNode());
565         sendAsNode(outputItem);
566     }
567 }
568
569 /*!
570  \reimp
571  */
572 void QXmlSerializer::atomicValue(const QVariant &value)
573 {
574     Q_UNUSED(value);
575 }
576
577 /*!
578  \reimp
579  */
580 void QXmlSerializer::startDocument()
581 {
582     Q_D(QXmlSerializer);
583     d->isPreviousAtomic = false;
584 }
585
586 /*!
587  \reimp
588  */
589 void QXmlSerializer::endDocument()
590 {
591     Q_D(QXmlSerializer);
592     d->isPreviousAtomic = false;
593 }
594
595 /*!
596
597   Returns a pointer to the output device. There is no corresponding
598   function to \e set the output device, because the output device must
599   be passed to the constructor. The serializer does not take ownership
600   of its IO device.
601  */
602 QIODevice *QXmlSerializer::outputDevice() const
603 {
604     Q_D(const QXmlSerializer);
605     return d->device;
606 }
607
608 /*!
609   Sets the codec the serializer will use for encoding its XML output.
610   The output codec is set to \a outputCodec. By default, the output
611   codec is set to the one for \c UTF-8. The serializer does not take
612   ownership of the codec.
613
614   \sa codec()
615
616  */
617 void QXmlSerializer::setCodec(const QTextCodec *outputCodec)
618 {
619     Q_D(QXmlSerializer);
620     d->codec = outputCodec;
621 }
622
623 /*!
624   Returns the codec being used by the serializer for encoding its
625   XML output.
626
627   \sa setCodec()
628  */
629 const QTextCodec *QXmlSerializer::codec() const
630 {
631     Q_D(const QXmlSerializer);
632     return d->codec;
633 }
634
635 /*!
636  \reimp
637  */
638 void QXmlSerializer::startOfSequence()
639 {
640 }
641
642 /*!
643  \reimp
644  */
645 void QXmlSerializer::endOfSequence()
646 {
647     /* If this function is changed to flush or close or something like that,
648      * take into consideration QXmlFormatter, especially
649      * QXmlFormatter::endOfSequence().
650      */
651 }
652
653 QT_END_NAMESPACE