Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:11:33

0001 #ifndef Fireworks_Core_FWXMLConfigParser
0002 #define Fireworks_Core_FWXMLConfigParser
0003 #include <istream>
0004 #include <iostream>
0005 #include "Fireworks/Core/interface/SimpleSAXParser.h"
0006 
0007 #include "Fireworks/Core/interface/FWConfiguration.h"
0008 
0009 /** Helper class which reads the XML configuration and constructs the
0010    FWConfiguration classes.
0011    
0012    State machine for the parser can be found by cut and pasting the following 
0013    in graphviz.
0014    
0015    digraph {
0016     IN_BEGIN_DOCUMENT->IN_PUSHED_CONFIG [label = "beginElement(config)"]
0017 
0018     IN_PUSHED_CONFIG->IN_PUSHED_CONFIG [label = "beginElement(config)"]
0019     IN_PUSHED_CONFIG->IN_POPPED_CONFIG [label = "endElement(config)"]
0020     IN_PUSHED_CONFIG->IN_BEGIN_STRING [label = "beginElement(string)"]
0021 
0022     IN_POPPED_CONFIG->IN_PUSHED_CONFIG [label = "beginElement(config)"]
0023     IN_POPPED_CONFIG->IN_POPPED_CONFIG [label = "endElement(config)"]
0024     IN_POPPED_CONFIG->DONE [label = "top level config popped"]
0025 
0026     IN_BEGIN_STRING->IN_STORED_STRING [label = "data()"];
0027     IN_BEGIN_STRING->IN_PUSHED_CONFIG [label = "endElement(string)"]
0028 
0029     IN_STORED_STRING->IN_PUSHED_CONFIG [label = "endElement(string)"]
0030    }
0031 */
0032 class FWXMLConfigParser : public SimpleSAXParser {
0033   enum STATES { IN_BEGIN_DOCUMENT, IN_PUSHED_CONFIG, IN_POPPED_CONFIG, IN_BEGIN_STRING, IN_STORED_STRING };
0034 
0035 public:
0036   FWXMLConfigParser(std::istream &f) : SimpleSAXParser(f), m_state(IN_BEGIN_DOCUMENT), m_first(nullptr) {}
0037 
0038   /** Pushes the configuration on stack eventually */
0039   void pushConfig(Attributes &attributes) {
0040     std::string name;
0041     int version = 0;
0042     for (size_t i = 0, e = attributes.size(); i != e; ++i) {
0043       Attribute &attr = attributes[i];
0044       if (attr.key == "name")
0045         name = attr.value;
0046       else if (attr.key == "version") {
0047         char *endptr;
0048         version = strtol(attr.value.c_str(), &endptr, 10);
0049         if (endptr == attr.value.c_str())
0050           throw ParserError("Version must be an integer.");
0051       } else
0052         throw ParserError("Unexpected attribute " + attr.key);
0053     }
0054     m_configs.push_back(std::make_pair(name, new FWConfiguration(version)));
0055   }
0056 
0057   /** Executes any transaction in the state machine which happens when the 
0058        xml parser finds an new element.
0059      */
0060   void startElement(const std::string &tag, Attributes &attributes) override {
0061     debug_config_state_machine("start", tag, m_state);
0062     if (m_state == IN_BEGIN_DOCUMENT) {
0063       if (tag != "config")
0064         throw ParserError("Expecting toplevel <config> tag");
0065       pushConfig(attributes);
0066       m_first.reset(m_configs.back().second);
0067       m_state = IN_PUSHED_CONFIG;
0068     } else if (m_state == IN_PUSHED_CONFIG) {
0069       if (tag == "config")
0070         pushConfig(attributes);
0071       else if (tag == "string")
0072         m_state = IN_BEGIN_STRING;
0073       else
0074         throw ParserError("Unexpected element " + tag);
0075     } else if (m_state == IN_POPPED_CONFIG) {
0076       if (tag != "config")
0077         throw ParserError("Unexpected element " + tag);
0078       pushConfig(attributes);
0079       m_state = IN_PUSHED_CONFIG;
0080     } else
0081       throw ParserError("Wrong opening tag found " + tag);
0082   }
0083 
0084   /** Executes any transaction in the state machine which happens when the 
0085        xml parser closes an element.
0086 
0087        Notice that we need to do addKeyValue on endElement (and carry around
0088        the FWConfigutation name) because of the "copy by value"
0089        policy of addKeyValue addition which would add empty
0090        FWConfiguration objects if done on startElement.
0091      */
0092   void endElement(const std::string &tag) override {
0093     debug_config_state_machine("end", tag, m_state);
0094     if (m_state == IN_PUSHED_CONFIG || m_state == IN_POPPED_CONFIG) {
0095       if (tag != "config")
0096         throw ParserError("Wrong closing tag found " + tag);
0097 
0098       FWConfiguration *current = m_configs.back().second;
0099       std::string key = m_configs.back().first;
0100       m_configs.pop_back();
0101       if (!m_configs.empty())
0102         m_configs.back().second->addKeyValue(key, *current);
0103       m_state = IN_POPPED_CONFIG;
0104     } else if (m_state == IN_BEGIN_STRING && tag == "string") {
0105       m_configs.back().second->addValue("");
0106       m_state = IN_PUSHED_CONFIG;
0107     } else if (m_state == IN_STORED_STRING && tag == "string")
0108       m_state = IN_PUSHED_CONFIG;
0109     else
0110       throw ParserError("Wrong closing tag found " + tag);
0111   }
0112 
0113   /** Executes any transaction in the state machine which happens when
0114        the xml parser finds some data (i.e. text) between tags
0115        This is mainly used to handle <string> element contents
0116        but also whitespace between tags.
0117      */
0118   void data(const std::string &data) override {
0119     debug_config_state_machine("data", data, m_state);
0120     // We ignore whitespace but complain about any text which is not
0121     // in the <string> tag.
0122     if (m_state == IN_BEGIN_STRING) {
0123       m_configs.back().second->addValue(data);
0124       m_state = IN_STORED_STRING;
0125     } else if (strspn(data.c_str(), " \t\n") != data.size())
0126       throw ParserError("Unexpected text " + data);
0127   }
0128 
0129   /** The parsed configuration. Notice that the parser owns it and destroys
0130        it when destroyed.
0131      */
0132   FWConfiguration *config(void) { return m_first.get(); }
0133 
0134   void debug_config_state_machine(const char *where, const std::string &tag, int state) {
0135 #ifdef FW_CONFIG_PARSER_DEBUG
0136     static char *debug_states[] = {
0137         "IN_BEGIN_DOCUMENT", "IN_PUSHED_CONFIG", "IN_POPPED_CONFIG", "IN_BEGIN_STRING", "IN_STORED_STRING"};
0138 
0139     std::cerr << "  " << where << " tag/data " << tag << "in state " << debug_states[state] << std::endl;
0140 #endif
0141   }
0142 
0143 private:
0144   std::vector<std::pair<std::string, FWConfiguration *> > m_configs;
0145   enum STATES m_state;
0146   std::unique_ptr<FWConfiguration> m_first;
0147   //   unsigned int                                            m_currentConfigVersion;
0148   std::string m_currentConfigName;
0149 };
0150 #endif