Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2012-2024 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 "GenericSAXHandler.h"
36 : #ifdef HAVE_ZLIB
37 : #include <foreign/zstr/zstr.hpp>
38 : #endif
39 : #include "IStreamInputSource.h"
40 : #include "SUMOSAXReader.h"
41 :
42 : using XERCES_CPP_NAMESPACE::SAX2XMLReader;
43 : using XERCES_CPP_NAMESPACE::XMLUni;
44 :
45 :
46 : // ===========================================================================
47 : // method definitions
48 : // ===========================================================================
49 :
50 100695 : SUMOSAXReader::SUMOSAXReader(GenericSAXHandler& handler, const std::string& validationScheme, XERCES_CPP_NAMESPACE::XMLGrammarPool* grammarPool) :
51 100695 : myHandler(nullptr),
52 100695 : myValidationScheme(validationScheme),
53 100695 : myGrammarPool(grammarPool),
54 100695 : myXMLReader(nullptr),
55 : myIStream(nullptr),
56 : myInputStream(nullptr),
57 100695 : mySchemaResolver(true, false),
58 100695 : myLocalResolver(false, false),
59 100695 : myNoOpResolver(false, true),
60 : myNextSection(-1, nullptr) {
61 100695 : setHandler(handler);
62 100695 : }
63 :
64 :
65 97724 : SUMOSAXReader::~SUMOSAXReader() {
66 97724 : delete myXMLReader;
67 97724 : delete myNextSection.second;
68 195448 : }
69 :
70 :
71 : void
72 214846 : SUMOSAXReader::setHandler(GenericSAXHandler& handler) {
73 214846 : myHandler = &handler;
74 214846 : if (myXMLReader != nullptr) {
75 114151 : myXMLReader->setContentHandler(&handler);
76 114151 : myXMLReader->setErrorHandler(&handler);
77 : }
78 214846 : }
79 :
80 :
81 : void
82 205202 : SUMOSAXReader::setValidation(std::string validationScheme) {
83 : // The settings ensure that by default (validationScheme "local" or "never") no network access occurs
84 : // this is achieved by either resolving no entities at all (myNoOpResolver) or resolving only
85 : // to local files (myLocalResolver). Thus we can safely disable the Sonar warnings in the parse methods below.
86 205202 : if (myXMLReader != nullptr && validationScheme != myValidationScheme) {
87 117862 : if (validationScheme == "") {
88 : validationScheme = myValidationScheme;
89 : }
90 : // see here https://svn.apache.org/repos/asf/xerces/c/trunk/samples/src/SAX2Count/SAX2Count.cpp for the way to set features
91 117862 : if (validationScheme == "never") {
92 82239 : myXMLReader->setEntityResolver(&myNoOpResolver);
93 82239 : myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgWFXMLScanner);
94 : } else {
95 35623 : myXMLReader->setEntityResolver(validationScheme == "local" ? &myLocalResolver : &mySchemaResolver);
96 35623 : myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgIGXMLScanner);
97 35623 : myXMLReader->setFeature(XMLUni::fgXercesSchema, true);
98 35623 : myXMLReader->setFeature(XMLUni::fgSAX2CoreValidation, true);
99 35623 : myXMLReader->setFeature(XMLUni::fgXercesDynamic, validationScheme == "local" || validationScheme == "auto");
100 35623 : myXMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, myValidationScheme == "always");
101 : }
102 : }
103 205202 : myValidationScheme = validationScheme;
104 205202 : }
105 :
106 :
107 : void
108 159972 : SUMOSAXReader::parse(std::string systemID) {
109 319944 : if (!FileHelpers::isReadable(systemID)) {
110 0 : throw IOError(TLF("Cannot read file '%'!", systemID));
111 : }
112 319944 : if (FileHelpers::isDirectory(systemID)) {
113 0 : throw IOError(TLF("File '%' is a directory!", systemID));
114 : }
115 159972 : ensureSAXReader();
116 : #ifdef HAVE_ZLIB
117 159972 : zstr::ifstream istream(StringUtils::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary);
118 159972 : myXMLReader->parse(IStreamInputSource(istream)); // NOSONAR
119 : #else
120 : myXMLReader->parse(StringUtils::transcodeToLocal(systemID).c_str()); // NOSONAR
121 : #endif
122 159972 : }
123 :
124 :
125 : void
126 0 : SUMOSAXReader::parseString(std::string content) {
127 0 : ensureSAXReader();
128 0 : XERCES_CPP_NAMESPACE::MemBufInputSource memBufIS((const XMLByte*)content.c_str(), content.size(), "registrySettings");
129 0 : myXMLReader->parse(memBufIS); // NOSONAR
130 0 : }
131 :
132 :
133 : bool
134 45243 : SUMOSAXReader::parseFirst(std::string systemID) {
135 90486 : if (!FileHelpers::isReadable(systemID)) {
136 39 : throw IOError(TLF("Cannot read file '%'!", systemID));
137 : }
138 90460 : if (FileHelpers::isDirectory(systemID)) {
139 0 : throw IOError(TLF("File '%' is a directory!", systemID));
140 : }
141 45230 : ensureSAXReader();
142 45230 : myToken = XERCES_CPP_NAMESPACE::XMLPScanToken();
143 : #ifdef HAVE_ZLIB
144 45230 : myIStream = std::unique_ptr<zstr::ifstream>(new zstr::ifstream(StringUtils::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary));
145 45230 : myInputStream = std::unique_ptr<IStreamInputSource>(new IStreamInputSource(*myIStream));
146 45230 : return myXMLReader->parseFirst(*myInputStream, myToken); // NOSONAR
147 : #else
148 : return myXMLReader->parseFirst(StringUtils::transcodeToLocal(systemID).c_str(), myToken); // NOSONAR
149 : #endif
150 : }
151 :
152 :
153 : bool
154 3033964 : SUMOSAXReader::parseNext() {
155 3033964 : if (myXMLReader == nullptr) {
156 0 : throw ProcessError(TL("The XML-parser was not initialized."));
157 : }
158 3033964 : return myXMLReader->parseNext(myToken);
159 : }
160 :
161 :
162 : bool
163 461 : SUMOSAXReader::parseSection(SumoXMLTag element) {
164 461 : if (myXMLReader == nullptr) {
165 0 : throw ProcessError(TL("The XML-parser was not initialized."));
166 : }
167 : bool started = false;
168 461 : if (myNextSection.first != -1) {
169 287 : started = myNextSection.first == element;
170 287 : if (!started) {
171 : // This enforces that the next parsed section starts right after the last one.
172 : // If we want to skip sections we need to change this.
173 2 : WRITE_WARNINGF("Expected different XML section '%', some content may be missing.", toString(element));
174 : }
175 287 : myHandler->myStartElement(myNextSection.first, *myNextSection.second);
176 287 : delete myNextSection.second;
177 287 : myNextSection.first = -1;
178 287 : myNextSection.second = nullptr;
179 : }
180 461 : myHandler->setSection(element, started);
181 3494793 : while (!myHandler->sectionFinished()) {
182 3494505 : if (!myXMLReader->parseNext(myToken)) {
183 : return false;
184 : }
185 : }
186 : myNextSection = myHandler->retrieveNextSectionStart();
187 288 : return true;
188 : }
189 :
190 :
191 : void
192 205202 : SUMOSAXReader::ensureSAXReader() {
193 205202 : if (myXMLReader == nullptr) {
194 100666 : myXMLReader = XERCES_CPP_NAMESPACE::XMLReaderFactory::createXMLReader(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager, myGrammarPool);
195 100666 : if (myXMLReader == nullptr) {
196 0 : throw ProcessError(TL("The XML-parser could not be build."));
197 : }
198 100666 : setValidation();
199 100666 : myXMLReader->setContentHandler(myHandler);
200 100666 : myXMLReader->setErrorHandler(myHandler);
201 : }
202 205202 : }
203 :
204 :
205 302085 : SUMOSAXReader::LocalSchemaResolver::LocalSchemaResolver(const bool haveFallback, const bool noOp) :
206 302085 : myHaveFallback(haveFallback),
207 302085 : myNoOp(noOp) {
208 302085 : }
209 :
210 :
211 : XERCES_CPP_NAMESPACE::InputSource*
212 225843 : SUMOSAXReader::LocalSchemaResolver::resolveEntity(const XMLCh* const /* publicId */, const XMLCh* const systemId) {
213 225843 : if (myNoOp) {
214 0 : return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, "");
215 : }
216 225843 : const std::string url = StringUtils::transcode(systemId);
217 : const std::string::size_type pos = url.find("/xsd/");
218 225843 : if (pos != std::string::npos) {
219 52201 : const char* sumoPath = std::getenv("SUMO_HOME");
220 : // no need for a warning if SUMO_HOME is not set, global preparsing should have done it.
221 52201 : if (sumoPath != nullptr) {
222 156579 : const std::string file = sumoPath + std::string("/data") + url.substr(pos);
223 104386 : if (FileHelpers::isReadable(file)) {
224 52164 : XMLCh* t = XERCES_CPP_NAMESPACE::XMLString::transcode(file.c_str());
225 52164 : XERCES_CPP_NAMESPACE::InputSource* const result = new XERCES_CPP_NAMESPACE::LocalFileInputSource(t);
226 52164 : XERCES_CPP_NAMESPACE::XMLString::release(&t);
227 : return result;
228 : } else {
229 89 : WRITE_WARNING("Cannot read local schema '" + file + (myHaveFallback ? "', will try website lookup." : "', XML validation will fail."));
230 : }
231 : }
232 : }
233 862509 : if (myHaveFallback || (!StringUtils::startsWith(url, "http:") && !StringUtils::startsWith(url, "https:") && !StringUtils::startsWith(url, "ftp:"))) {
234 : return nullptr;
235 : }
236 2 : return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, "");
237 : }
238 :
239 :
240 : /****************************************************************************/
|