File indexing completed on 2025-01-05 03:59:10
0001 /* 0002 SPDX-FileCopyrightText: 2008 Nikolas Zimmermann <zimmermann@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 0008 // Own 0009 #include "GeoParser.h" 0010 0011 // Marble 0012 #include "digikam_debug.h" 0013 0014 // Geodata 0015 #include "GeoDocument.h" 0016 #include "GeoTagHandler.h" 0017 0018 #include <klocalizedstring.h> 0019 0020 namespace Marble 0021 { 0022 0023 // Set to a value greater than 0, to dump parent node chain while parsing 0024 #define DUMP_PARENT_STACK 0 0025 0026 GeoParser::GeoParser( GeoDataGenericSourceType source ) 0027 : QXmlStreamReader(), 0028 m_document( nullptr ), 0029 m_source( source ) 0030 { 0031 } 0032 0033 GeoParser::~GeoParser() 0034 { 0035 delete m_document; 0036 } 0037 0038 #if DUMP_PARENT_STACK > 0 0039 static void dumpParentStack( const QString& name, int size, bool close ) 0040 { 0041 static int depth = 0; 0042 0043 if ( !close ) 0044 depth++; 0045 0046 QString result; 0047 for ( int i = 0; i < depth; ++i ) 0048 result += QLatin1Char(' '); 0049 0050 if ( close ) { 0051 depth--; 0052 result += QLatin1String("</"); 0053 } else 0054 result += QLatin1Char('<'); 0055 0056 result += name + QLatin1String("> stack size ") + QString::number(size); 0057 fprintf( stderr, "%s\n", qPrintable( result )); 0058 } 0059 #endif 0060 0061 bool GeoParser::read( QIODevice* device ) 0062 { 0063 // Assert previous document got released. 0064 Q_ASSERT( !m_document ); 0065 m_document = createDocument(); 0066 Q_ASSERT( m_document ); 0067 0068 // Set data source 0069 setDevice( device ); 0070 0071 // Start parsing 0072 while ( !atEnd() ) { 0073 readNext(); 0074 0075 if ( isStartElement() ) { 0076 if ( isValidRootElement() ) { 0077 #if DUMP_PARENT_STACK > 0 0078 dumpParentStack( name().toString(), m_nodeStack.size(), false ); 0079 #endif 0080 0081 parseDocument(); 0082 0083 if ( !m_nodeStack.isEmpty() ) 0084 raiseError( 0085 // Keep trailing space in both strings, to match translated string 0086 // TODO: check if that space is kept through the tool pipeline 0087 //~ singular Parsing failed line %1. Still %n unclosed tag after document end. 0088 //~ plural Parsing failed line %1. Still %n unclosed tags after document end. 0089 i18n("Parsing failed line %1. Still %2 unclosed tag(s) after document end. ", 0090 lineNumber(), m_nodeStack.size() ) + errorString()); 0091 } else 0092 return false; 0093 } 0094 } 0095 0096 if ( error() ) { 0097 if ( lineNumber() == 1) { 0098 raiseError(QString()); 0099 } 0100 // Defer the deletion to the dtor 0101 /** @todo: Remove this workaround around Marble 1.4 */ 0102 // delete releaseDocument(); 0103 } 0104 return !error(); 0105 } 0106 0107 bool GeoParser::isValidElement( const QString& tagName ) const 0108 { 0109 return name() == tagName; 0110 } 0111 0112 GeoStackItem GeoParser::parentElement( unsigned int depth ) const 0113 { 0114 QStack<GeoStackItem>::const_iterator it = m_nodeStack.constEnd() - 1; 0115 0116 if ( it - depth < m_nodeStack.constBegin() ) 0117 return GeoStackItem(); 0118 0119 return *(it - depth); 0120 } 0121 0122 void GeoParser::parseDocument() 0123 { 0124 if( !isStartElement() ) { 0125 raiseError( i18n("Error parsing file at line: %1 and column %2 . ") 0126 .arg( lineNumber() ).arg( columnNumber() ) 0127 + i18n("This is an Invalid File") ); 0128 return; 0129 } 0130 0131 bool processChildren = true; 0132 QualifiedName qName( name().toString(), namespaceUri().toString() ); 0133 0134 if( tokenType() == QXmlStreamReader::Invalid ) 0135 raiseWarning( QString::fromUtf8( "%1: %2" ).arg( error() ).arg( errorString() ) ); 0136 0137 GeoStackItem stackItem( qName, nullptr ); 0138 0139 if ( const GeoTagHandler* handler = GeoTagHandler::recognizes( qName )) { 0140 stackItem.assignNode( handler->parse( *this )); 0141 processChildren = !isEndElement(); 0142 } 0143 // Only add GeoStackItem to the parent chain, if the tag handler 0144 // for the current element possibly contains non-textual children. 0145 // Consider following DGML snippet "<name>Test</name>" - the 0146 // DGMLNameTagHandler assumes that <name> only contains textual 0147 // children, and reads the joined value of all children using 0148 // readElementText(). This implicates that tags like <name> 0149 // don't contain any children that would need to be processed using 0150 // this parseDocument() function. 0151 if ( processChildren ) { 0152 m_nodeStack.push( stackItem ); 0153 #if DUMP_PARENT_STACK > 0 0154 dumpParentStack( name().toString(), m_nodeStack.size(), false ); 0155 #endif 0156 while ( !atEnd() ) { 0157 readNext(); 0158 if ( isEndElement() ) { 0159 m_nodeStack.pop(); 0160 #if DUMP_PARENT_STACK > 0 0161 dumpParentStack( name().toString(), m_nodeStack.size(), true ); 0162 #endif 0163 break; 0164 } 0165 0166 if ( isStartElement() ) { 0167 parseDocument(); 0168 } 0169 } 0170 } 0171 #if DUMP_PARENT_STACK > 0 0172 else { 0173 // This is only used for debugging purposes. 0174 m_nodeStack.push( stackItem ); 0175 dumpParentStack(name().toString() + QLatin1String("-discarded"), m_nodeStack.size(), false); 0176 0177 m_nodeStack.pop(); 0178 dumpParentStack(name().toString() + QLatin1String("-discarded"), m_nodeStack.size(), true); 0179 } 0180 #endif 0181 } 0182 0183 void GeoParser::raiseWarning( const QString& warning ) 0184 { 0185 // TODO: Maybe introduce a strict parsing mode where we feed the warning to 0186 // raiseError() (which stops parsing). 0187 qCDebug(DIGIKAM_MARBLE_LOG) << "[GeoParser::raiseWarning] -> " << warning; 0188 } 0189 0190 QString GeoParser::attribute( const char* attributeName ) const 0191 { 0192 return attributes().value(QLatin1String(attributeName)).toString(); 0193 } 0194 0195 GeoDocument* GeoParser::releaseDocument() 0196 { 0197 GeoDocument* document = m_document; 0198 m_document = nullptr; 0199 return document; 0200 } 0201 0202 }