Line data Source code
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 : /****************************************************************************/
14 : /// @file SUMOSAXReader.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @date Nov 2012
19 : ///
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 :
31 : #include <utils/common/FileHelpers.h>
32 : #include <utils/common/MsgHandler.h>
33 : #include <utils/common/ToString.h>
34 : #include <utils/common/StringUtils.h>
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 :
43 : using XERCES_CPP_NAMESPACE::SAX2XMLReader;
44 : using XERCES_CPP_NAMESPACE::XMLUni;
45 :
46 :
47 : // ===========================================================================
48 : // method definitions
49 : // ===========================================================================
50 :
51 88994 : SUMOSAXReader::SUMOSAXReader(GenericSAXHandler& handler, const std::string& validationScheme, XERCES_CPP_NAMESPACE::XMLGrammarPool* grammarPool) :
52 88994 : myHandler(nullptr),
53 88994 : myValidationScheme(validationScheme),
54 88994 : myGrammarPool(grammarPool),
55 88994 : myXMLReader(nullptr),
56 : myIStream(nullptr),
57 : myInputStream(nullptr),
58 88994 : mySchemaResolver(true, false),
59 88994 : myLocalResolver(false, false),
60 88994 : myNoOpResolver(false, true),
61 : myNextSection(-1, nullptr) {
62 88994 : setHandler(handler);
63 88994 : }
64 :
65 :
66 88485 : SUMOSAXReader::~SUMOSAXReader() {
67 88485 : delete myXMLReader;
68 88485 : delete myNextSection.second;
69 176970 : }
70 :
71 :
72 : void
73 198190 : SUMOSAXReader::setHandler(GenericSAXHandler& handler) {
74 198190 : myHandler = &handler;
75 198190 : if (myXMLReader != nullptr) {
76 109196 : myXMLReader->setContentHandler(&handler);
77 109196 : myXMLReader->setErrorHandler(&handler);
78 : }
79 198190 : }
80 :
81 :
82 : void
83 186975 : SUMOSAXReader::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 186975 : if (myXMLReader != nullptr && validationScheme != myValidationScheme) {
88 102097 : 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 102097 : if (validationScheme == "never") {
93 72967 : myXMLReader->setEntityResolver(&myNoOpResolver);
94 72967 : myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgWFXMLScanner);
95 : } else {
96 29130 : myXMLReader->setEntityResolver(validationScheme == "local" ? &myLocalResolver : &mySchemaResolver);
97 29130 : myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgIGXMLScanner);
98 29130 : myXMLReader->setFeature(XMLUni::fgXercesSchema, true);
99 29130 : myXMLReader->setFeature(XMLUni::fgSAX2CoreValidation, true);
100 29130 : myXMLReader->setFeature(XMLUni::fgXercesDynamic, validationScheme == "local" || validationScheme == "auto");
101 29130 : myXMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, myValidationScheme == "always");
102 : }
103 : }
104 186975 : myValidationScheme = validationScheme;
105 186975 : }
106 :
107 :
108 : void
109 147637 : SUMOSAXReader::parse(std::string systemID) {
110 295274 : if (!FileHelpers::isReadable(systemID)) {
111 0 : throw IOError(TLF("Cannot read file '%'!", systemID));
112 : }
113 295274 : if (FileHelpers::isDirectory(systemID)) {
114 0 : throw IOError(TLF("File '%' is a directory!", systemID));
115 : }
116 147637 : ensureSAXReader();
117 : #ifdef HAVE_ZLIB
118 147637 : zstr::ifstream istream(XMLSubSys::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary);
119 147637 : myXMLReader->parse(IStreamInputSource(istream)); // NOSONAR
120 : #else
121 : myXMLReader->parse(XMLSubSys::transcodeToLocal(systemID).c_str()); // NOSONAR
122 : #endif
123 147637 : }
124 :
125 :
126 : void
127 8 : SUMOSAXReader::parseString(std::string content) {
128 8 : ensureSAXReader();
129 8 : XERCES_CPP_NAMESPACE::MemBufInputSource memBufIS((const XMLByte*)content.c_str(), content.size(), "registrySettings");
130 8 : myXMLReader->parse(memBufIS); // NOSONAR
131 8 : }
132 :
133 :
134 : bool
135 39342 : SUMOSAXReader::parseFirst(std::string systemID) {
136 78684 : if (!FileHelpers::isReadable(systemID)) {
137 36 : throw IOError(TLF("Cannot read file '%'!", systemID));
138 : }
139 78660 : if (FileHelpers::isDirectory(systemID)) {
140 0 : throw IOError(TLF("File '%' is a directory!", systemID));
141 : }
142 39330 : ensureSAXReader();
143 39330 : myToken = XERCES_CPP_NAMESPACE::XMLPScanToken();
144 : #ifdef HAVE_ZLIB
145 39330 : myIStream = std::unique_ptr<zstr::ifstream>(new zstr::ifstream(XMLSubSys::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary));
146 39330 : myInputStream = std::unique_ptr<IStreamInputSource>(new IStreamInputSource(*myIStream));
147 39330 : return myXMLReader->parseFirst(*myInputStream, myToken); // NOSONAR
148 : #else
149 : return myXMLReader->parseFirst(XMLSubSys::transcodeToLocal(systemID).c_str(), myToken); // NOSONAR
150 : #endif
151 : }
152 :
153 :
154 : bool
155 3386905 : SUMOSAXReader::parseNext() {
156 3386905 : if (myXMLReader == nullptr) {
157 0 : throw ProcessError(TL("The XML-parser was not initialized."));
158 : }
159 3386905 : return myXMLReader->parseNext(myToken);
160 : }
161 :
162 :
163 : bool
164 1162 : SUMOSAXReader::parseSection(SumoXMLTag element) {
165 1162 : if (myXMLReader == nullptr) {
166 0 : throw ProcessError(TL("The XML-parser was not initialized."));
167 : }
168 : bool started = false;
169 1162 : if (myNextSection.first != -1) {
170 642 : started = myNextSection.first == element;
171 642 : 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 2 : WRITE_WARNINGF("Expected different XML section '%', some content may be missing.", toString(element));
175 : }
176 642 : myHandler->myStartElement(myNextSection.first, *myNextSection.second);
177 642 : delete myNextSection.second;
178 642 : myNextSection.first = -1;
179 642 : myNextSection.second = nullptr;
180 : }
181 1162 : myHandler->setSection(element, started);
182 3647542 : while (!myHandler->sectionFinished()) {
183 3646899 : if (!myXMLReader->parseNext(myToken)) {
184 : return false;
185 : }
186 : }
187 : myNextSection = myHandler->retrieveNextSectionStart();
188 643 : return true;
189 : }
190 :
191 :
192 : void
193 186975 : SUMOSAXReader::ensureSAXReader() {
194 186975 : if (myXMLReader == nullptr) {
195 88963 : myXMLReader = XERCES_CPP_NAMESPACE::XMLReaderFactory::createXMLReader(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager, myGrammarPool);
196 88963 : if (myXMLReader == nullptr) {
197 0 : throw ProcessError(TL("The XML-parser could not be build."));
198 : }
199 88963 : setValidation();
200 88963 : myXMLReader->setContentHandler(myHandler);
201 88963 : myXMLReader->setErrorHandler(myHandler);
202 : }
203 186975 : }
204 :
205 :
206 266982 : SUMOSAXReader::LocalSchemaResolver::LocalSchemaResolver(const bool haveFallback, const bool noOp) :
207 266982 : myHaveFallback(haveFallback),
208 266982 : myNoOp(noOp) {
209 266982 : }
210 :
211 :
212 : XERCES_CPP_NAMESPACE::InputSource*
213 838357 : SUMOSAXReader::LocalSchemaResolver::resolveEntity(const XMLCh* const /* publicId */, const XMLCh* const systemId) {
214 838357 : if (myNoOp) {
215 0 : return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, "");
216 : }
217 838357 : const std::string url = XMLSubSys::transcode(systemId);
218 : const std::string::size_type pos = url.find("/xsd/");
219 838357 : if (pos != std::string::npos) {
220 57002 : 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 57002 : if (sumoPath != nullptr) {
223 170982 : const std::string file = sumoPath + std::string("/data") + url.substr(pos);
224 113988 : if (FileHelpers::isReadable(file)) {
225 56965 : XMLCh* t = XERCES_CPP_NAMESPACE::XMLString::transcode(file.c_str());
226 56965 : XERCES_CPP_NAMESPACE::InputSource* const result = new XERCES_CPP_NAMESPACE::LocalFileInputSource(t);
227 56965 : XERCES_CPP_NAMESPACE::XMLString::release(&t);
228 : return result;
229 : } else {
230 89 : WRITE_WARNING("Cannot read local schema '" + file + (myHaveFallback ? "', will try website lookup." : "', XML validation will fail."));
231 : }
232 : }
233 : }
234 3900966 : if (myHaveFallback || (!StringUtils::startsWith(url, "http:") && !StringUtils::startsWith(url, "https:") && !StringUtils::startsWith(url, "ftp:"))) {
235 : return nullptr;
236 : }
237 2 : return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, "");
238 : }
239 :
240 :
241 : /****************************************************************************/
|