Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
SUMOSAXReader.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2012-2026 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
20// SAX-reader encapsulation
21/****************************************************************************/
22#include <config.h>
23
24#include <string>
25#include <memory>
26#include <iostream>
27#include <xercesc/sax2/XMLReaderFactory.hpp>
28#include <xercesc/framework/LocalFileInputSource.hpp>
29#include <xercesc/framework/MemBufInputSource.hpp>
30
35#include <utils/xml/XMLSubSys.h>
36#include "GenericSAXHandler.h"
37#ifdef HAVE_ZLIB
38#include <foreign/zstr/zstr.hpp>
39#endif
40#include "IStreamInputSource.h"
41#include "SUMOSAXReader.h"
42
43using XERCES_CPP_NAMESPACE::SAX2XMLReader;
44using XERCES_CPP_NAMESPACE::XMLUni;
45
46
47// ===========================================================================
48// method definitions
49// ===========================================================================
50
51SUMOSAXReader::SUMOSAXReader(GenericSAXHandler& handler, const std::string& validationScheme, XERCES_CPP_NAMESPACE::XMLGrammarPool* grammarPool) :
52 myHandler(nullptr),
53 myValidationScheme(validationScheme),
54 myGrammarPool(grammarPool),
55 myXMLReader(nullptr),
56 myIStream(nullptr),
57 myInputStream(nullptr),
58 mySchemaResolver(true, false),
59 myLocalResolver(false, false),
60 myNoOpResolver(false, true),
61 myNextSection(-1, nullptr) {
62 setHandler(handler);
63}
64
65
67 delete myXMLReader;
68 delete myNextSection.second;
69}
70
71
72void
74 myHandler = &handler;
75 if (myXMLReader != nullptr) {
76 myXMLReader->setContentHandler(&handler);
77 myXMLReader->setErrorHandler(&handler);
78 }
79}
80
81
82void
83SUMOSAXReader::setValidation(std::string validationScheme) {
84 // The settings ensure that by default (validationScheme "local" or "never") no network access occurs
85 // this is achieved by either resolving no entities at all (myNoOpResolver) or resolving only
86 // to local files (myLocalResolver). Thus we can safely disable the Sonar warnings in the parse methods below.
87 if (myXMLReader != nullptr && validationScheme != myValidationScheme) {
88 if (validationScheme == "") {
89 validationScheme = myValidationScheme;
90 }
91 // see here https://svn.apache.org/repos/asf/xerces/c/trunk/samples/src/SAX2Count/SAX2Count.cpp for the way to set features
92 if (validationScheme == "never") {
93 myXMLReader->setEntityResolver(&myNoOpResolver);
94 myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgWFXMLScanner);
95 } else {
96 myXMLReader->setEntityResolver(validationScheme == "local" ? &myLocalResolver : &mySchemaResolver);
97 myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgIGXMLScanner);
98 myXMLReader->setFeature(XMLUni::fgXercesSchema, true);
99 myXMLReader->setFeature(XMLUni::fgSAX2CoreValidation, true);
100 myXMLReader->setFeature(XMLUni::fgXercesDynamic, validationScheme == "local" || validationScheme == "auto");
101 myXMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, myValidationScheme == "always");
102 }
103 }
104 myValidationScheme = validationScheme;
105}
106
107
108void
109SUMOSAXReader::parse(std::string systemID) {
110 if (!FileHelpers::isReadable(systemID)) {
111 throw IOError(TLF("Cannot read file '%'!", systemID));
112 }
113 if (FileHelpers::isDirectory(systemID)) {
114 throw IOError(TLF("File '%' is a directory!", systemID));
115 }
117#ifdef HAVE_ZLIB
118 zstr::ifstream istream(XMLSubSys::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary);
119 myXMLReader->parse(IStreamInputSource(istream)); // NOSONAR
120#else
121 myXMLReader->parse(XMLSubSys::transcodeToLocal(systemID).c_str()); // NOSONAR
122#endif
123}
124
125
126void
127SUMOSAXReader::parseString(std::string content) {
129 XERCES_CPP_NAMESPACE::MemBufInputSource memBufIS((const XMLByte*)content.c_str(), content.size(), "registrySettings");
130 myXMLReader->parse(memBufIS); // NOSONAR
131}
132
133
134bool
135SUMOSAXReader::parseFirst(std::string systemID) {
136 if (!FileHelpers::isReadable(systemID)) {
137 throw IOError(TLF("Cannot read file '%'!", systemID));
138 }
139 if (FileHelpers::isDirectory(systemID)) {
140 throw IOError(TLF("File '%' is a directory!", systemID));
141 }
143 myToken = XERCES_CPP_NAMESPACE::XMLPScanToken();
144#ifdef HAVE_ZLIB
145 myIStream = std::unique_ptr<zstr::ifstream>(new zstr::ifstream(XMLSubSys::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary));
146 myInputStream = std::unique_ptr<IStreamInputSource>(new IStreamInputSource(*myIStream));
147 return myXMLReader->parseFirst(*myInputStream, myToken); // NOSONAR
148#else
149 return myXMLReader->parseFirst(XMLSubSys::transcodeToLocal(systemID).c_str(), myToken); // NOSONAR
150#endif
151}
152
153
154bool
156 if (myXMLReader == nullptr) {
157 throw ProcessError(TL("The XML-parser was not initialized."));
158 }
159 return myXMLReader->parseNext(myToken);
160}
161
162
163bool
165 if (myXMLReader == nullptr) {
166 throw ProcessError(TL("The XML-parser was not initialized."));
167 }
168 bool started = false;
169 if (myNextSection.first != -1) {
170 started = myNextSection.first == element;
171 if (!started) {
172 // This enforces that the next parsed section starts right after the last one.
173 // If we want to skip sections we need to change this.
174 WRITE_WARNINGF("Expected different XML section '%', some content may be missing.", toString(element));
175 }
177 delete myNextSection.second;
178 myNextSection.first = -1;
179 myNextSection.second = nullptr;
180 }
181 myHandler->setSection(element, started);
182 while (!myHandler->sectionFinished()) {
183 if (!myXMLReader->parseNext(myToken)) {
184 return false;
185 }
186 }
188 return true;
189}
190
191
192void
194 if (myXMLReader == nullptr) {
195 myXMLReader = XERCES_CPP_NAMESPACE::XMLReaderFactory::createXMLReader(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager, myGrammarPool);
196 if (myXMLReader == nullptr) {
197 throw ProcessError(TL("The XML-parser could not be build."));
198 }
200 myXMLReader->setContentHandler(myHandler);
201 myXMLReader->setErrorHandler(myHandler);
202 }
203}
204
205
206SUMOSAXReader::LocalSchemaResolver::LocalSchemaResolver(const bool haveFallback, const bool noOp) :
207 myHaveFallback(haveFallback),
208 myNoOp(noOp) {
209}
210
211
212XERCES_CPP_NAMESPACE::InputSource*
213SUMOSAXReader::LocalSchemaResolver::resolveEntity(const XMLCh* const /* publicId */, const XMLCh* const systemId) {
214 if (myNoOp) {
215 return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, "");
216 }
217 const std::string url = XMLSubSys::transcode(systemId);
218 const std::string::size_type pos = url.find("/xsd/");
219 if (pos != std::string::npos) {
220 const char* sumoPath = std::getenv("SUMO_HOME");
221 // no need for a warning if SUMO_HOME is not set, global preparsing should have done it.
222 if (sumoPath != nullptr) {
223 const std::string file = sumoPath + std::string("/data") + url.substr(pos);
224 if (FileHelpers::isReadable(file)) {
225 XMLCh* t = XERCES_CPP_NAMESPACE::XMLString::transcode(file.c_str());
226 XERCES_CPP_NAMESPACE::InputSource* const result = new XERCES_CPP_NAMESPACE::LocalFileInputSource(t);
227 XERCES_CPP_NAMESPACE::XMLString::release(&t);
228 return result;
229 } else {
230 WRITE_WARNING("Cannot read local schema '" + file + (myHaveFallback ? "', will try website lookup." : "', XML validation will fail."));
231 }
232 }
233 }
234 if (myHaveFallback || (!StringUtils::startsWith(url, "http:") && !StringUtils::startsWith(url, "https:") && !StringUtils::startsWith(url, "ftp:"))) {
235 return nullptr;
236 }
237 return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, "");
238}
239
240
241/****************************************************************************/
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define WRITE_WARNING(msg)
Definition MsgHandler.h:286
#define TL(string)
Definition MsgHandler.h:304
#define TLF(string,...)
Definition MsgHandler.h:306
SumoXMLTag
Numbers representing SUMO-XML - element names.
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:49
static bool isReadable(std::string path)
Checks whether the given file is readable.
static bool isDirectory(std::string path)
Checks whether the given file is a directory.
A handler which converts occurring elements and attributes into enums.
bool sectionFinished() const
void setSection(const int element, const bool seen)
std::pair< int, SUMOSAXAttributes * > retrieveNextSectionStart()
virtual void myStartElement(int element, const SUMOSAXAttributes &attrs)
Callback method for an opening tag to implement by derived classes.
Xerces InputSource reading from arbitrary std::istream.
LocalSchemaResolver(const bool haveFallback, const bool noOp)
constructor
XERCES_CPP_NAMESPACE::InputSource * resolveEntity(const XMLCh *const publicId, const XMLCh *const systemId)
resolve entity
std::unique_ptr< IStreamInputSource > myInputStream
input stream
std::string myValidationScheme
Information whether built reader/parser shall validate XML-documents against schemata.
SUMOSAXReader(GenericSAXHandler &handler, const std::string &validationScheme, XERCES_CPP_NAMESPACE::XMLGrammarPool *grammarPool)
Constructor.
std::pair< int, SUMOSAXAttributes * > myNextSection
next section
void setHandler(GenericSAXHandler &handler)
Sets the given handler as content and error handler for the reader.
void parseString(std::string content)
Parse XML from the given string.
LocalSchemaResolver myLocalResolver
local resolver
bool parseFirst(std::string systemID)
Start parsing the given file using parseFirst of myXMLReader.
std::unique_ptr< std::istream > myIStream
istream
LocalSchemaResolver mySchemaResolver
schema resolver
bool parseSection(SumoXMLTag element)
Continue a progressive parse started by parseFirst until the given element is encountered.
void setValidation(std::string validationScheme="")
Sets a new validation scheme and applies the validation settings to the XML reader.
~SUMOSAXReader()
Destructor.
GenericSAXHandler * myHandler
generic SAX Handler
void parse(std::string systemID)
Parse the given file completely by calling parse of myXMLReader.
XERCES_CPP_NAMESPACE::SAX2XMLReader * myXMLReader
XML reader.
LocalSchemaResolver myNoOpResolver
no operation resolver
XERCES_CPP_NAMESPACE::XMLGrammarPool * myGrammarPool
Schema cache to be used for grammars which are not declared.
XERCES_CPP_NAMESPACE::XMLPScanToken myToken
token
void ensureSAXReader()
Builds a reader, if needed.
bool parseNext()
Continue a progressive parse started by parseFirst.
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
static std::string transcode(const XMLCh *const data, int length=-1)
converts a 0-terminated XMLCh* array (usually UTF-16, stemming from Xerces) into std::string in UTF-8
static std::string transcodeToLocal(const std::string &utf8String)
convert a string from UTF-8 to the local codepage