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