/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2002-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GenericHandler.h14/// @author Pablo Alvarez Lopez15/// @date Dec 202216///17// A handler which converts occurring elements and attributes into strings18/****************************************************************************/19#pragma once20#include <config.h>2122#include <string>23#include <map>24#include <stack>25#include <sstream>26#include <vector>27#include <xercesc/sax2/Attributes.hpp>28#include <xercesc/sax2/DefaultHandler.hpp>29#include <utils/common/UtilExceptions.h>30#include <utils/common/StringBijection.h>31#include "SUMOSAXAttributes.h"323334// ===========================================================================35// class definitions36// ===========================================================================37/**38* @class GenericHandler39* @brief A handler which converts occurring elements and attributes into enums40*41* Normally, when using a standard SAX-handler, we would have to compare42* the incoming XMLCh*-element names with the ones we can parse. The same43* applies to parsing the attributes. This was assumed to be very time consuming,44* that's why we derive our handlers from this class.45*46* The idea behind this second handler layer was avoid repeated conversion47* from strings/whatever to XMLCh* and back again. The usage is quite straight48* forward, the only overhead is the need to define the enums - both elements49* and attributes within "SUMOXMLDefinitions". Still, it maybe helps to avoid typos.50*51* This class implements the SAX-callback and offers a new set of callbacks52* which must be implemented by derived classes. Instead of XMLCh*-values,53* element names are supplied to the derived classes as enums (int).54*55* Also, this class allows to retrieve attributes using enums (int) within56* the implemented "myStartElement" method.57*58* Basically, GenericHandler is not derived within SUMO directly, but via SUMOSAXHandler59* which knows all tags/attributes used by SUMO. It is still kept separate for60* an easier maintainability and later extensions.61*/62class GenericHandler : public XERCES_CPP_NAMESPACE::DefaultHandler {6364public:65// Reader needs access to myStartElement, myEndElement66friend class SUMOSAXReader;6768/**69* @brief Constructor70*71* This constructor gets the lists of known tag and attribute names with72* their enums (sumotags and sumoattrs in most cases). The end of the list73* is signaled by terminatorTag/terminatorAttr respectively.74*75* The attribute names are converted into XMLCh* and stored within an76* internal container. This container is cleared within the destructor.77*78* @param[in] tags The list of known tags79* @param[in] terminatorTag The tag which indicates the end of tags (usually the last entry)80* @param[in] attrs The list of known attributes81* @param[in] terminatorAttr The attr which indicates the end of attrs (usually the last entry)82* @param[in] file The name of the processed file83* @param[in] expectedRoot The expected root element, empty string disables the check84*85* @todo Why are both lists non-const and given as pointers?86*/87GenericHandler(const std::string& file, const std::string& expectedRoot = "");8889/// @brief Destructor90virtual ~GenericHandler();9192/**93* @brief The inherited method called when a new tag opens94*95* The method parses the supplied XMLCh*-qname using the internal name/enum-map96* to obtain the enum representation of the attribute name.97*98* Then, "myStartElement" is called supplying the enumeration value, the99* string-representation of the name and the attributes.100*101* @todo recheck/describe encoding of the string-representation102* @todo do not generate and report the string-representation103*/104void startElement(const XMLCh* const uri, const XMLCh* const localname,105const XMLCh* const qname, const XERCES_CPP_NAMESPACE::Attributes& attrs);106107/**108* @brief The inherited method called when characters occurred109*110* The retrieved characters are converted into a string and appended into a111* private buffer. They are reported as soon as the element ends.112*113* @todo recheck/describe what happens with characters when a new element is opened114* @todo describe characters processing in the class' head115*/116void characters(const XMLCh* const chars, const XERCES3_SIZE_t length);117118/**119* @brief The inherited method called when a tag is being closed120*121* This method calls the user-implemented methods myCharacters with the previously122* collected and converted characters.123*124* Then, myEndElement is called, supplying it the qname converted to its enum-125* and string-representations.126*127* @todo recheck/describe encoding of the string-representation128* @todo do not generate and report the string-representation129*/130void endElement(const XMLCh* const uri, const XMLCh* const localname,131const XMLCh* const qname);132133/// @brief Assigning a parent handler which is enabled when the specified tag is closed134void registerParent(const int tag, GenericHandler* handler);135136/**137* @brief Sets the current file name138*139* @param[in] name The name of the currently processed file140*141* @todo Hmmm - this is as unsafe as having a direct access to the variable; recheck142*/143void setFileName(const std::string& name);144145/**146* @brief returns the current file name147*148* @return The name of the currently processed file149*/150const std::string& getFileName() const;151152/// @name SAX ErrorHandler callbacks153//@{154155/**156* @brief Handler for XML-warnings157*158* The message is built using buildErrorMessage and reported159* to the warning-instance of the MsgHandler.160*161* @param[in] exception The occurred exception to process162*/163void warning(const XERCES_CPP_NAMESPACE::SAXParseException& exception);164165/**166* @brief Handler for XML-errors167*168* The message is built using buildErrorMessage and thrown within a ProcessError.169*170* @param[in] exception The occurred exception to process171* @exception ProcessError On any call172*/173void error(const XERCES_CPP_NAMESPACE::SAXParseException& exception);174175/**176* @brief Handler for XML-errors177*178* The message is built using buildErrorMessage and thrown within a ProcessError.179*180* @exception ProcessError On any call181* @param[in] exception The occurred exception to process182*/183void fatalError(const XERCES_CPP_NAMESPACE::SAXParseException& exception);184185//@}186187void setSection(const int element, const bool seen) {188mySection = element;189mySectionSeen = seen;190mySectionOpen = seen;191mySectionEnded = false;192}193194bool sectionFinished() const {195return mySectionEnded;196}197198std::pair<int, SUMOSAXAttributes*> retrieveNextSectionStart() {199std::pair<int, SUMOSAXAttributes*> ret = myNextSectionStart;200myNextSectionStart.first = -1;201myNextSectionStart.second = nullptr;202return ret;203}204205void needsCharacterData(const bool value = true) {206myCollectCharacterData = value;207}208209protected:210/**211* @brief Builds an error message212*213* The error message includes the file name and the line/column information214* as supported by the given SAXParseException215*216* @param[in] exception The name of the currently processed file217* @return A string describing the given exception218*/219std::string buildErrorMessage(const XERCES_CPP_NAMESPACE::SAXParseException& exception);220221/**222* @brief Callback method for an opening tag to implement by derived classes223*224* Called by "startElement" (see there).225* @param[in] element The element that contains the characters, given as a int226* @param[in] attrs The SAX-attributes, wrapped as SUMOSAXAttributes227* @exceptions ProcessError These method may throw a ProcessError if something fails228*/229virtual void myStartElement(int element,230const SUMOSAXAttributes& attrs);231232/**233* @brief Callback method for characters to implement by derived classes234*235* Called by "endElement" (see there).236* @param[in] element The opened element, given as a int237* @param[in] chars The complete embedded character string238* @exceptions ProcessError These method may throw a ProcessError if something fails239*/240virtual void myCharacters(int element,241const std::string& chars);242243/** @brief Callback method for a closing tag to implement by derived classes244*245* Called by "endElement" (see there).246* @param[in] element The closed element, given as a int247* @exceptions ProcessError These method may throw a ProcessError if something fails248*/249virtual void myEndElement(int element);250251/// @brief signal endElement to the parent handler (special case for MSCalibrator)252void callParentEnd(int element);253254private:255/**256* @brief converts from c++-string into unicode257*258* @todo recheck encoding259* @param[in] name The string to convert260* @return The string converted into a XMLCh-string261*/262XMLCh* convert(const std::string& name) const;263264/**265* @brief Converts a tag from its string into its numerical representation266*267* Returns the enum-representation stored for the given tag. If the tag is not268* known, SUMO_TAG_NOTHING is returned.269* @param[in] tag The string to convert270* @return The int-value that represents the string, SUMO_TAG_NOTHING if the named attribute is not known271*/272int convertTag(const std::string& tag) const;273274private:275/// @name elements parsing276//@{277278// the type of the map that maps tag names to ints279typedef std::map<std::string, int> TagMap;280281// the map of tag names to their internal numerical representation282TagMap myTagMap;283284//@}285286/// A list of character strings obtained so far to build the complete characters string at the end287std::vector<std::string> myCharactersVector;288289/// @brief The handler to give control back to290GenericHandler* myParentHandler;291292/// @brief The tag indicating that control should be given back293int myParentIndicator;294295/// @brief The name of the currently parsed file296std::string myFileName;297298/// @brief The root element to expect, empty string disables the check299std::string myExpectedRoot;300301/// @brief whether the reader should collect character data302bool myCollectCharacterData = false;303304/// @brief whether the reader has already seen the root element305bool myRootSeen = false;306307/// @brief The tag indicating the current section to parse308int mySection = -1;309310/// @brief whether the reader has already seen the begin of the section311bool mySectionSeen = false;312313/// @brief whether the reader has already seen the end of the section314bool mySectionEnded = false;315316/// @brief whether an element of the current section is open317bool mySectionOpen = false;318319std::pair<int, SUMOSAXAttributes*> myNextSectionStart;320321private:322/// @brief invalidated copy constructor323GenericHandler(const GenericHandler& s);324325/// @brief invalidated assignment operator326const GenericHandler& operator=(const GenericHandler& s);327328};329330331