Marble

GeoParser.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Nikolas Zimmermann <zimmermann@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7// Own
8#include "GeoParser.h"
9
10// Marble
11#include "MarbleDebug.h"
12
13// Geodata
14#include "GeoDocument.h"
15#include "GeoTagHandler.h"
16
17namespace Marble
18{
19
20// Set to a value greater than 0, to dump parent node chain while parsing
21#define DUMP_PARENT_STACK 0
22
23GeoParser::GeoParser(GeoDataGenericSourceType source)
25 , m_document(nullptr)
26 , m_source(source)
27{
28}
29
30GeoParser::~GeoParser()
31{
32 delete m_document;
33}
34
35#if DUMP_PARENT_STACK > 0
36static void dumpParentStack(const QString &name, int size, bool close)
37{
38 static int depth = 0;
39
40 if (!close)
41 depth++;
42
43 QString result;
44 for (int i = 0; i < depth; ++i)
45 result += QLatin1Char(' ');
46
47 if (close) {
48 depth--;
49 result += QLatin1StringView("</");
50 } else
51 result += QLatin1Char('<');
52
53 result += name + QLatin1StringView("> stack size ") + QString::number(size);
54 fprintf(stderr, "%s\n", qPrintable(result));
55}
56#endif
57
58bool GeoParser::read(QIODevice *device)
59{
60 // Assert previous document got released.
61 Q_ASSERT(!m_document);
62 m_document = createDocument();
63 Q_ASSERT(m_document);
64
65 // Set data source
66 setDevice(device);
67
68 // Start parsing
69 while (!atEnd()) {
70 readNext();
71
72 if (isStartElement()) {
73 if (isValidRootElement()) {
74#if DUMP_PARENT_STACK > 0
75 dumpParentStack(name().toString(), m_nodeStack.size(), false);
76#endif
77
78 parseDocument();
79
80 if (!m_nodeStack.isEmpty())
81 raiseError(
82 // Keep trailing space in both strings, to match translated string
83 // TODO: check if that space is kept through the tool pipeline
84 //~ singular Parsing failed line %1. Still %n unclosed tag after document end.
85 //~ plural Parsing failed line %1. Still %n unclosed tags after document end.
86 QObject::tr("Parsing failed line %1. Still %n unclosed tag(s) after document end. ", "", m_nodeStack.size()).arg(lineNumber())
87 + errorString());
88 } else
89 return false;
90 }
91 }
92
93 if (error()) {
94 if (lineNumber() == 1) {
95 raiseError(QString());
96 }
97 // Defer the deletion to the dtor
98 // This allows the BookmarkManager to recover the broken .kml files it produced in Marble 1.0 and 1.1
99 /** @todo: Remove this workaround around Marble 1.4 */
100 // delete releaseDocument();
101 }
102 return !error();
103}
104
105bool GeoParser::isValidElement(const QString &tagName) const
106{
107 return name() == tagName;
108}
109
110GeoStackItem GeoParser::parentElement(unsigned int depth) const
111{
112 QStack<GeoStackItem>::const_iterator it = m_nodeStack.constEnd() - 1;
113
114 if (it - depth < m_nodeStack.constBegin())
115 return {};
116
117 return *(it - depth);
118}
119
120void GeoParser::parseDocument()
121{
122 if (!isStartElement()) {
123 raiseError(QObject::tr("Error parsing file at line: %1 and column %2 . ").arg(lineNumber()).arg(columnNumber())
124 + QObject::tr("This is an Invalid File"));
125 return;
126 }
127
128 bool processChildren = true;
129 QualifiedName qName(name().toString(), namespaceUri().toString());
130
131 if (tokenType() == QXmlStreamReader::Invalid)
132 raiseWarning(QStringLiteral("%1: %2").arg(error()).arg(errorString()));
133
134 GeoStackItem stackItem(qName, nullptr);
135
136 if (const GeoTagHandler *handler = GeoTagHandler::recognizes(qName)) {
137 stackItem.assignNode(handler->parse(*this));
138 processChildren = !isEndElement();
139 }
140 // Only add GeoStackItem to the parent chain, if the tag handler
141 // for the current element possibly contains non-textual children.
142 // Consider following DGML snippet "<name>Test</name>" - the
143 // DGMLNameTagHandler assumes that <name> only contains textual
144 // children, and reads the joined value of all children using
145 // readElementText(). This implicates that tags like <name>
146 // don't contain any children that would need to be processed using
147 // this parseDocument() function.
148 if (processChildren) {
149 m_nodeStack.push(stackItem);
150#if DUMP_PARENT_STACK > 0
151 dumpParentStack(name().toString(), m_nodeStack.size(), false);
152#endif
153 while (!atEnd()) {
154 readNext();
155 if (isEndElement()) {
156 m_nodeStack.pop();
157#if DUMP_PARENT_STACK > 0
158 dumpParentStack(name().toString(), m_nodeStack.size(), true);
159#endif
160 break;
161 }
162
163 if (isStartElement()) {
164 parseDocument();
165 }
166 }
167 }
168#if DUMP_PARENT_STACK > 0
169 else {
170 // This is only used for debugging purposes.
171 m_nodeStack.push(stackItem);
172 dumpParentStack(name().toString() + QLatin1StringView("-discarded"), m_nodeStack.size(), false);
173
174 m_nodeStack.pop();
175 dumpParentStack(name().toString() + QLatin1StringView("-discarded"), m_nodeStack.size(), true);
176 }
177#endif
178}
179
180void GeoParser::raiseWarning(const QString &warning)
181{
182 // TODO: Maybe introduce a strict parsing mode where we feed the warning to
183 // raiseError() (which stops parsing).
184 mDebug() << "[GeoParser::raiseWarning] -> " << warning;
185}
186
187QString GeoParser::attribute(const char *attributeName) const
188{
189 return attributes().value(QLatin1StringView(attributeName)).toString();
190}
191
192GeoDocument *GeoParser::releaseDocument()
193{
194 GeoDocument *document = m_document;
195 m_document = nullptr;
196 return document;
197}
198
199}
char * toString(const EngineQuery &query)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString name(StandardAction id)
Binds a QML item to a specific geodetic location in screen coordinates.
qsizetype size() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.