1 /*
2  *    Copyright 2013-2019 Kai Pastor
3  *
4  *    This file is part of OpenOrienteering.
5  *
6  *    OpenOrienteering is free software: you can redistribute it and/or modify
7  *    it under the terms of the GNU General Public License as published by
8  *    the Free Software Foundation, either version 3 of the License, or
9  *    (at your option) any later version.
10  *
11  *    OpenOrienteering is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    GNU General Public License for more details.
15  *
16  *    You should have received a copy of the GNU General Public License
17  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef OPENORIENTEERING_XML_STREAM_UTIL_H
21 #define OPENORIENTEERING_XML_STREAM_UTIL_H
22 
23 #include <QtGlobal>
24 #include <QHash>
25 #include <QLatin1String>
26 #include <QRectF>
27 #include <QSizeF>
28 #include <QString>
29 #include <QStringRef>
30 #include <QXmlStreamAttributes>
31 #include <QXmlStreamReader>
32 #include <QXmlStreamWriter>
33 
34 #include "core/map_coord.h"
35 
36 class QRectF;
37 class QSizeF;
38 class QXmlStreamReader;
39 class QXmlStreamWriter;
40 
41 namespace OpenOrienteering {
42 
43 
44 /**
45  * Writes a line break to the XML stream unless auto formatting is active.
46  */
47 void writeLineBreak(QXmlStreamWriter& xml);
48 
49 
50 /**
51  * Returns the number of characters which are significant for input/output.
52  */
53 QString numberToString(double value, int precision);
54 
55 
56 /**
57  * This class provides recovery from invalid characters in an XML stream.
58  *
59  * Some characters are not allowed in well-formed XML 1.0 (cf.
60  * https://www.w3.org/TR/2008/REC-xml-20081126/#NT-Char). While QXmlStreamWriter
61  * will not complain when writing such characters, QXmlStreamReader will raise
62  * a NotWellFormedError. This class will remove offending characters from the
63  * input and reset the stream reader to the state it had when the helper object
64  * was initialized.
65  *
66  * In a single recovery attempt, the utility tries to handle all offending
67  * characters from the element for which the tool was constructed. For each
68  * offending character, the whole XML data is parsed again from the start.
69  * That's why multiple corrections may take a long time to run.
70  *
71  * The XML stream must be based on a QIODevice which supports QIODevice::seek.
72  *
73  * Synopsis:
74  *
75  *     XmlRecoveryHelper recovery(xml);
76  *     auto text = xml.readElementText();
77  *     if (xml.hasError() && recovery())
78  *     {
79  *         addWarning(tr("Some invalid characters had to be removed.");
80  *         text = xml.readElementText();
81  *     }
82  */
83 class XmlRecoveryHelper
84 {
85 public:
86 	/**
87 	 * Constructs a new recovery helper for the given xml stream.
88 	 *
89 	 * Captures the current position in the XML stream (QXmlStreamReader::characterOffset()).
90 	 */
XmlRecoveryHelper(QXmlStreamReader & xml)91 	XmlRecoveryHelper(QXmlStreamReader& xml) : xml (xml), recovery_start {xml.characterOffset()} {}
92 
93 	/**
94 	 * Checks the stream for an error which this utility can handle,
95 	 * applies corrections, and resets the stream.
96 	 *
97 	 * If this operator returns false if either there was a different type of
98 	 * error, or if recovery failed. If it returns true, the stream was modified
99 	 * in order to fix the errors which are handled by this utility, and a new
100 	 * attempt can be made to parse the remainder of the stream.
101 	 */
102 	bool operator() ();
103 
104 private:
105 	QXmlStreamReader& xml;
106 	const qint64 recovery_start;
107 };
108 
109 
110 
111 /**
112  * The XmlElementWriter helps to construct a single element in an XML document.
113  *
114  * It starts a new element on a QXmlStreamWriter when it is constructed,
115  * and it writes the end tag when it is destructed. After construction, but
116  * before (child) elements are created on the QXmlStreamWriter, it offers
117  * convenient functions for writing named attributes of common types.
118  *
119  * Typical use:
120  *
121  * \code
122    {
123        // Construction, begins with start tag
124        XmlElementWriter coord(xml_writer, QLatin1String("coord"));
125        coord.writeAttribute(QLatin1String("x"), 34);
126        coord.writeAttribute(QLatin1String("y"), 3.4);
127 
128        // Don't use coord once you wrote other data to the stream
129        writeChildElements(xml_writer);
130 
131    }   // coord goes out of scope here, destructor called, end tag written
132  * \endcode
133  */
134 class XmlElementWriter
135 {
136 public:
137 	/**
138 	 * Begins a new element with the given name on the XML writer.
139 	 */
140 	XmlElementWriter(QXmlStreamWriter& xml, const QLatin1String& element_name);
141 
142 	XmlElementWriter(const XmlElementWriter&) = delete;
143 	XmlElementWriter(XmlElementWriter&&) = delete;
144 
145 	/**
146 	 * Writes the end tag of the element.
147 	 */
148 	~XmlElementWriter();
149 
150 
151 	XmlElementWriter& operator=(const XmlElementWriter&) = delete;
152 	XmlElementWriter& operator=(XmlElementWriter&&) = delete;
153 
154 
155 	/**
156 	 * Writes an attribute with the given name and value.
157 	 */
158 	void writeAttribute(const QLatin1String& qualifiedName, const char* value);
159 
160 	/**
161 	 * Writes an attribute with the given name and value.
162 	 */
163 	void writeAttribute(const QLatin1String& qualifiedName, const QString& value);
164 
165 	/**
166 	 * Writes an attribute with the given name and value.
167 	 * This methods uses Qt's default QString::number(double) implementation.
168 	 */
169 	void writeAttribute(const QLatin1String& qualifiedName, const double value);
170 
171 	/**
172 	 * Writes an attribute with the given name and value.
173 	 * The precision represents the number of digits after the decimal point.
174 	 */
175 	void writeAttribute(const QLatin1String& qualifiedName, const double value, int precision);
176 
177 	/**
178 	 * Writes an attribute with the given name and value.
179 	 * This methods uses Qt's default QString::number(float) implementation.
180 	 */
181 	void writeAttribute(const QLatin1String& qualifiedName, const float value);
182 
183 	/**
184 	 * Writes an attribute with the given name and value.
185 	 * The precision represents the number of digits after the decimal point.
186 	 */
187 	void writeAttribute(const QLatin1String& qualifiedName, const float value, int precision);
188 
189 	/**
190 	 * Writes an attribute with the given name and value.
191 	 */
192 	void writeAttribute(const QLatin1String& qualifiedName, const qint64 value);
193 
194 	/**
195 	 * Writes an attribute with the given name and value.
196 	 */
197 	void writeAttribute(const QLatin1String& qualifiedName, const int value);
198 
199 	/**
200 	 * Writes an attribute with the given name and value.
201 	 */
202 	void writeAttribute(const QLatin1String& qualifiedName, const unsigned int value);
203 
204 	/**
205 	 * Writes an attribute with the given name and value.
206 	 */
207 	void writeAttribute(const QLatin1String& qualifiedName, const long unsigned int value);
208 
209 	/**
210 	 * Writes an attribute with the given name and value.
211 	 */
212 	void writeAttribute(const QLatin1String& qualifiedName, const quint64 value);
213 
214 	/**
215 	 * Writes an attribute with the given name and value.
216 	 */
217 	void writeAttribute(const QLatin1String& qualifiedName, bool value);
218 
219 	/**
220 	 * Writes attributes named left, top, width and height,
221 	 * representing the given area.
222 	 * This methods uses Qt's default QString::number(qreal) implementation.
223 	 */
224 	void write(const QRectF& area);
225 
226 	/**
227 	 * Writes attributes named left, top, width and height,
228 	 * representing the given area.
229 	 */
230 	void write(const QRectF& area, int precision);
231 
232 	/**
233 	 * Writes attributes named width and height, representing the given size.
234 	 * This methods uses Qt's default QString::number(qreal) implementation.
235 	 */
236 	void write(const QSizeF& size);
237 
238 	/**
239 	 * Writes attributes named width and height, representing the given size.
240 	 */
241 	void write(const QSizeF& size, int precision);
242 
243 	/**
244 	 * Writes the coordinates vector as a simple text format.
245 	 * This is much more efficient than saving each coordinate as rich XML.
246 	 */
247 	void write(const MapCoordVector& coords);
248 
249 	/**
250 	 * Writes tags.
251 	 */
252 	void write(const QHash<QString, QString>& tags);
253 
254 private:
255 	QXmlStreamWriter& xml;
256 };
257 
258 
259 /**
260  * The XmlElementReader helps to read a single element in an XML document.
261  *
262  * It assumes to be on a QXmlStreamReader::StartElement when constructed,
263  * and it reads until the end of the current element, skipping any child nodes,
264  * when it is destructed. After construction, it offers convenient functions
265  * for reading named attributes of common types.
266  *
267  * Typical use:
268  *
269  * \code
270    while (xml_reader.readNextStartElement())
271    {
272        // Construction, begins with start tag
273        XmlElementReader coord(xml_reader);
274        int x = coord.attribute<int>(QLatin1String("x"));
275        double y = coord.attribute<double>(QLatin1String("y"));
276        FlagEnum flags = coord.attribute<FlagEnum>(QLatin1String("flags"));
277 
278        readChildData(xml_reader);
279 
280    }   // coord goes out of scope here, destructor called, reads until end of element
281  * \endcode
282  */
283 class XmlElementReader
284 {
285 public:
286 	/**
287 	 * Constructs a new element reader on the given XML reader.
288 	 *
289 	 * It assumes to be on a QXmlStreamReader::StartElement.
290 	 */
291 	XmlElementReader(QXmlStreamReader& xml);
292 
293 	XmlElementReader(const XmlElementReader&) = delete;
294 	XmlElementReader(XmlElementReader&&) = delete;
295 
296 	/**
297 	 * Destructor.
298 	 *
299 	 * Reads until the end of the current element, skipping any child nodes.
300 	 */
301 	~XmlElementReader();
302 
303 
304 	XmlElementReader& operator=(const XmlElementReader&) = delete;
305 	XmlElementReader& operator=(XmlElementReader&&) = delete;
306 
307 
308 	/**
309 	 * Tests whether the element has an attribute with the given name.
310 	 */
311 	bool hasAttribute(const QLatin1String&  qualifiedName) const;
312 
313 	/**
314 	 * Tests whether the element has an attribute with the given name.
315 	 */
316 	bool hasAttribute(const QString& qualifiedName) const;
317 
318 	/**
319 	 * Returns the value of an attribute of type T.
320 	 *
321 	 * Apart from a number of specializations for common types,
322 	 * it has a general implementation which read the attribute as int
323 	 * and does static_cast to the actual type. This is useful for enumerations,
324 	 * but might also be a cause of buildtime or runtime errors.
325 	 */
326 	template< typename T >
327 	T attribute(const QLatin1String& qualifiedName) const;
328 
329 	/**
330 	 * Reads attributes named left, top, width, height into the given area object.
331 	 * Counterpart for XmlElementWriter::write(const QRectF&, int).
332 	 */
333 	void read(QRectF& area);
334 
335 	/**
336 	 * Reads attributes named width and height into the given size object.
337 	 * Counterpart for XmlElementWriter::write(const QSizeF&, int).
338 	 */
339 	void read(QSizeF& size);
340 
341 	/**
342 	 * Reads the coordinates vector from a simple text format.
343 	 * This is much more efficient than loading each coordinate from rich XML.
344 	 */
345 	void read(MapCoordVector& coords);
346 
347 	/**
348 	 * Reads the coordinates vector for a text object.
349 	 *
350 	 * This is either a single anchor, or an anchor and a size, packed as a
351 	 * coordinates vector. Regular coordinate bounds checking is not applied
352 	 * to the size.
353 	 *
354 	 * \todo Make box size explicit data.
355 	 *
356 	 * \see read(MapCoordVector&)
357 	 */
358 	void readForText(MapCoordVector& coords);
359 
360 	/**
361 	 * Read tags.
362 	 */
363 	void read(QHash<QString, QString>& tags);
364 
365 private:
366 	QXmlStreamReader& xml;
367 	const QXmlStreamAttributes attributes; // implicitly shared QVector
368 };
369 
370 
371 /**
372  * @namespace literal
373  * @brief Namespace for \c QLatin1String constants
374  *
375  * It's current main use is in connection with XMLFileFormat.
376  * XMLFileFormat is built on \c QXmlStreamReader/\c QXmlStreamWriter which
377  * expect \c QLatin1String arguments in many places.
378  * In addition, a \c QLatin1String can be compared to a \c QStringRef without
379  * implicit conversion.
380  *
381  * The namespace \c literal cannot be used directly in header files because it
382  * would easily lead to name conflicts in including files.
383  * However, custom namespaces in header files can be aliased to \c literal
384  * locally in method definitions:
385  *
386  * \code
387  * void someFuntion()
388  * {
389  *     namespace literal = XmlStreamLiteral;
390  *     writeAttribute(literal::left, 37.0);
391  * }
392  * \endcode
393  *
394  * @sa MapCoordLiteral, XmlStreamLiteral
395  */
396 
397 
398 /**
399  * @brief Local namespace for \c QLatin1String constants
400  *
401  * This namespace collects various \c QLatin1String constants in xml_stream_util.h.
402  * The namespace \link literal \endlink cannot be used directly in the
403  * xml_stream_util.h header because it would easily lead to name conflicts
404  * in including files. However, XmlStreamLiteral can be aliased to \c literal
405  * locally in method definitions:
406  *
407  * \code
408  * void someFuntion()
409  * {
410  *     namespace literal = XmlStreamLiteral;
411  *     writeAttribute(literal::left, 37.0);
412  * }
413  * \endcode
414  *
415  * @sa literal
416  */
417 namespace XmlStreamLiteral
418 {
419 	static const QLatin1String string_true("true");
420 
421 	static const QLatin1String left("left");
422 	static const QLatin1String top("top");
423 	static const QLatin1String width("width");
424 	static const QLatin1String height("height");
425 
426 	static const QLatin1String count("count");
427 
428 	static const QLatin1String object("object");
429 	static const QLatin1String tags("tags");
430 	static const QLatin1String tag("tag"); ///< @deprecated
431 	static const QLatin1String key("key"); ///< @deprecated
432 	static const QLatin1String t("t");
433 	static const QLatin1String k("k");
434 
435 	static const QLatin1String coord("coord");
436 }
437 
438 
439 
440 //### XmlElementWriter inline implemenentation ###
441 
442 inline
XmlElementWriter(QXmlStreamWriter & xml,const QLatin1String & element_name)443 XmlElementWriter::XmlElementWriter(QXmlStreamWriter& xml, const QLatin1String& element_name)
444  : xml(xml)
445 {
446 	xml.writeStartElement(element_name);
447 }
448 
449 inline
~XmlElementWriter()450 XmlElementWriter::~XmlElementWriter()
451 {
452 	xml.writeEndElement();
453 }
454 
455 inline
writeAttribute(const QLatin1String & qualifiedName,const char * value)456 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const char* value)
457 {
458 	xml.writeAttribute(qualifiedName, QString::fromUtf8(value));
459 }
460 
461 inline
writeAttribute(const QLatin1String & qualifiedName,const QString & value)462 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const QString& value)
463 {
464 	xml.writeAttribute(qualifiedName, value);
465 }
466 
467 inline
writeAttribute(const QLatin1String & qualifiedName,const double value)468 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const double value)
469 {
470 	xml.writeAttribute(qualifiedName, QString::number(value));
471 }
472 
473 inline
writeAttribute(const QLatin1String & qualifiedName,const double value,int precision)474 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const double value, int precision)
475 {
476 	xml.writeAttribute(qualifiedName, numberToString(value, precision));
477 }
478 
479 inline
writeAttribute(const QLatin1String & qualifiedName,const float value)480 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const float value)
481 {
482 	writeAttribute(qualifiedName, double(value));
483 }
484 
485 inline
writeAttribute(const QLatin1String & qualifiedName,const float value,int precision)486 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const float value, int precision)
487 {
488 	writeAttribute(qualifiedName, double(value), precision);
489 }
490 
491 inline
writeAttribute(const QLatin1String & qualifiedName,const qint64 value)492 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const qint64 value)
493 {
494 	xml.writeAttribute(qualifiedName, QString::number(value));
495 }
496 
497 inline
writeAttribute(const QLatin1String & qualifiedName,const int value)498 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const int value)
499 {
500 	xml.writeAttribute(qualifiedName, QString::number(value));
501 }
502 
503 inline
writeAttribute(const QLatin1String & qualifiedName,const unsigned int value)504 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const unsigned int value)
505 {
506 	xml.writeAttribute(qualifiedName, QString::number(value));
507 }
508 
509 inline
writeAttribute(const QLatin1String & qualifiedName,const long unsigned int value)510 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const long unsigned int value)
511 {
512 	xml.writeAttribute(qualifiedName, QString::number(value));
513 }
514 
515 inline
writeAttribute(const QLatin1String & qualifiedName,const quint64 value)516 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, const quint64 value)
517 {
518 	xml.writeAttribute(qualifiedName, QString::number(value));
519 }
520 
521 inline
writeAttribute(const QLatin1String & qualifiedName,bool value)522 void XmlElementWriter::writeAttribute(const QLatin1String& qualifiedName, bool value)
523 {
524 	namespace literal = XmlStreamLiteral;
525 
526 	if (value)
527 		xml.writeAttribute(qualifiedName, literal::string_true);
528 }
529 
530 inline
write(const QRectF & area)531 void XmlElementWriter::write(const QRectF& area)
532 {
533 	namespace literal = XmlStreamLiteral;
534 
535 	writeAttribute( literal::left,   area.left());
536 	writeAttribute( literal::top,    area.top());
537 	writeAttribute( literal::width,  area.width());
538 	writeAttribute( literal::height, area.height());
539 }
540 
541 inline
write(const QRectF & area,int precision)542 void XmlElementWriter::write(const QRectF& area, int precision)
543 {
544 	namespace literal = XmlStreamLiteral;
545 
546 	writeAttribute( literal::left,   area.left(),   precision );
547 	writeAttribute( literal::top,    area.top(),    precision );
548 	writeAttribute( literal::width,  area.width(),  precision );
549 	writeAttribute( literal::height, area.height(), precision );
550 }
551 
552 inline
write(const QSizeF & size)553 void XmlElementWriter::write(const QSizeF& size)
554 {
555 	namespace literal = XmlStreamLiteral;
556 
557 	writeAttribute( literal::width,  size.width());
558 	writeAttribute( literal::height, size.height());
559 }
560 
561 inline
write(const QSizeF & size,int precision)562 void XmlElementWriter::write(const QSizeF& size, int precision)
563 {
564 	namespace literal = XmlStreamLiteral;
565 
566 	writeAttribute( literal::width,  size.width(),  precision );
567 	writeAttribute( literal::height, size.height(), precision );
568 }
569 
570 inline
write(const QHash<QString,QString> & tags)571 void XmlElementWriter::write(const QHash<QString, QString> &tags)
572 {
573 	namespace literal = XmlStreamLiteral;
574 	typedef QHash<QString, QString> Tags;
575 
576 	for (Tags::const_iterator tag = tags.constBegin(), end = tags.constEnd(); tag != end; ++tag)
577 	{
578 		XmlElementWriter tag_element(xml, literal::t);
579 		tag_element.writeAttribute(literal::k, tag.key());
580 		xml.writeCharacters(tag.value());
581 	}
582 }
583 
584 //### XmlElementReader inline implemenentation ###
585 
586 inline
XmlElementReader(QXmlStreamReader & xml)587 XmlElementReader::XmlElementReader(QXmlStreamReader& xml)
588  : xml(xml),
589    attributes(xml.attributes())
590 {
591 }
592 
593 inline
~XmlElementReader()594 XmlElementReader::~XmlElementReader()
595 {
596 	if (!xml.isEndElement())
597 		xml.skipCurrentElement();
598 }
599 
600 inline
hasAttribute(const QLatin1String & qualifiedName)601 bool XmlElementReader::hasAttribute(const QLatin1String& qualifiedName) const
602 {
603 	return attributes.hasAttribute(qualifiedName);
604 }
605 
606 inline
hasAttribute(const QString & qualifiedName)607 bool XmlElementReader::hasAttribute(const QString& qualifiedName) const
608 {
609 	return attributes.hasAttribute(qualifiedName);
610 }
611 
612 template< >
613 inline
attribute(const QLatin1String & qualifiedName)614 QString XmlElementReader::attribute(const QLatin1String& qualifiedName) const
615 {
616 	return attributes.value(qualifiedName).toString();
617 }
618 
619 template< >
620 inline
attribute(const QLatin1String & qualifiedName)621 qint64 XmlElementReader::attribute(const QLatin1String& qualifiedName) const
622 {
623 	qint64 value = 0;
624 	const QStringRef ref = attributes.value(qualifiedName);
625 	if (ref.size())
626 		value = QString::fromRawData(ref.data(), ref.size()).toLongLong();
627 	return value;
628 }
629 
630 template< >
631 inline
attribute(const QLatin1String & qualifiedName)632 int XmlElementReader::attribute(const QLatin1String& qualifiedName) const
633 {
634 	int value = 0;
635 	const QStringRef ref = attributes.value(qualifiedName);
636 	if (ref.size())
637 		value = QString::fromRawData(ref.data(), ref.size()).toInt();
638 	return value;
639 }
640 
641 template< >
642 inline
attribute(const QLatin1String & qualifiedName)643 unsigned int XmlElementReader::attribute(const QLatin1String& qualifiedName) const
644 {
645 	unsigned int value = 0;
646 	const QStringRef ref = attributes.value(qualifiedName);
647 	if (ref.size())
648 		value = QString::fromRawData(ref.data(), ref.size()).toUInt();
649 	return value;
650 }
651 
652 template< >
653 inline
attribute(const QLatin1String & qualifiedName)654 long unsigned int XmlElementReader::attribute(const QLatin1String& qualifiedName) const
655 {
656 	unsigned int value = 0;
657 	const QStringRef ref = attributes.value(qualifiedName);
658 	if (ref.size())
659 		value = QString::fromRawData(ref.data(), ref.size()).toUInt();
660 	return value;
661 }
662 
663 template< >
664 inline
attribute(const QLatin1String & qualifiedName)665 double XmlElementReader::attribute(const QLatin1String& qualifiedName) const
666 {
667 	double value = 0;
668 	const QStringRef ref = attributes.value(qualifiedName);
669 	if (ref.size())
670 		value = QString::fromRawData(ref.data(), ref.size()).toDouble();
671 	return value;
672 }
673 
674 template< >
675 inline
attribute(const QLatin1String & qualifiedName)676 float XmlElementReader::attribute(const QLatin1String& qualifiedName) const
677 {
678 	float value = 0;
679 	const QStringRef ref = attributes.value(qualifiedName);
680 	if (ref.size())
681 		value = QString::fromRawData(ref.data(), ref.size()).toFloat();
682 	return value;
683 }
684 
685 template< >
686 inline
attribute(const QLatin1String & qualifiedName)687 bool XmlElementReader::attribute(const QLatin1String& qualifiedName) const
688 {
689 	namespace literal = XmlStreamLiteral;
690 
691 	bool value = (attributes.value(qualifiedName) == literal::string_true);
692 	return value;
693 }
694 
695 template<  >
696 inline
attribute(const QLatin1String & qualifiedName)697 QStringRef XmlElementReader::attribute(const QLatin1String& qualifiedName) const
698 {
699 	return attributes.value(qualifiedName);
700 }
701 
702 template< typename T >
703 inline
attribute(const QLatin1String & qualifiedName)704 T XmlElementReader::attribute(const QLatin1String& qualifiedName) const
705 {
706 	T value = static_cast<T>(0);
707 	const QStringRef ref = attributes.value(qualifiedName);
708 	if (ref.size())
709 		value = static_cast<T>(QString::fromRawData(ref.data(), ref.size()).toInt());
710 	return value;
711 }
712 
713 inline
read(QRectF & area)714 void XmlElementReader::read(QRectF& area)
715 {
716 	namespace literal = XmlStreamLiteral;
717 
718 	QStringRef ref = attributes.value(literal::left);
719 	area.setLeft(QString::fromRawData(ref.data(), ref.size()).toDouble());
720 	ref = attributes.value(literal::top);
721 	area.setTop(QString::fromRawData(ref.data(), ref.size()).toDouble());
722 	ref = attributes.value(literal::width);
723 	area.setWidth(QString::fromRawData(ref.data(), ref.size()).toDouble());
724 	ref = attributes.value(literal::height);
725 	area.setHeight(QString::fromRawData(ref.data(), ref.size()).toDouble());
726 }
727 
728 inline
read(QSizeF & size)729 void XmlElementReader::read(QSizeF& size)
730 {
731 	namespace literal = XmlStreamLiteral;
732 
733 	QStringRef ref = attributes.value(literal::width);
734 	size.setWidth(QString::fromRawData(ref.data(), ref.size()).toDouble());
735 	ref = attributes.value(literal::height);
736 	size.setHeight(QString::fromRawData(ref.data(), ref.size()).toDouble());
737 }
738 
739 inline
read(QHash<QString,QString> & tags)740 void XmlElementReader::read(QHash<QString, QString> &tags)
741 {
742 	namespace literal = XmlStreamLiteral;
743 
744 	tags.clear();
745 	while (xml.readNextStartElement())
746 	{
747 		if (xml.name() == literal::t)
748 		{
749 			const QString key(xml.attributes().value(literal::k).toString());
750 			tags.insert(key, xml.readElementText());
751 		}
752 		else if (xml.name() == literal::tag)
753 		{
754 			// Full keywords were used in pre-0.6.0 master branch
755 			// TODO Remove after Mapper 0.6.x releases
756 			const QString key(xml.attributes().value(literal::key).toString());
757 			tags.insert(key, xml.readElementText());
758 		}
759 		else if (xml.name() == literal::tags)
760 		{
761 			// Fix for broken Object::save in pre-0.6.0 master branch
762 			// TODO Remove after Mapper 0.6.x releases
763 			const QString key(xml.attributes().value(literal::key).toString());
764 			tags.insert(key, xml.readElementText());
765 		}
766 		else
767 			xml.skipCurrentElement();
768 	}
769 }
770 
771 
772 }  // namespace OpenOrienteering
773 
774 #endif
775