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 72584 : SUMOSAXReader::SUMOSAXReader(GenericSAXHandler& handler, const std::string& validationScheme, XERCES_CPP_NAMESPACE::XMLGrammarPool* grammarPool) : 51 72584 : myHandler(nullptr), 52 72584 : myValidationScheme(validationScheme), 53 72584 : myGrammarPool(grammarPool), 54 72584 : myXMLReader(nullptr), 55 : myIStream(nullptr), 56 : myInputStream(nullptr), 57 72584 : mySchemaResolver(true, false), 58 72584 : myLocalResolver(false, false), 59 72584 : myNoOpResolver(false, true), 60 : myNextSection(-1, nullptr) { 61 72584 : setHandler(handler); 62 72584 : } 63 : 64 : 65 72121 : SUMOSAXReader::~SUMOSAXReader() { 66 72121 : delete myXMLReader; 67 72121 : delete myNextSection.second; 68 72121 : } 69 : 70 : 71 : void 72 142832 : SUMOSAXReader::setHandler(GenericSAXHandler& handler) { 73 142832 : myHandler = &handler; 74 142832 : if (myXMLReader != nullptr) { 75 70248 : myXMLReader->setContentHandler(&handler); 76 70248 : myXMLReader->setErrorHandler(&handler); 77 : } 78 142832 : } 79 : 80 : 81 : void 82 134696 : 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 134696 : if (myXMLReader != nullptr && validationScheme != myValidationScheme) { 87 82271 : 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 82271 : if (validationScheme == "never") { 92 61773 : myXMLReader->setEntityResolver(&myNoOpResolver); 93 61773 : myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgWFXMLScanner); 94 : } else { 95 20498 : myXMLReader->setEntityResolver(validationScheme == "local" ? &myLocalResolver : &mySchemaResolver); 96 20498 : myXMLReader->setProperty(XMLUni::fgXercesScannerName, (void*)XMLUni::fgIGXMLScanner); 97 20498 : myXMLReader->setFeature(XMLUni::fgXercesSchema, true); 98 20498 : myXMLReader->setFeature(XMLUni::fgSAX2CoreValidation, true); 99 23406 : myXMLReader->setFeature(XMLUni::fgXercesDynamic, validationScheme == "local" || validationScheme == "auto"); 100 20498 : myXMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, myValidationScheme == "always"); 101 : } 102 : } 103 134696 : myValidationScheme = validationScheme; 104 134696 : } 105 : 106 : 107 : void 108 105223 : SUMOSAXReader::parse(std::string systemID) { 109 210446 : if (!FileHelpers::isReadable(systemID)) { 110 0 : throw IOError(TLF("Cannot read file '%'!", systemID)); 111 : } 112 210446 : if (FileHelpers::isDirectory(systemID)) { 113 0 : throw IOError(TLF("File '%' is a directory!", systemID)); 114 : } 115 105223 : ensureSAXReader(); 116 : #ifdef HAVE_ZLIB 117 210446 : zstr::ifstream istream(StringUtils::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary); 118 105223 : myXMLReader->parse(IStreamInputSource(istream)); // NOSONAR 119 : #else 120 : myXMLReader->parse(StringUtils::transcodeToLocal(systemID).c_str()); // NOSONAR 121 : #endif 122 105223 : } 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 29487 : SUMOSAXReader::parseFirst(std::string systemID) { 135 58974 : if (!FileHelpers::isReadable(systemID)) { 136 56 : throw IOError(TLF("Cannot read file '%'!", systemID)); 137 : } 138 58946 : if (FileHelpers::isDirectory(systemID)) { 139 0 : throw IOError(TLF("File '%' is a directory!", systemID)); 140 : } 141 29473 : ensureSAXReader(); 142 29473 : myToken = XERCES_CPP_NAMESPACE::XMLPScanToken(); 143 : #ifdef HAVE_ZLIB 144 83486 : myIStream = std::unique_ptr<zstr::ifstream>(new zstr::ifstream(StringUtils::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary)); 145 29473 : myInputStream = std::unique_ptr<IStreamInputSource>(new IStreamInputSource(*myIStream)); 146 29473 : 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 4302705 : SUMOSAXReader::parseNext() { 155 4302705 : if (myXMLReader == nullptr) { 156 0 : throw ProcessError(TL("The XML-parser was not initialized.")); 157 : } 158 4302705 : return myXMLReader->parseNext(myToken); 159 : } 160 : 161 : 162 : bool 163 474 : SUMOSAXReader::parseSection(int element) { 164 474 : if (myXMLReader == nullptr) { 165 0 : throw ProcessError(TL("The XML-parser was not initialized.")); 166 : } 167 : bool started = false; 168 474 : if (myNextSection.first != -1) { 169 296 : started = myNextSection.first == element; 170 296 : myHandler->myStartElement(myNextSection.first, *myNextSection.second); 171 296 : delete myNextSection.second; 172 296 : myNextSection.first = -1; 173 296 : myNextSection.second = nullptr; 174 : } 175 474 : myHandler->setSection(element, started); 176 5207156 : while (!myHandler->sectionFinished()) { 177 5206859 : if (!myXMLReader->parseNext(myToken)) { 178 : return false; 179 : } 180 : } 181 : myNextSection = myHandler->retrieveNextSectionStart(); 182 297 : return true; 183 : } 184 : 185 : 186 : void 187 134696 : SUMOSAXReader::ensureSAXReader() { 188 134696 : if (myXMLReader == nullptr) { 189 72554 : myXMLReader = XERCES_CPP_NAMESPACE::XMLReaderFactory::createXMLReader(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager, myGrammarPool); 190 72554 : if (myXMLReader == nullptr) { 191 0 : throw ProcessError(TL("The XML-parser could not be build.")); 192 : } 193 72554 : setValidation(); 194 72554 : myXMLReader->setContentHandler(myHandler); 195 72554 : myXMLReader->setErrorHandler(myHandler); 196 : } 197 134696 : } 198 : 199 : 200 217752 : SUMOSAXReader::LocalSchemaResolver::LocalSchemaResolver(const bool haveFallback, const bool noOp) : 201 217752 : myHaveFallback(haveFallback), 202 217752 : myNoOp(noOp) { 203 217752 : } 204 : 205 : 206 : XERCES_CPP_NAMESPACE::InputSource* 207 98650 : SUMOSAXReader::LocalSchemaResolver::resolveEntity(const XMLCh* const /* publicId */, const XMLCh* const systemId) { 208 98650 : if (myNoOp) { 209 0 : return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, ""); 210 : } 211 98650 : const std::string url = StringUtils::transcode(systemId); 212 : const std::string::size_type pos = url.find("/xsd/"); 213 98650 : if (pos != std::string::npos) { 214 23792 : const char* sumoPath = std::getenv("SUMO_HOME"); 215 : // no need for a warning if SUMO_HOME is not set, global preparsing should have done it. 216 23792 : if (sumoPath != nullptr) { 217 71296 : const std::string file = sumoPath + std::string("/data") + url.substr(pos); 218 47552 : if (FileHelpers::isReadable(file)) { 219 23719 : XMLCh* t = XERCES_CPP_NAMESPACE::XMLString::transcode(file.c_str()); 220 23719 : XERCES_CPP_NAMESPACE::InputSource* const result = new XERCES_CPP_NAMESPACE::LocalFileInputSource(t); 221 23719 : XERCES_CPP_NAMESPACE::XMLString::release(&t); 222 : return result; 223 : } else { 224 118 : WRITE_WARNING("Cannot read local schema '" + file + (myHaveFallback ? "', will try website lookup." : "', XML validation will fail.")); 225 : } 226 : } 227 : } 228 362919 : if (myHaveFallback || (!StringUtils::startsWith(url, "http:") && !StringUtils::startsWith(url, "https:") && !StringUtils::startsWith(url, "ftp:"))) { 229 : return nullptr; 230 : } 231 4 : return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, ""); 232 : } 233 : 234 : /****************************************************************************/