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

KDE's Doxygen guidelines are available online.