// configuration.hpp
//
// (C) 2006 - 2009 MicroNeil Research Corporation.
//
// This program is part of the MicroNeil Research Open Library Project. For
// more information go to http://www.microneil.com/OpenLibrary/index.html
//
// 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 configuration 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 "configuration" 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 configuration in code and that same
// configuration 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 ConfigurationHandler {                          // Create a special handler to build a list
//   ...
//   public:
//
//     ConfigurationHandler& Startup(ConfigurationElement& E) {                 // This function returns a handy handler to
//         return MyStartupConfigurationHandler;                                // (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 ConfigurationHandler COULD
//     string Attribute3;                                                       // do something entirely different though ;-)
//     string Contents;
//     ...
// } Special;
//
// ConfigurationElement 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 configuration_included
#define configuration_included

#include <string>
#include <sstream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <list>

using namespace std;

class ConfigurationElement;                                                     // Elements exist
class ConfigurationAttribute;                                                   // Attributes exist
class ConfigurationData;                                                        // Data exists
class ConfigurationTranslator;                                                  // Translators exist
class ConfigurationMnemonic;                                                    // Mnemonics exist
class Configurator;                                                             // Configurators exist

//// Configuration 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 ConfigurationElement {

  private:

    string myName;                                                              // Elements have a name.

    // External important things I remember but don't touch...

    ConfigurationElement* myParent;                                             // They may have a parrent.

    list<Configurator*> myStartConfigurators;                                   // Call these when we start Interpret()
    list<Configurator*> myEndConfigurators;                                     // Call these when we finish Interpret()

    // Internal / subordinate things I own and kill...

    list<ConfigurationAttribute*> myAttributes;                                 // They may have a list of attributes.
    list<ConfigurationElement*> myElements;                                     // They may have a list of sub-elements.
    list<ConfigurationMnemonic*> myMnemonics;                                   // They may have a list of mnemonics.
    list<ConfigurationTranslator*> myTranslators;                               // They may have a list of translators.

    // 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 myLength;                                                               // Last segment length.

    bool myCleanFlag;                                                           // Keep track of initialization.

    bool myInitOnInterpretFlag;                                                 // Initialize() at each Interpret()?

    void runStartConfigurators(ConfigurationData& D);                           // Does what it says ;-)
    void runEndConfigurators(ConfigurationData& D);                             // Does what it says ;-)

  public:

    ConfigurationElement(const char* Name);                                     // Must be constructed with a name
    ConfigurationElement(const string Name);                                    // either c string or c++ string.

    ConfigurationElement(const char* Name, ConfigurationElement& Parent);       // Sub-elements are constructed with a
    ConfigurationElement(const string Name, ConfigurationElement& 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 Configurators - they must
    // be deleted elsewhere because they may have been
    // re-used and this element wouldn't know about it ;-)

    ~ConfigurationElement();                                                    // The descrutor clears and deletes all!

    // Elements can be probed for some simple, useful things.

    string Name();                                                              // Get the name of this element.
    ConfigurationElement& Parent();                                             // Get the parent of this element.
    ConfigurationElement& Parent(ConfigurationElement& 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.

    ConfigurationElement& Element(const char* Name);                            // Add a new sub element by c string name.
    ConfigurationElement& Element(const 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

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationElement& 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.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      bool& x, bool init = false);                                              // Map to a boolean.

    // string versions

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      int& x, int init = 0, int radix = 0);                                     // Map to an int.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const 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.

    class EndNameDoesNotMatch {};                                               // Throw when End(name) doesn't match.

    ConfigurationElement& End();                                                // Return this element's parent.
    ConfigurationElement& End(const char* Name);                                // Check the name and return the parent
    ConfigurationElement& End(const string Name);                               // if the name is correct - or throw!

    // Elements can have attributes.

    ConfigurationAttribute& Attribute(const char* Name);                        // Add an attribute using a cstring.
    ConfigurationAttribute& Attribute(const string Name);                       // Add an attribute using a c++ string.

    //// Mapping Attribute factory methods for convenience.

    // char* versions

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationAttribute& 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.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      bool& x, bool init = false);                                              // Map to a boolean.

    // string versions

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      int& x, int init = 0, int radix = 0);                                     // Map to an int.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      bool& x, bool init = false);                                              // Map to a boolean.

    // Elements can Initialize() at each Interpret() call.

    ConfigurationElement& setInitOnInterpret();                                 // Set the init on interpret flag.

    // Elements can call external functions to aid in special operations
    // such as building lists.

    ConfigurationElement& atStartCall(Configurator& Functor);                   // Add an atStart call-back to this element.
    ConfigurationElement& atEndCall(Configurator& 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.

    ConfigurationElement& mapTo(ConfigurationTranslator& newTranslator);        // Add a Translator to this element.
    ConfigurationElement& mapTo(string& x, string init = string(""));           // Map to a string.
    ConfigurationElement& mapTo(int& x, int init = 0, int radix = 0);           // Map to an int.
    ConfigurationElement& mapTo(double& x, double init = 0.0);                  // Map to a double.
    ConfigurationElement& mapTo(bool& x, bool init = false);                    // Map to a boolean.

    // An Element's contents may use some special mnemonics to make a
    // configuration 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.

    ConfigurationElement& Mnemonic(const char* name, const char* value);        // Add a mnemonic using c strings.
    ConfigurationElement& Mnemonic(const char* name, const string value);       // Add a mnemonic using c & c++ strings.
    ConfigurationElement& Mnemonic(const string name, const char* value);       // Add a mnemonic using c++ & c strings.
    ConfigurationElement& Mnemonic(const string name, const 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 ConfigurationData 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 ConfigurationData 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(ConfigurationData& Data);                                    // (re) Interpret this data.

};

//// Configuration Attribute ///////////////////////////////////////////////////
//
// Attributes translate directly to well formed xml attributes (within the
// start tag of an element).

class ConfigurationAttribute {

  private:

    string myName;                                                              // Elements have a name.
    ConfigurationElement& myParent;                                             // They may have a parrent.

    list<ConfigurationMnemonic*> myMnemonics;                                   // They may have a list of mnemonics.
    list<ConfigurationTranslator*> 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:

    ConfigurationAttribute(const char* Name, ConfigurationElement& Parent);     // Sub-elements are constructed with a
    ConfigurationAttribute(const string Name, ConfigurationElement& 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 ;-)

    ~ConfigurationAttribute();                                                  // Crush, Kill, Destroy!

    // Attributes can be probed for some simple, useful things.

    string Name();                                                              // Get the name of this attribute.
    ConfigurationElement& 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.

    ConfigurationElement& Element(const char* Name);                            // Add a new sub element by c string name.
    ConfigurationElement& Element(const 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

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationElement& 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.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      bool& x, bool init = false);                                              // Map to a boolean.

    // string versions

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      int& x, int init = 0, int radix = 0);                                     // Map to an int.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationElement& Element(                                              // Mapping factory for convenience,
      const 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.

    ConfigurationElement& End();                                                // Return this element's parent.
    ConfigurationElement& End(const char* Name);                                // Check the name and return the parent
    ConfigurationElement& End(const string Name);                               // if the name is correct - or throw!

    //// For adding new attributes to the parent element.

    ConfigurationAttribute& Attribute(const char* Name);                        // Add an attribute using a cstring.
    ConfigurationAttribute& Attribute(const string Name);                       // Add an attribute using a c++ string.

    //// Mapping Attribute factory methods for convenience.

    // char* versions

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationAttribute& 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.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const char* Name,                                                         // requires a name, of course,
      bool& x, bool init = false);                                              // Map to a boolean.

    // string versions

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      ConfigurationTranslator& newTranslator);                                  // Add a Translator to this element.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      string& x, string init = string(""));                                     // Map to a string.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      int& x, int init = 0, int radix = 0);                                     // Map to an int.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      double& x, double init = 0.0);                                            // Map to a double.

    ConfigurationAttribute& Attribute(                                          // Mapping factory for convenience,
      const string Name,                                                        // requires a name, of course,
      bool& x, bool init = false);                                              // Map to a boolean.

    //// Set Init On Interprete for the parent element.

    ConfigurationElement& setInitOnInterpret();                                 // Set the init on interpret flag.

    //// For adding configurators to the parent element.

    ConfigurationElement& atStartCall(Configurator& Functor);                   // Add an atStart call-back to this element.
    ConfigurationElement& atEndCall(Configurator& 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.

    ConfigurationAttribute& mapTo(ConfigurationTranslator& newTranslator);      // Add a Translator to this attribute.
    ConfigurationAttribute& mapTo(string& x, string init = string(""));         // Map to a string.
    ConfigurationAttribute& mapTo(int& x, int init, int radix = 0);             // Map to an int.
    ConfigurationAttribute& mapTo(double& x, double init = 0.0);                // Map to a double.
    ConfigurationAttribute& mapTo(bool& x, bool init = false);                  // Map to a boolean.

    // Attributes can have mnemonics just like elements.

    ConfigurationAttribute& Mnemonic(const char* name, const char* value);      // Add a mnemonic using a c string.
    ConfigurationAttribute& Mnemonic(const char* name, const string value);     // Add a mnemonic using c & c++ strings.
    ConfigurationAttribute& Mnemonic(const string name, const char* value);     // Add a mnemonic using c++ & c strings.
    ConfigurationAttribute& Mnemonic(const string name, const 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(ConfigurationData& Data);                                    // (re) Interpret this data.

};

//// Configuration Data ////////////////////////////////////////////////////////
//
// A ConfigurationData 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 ConfigurationData {                                                       // Configuration 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:

    ConfigurationData(const char* FileName);                                    // Constructor from c string file name.
    ConfigurationData(const string FileName);                                   // Constructor from c++ string file name.
    ConfigurationData(const char* Data, int Length);                            // Raw constructor from text buffer.

    ~ConfigurationData();                                                       // 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.

    stringstream Log;                                                           // Convenient Interpret log.

};

//// Configuration 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 ConfigurationTranslator {                                                 // Translators exist
  public:
    virtual void translate(const char* Value) = 0;                              // Pure virtual translator.
    virtual void initialize() = 0;                                              // Pure virtual initializer.
};

class StringTranslator : public ConfigurationTranslator {
  private:
    string& myVariable;                                                         // Variable to map.
    string myInitializer;                                                       // Initial/Default value.

  public:
    StringTranslator(                                                           // Construct this with
      string& Variable,                                                         // the variable to map,
      string Inititializer);                                                    // and the default value.

    void translate(const char* Value);                                          // Provide a translation method.
    void initialize();                                                          // Provide an initialization method.
};

class IntegerTranslator : public ConfigurationTranslator {
  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 ConfigurationTranslator {
  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 ConfigurationTranslator {
  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.
};

//// Configuration 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 ConfigurationMnemonic {                                                   // Mnemonics
  private:
    string myName;                                                              // What is the Mnemonic?
    string myValue;                                                             // What is the translation?

  public:
    ConfigurationMnemonic(string Name, string Value);                           // To make one, provide both parts.
    bool test(string Name);                                                     // Test to see if this Mnemonic matches.
    string Value();                                                             // If it does then we will need it's value.
};

//// Configurator //////////////////////////////////////////////////////////////
//
// A configurator 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 Configurator {                                                            // Configurators exist
  public:
    virtual void operator()(ConfigurationElement& E, ConfigurationData& D) = 0; // Pure virtual configurator.
};

//// Include our inline methods ////////////////////////////////////////////////

#include "configuration.inline.hpp"

//// Utilities /////////////////////////////////////////////////////////////////

// SetTrueOnComplete Configurator //////////////////////////////////////////////

class ConfiguratorSetTrueOnComplete : public Configurator {                     // Configurator set's a boolean true.
  private:
    bool* myBoolean;                                                            // The boolean to set.
  public:
    ConfiguratorSetTrueOnComplete();                                            // Must init to NULL for safety.
    void setup(bool& Target);                                                   // Link to the target boolean.

    void operator()(ConfigurationElement& E, ConfigurationData& D);             // Handle the operation.
};

#endif

// End Of Include Only Once

