|
|
|
|
|
|
|
|
|
|
|
// XMLReader.hpp |
|
|
|
|
|
// |
|
|
|
|
|
// (C) 2006 - 2009 MicroNeil Research Corporation. |
|
|
|
|
|
// See http://www.codedweller.com for details. |
|
|
|
|
|
// |
|
|
|
|
|
// This program is free software; you can redistribute it and/or modify it |
|
|
|
|
|
// under the terms of the GNU General Public License as published by the |
|
|
|
|
|
// Free Software Foundation; either version 2 of the License, or (at your |
|
|
|
|
|
// option) any later version. |
|
|
|
|
|
// |
|
|
|
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT |
|
|
|
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|
|
|
|
|
// more details. |
|
|
|
|
|
// |
|
|
|
|
|
// You should have received a copy of the GNU General Public License along with |
|
|
|
|
|
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
|
|
|
|
|
// Place, Suite 330, Boston, MA 02111-1307 USA |
|
|
|
|
|
// |
|
|
|
|
|
|
|
|
|
|
|
// What about this ============================================================= |
|
|
|
|
|
|
|
|
|
|
|
// The XMLReader module provides a platform for reading configuration files |
|
|
|
|
|
// (or string data) containing well-formed xml and mapping that data to program |
|
|
|
|
|
// variables. |
|
|
|
|
|
// The idea is to provide the ability for an object or application to provide |
|
|
|
|
|
// a modular "XMLReader" object that models a hierarchical collection of |
|
|
|
|
|
// "settings" that can be represented easily in code and in xml. |
|
|
|
|
|
// |
|
|
|
|
|
// The following is an example model of a XMLReader in code and that same |
|
|
|
|
|
// XMLReader fully populated in xml. |
|
|
|
|
|
// |
|
|
|
|
|
// The code might look like this... |
|
|
|
|
|
// |
|
|
|
|
|
// int IntValue, DefaultInt = 3; |
|
|
|
|
|
// double DblValue, DefaultDbl = 3.14159; |
|
|
|
|
|
// bool BooleanValue, DefaultBool = false; |
|
|
|
|
|
// string StringValue, DefaultString = "NoStringHere"; |
|
|
|
|
|
// |
|
|
|
|
|
// SpecialConfigurator : public XMLReaderHandler { // Create a special handler to build a list |
|
|
|
|
|
// ... |
|
|
|
|
|
// public: |
|
|
|
|
|
// |
|
|
|
|
|
// XMLReaderHandler& Startup(XMLReaderElement& E) { // This function returns a handy handler to |
|
|
|
|
|
// return MyStartupXMLReaderHandler; // (re) initialize this handler ;-) |
|
|
|
|
|
// } |
|
|
|
|
|
// |
|
|
|
|
|
// void Operator()() { // Each time the configurator is called |
|
|
|
|
|
// ... |
|
|
|
|
|
// } |
|
|
|
|
|
// |
|
|
|
|
|
// int Attribute1; // these items are interpreted and added |
|
|
|
|
|
// double Attribute2; // to the list. A XMLReaderHandler COULD |
|
|
|
|
|
// string Attribute3; // do something entirely different though ;-) |
|
|
|
|
|
// string Contents; |
|
|
|
|
|
// ... |
|
|
|
|
|
// } Special; |
|
|
|
|
|
// |
|
|
|
|
|
// XMLReaderElement SampleConfig("SampleConfiguration"); // Define a sample config (doc element) |
|
|
|
|
|
// SampleConfig // Populate the SampleConfig |
|
|
|
|
|
// .atStartCall(Special.Startup()) |
|
|
|
|
|
// .Element("Integer", IntValue, DefaultInt).End() // Link an element to an int w/ default. |
|
|
|
|
|
// .Element("Double", DblValue, DefaultDbl).End("Double") // Link an element to a dbl w/ default. |
|
|
|
|
|
// .Element("String", StringValue, DefaultString).End("String") // Link an element to a string w/ default. |
|
|
|
|
|
// .Element("ComplexElements") // Create a sub element. |
|
|
|
|
|
// .Element("Complex1") // Sub element Complex1 has attributes. |
|
|
|
|
|
// .Attribute("IntAtt", IntValue, DefaultInt) // Complex1 has an integer attribute. |
|
|
|
|
|
// .Attribute("DblAtt", DblValue, DefaultDbl) // Complex1 has a dbl attribute. |
|
|
|
|
|
// .Element("IntAtt", IntValue).End() // IntAtt can also be set by a sub element. |
|
|
|
|
|
// .Element("DblAtt", DblValue).End() // DblAtt can also be set by a sub element. |
|
|
|
|
|
// .End() // That's it for Complex1. |
|
|
|
|
|
// .Element("Complex2") // Create the Complex2 sub element. |
|
|
|
|
|
// .Attribute("C2I", IntValue, DefaultInt) // C2I attribute. |
|
|
|
|
|
// .Attribute("C2D", DblValue) // C2D attribute - no default. |
|
|
|
|
|
// .Attribute("C2S", StringValue, DefultString) // C2S attribute - string w/ default. |
|
|
|
|
|
// .End("Complex2") // End of element throws if doesn't match. |
|
|
|
|
|
// .Element("Complex3", Special.Contents) // Element 3 using a special configurator. |
|
|
|
|
|
// .Attribute("A1", Special.Attribute1) // Set A1 and A2 and A3 and when the |
|
|
|
|
|
// .Attribute("A2", Special.Attribute2) // element has been completed, Special() |
|
|
|
|
|
// .Attribute("A3", Special.Attribute3) // will be called to record the entries. |
|
|
|
|
|
// .atEndCall(Special) // Here's where we register the handler. |
|
|
|
|
|
// .End() // Closing Complex3 to be ice. |
|
|
|
|
|
// .End() // Closing ComplexElements to be nice. |
|
|
|
|
|
// .End(); // Closing SampleConfiguration to be nice. |
|
|
|
|
|
// |
|
|
|
|
|
// The XML might look like this... |
|
|
|
|
|
// |
|
|
|
|
|
// <SampleConfiguration> |
|
|
|
|
|
// <Integer>10</Integer> |
|
|
|
|
|
// <Double>2.4</Double> |
|
|
|
|
|
// <String>This is a sample string</String> |
|
|
|
|
|
// <ComplexElements> |
|
|
|
|
|
// <Complex1 IntAtt="4" DblAtt="2.1324"> |
|
|
|
|
|
// <IntAtt>24</IntAtt> <!-- changed IntAtt --> |
|
|
|
|
|
// </Complex1> |
|
|
|
|
|
// <Complex2 C2I='3' C2D='5.14' C2S='Some "string" we like' /> |
|
|
|
|
|
// <Complex3> stuff in here </Complex3> |
|
|
|
|
|
// <Complex3> Another instance </Complex3> |
|
|
|
|
|
// <Complex3> Each one gets passed to Special() on activation </Complex3> |
|
|
|
|
|
// <Complex3> This way, Special() can build a list or some other </Complex3> |
|
|
|
|
|
// <Complex3> interesting thing with all of these. </Complex3> |
|
|
|
|
|
// <ComplexElements> |
|
|
|
|
|
// </SampleConfiguration> |
|
|
|
|
|
// |
|
|
|
|
|
|
|
|
|
|
|
// Include This Header Once Only =============================================== |
|
|
|
|
|
|
|
|
|
|
|
#ifndef XMLReader_included |
|
|
|
|
|
#define XMLReader_included |
|
|
|
|
|
|
|
|
|
|
|
#include <string> |
|
|
|
|
|
#include <sstream> |
|
|
|
|
|
#include <fstream> |
|
|
|
|
|
#include <cstring> |
|
|
|
|
|
#include <cstdlib> |
|
|
|
|
|
#include <list> |
|
|
|
|
|
|
|
|
|
|
|
namespace CodeDweller { |
|
|
|
|
|
|
|
|
|
|
|
class XMLReaderElement; // Elements exist |
|
|
|
|
|
class XMLReaderAttribute; // Attributes exist |
|
|
|
|
|
class XMLReaderData; // Data exists |
|
|
|
|
|
class XMLReaderTranslator; // Translators exist |
|
|
|
|
|
class XMLReaderMnemonic; // Mnemonics exist |
|
|
|
|
|
class XMLerator; // XMLerators exist |
|
|
|
|
|
|
|
|
|
|
|
typedef XMLReaderElement ConfigurationElement; |
|
|
|
|
|
typedef XMLReaderAttribute ConfigurationAttribute; |
|
|
|
|
|
typedef XMLReaderData ConfigurationData; |
|
|
|
|
|
typedef XMLReaderTranslator ConfigurationTranslator; |
|
|
|
|
|
typedef XMLReaderMnemonic ConfigurationMnemonic; |
|
|
|
|
|
typedef XMLerator Configurator; |
|
|
|
|
|
|
|
|
|
|
|
class RawTranslator { |
|
|
|
|
|
private: |
|
|
|
|
|
int *IndexAssignment = 0; |
|
|
|
|
|
int *EndexAssignment = 0; |
|
|
|
|
|
std::string *RawDataAssignment = 0; |
|
|
|
|
|
public: |
|
|
|
|
|
void setRawDataAssignment(std::string &RawData); |
|
|
|
|
|
void setIndexAssignment(int &Index, int &Endex); |
|
|
|
|
|
void translate(int Index, int Endex, XMLReaderData &Data); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//// XMLReader Element ///////////////////////////////////////////////////// |
|
|
|
|
|
// |
|
|
|
|
|
// Elements make up the core of a configuration. That is, a configuration is a |
|
|
|
|
|
// tree of elements. Elements translate directly to well formed xml elements in |
|
|
|
|
|
// a configuration file or string. |
|
|
|
|
|
|
|
|
|
|
|
class XMLReaderElement { |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
|
|
|
|
std::string myName; // Elements have a name. |
|
|
|
|
|
|
|
|
|
|
|
// External important things I remember but don't touch... |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement* myParent; // They may have a parrent. |
|
|
|
|
|
|
|
|
|
|
|
std::list<XMLerator*> myStartXMLerators; // Call these when we start Interpret() |
|
|
|
|
|
std::list<XMLerator*> myEndXMLerators; // Call these when we finish Interpret() |
|
|
|
|
|
|
|
|
|
|
|
// Internal / subordinate things I own and kill... |
|
|
|
|
|
|
|
|
|
|
|
std::list<XMLReaderAttribute*> myAttributes; // They may have a list of attributes. |
|
|
|
|
|
std::list<XMLReaderElement*> myElements; // They may have a list of sub-elements. |
|
|
|
|
|
std::list<XMLReaderMnemonic*> myMnemonics; // They may have a list of mnemonics. |
|
|
|
|
|
std::list<XMLReaderTranslator*> myTranslators; // They may have a list of translators. |
|
|
|
|
|
|
|
|
|
|
|
RawTranslator myRawTranslator; // Provides entire element. |
|
|
|
|
|
|
|
|
|
|
|
// During Interpret() operations we keep track of where we are seen... |
|
|
|
|
|
|
|
|
|
|
|
int myLine; // Last line number I was seen on. |
|
|
|
|
|
int myIndex; // Last char position I was seen on. |
|
|
|
|
|
int myEndex; // Last char of element. |
|
|
|
|
|
int myLength; // Last segment length. |
|
|
|
|
|
|
|
|
|
|
|
bool myCleanFlag; // Keep track of initialization. |
|
|
|
|
|
|
|
|
|
|
|
bool myInitOnInterpretFlag; // Initialize() at each Interpret()? |
|
|
|
|
|
|
|
|
|
|
|
void runStartXMLerators(XMLReaderData& D); // Does what it says ;-) |
|
|
|
|
|
void runEndXMLerators(XMLReaderData& D); // Does what it says ;-) |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement(const char* Name); // Must be constructed with a name |
|
|
|
|
|
XMLReaderElement(const std::string Name); // either c string or c++ string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement(const char* Name, XMLReaderElement& Parent); // Sub-elements are constructed with a |
|
|
|
|
|
XMLReaderElement(const std::string Name, XMLReaderElement& Parent); // parrent. |
|
|
|
|
|
|
|
|
|
|
|
// Upon desctruction an element will delete all subordinate objects: |
|
|
|
|
|
// * All sub element objects. |
|
|
|
|
|
// * All attribute objects. |
|
|
|
|
|
// * All mnemonic objects. |
|
|
|
|
|
// * All translator objects. |
|
|
|
|
|
// It is important to use new when passing one of these objects to an |
|
|
|
|
|
// element or attribute to prevent problems with the delete operation. |
|
|
|
|
|
// NORMALLY these things would be created using factory methods on the |
|
|
|
|
|
// element and attribute objects themselves - so be careful. |
|
|
|
|
|
// It will not delete XMLerators - they must |
|
|
|
|
|
// be deleted elsewhere because they may have been |
|
|
|
|
|
// re-used and this element wouldn't know about it ;-) |
|
|
|
|
|
|
|
|
|
|
|
~XMLReaderElement(); // The descrutor clears and deletes all! |
|
|
|
|
|
|
|
|
|
|
|
// Elements can be probed for some simple, useful things. |
|
|
|
|
|
|
|
|
|
|
|
std::string Name(); // Get the name of this element. |
|
|
|
|
|
XMLReaderElement& Parent(); // Get the parent of this element. |
|
|
|
|
|
XMLReaderElement& Parent(XMLReaderElement& newParent); // Set the parent of this element. |
|
|
|
|
|
|
|
|
|
|
|
// Note - if there is no parent (an element is the root) then it will |
|
|
|
|
|
// return a reference to itself when Parent() is called. |
|
|
|
|
|
|
|
|
|
|
|
int Line(); // Get the last line number. |
|
|
|
|
|
int Index(); // Get the last data position. |
|
|
|
|
|
int Length(); // Get the last length. |
|
|
|
|
|
|
|
|
|
|
|
// Elements can contain either data or sub-elements. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element(const char* Name); // Add a new sub element by c string name. |
|
|
|
|
|
XMLReaderElement& Element(const std::string Name); // Add a new sub element by c++ string name. |
|
|
|
|
|
|
|
|
|
|
|
//// Mapping element factory methods for convenience. |
|
|
|
|
|
//// Root-Node elements are _usually_ empty and without attributes in xml |
|
|
|
|
|
//// so we don't make any of that type of convenience constructor here. |
|
|
|
|
|
|
|
|
|
|
|
// char* versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// string versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& RawData( // Copy entire element to |
|
|
|
|
|
std::string &Element); // a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& RawIndex( // Copy indices of the entire |
|
|
|
|
|
int &Index, int &Endex); // element. |
|
|
|
|
|
|
|
|
|
|
|
// End methods for heading back up the tree at the end of an element. |
|
|
|
|
|
|
|
|
|
|
|
class EndNameDoesNotMatch {}; // Throw when End(name) doesn't match. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& End(); // Return this element's parent. |
|
|
|
|
|
XMLReaderElement& End(const char* Name); // Check the name and return the parent |
|
|
|
|
|
XMLReaderElement& End(const std::string Name); // if the name is correct - or throw! |
|
|
|
|
|
|
|
|
|
|
|
// Elements can have attributes. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute(const char* Name); // Add an attribute using a cstring. |
|
|
|
|
|
XMLReaderAttribute& Attribute(const std::string Name); // Add an attribute using a c++ string. |
|
|
|
|
|
|
|
|
|
|
|
//// Mapping Attribute factory methods for convenience. |
|
|
|
|
|
|
|
|
|
|
|
// char* versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// string versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// Elements can Initialize() at each Interpret() call. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& setInitOnInterpret(); // Set the init on interpret flag. |
|
|
|
|
|
|
|
|
|
|
|
// Elements can call external functions to aid in special operations |
|
|
|
|
|
// such as building lists. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& atStartCall(XMLerator& Functor); // Add an atStart call-back to this element. |
|
|
|
|
|
XMLReaderElement& atEndCall(XMLerator& Functor); // Add an atEnd call-back to this element. |
|
|
|
|
|
|
|
|
|
|
|
// Extracting data from the element's contents is done with |
|
|
|
|
|
// translators. A good set of primatives are built in, but the user |
|
|
|
|
|
// can also make their own. If an Element is mapped to more than |
|
|
|
|
|
// one then they are all called once the element's contents are |
|
|
|
|
|
// collected. A translator takes the data provided by the element, |
|
|
|
|
|
// converts it into the expected type, and sets one or more variables |
|
|
|
|
|
// to the converted value. Usually - just one variable. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& mapTo(XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
XMLReaderElement& mapTo(std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
XMLReaderElement& mapTo(int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
XMLReaderElement& mapTo(double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
XMLReaderElement& mapTo(bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// An Element's contents may use some special mnemonics to make a |
|
|
|
|
|
// XMLReader easier to understand and less error prone. When the |
|
|
|
|
|
// contents match a mnemnoic then the translation of the mnemonic is |
|
|
|
|
|
// passed to the Translators instead of the raw contents. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Mnemonic(const char* name, const char* value); // Add a mnemonic using c strings. |
|
|
|
|
|
XMLReaderElement& Mnemonic(const char* name, const std::string value); // Add a mnemonic using c & c++ strings. |
|
|
|
|
|
XMLReaderElement& Mnemonic(const std::string name, const char* value); // Add a mnemonic using c++ & c strings. |
|
|
|
|
|
XMLReaderElement& Mnemonic(const std::string name, const std::string value); // Add a mnemonic using c++ strings. |
|
|
|
|
|
|
|
|
|
|
|
// The way data gets into an element tree is that it is Interpret()ed |
|
|
|
|
|
// recursively. The data is loaded into a XMLReaderData object which |
|
|
|
|
|
// is passed to the top Element. That element interpretes the data, moves |
|
|
|
|
|
// the interpretation pointers, and passes the data on to it's subordinate |
|
|
|
|
|
// elements in turn. They do the same recursively. When the last sub - |
|
|
|
|
|
// element has had it's way with the data, the interpretation process is |
|
|
|
|
|
// complete. The XMLReaderData object will contain the original data |
|
|
|
|
|
// and a log of anything that happened during the interpretation process. |
|
|
|
|
|
// |
|
|
|
|
|
// Each time an element is asked to Interpret() data, it calls any atStart |
|
|
|
|
|
// configurators, translates any attributes, then either translates it's |
|
|
|
|
|
// contents or passes the data to it's children, then calls any atEnd |
|
|
|
|
|
// configurators. |
|
|
|
|
|
// |
|
|
|
|
|
// To ensure that the correct default values are used the Initialize() is |
|
|
|
|
|
// always called on all internal attributes and elements before any data is |
|
|
|
|
|
// interpreted. To prevent this from being inefficient, a boolean flag is |
|
|
|
|
|
// kept in each element to keep track of whether it is clean and if it is |
|
|
|
|
|
// then the call to Initialize will simply return (skipping subordinate |
|
|
|
|
|
// elements along the way). |
|
|
|
|
|
// |
|
|
|
|
|
// Interpret returns true if this object found itself at the current |
|
|
|
|
|
// Data.Index and false if not. This helps keep the recursive parsing |
|
|
|
|
|
// code simpler ;-) |
|
|
|
|
|
|
|
|
|
|
|
void initialize(); // Reset all translators to defaults. |
|
|
|
|
|
|
|
|
|
|
|
void notifyDirty(); // Set dirty (if translators change). |
|
|
|
|
|
|
|
|
|
|
|
bool interpret(XMLReaderData& Data); // (re) Interpret this data. |
|
|
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//// XMLReader Attribute /////////////////////////////////////////////////// |
|
|
|
|
|
// |
|
|
|
|
|
// Attributes translate directly to well formed xml attributes (within the |
|
|
|
|
|
// start tag of an element). |
|
|
|
|
|
|
|
|
|
|
|
class XMLReaderAttribute { |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
|
|
|
|
std::string myName; // Elements have a name. |
|
|
|
|
|
XMLReaderElement& myParent; // They may have a parrent. |
|
|
|
|
|
|
|
|
|
|
|
std::list<XMLReaderMnemonic*> myMnemonics; // They may have a list of mnemonics. |
|
|
|
|
|
std::list<XMLReaderTranslator*> myTranslators; // They may have a list of translators. |
|
|
|
|
|
|
|
|
|
|
|
int myLine; // Last line number I was seen on. |
|
|
|
|
|
int myIndex; // Last char position I was seen on. |
|
|
|
|
|
int myLength; // Last segment length. |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute(const char* Name, XMLReaderElement& Parent); // Sub-elements are constructed with a |
|
|
|
|
|
XMLReaderAttribute(const std::string Name, XMLReaderElement& Parent); // parrent. |
|
|
|
|
|
|
|
|
|
|
|
// Attributes delete their Mnemonics and Translators when they go. |
|
|
|
|
|
// See Elements for similar warnings about objects provided to |
|
|
|
|
|
// this object... you must use new to be safe, or better yet - stick to |
|
|
|
|
|
// the built in factory methods ;-) |
|
|
|
|
|
|
|
|
|
|
|
~XMLReaderAttribute(); // Crush, Kill, Destroy! |
|
|
|
|
|
|
|
|
|
|
|
// Attributes can be probed for some simple, useful things. |
|
|
|
|
|
|
|
|
|
|
|
std::string Name(); // Get the name of this attribute. |
|
|
|
|
|
XMLReaderElement& Parent(); // Get the parent of this attribute. |
|
|
|
|
|
int Line(); // Get the last line number. |
|
|
|
|
|
int Index(); // Get the last data position. |
|
|
|
|
|
int Length(); // Get the last length. |
|
|
|
|
|
|
|
|
|
|
|
void notifyDirty(); // Attributes use this when they change. |
|
|
|
|
|
|
|
|
|
|
|
// For convenience in building configurations, an Attribute offers |
|
|
|
|
|
// some call-through methods to it's parrent Element. This allows for |
|
|
|
|
|
// clear, concise .method() coding that mimics an outline of the |
|
|
|
|
|
// configuration structure. |
|
|
|
|
|
|
|
|
|
|
|
//// For switching back to the parent element and adding new sub-elements. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element(const char* Name); // Add a new sub element by c string name. |
|
|
|
|
|
XMLReaderElement& Element(const std::string Name); // Add a new sub element by c++ string name. |
|
|
|
|
|
|
|
|
|
|
|
//// Mapping element factory methods for convenience. |
|
|
|
|
|
//// Root-Node elements are _usually_ empty and without attributes in xml |
|
|
|
|
|
//// so we don't make any of that type of convenience constructor here. |
|
|
|
|
|
|
|
|
|
|
|
// char* versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// string versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& Element( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// End methods for heading back up the tree at the end of an element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& End(); // Return this element's parent. |
|
|
|
|
|
XMLReaderElement& End(const char* Name); // Check the name and return the parent |
|
|
|
|
|
XMLReaderElement& End(const std::string Name); // if the name is correct - or throw! |
|
|
|
|
|
|
|
|
|
|
|
//// For adding new attributes to the parent element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute(const char* Name); // Add an attribute using a cstring. |
|
|
|
|
|
XMLReaderAttribute& Attribute(const std::string Name); // Add an attribute using a c++ string. |
|
|
|
|
|
|
|
|
|
|
|
//// Mapping Attribute factory methods for convenience. |
|
|
|
|
|
|
|
|
|
|
|
// char* versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const char* Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// string versions |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
XMLReaderTranslator& newTranslator); // Add a Translator to this element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
int& x, int init = 0, int radix = 0); // Map to an int. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Attribute( // Mapping factory for convenience, |
|
|
|
|
|
const std::string Name, // requires a name, of course, |
|
|
|
|
|
bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
//// Set Init On Interprete for the parent element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& setInitOnInterpret(); // Set the init on interpret flag. |
|
|
|
|
|
|
|
|
|
|
|
//// For adding configurators to the parent element. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderElement& atStartCall(XMLerator& Functor); // Add an atStart call-back to this element. |
|
|
|
|
|
XMLReaderElement& atEndCall(XMLerator& Functor); // Add an atEnd call-back to this element. |
|
|
|
|
|
|
|
|
|
|
|
// Of course, the most useful thing about attributes is that they can |
|
|
|
|
|
// be mapped to variables using translators. The same as those that |
|
|
|
|
|
// apply to the parent element's contents. Here they are for use on this |
|
|
|
|
|
// attribute. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& mapTo(XMLReaderTranslator& newTranslator); // Add a Translator to this attribute. |
|
|
|
|
|
XMLReaderAttribute& mapTo(std::string& x, std::string init = std::string("")); // Map to a string. |
|
|
|
|
|
XMLReaderAttribute& mapTo(int& x, int init, int radix = 0); // Map to an int. |
|
|
|
|
|
XMLReaderAttribute& mapTo(double& x, double init = 0.0); // Map to a double. |
|
|
|
|
|
XMLReaderAttribute& mapTo(bool& x, bool init = false); // Map to a boolean. |
|
|
|
|
|
|
|
|
|
|
|
// Attributes can have mnemonics just like elements. |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderAttribute& Mnemonic(const char* name, const char* value); // Add a mnemonic using a c string. |
|
|
|
|
|
XMLReaderAttribute& Mnemonic(const char* name, const std::string value); // Add a mnemonic using c & c++ strings. |
|
|
|
|
|
XMLReaderAttribute& Mnemonic(const std::string name, const char* value); // Add a mnemonic using c++ & c strings. |
|
|
|
|
|
XMLReaderAttribute& Mnemonic(const std::string name, const std::string value); // Add a mnemonic using a c++ string. |
|
|
|
|
|
|
|
|
|
|
|
// Attributes participate in the Interprete() task just like elements. |
|
|
|
|
|
|
|
|
|
|
|
void initialize(); // Reset all translators to defaults. |
|
|
|
|
|
bool interpret(XMLReaderData& Data); // (re) Interpret this data. |
|
|
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//// XMLReader Data //////////////////////////////////////////////////////// |
|
|
|
|
|
// |
|
|
|
|
|
// A XMLReaderData object holds on to the configuration source data and |
|
|
|
|
|
// provideds a place to log any information about how the configuration was |
|
|
|
|
|
// interpreted. It also creates and destroys a handy char[] to contain the |
|
|
|
|
|
// data. To make this beastie easier to handle, we use the Named Constructor |
|
|
|
|
|
// Idiom and hide the true constructor in the private section. |
|
|
|
|
|
|
|
|
|
|
|
class XMLReaderData { // XMLReader Data Source |
|
|
|
|
|
private: |
|
|
|
|
|
|
|
|
|
|
|
char* myDataBuffer; // The actual data buffer. |
|
|
|
|
|
int myBufferSize; // Size of the current buffer. |
|
|
|
|
|
int myIndex; // The current interpretation index. |
|
|
|
|
|
int myLine; // Current line number. |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
XMLReaderData(const char* FileName); // Constructor from c string file name. |
|
|
|
|
|
XMLReaderData(const std::string FileName); // Constructor from c++ string file name. |
|
|
|
|
|
XMLReaderData(const char* Data, int Length); // Raw constructor from text buffer. |
|
|
|
|
|
|
|
|
|
|
|
~XMLReaderData(); // Destroys the internal buffer etc. |
|
|
|
|
|
|
|
|
|
|
|
char Data(int Index); // Returns char from Data[Index] |
|
|
|
|
|
int Index(); // Reads the current Index. |
|
|
|
|
|
int Index(int i); // Changes the current Index. |
|
|
|
|
|
int Line(); // Reads the current Line number. |
|
|
|
|
|
int addNewLines(int Count); // Increments the Line number. |
|
|
|
|
|
std::string extract(int Index, int Endex) const ; // Return substring of buffer. |
|
|
|
|
|
|
|
|
|
|
|
std::stringstream Log; // Convenient Interpret log. |
|
|
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//// XMLReader Translator ////////////////////////////////////////////////// |
|
|
|
|
|
// |
|
|
|
|
|
// A Translator converts the contents provided to it in string form into some |
|
|
|
|
|
// other data type. The object here is a prototype for that, followed by a |
|
|
|
|
|
// collection of the basic translators used for built-in mapTo()s. |
|
|
|
|
|
|
|
|
|
|
|
class XMLReaderTranslator { // Translators exist |
|
|
|
|
|
public:
|
|
|
|
|
|
virtual ~XMLReaderTranslator(){}; // Stop No Virt Dtor warnings. |
|
|
|
|
|
virtual void translate(const char* Value) = 0; // Pure virtual translator. |
|
|
|
|
|
virtual void initialize() = 0; // Pure virtual initializer. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class StringTranslator : public XMLReaderTranslator { |
|
|
|
|
|
private: |
|
|
|
|
|
std::string& myVariable; // Variable to map. |
|
|
|
|
|
std::string myInitializer; // Initial/Default value. |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
StringTranslator( // Construct this with |
|
|
|
|
|
std::string& Variable, // the variable to map, |
|
|
|
|
|
std::string Inititializer); // and the default value. |
|
|
|
|
|
|
|
|
|
|
|
void translate(const char* Value); // Provide a translation method. |
|
|
|
|
|
void initialize(); // Provide an initialization method. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class IntegerTranslator : public XMLReaderTranslator { |
|
|
|
|
|
private: |
|
|
|
|
|
int& myVariable; // Variable to map. |
|
|
|
|
|
int myInitializer; // Initial/Default value. |
|
|
|
|
|
int myRadix; // Radix for strtol() |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
IntegerTranslator( // Construct this with |
|
|
|
|
|
int& Variable, // the variable to map, |
|
|
|
|
|
int Inititializer, // and the default value. |
|
|
|
|
|
int Radix); // For this one we also need a Radix. |
|
|
|
|
|
|
|
|
|
|
|
void translate(const char* Value); // Provide a translation method. |
|
|
|
|
|
void initialize(); // Provide an initialization method. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class DoubleTranslator : public XMLReaderTranslator { |
|
|
|
|
|
private: |
|
|
|
|
|
double& myVariable; // Variable to map. |
|
|
|
|
|
double myInitializer; // Initial/Default value. |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
DoubleTranslator( // Construct this with |
|
|
|
|
|
double& Variable, // the variable to map, |
|
|
|
|
|
double Inititializer); // and the default value. |
|
|
|
|
|
|
|
|
|
|
|
void translate(const char* Value); // Provide a translation method. |
|
|
|
|
|
void initialize(); // Provide an initialization method. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class BoolTranslator : public XMLReaderTranslator { |
|
|
|
|
|
private: |
|
|
|
|
|
bool& myVariable; // Variable to map. |
|
|
|
|
|
bool myInitializer; // Initial/Default value. |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
BoolTranslator( // Construct this with |
|
|
|
|
|
bool& Variable, // the variable to map, |
|
|
|
|
|
bool Inititializer); // and the default value. |
|
|
|
|
|
|
|
|
|
|
|
void translate(const char* Value); // Provide a translation method. |
|
|
|
|
|
void initialize(); // Provide an initialization method. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//// XMLReader Mnemonic //////////////////////////////////////////////////// |
|
|
|
|
|
// |
|
|
|
|
|
// A Mnemonic allows the actual contents of an element or attribute to be |
|
|
|
|
|
// exchanged for a different "named" value to help eliminate "magic numbers" |
|
|
|
|
|
// and "secret codes" from configurations. One way this might be used is to |
|
|
|
|
|
// map an enumeration to the appropriate integer values, or things like YES and |
|
|
|
|
|
// NO to boolean true and false (respectively) when turning on/off program |
|
|
|
|
|
// options. |
|
|
|
|
|
|
|
|
|
|
|
class XMLReaderMnemonic { // Mnemonics |
|
|
|
|
|
private: |
|
|
|
|
|
std::string myName; // What is the Mnemonic? |
|
|
|
|
|
std::string myValue; // What is the translation? |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
XMLReaderMnemonic(std::string Name, std::string Value); // To make one, provide both parts. |
|
|
|
|
|
bool test(std::string Name); // Test to see if this Mnemonic matches. |
|
|
|
|
|
std::string Value(); // If it does then we will need it's value. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//// XMLerator ////////////////////////////////////////////////////////////// |
|
|
|
|
|
// |
|
|
|
|
|
// An XMLerator is a "functor" or "closure" or "callback" that can be used to |
|
|
|
|
|
// support sophisticated interpretation options. The most basic and necessary |
|
|
|
|
|
// of these is support for list building. Consider an object created to contain |
|
|
|
|
|
// a list of records where each record might be represented as a collection of |
|
|
|
|
|
// attributes and elements. The object would have data elements mapped to the |
|
|
|
|
|
// attributes and elements in the configuration and then control elements which |
|
|
|
|
|
// are functors for initializing the list and storing new entries as they are |
|
|
|
|
|
// completed. The object here is a pure virtual prototype. |
|
|
|
|
|
|
|
|
|
|
|
class XMLerator { // XMLerators exist |
|
|
|
|
|
public: |
|
|
|
|
|
virtual void operator()(XMLReaderElement& E, XMLReaderData& D) = 0; // Pure virtual configurator.
|
|
|
|
|
|
virtual ~XMLerator() {} // Virtual dtor keeps warnings away. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//// Include our inline methods //////////////////////////////////////////////// |
|
|
|
|
|
|
|
|
|
|
|
#include "XMLReader.inline.hpp" |
|
|
|
|
|
|
|
|
|
|
|
//// Utilities ///////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
|
|
|
|
// SetTrueOnComplete XMLerator ////////////////////////////////////////////// |
|
|
|
|
|
|
|
|
|
|
|
class XMLeratorSetTrueOnComplete : public XMLerator { // XMLerator set's a boolean true. |
|
|
|
|
|
private: |
|
|
|
|
|
bool* myBoolean; // The boolean to set. |
|
|
|
|
|
public: |
|
|
|
|
|
XMLeratorSetTrueOnComplete(); // Must init to NULL for safety. |
|
|
|
|
|
void setup(bool& Target); // Link to the target boolean. |
|
|
|
|
|
|
|
|
|
|
|
void operator()(XMLReaderElement& E, XMLReaderData& D); // Handle the operation. |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
// End Of Include Only Once |
|
|
|
|
|
|