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-07-05 15:55:58 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        89468 : SUMOSAXReader::SUMOSAXReader(GenericSAXHandler& handler, const std::string& validationScheme, XERCES_CPP_NAMESPACE::XMLGrammarPool* grammarPool) :
      52        89468 :     myHandler(nullptr),
      53        89468 :     myValidationScheme(validationScheme),
      54        89468 :     myGrammarPool(grammarPool),
      55        89468 :     myXMLReader(nullptr),
      56              :     myIStream(nullptr),
      57              :     myInputStream(nullptr),
      58        89468 :     mySchemaResolver(true, false),
      59        89468 :     myLocalResolver(false, false),
      60        89468 :     myNoOpResolver(false, true),
      61              :     myNextSection(-1, nullptr) {
      62        89468 :     setHandler(handler);
      63        89468 : }
      64              : 
      65              : 
      66        88954 : SUMOSAXReader::~SUMOSAXReader() {
      67        88954 :     delete myXMLReader;
      68        88954 :     delete myNextSection.second;
      69       177908 : }
      70              : 
      71              : 
      72              : void
      73       199678 : SUMOSAXReader::setHandler(GenericSAXHandler& handler) {
      74       199678 :     myHandler = &handler;
      75       199678 :     if (myXMLReader != nullptr) {
      76       110210 :         myXMLReader->setContentHandler(&handler);
      77       110210 :         myXMLReader->setErrorHandler(&handler);
      78              :     }
      79       199678 : }
      80              : 
      81              : 
      82              : void
      83       188245 : 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       188245 :     if (myXMLReader != nullptr && validationScheme != myValidationScheme) {
      88       102573 :         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       102573 :         if (validationScheme == "never") {
      93        73443 :             myXMLReader->setEntityResolver(&myNoOpResolver);
      94        73443 :             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       188245 :     myValidationScheme = validationScheme;
     105       188245 : }
     106              : 
     107              : 
     108              : void
     109       148684 : SUMOSAXReader::parse(std::string systemID) {
     110       297368 :     if (!FileHelpers::isReadable(systemID)) {
     111            0 :         throw IOError(TLF("Cannot read file '%'!", systemID));
     112              :     }
     113       297368 :     if (FileHelpers::isDirectory(systemID)) {
     114            0 :         throw IOError(TLF("File '%' is a directory!", systemID));
     115              :     }
     116       148684 :     ensureSAXReader();
     117              : #ifdef HAVE_ZLIB
     118       148684 :     zstr::ifstream istream(XMLSubSys::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary);
     119       148684 :     myXMLReader->parse(IStreamInputSource(istream));  // NOSONAR
     120              : #else
     121              :     myXMLReader->parse(XMLSubSys::transcodeToLocal(systemID).c_str());  // NOSONAR
     122              : #endif
     123       148684 : }
     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        39565 : SUMOSAXReader::parseFirst(std::string systemID) {
     136        79130 :     if (!FileHelpers::isReadable(systemID)) {
     137           36 :         throw IOError(TLF("Cannot read file '%'!", systemID));
     138              :     }
     139        79106 :     if (FileHelpers::isDirectory(systemID)) {
     140            0 :         throw IOError(TLF("File '%' is a directory!", systemID));
     141              :     }
     142        39553 :     ensureSAXReader();
     143        39553 :     myToken = XERCES_CPP_NAMESPACE::XMLPScanToken();
     144              : #ifdef HAVE_ZLIB
     145        39553 :     myIStream = std::unique_ptr<zstr::ifstream>(new zstr::ifstream(XMLSubSys::transcodeToLocal(systemID).c_str(), std::fstream::in | std::fstream::binary));
     146        39553 :     myInputStream = std::unique_ptr<IStreamInputSource>(new IStreamInputSource(*myIStream));
     147        39553 :     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      3391496 : SUMOSAXReader::parseNext() {
     156      3391496 :     if (myXMLReader == nullptr) {
     157            0 :         throw ProcessError(TL("The XML-parser was not initialized."));
     158              :     }
     159      3391496 :     return myXMLReader->parseNext(myToken);
     160              : }
     161              : 
     162              : 
     163              : bool
     164         1165 : SUMOSAXReader::parseSection(SumoXMLTag element) {
     165         1165 :     if (myXMLReader == nullptr) {
     166            0 :         throw ProcessError(TL("The XML-parser was not initialized."));
     167              :     }
     168              :     bool started = false;
     169         1165 :     if (myNextSection.first != -1) {
     170          644 :         started = myNextSection.first == element;
     171          644 :         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          644 :         myHandler->myStartElement(myNextSection.first, *myNextSection.second);
     177          644 :         delete myNextSection.second;
     178          644 :         myNextSection.first = -1;
     179          644 :         myNextSection.second = nullptr;
     180              :     }
     181         1165 :     myHandler->setSection(element, started);
     182      3649836 :     while (!myHandler->sectionFinished()) {
     183      3649191 :         if (!myXMLReader->parseNext(myToken)) {
     184              :             return false;
     185              :         }
     186              :     }
     187              :     myNextSection = myHandler->retrieveNextSectionStart();
     188          645 :     return true;
     189              : }
     190              : 
     191              : 
     192              : void
     193       188245 : SUMOSAXReader::ensureSAXReader() {
     194       188245 :     if (myXMLReader == nullptr) {
     195        89437 :         myXMLReader = XERCES_CPP_NAMESPACE::XMLReaderFactory::createXMLReader(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager, myGrammarPool);
     196        89437 :         if (myXMLReader == nullptr) {
     197            0 :             throw ProcessError(TL("The XML-parser could not be build."));
     198              :         }
     199        89437 :         setValidation();
     200        89437 :         myXMLReader->setContentHandler(myHandler);
     201        89437 :         myXMLReader->setErrorHandler(myHandler);
     202              :     }
     203       188245 : }
     204              : 
     205              : 
     206       268404 : SUMOSAXReader::LocalSchemaResolver::LocalSchemaResolver(const bool haveFallback, const bool noOp) :
     207       268404 :     myHaveFallback(haveFallback),
     208       268404 :     myNoOp(noOp) {
     209       268404 : }
     210              : 
     211              : 
     212              : XERCES_CPP_NAMESPACE::InputSource*
     213       838397 : SUMOSAXReader::LocalSchemaResolver::resolveEntity(const XMLCh* const /* publicId */, const XMLCh* const systemId) {
     214       838397 :     if (myNoOp) {
     215            0 :         return new XERCES_CPP_NAMESPACE::MemBufInputSource((const XMLByte*)"", 0, "");
     216              :     }
     217       838397 :     const std::string url = XMLSubSys::transcode(systemId);
     218              :     const std::string::size_type pos = url.find("/xsd/");
     219       838397 :     if (pos != std::string::npos) {
     220        56998 :         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        56998 :         if (sumoPath != nullptr) {
     223       170970 :             const std::string file = sumoPath + std::string("/data") + url.substr(pos);
     224       113980 :             if (FileHelpers::isReadable(file)) {
     225        56961 :                 XMLCh* t = XERCES_CPP_NAMESPACE::XMLString::transcode(file.c_str());
     226        56961 :                 XERCES_CPP_NAMESPACE::InputSource* const result = new XERCES_CPP_NAMESPACE::LocalFileInputSource(t);
     227        56961 :                 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      3901186 :     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