LCOV - code coverage report
Current view: top level - src/utils/xml - SUMOSAXReader.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 93.3 % 105 98
Test Date: 2026-06-15 15:46:12 Functions: 100.0 % 12 12

            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              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1