Changeset 304

Show
Ignore:
Timestamp:
03/31/05 12:00:54 (4 years ago)
Author:
athomas
Message:

Finished migrating to new Property Map? hierarchy. Much, much cleaner. It
should also let me extend it seamlessly.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • todo2/trunk/src/Category.cc

    r295 r304  
    11#include "Category.h" 
    22 
    3 Category::Category(const PropertyMap &props) : mid(props.get("id")), mdescription(props.get("description")) { 
     3Category::Category(const AbstractPropertyMap &props) : mid(props.get("id")), mdescription(props.get("description")) { 
    44} 
  • todo2/trunk/src/Category.h

    r295 r304  
    2121 
    2222                Category() {} 
    23                 Category(const PropertyMap &props); 
     23                Category(const AbstractPropertyMap &props); 
    2424 
    2525                std::string id() const { return mid; } 
  • todo2/trunk/src/filter/Expression.cc

    r302 r304  
    1616 
    1717        // Load aliases 
    18 Manager::PropertyMap aliases = MANAGER.prefixToPropertyMap("expression.alias."); 
    19 Manager::Keys keys = aliases.keys(); 
     18OverlayPropertyMap aliases(&MANAGER, "expression.alias."); 
     19PropertyMap::Keys keys = aliases.keys(); 
    2020crash::Expression expr; 
    2121 
     
    8282        "  children  the number of children of the current node (integer)\n" 
    8383        "  index     the numeric index of the current node (integer)\n" 
    84         "  aindex    the absolute index for the current node (string in the form\n" 
     84        "  absindex  the absolute index for the current node (string in the form\n" 
    8585        "            <n>.<m>.<o> ...)\n" 
    8686        "  depth     the numeric depth of the current node (integer)\n" 
     
    258258                        if (ev == "children") return (double)item->children(); 
    259259                        if (ev == "index") return (double)item->index(); 
    260                         if (ev == "aindex") return (string)item->aindex(); 
     260                        if (ev == "absindex") return (string)item->absIndex(); 
    261261                        if (ev == "depth") return (double)item->depth(); 
    262262                        if (ev == "true") return 1.0; 
  • todo2/trunk/src/Item.cc

    r302 r304  
    132132} 
    133133 
    134 Item::Index Item::aindex() const { 
     134Item::Index Item::absIndex() const { 
    135135Index index; 
    136136 
     
    141141} 
    142142 
    143 /*void Item::vacuum(bool clean) { 
    144         for (KeyValues::iterator i = mkv.begin(); i != mkv.end(); ++i) 
    145                 i->second.dirty(clean); 
    146 } 
    147  
    148 void Item::vacuum(std::string key, bool clean) { 
    149         if (mkv.find(key) == mkv.end()) throw Exception(_("can't clean non-existent key '") + key + "'"); 
    150         mkv[key].dirty(clean); 
    151 } 
    152  
    153 bool Item::dirty(std::string key) const { 
    154 KeyValues::const_iterator i = mkv.find(key); 
    155         if (i == mkv.end()) 
    156                 throw Exception(_("non-existent key '") + key + "'"); 
    157         return i->second.dirty(); 
    158 }*/ 
    159  
    160143bool Item::dirty() const { 
    161         for (KeyValues::const_iterator i = mkv.begin(); i != mkv.end(); ++i
    162                 if (i->second.dirty()) return true; 
     144        if (COWPropertyMap::dirty()
     145                return true; 
    163146        if (child()) return child()->dirty(); 
    164147        if (next()) return next()->dirty(); 
  • todo2/trunk/src/Item.h

    r302 r304  
    22#define ITEM_H__ 
    33 
    4 #include <time.h> 
    54#include <stdexcept> 
    65#include <string> 
    76#include <vector> 
    87#include <map> 
     8#include <ctime> 
    99#include <crash/Tree.h> 
    1010#include <crash/Exception.h> 
     
    1616/**     An Item represents a single entry in the devtodo database. */ 
    1717 
    18 class Item : public crash::Tree<Item>, public PropertyMap { 
     18class Item : public crash::Tree<Item>, public COWPropertyMap { 
    1919        public : 
    2020                SUBCLASS_DEFAULT_EXCEPTION 
     
    5252 
    5353                /**     Find root of tree */ 
    54                 Item *root() { return parent() ? parent() : this; } 
     54                Item *root() { return crash::Tree<Item>::parent() ? crash::Tree<Item>::parent() : this; } 
    5555                /**     Find root of tree */ 
    56                 const Item *root() const { return parent() ? parent() : this; } 
     56                const Item *root() const { return crash::Tree<Item>::parent() ? crash::Tree<Item>::parent() : this; } 
    5757 
    58                 /* Hierarchy traversal */ 
    59 /*              Item *parent() { return crash::Tree<Item>::parent(); } 
    60                 const Item *parent() const { return crash::Tree<Item>::parent(); } 
    61                 Item *child() { return crash::Tree<Item>::child(); } 
    62                 const Item *child() const { return crash::Tree<Item>::child(); } 
    63                 Item *next() { return crash::Tree<Item>::next(); } 
    64                 Item *prev() { return crash::Tree<Item>::prev(); } 
    65                 const Item *next() const { return crash::Tree<Item>::next(); } 
    66                 const Item *prev() const { return crash::Tree<Item>::prev(); } 
    67                 unsigned children() const { return crash::Tree<Item>::children(); } 
    68                 unsigned depth() const { return crash::Tree<Item>::depth(); }*/ 
    69  
    70                 /*      Hierarchy modification */ 
    71 /*              Item *peer(Item *peer) { return crash::Tree<Item>::peer(peer); } 
    72                 Item *next(Item *next) { return crash::Tree<Item>::next(next); } 
    73                 Item *prev(Item *prev) { return crash::Tree<Item>::prev(prev); }*/ 
    7458                Item *adopt(Item *child) { 
    75                         if (child) propertyParent(this); 
     59                        if (child) child->backing(this); 
    7660                        return crash::Tree<Item>::adopt(child); 
    7761                } 
    78                 Item *detach() { propertyParent(0); return crash::Tree<Item>::detach(); } 
     62                Item *detach() { backing(0); return crash::Tree<Item>::detach(); } 
    7963 
    8064                // Attribute queries 
    8165                ///     Get body of Item 
    82                 const std::string &body() const { return get("body"); } 
     66                std::string body() const { return get("body"); } 
    8367                ///     Get title of Item 
    84                 const std::string &title() const { return get("title"); } 
     68                std::string title() const { return get("title"); } 
    8569                // Scheduling times 
    8670                ///     Get beginning of scheduled time 
     
    125109                Priority priority() const { return static_cast<Priority>(get<int>("priority")); } 
    126110                ///     Return the absolute index to this item. 
    127                 Index aindex() const; 
     111                Index absIndex() const; 
    128112                /** Note that active() is not equivalent to !completed(), because an 
    129113                        item can be inactive until a dependency is satisfied, or it's  
     
    147131                bool dirty() const; 
    148132        private : 
     133                //      Demoted methods we don't want to be used in Item context. 
     134                void flatten(const std::string &) {} 
     135                void flatten() {} 
     136 
    149137                std::vector<Item*> mdeps; 
    150138                bool mfiltered; 
  • todo2/trunk/src/Manager.cc

    r302 r304  
    9191} 
    9292 
    93 Manager::Manager() : mself(0), mfrontend(0), mbackend(0), mprogramname("todo2") { 
     93////////////////////////////// Manager 
     94  
     95 
     96Manager::Manager() : COWPropertyMap(new PropertyMap()), mself(0), mfrontend(0), mbackend(0), mprogramname("todo2") { 
    9497        // Find this time zones offset from UTC 
    9598time_t now = ::time(0); 
     
    118121        /*      Add ourselves to the AddressBook. Hopefully, though, the user will 
    119122                override this with a valid E-Mail address. */ 
    120         if (pw && !defined("addressbook." + string(pw->pw_name) + ".name")) { 
     123        if (pw && !exists("addressbook." + string(pw->pw_name) + ".name")) { 
    121124                set("addressbook." + string(pw->pw_name) + ".name", pw->pw_gecos); 
    122125                set("addressbook." + string(pw->pw_name) + ".contact", "mail:" + string(pw->pw_name) + "@" + hostname()); 
     
    281284                /*      All configuration changes from here on will be written to the  
    282285                        currently open database. */ 
    283                 vacuum(); 
     286                commit(); 
    284287 
    285288                // Final pass before handing off to modules. 
     
    345348                                break; 
    346349                                case DUMPCONFIG : { 
    347                                 Manager::Keys keys = PropertyMap::keys(); 
     350                                Manager::Keys keys = this->keys(); 
    348351 
    349352                                        for (Manager::Keys::iterator i = keys.begin(); i != keys.end(); ++i) 
     
    486489                props.set("id", (*i).substr(9)); 
    487490                props.set("description", get(*i)); 
     491 
     492                mcategories[props.get("id")] = new Category(props); 
    488493        } 
    489494} 
     
    494499        for (Keys::iterator i = ab.begin(); i != ab.end(); ++i) { 
    495500        vector<string> c = split(".", *i); 
    496         PropertyMap props = prefixToPropertyMap("addressbook." + c[1] + "."); 
     501        OverlayPropertyMap props(this, "addressbook." + c[1] + "."); 
    497502 
    498503                props.set("id", c[1]); 
     
    502507 
    503508void Manager::populatePriorities() { 
    504 PropertyMap props = prefixToPropertyMap("priority."); 
    505 PropertyMap::Keys keys = props.keys(); 
    506  
    507         for (PropertyMap::Keys::const_iterator i = keys.begin(); i != keys.end(); ++i) { 
     509OverlayPropertyMap props(this, "priority."); 
     510Keys keys = props.keys(); 
     511 
     512        for (Keys::const_iterator i = keys.begin(); i != keys.end(); ++i) { 
    508513                try { 
    509514                int p = from_string<int>(*i); 
     
    553558} 
    554559 
    555 void Manager::addUser(const PropertyMap &prop) { 
     560void Manager::addUser(const AbstractPropertyMap &prop) { 
    556561        DEBUG(9, "adding user " << prop.get("id") << " to global address book"); 
    557562        musers[prop.get("id")] = new User(prop); 
     
    572577} 
    573578 
    574 void Manager::addCategory(const PropertyMap &prop) { 
     579void Manager::addCategory(const AbstractPropertyMap &prop) { 
    575580        mcategories[prop.get("id")] = new Category(prop); 
    576581} 
  • todo2/trunk/src/Manager.h

    r302 r304  
    1717 
    1818/** 
    19         Manager is ... (TODO: complete description) 
     19        Manager is the core singleton class for DT2. 
     20 
     21        NOTE: Manager uses COWPropertyMap rather than the more basic PropertyMap in 
     22        order to write "dirty" properties to the current database, allowing 
     23        per-database overrides. 
    2024*/ 
    21  
    22 class Manager : public PropertyMap, public crash::Singleton<Manager> { 
     25class Manager : public COWPropertyMap, public crash::Singleton<Manager> { 
    2326        public : 
    2427                SUBCLASS_DEFAULT_EXCEPTION 
     
    4346                /**     Return the User entry for the current user. */ 
    4447                const User *self() { return mself; } 
    45                 void addUser(const PropertyMap &prop); 
     48                void addUser(const AbstractPropertyMap &prop); 
    4649                const User *getUser(std::string id); 
    4750                User::List allUsers() const; 
    4851 
    4952                ///     Add a new category 
    50                 void addCategory(const PropertyMap &prop); 
     53                void addCategory(const AbstractPropertyMap &prop); 
    5154                const Category *getCategory(std::string name) const; 
    5255                const Category *defaultCategory() const; 
  • todo2/trunk/src/PropertyMap.cc

    r302 r304  
    11#include <fstream> 
    22#include <iostream> 
     3#include <algorithm> 
     4#include <set> 
    35#include "PropertyMap.h" 
    46#include "main.h" 
     
    79using namespace crash; 
    810 
    9 PropertyMap::Keys PropertyMap::keys() const { 
    10 Keys out; 
    11  
    12         for (KeyValues::const_iterator i = mkv.begin(); i != mkv.end(); ++i) 
    13                 out.push_back(i->first); 
    14         return out; 
    15 
    16  
    17 ///     Return all keys that match glob 
    18 PropertyMap::Keys PropertyMap::keys(const std::string &glob) const { 
    19 Keys out; 
    20  
    21         for (KeyValues::const_iterator i = mkv.begin(); i != mkv.end(); ++i) 
    22                 if (crash::glob(glob, i->first)) 
    23                         out.push_back(i->first); 
    24         return out; 
    25 
    26  
    27 PropertyMap PropertyMap::prefixToPropertyMap(const std::string &prefix) { 
    28 PropertyMap props(this); 
    29  
    30         for (KeyValues::const_iterator i = mkv.begin(); i != mkv.end(); ++i) 
    31                 if (i->first.compare(0, prefix.size(), prefix) == 0) 
    32                         props.set(i->first.substr(prefix.size()), i->second.get()); 
    33         return props; 
    34 
    35  
    36 void PropertyMap::load(const std::string &file) { 
     11void AbstractPropertyMap::load(const std::string &file) { 
    3712ifstream in(file.c_str()); 
    3813string line, section; 
     
    6237} 
    6338 
    64 void PropertyMap::vacuum(const std::string &key, bool state) { 
    65         mkv[key].dirty(!state); 
     39AbstractPropertyMap::Keys COWPropertyMap::keys() const { 
     40std::set<std::string> transient; 
     41Keys skeys = mstore->keys(); 
     42Keys pkeys; 
     43        if (mbacking) pkeys = mbacking->keys(); 
     44        transient.insert(skeys.begin(), skeys.end()); 
     45        transient.insert(pkeys.begin(), pkeys.end()); 
     46Keys out(transient.size()); 
     47        std::copy(transient.begin(), transient.end(), out.begin()); 
     48        return out; 
    6649} 
    6750 
    68 void PropertyMap::vacuum(bool state) { 
    69         for (KeyValues::iterator i = mkv.begin(); i != mkv.end(); ++i) 
    70                 i->second.dirty(!state); 
    71 
    72  
    73 bool PropertyMap::dirty(const std::string &key) { 
    74         return mkv[key].dirty(); 
    75 
    76  
    77 PropertyMap::Keys PropertyMap::dirty() { 
    78 Keys out; 
    79  
    80         for (KeyValues::iterator i = mkv.begin(); i != mkv.end(); ++i) 
    81                 if (i->second.dirty()) 
    82                         out.push_back(i->first); 
     51AbstractPropertyMap::Keys COWPropertyMap::keys(const std::string &glob) const { 
     52std::set<std::string> transient; 
     53Keys skeys = mstore->keys(glob); 
     54Keys pkeys; 
     55        if (mbacking) pkeys = mbacking->keys(glob); 
     56        transient.insert(skeys.begin(), skeys.end()); 
     57        transient.insert(pkeys.begin(), pkeys.end()); 
     58Keys out(transient.size()); 
     59        std::copy(transient.begin(), transient.end(), out.begin()); 
    8360        return out; 
    8461} 
  • todo2/trunk/src/PropertyMap.h

    r302 r304  
    1 #ifndef BASICCONFIG_H__ 
    2 #define BASICCONFIG_H__ 
     1#ifndef PROPERTYMAP_H__ 
     2#define PROPERTYMAP_H__ 
    33 
    44#include <crash/Exception.h> 
    55#include <crash/StringManip.h> 
    66#include <crash/TypeResolver.h> 
    7 #include <crash/smart_ptr.h> 
    87#include <map> 
    9  
    10 /** 
    11         PropertyMap is a generic key/value pair storage mechanism. Various queries 
    12         can be used against the store to retrieve sets of keys. 
    13  
    14         When a configuration key is created it is marked as dirty. 
    15  
    16         There are two situations where PropertyMap is used: 
    17          
    18                 1.      Each Item in the Database is a PropertyMap with its parent Item 
    19                         also the parent PropertyMap. This lets properties be inherited 
    20                         through the Database hierarchy. 
    21  
    22                 2.      The global configuration, rooted in Manager. Each Module has its 
    23                         own "namespace" uniquely prefixed by its name "<module>.". 
    24  
    25         The second of these two implies that dirty keys, which should be stored in 
    26         the Database when it is committed, must be obtainable through the root map 
    27         even if they are in sub-maps. 
    28 */ 
    29  
    30 class PropertyMap { 
     8#include <iostream> 
     9#include <vector> 
     10#include <string> 
     11 
     12class AbstractProperty { 
     13        public : 
     14                virtual ~AbstractProperty() {} 
     15 
     16                virtual std::string get() const = 0; 
     17                virtual void set(const std::string &value) = 0; 
     18        private : 
     19}; 
     20 
     21class ValueProperty : public AbstractProperty { 
     22        public : 
     23                ValueProperty() {} 
     24                ValueProperty(const std::string &value) : mvalue(value) {} 
     25 
     26                virtual std::string get() const { return mvalue; } 
     27                virtual void set(const std::string &value) { mvalue = value; } 
     28        private : 
     29                std::string mvalue; 
     30}; 
     31 
     32template <typename T> 
     33class ReferenceProperty : public AbstractProperty { 
     34        public : 
     35                ReferenceProperty(T &value) : mvalue(value) {} 
     36 
     37                virtual std::string get() const { return crash::to_string(mvalue); } 
     38                virtual void set(const std::string &value) { mvalue = crash::from_string<T>(value); } 
     39        private : 
     40                T &mvalue; 
     41}; 
     42 
     43class AbstractPropertyMap { 
    3144        public : 
    3245                SUBCLASS_DEFAULT_EXCEPTION 
    3346                SUBCLASS_NAMED_EXCEPTION(InvalidValue, Exception) 
    3447 
    35                 class Property { 
    36                         public : 
    37                                 Property() : mdirty(true) {} 
    38                                 explicit Property(const std::string &value) : mvalue(value), mdirty(true) {} 
    39  
    40                                 void dirty(bool state) { mdirty = state; } 
    41                                 bool dirty() const { return mdirty; } 
    42  
    43                                 const std::string &get() const { return mvalue; } 
    44                                 void set(const std::string &value) { mvalue = value; mdirty = true; } 
    45                         private : 
    46                                 std::string mvalue; 
    47                                 bool mdirty; 
    48                 }; 
    49  
    50                 typedef std::map<std::string, Property> KeyValues; 
    51                 typedef KeyValues::const_iterator const_iterator; 
    52                 typedef KeyValues::iterator iterator; 
    53  
    5448                typedef std::vector<std::string> Keys; 
    5549 
    56                 explicit PropertyMap(PropertyMap *parent = 0) : mparentpm(parent) {} 
    57                 virtual ~PropertyMap() {} 
    58  
    59                 /*      XXX     This is called "propertyParent" due to ambiguities with multiple 
    60                                 inheritance when joined with crash::Tree<T>. XXX */ 
    61                 PropertyMap *propertyParent() { return mparentpm; } 
    62                 const PropertyMap *propertyParent() const { return mparentpm; } 
    63                 PropertyMap *propertyParent(PropertyMap *parent) { mparentpm = parent; return parent; } 
    64  
    65                 ///     Return all keys 
    66                 virtual Keys keys() const; 
    67                 ///     Return all keys that match glob 
    68                 virtual Keys keys(const std::string &glob) const; 
    69  
    70                 ///     Query the existence of a key/value. 
    71                 virtual bool exists(const std::string &key) const { 
    72                         return mkv.find(key) != mkv.end(); 
    73                 } 
    74  
    75                 ///     Return the value of a key. 
    76                 virtual const std::string & get(const std::string &key) const { 
    77                 KeyValues::const_iterator i = mkv.find(key); 
    78  
    79                         if (i == mkv.end()) throw Exception("no such key '" + key + "'"); 
    80                         return i->second.get(); 
    81                 } 
    82  
    83                 ///     Set the value of a key. 
    84                 virtual void set(const std::string &key, const std::string &value) { 
    85                 KeyValues::iterator i = mkv.find(key); 
    86                         if (i == mkv.end()) 
    87                                 mkv[key] = Property(value); 
    88                         else 
    89                                 i->second.set(value); 
    90                 } 
    91  
    92                 ///     Destroy a key/value pair. 
    93                 virtual void unset(const std::string &key) { 
    94                 KeyValues::iterator i = mkv.find(key); 
    95  
    96                         if (i != mkv.end()) 
    97                                 mkv.erase(i); 
    98                 } 
    99  
    100                 /********************************************************************** 
    101                         Most methods from here on are for convenience only 
    102                 **********************************************************************/ 
    103  
    104                 ///     Query the existence of a key/value. 
    105                 bool isset(const std::string &key) const { return exists(key); } 
    106  
    107                 ///     Query the existence of a key/value. 
    108                 bool defined(const std::string &key) const { return exists(key); } 
    109  
    110                 /**     Return the value of a key. If the key does not exist, return a 
    111                         default value. */ 
    112                 const std::string &get(const std::string &key, const std::string &def) const { 
    113                         if (!exists(key)) return def; 
    114                         return get(key); 
    115                 } 
    116  
    117                 /**     Return the value of a key as a specific type. If the key does not 
    118                         exist throw an Exception. */ 
     50                virtual ~AbstractPropertyMap() {} 
     51 
     52                ///     Abstract "get" method 
     53                virtual std::string vget(const std::string &key) const = 0; 
     54                virtual bool exists(const std::string &key) const = 0; 
     55                /**     Set a value to the given property. This will overwrite the existing 
     56                        property. */ 
     57                virtual void pset(const std::string &key, AbstractProperty *property) = 0; 
     58                /**     Set a value to a basic string. If the key exists its set() method 
     59                        will be used. */ 
     60                virtual void vset(const std::string &key, const std::string &value) = 0; 
     61                virtual void unset(const std::string &key) = 0; 
     62                virtual void clear() = 0; 
     63                ///     Retrieve list of keys 
     64                virtual Keys keys() const = 0; 
     65                /**     Retrieve list of keys matching glob. This should probably be  
     66                        overloaded for efficiency. */ 
     67                virtual Keys keys(const std::string &glob) const { 
     68                Keys k = keys(); 
     69                Keys out; 
     70 
     71                        for (Keys::const_iterator i = k.begin(); i != k.end(); ++i) 
     72                                if (crash::glob(glob, *i)) 
     73                                        out.push_back(*i); 
     74                        return out; 
     75                } 
     76                virtual unsigned size() const { 
     77                        return keys().size(); 
     78                } 
     79 
     80                /********************************************************************* 
     81                        Convenience functions. 
     82                *********************************************************************/ 
     83                void load(const std::string &filename); 
     84 
     85                std::string get(const std::string &key, const std::string &defaultvalue) const { 
     86                        if (exists(key)) 
     87                                return vget(key); 
     88                        return defaultvalue; 
     89                } 
     90                inline void set(const std::string &key, const std::string &value) { 
     91                        vset(key, value); 
     92                } 
     93 
    11994                template <typename T> 
    120                 const T get(const std::string &key) const { 
    121  
    122                         if (!exists(key)) throw Exception("no such key '" + key + "'"); 
     95                inline void bind(const std::string &key, T &value) { 
     96                        pset(key, new ReferenceProperty<T>(value)); 
     97                } 
     98 
     99                template <typename T> 
     100                inline void set(const std::string &key, const T &value) { 
     101                        vset(key, crash::to_string(value)); 
     102                } 
     103 
     104                inline std::string get(const std::string &key) const { 
     105                        return vget(key); 
     106                } 
     107 
     108                template <typename T> 
     109                inline T get(const std::string &key) const { 
    123110                        try { 
    124                                 return crash::from_string<T>(get(key)); 
     111                                return crash::from_string<T>(vget(key)); 
    125112                        } catch (crash::BadCast) { 
    126                                 throw Exception("could not convert property " + key + " with value '" + get(key) + "' to " + crash::type2string<T>()); 
     113                                throw Exception("could not convert property " + key + " with value '" + vget(key) + "' to " + crash::type2string<T>()); 
    127114                        } 
    128115                } 
    129116 
    130                 /**     Return the value of a key as a specific type. If the key does not 
    131                         exist return a default value. */ 
    132117                template <typename T> 
    133                 const T get(const std::string &key, const T &def) const { 
    134                         if (!exists(key)) return def; 
     118                inline T get(const std::string &key, const T &defaultvalue) const { 
    135119                        try { 
    136                                 return crash::from_string<T>(get(key)); 
     120                                if (exists(key)) 
     121                                        return crash::from_string<T>(vget(key)); 
     122                                return defaultvalue; 
    137123                        } catch (crash::BadCast) { 
    138                                 throw Exception("could not convert property " + key + " with value '" + get(key) + "' to " + crash::type2string<T>()); 
     124                                throw Exception("could not convert property " + key + " with value '" + vget(key) + "' to " + crash::type2string<T>()); 
    139125                        } 
    140126                } 
    141127 
    142                 ///     Set the value of a key to a specific type. 
    143                 template <typename T> 
    144                 void set(const std::string &key, const T &value) { 
    145                         set(key, crash::to_string(value)); 
    146                 } 
    147  
    148                 ///     Set the value of a key. 
    149128                void setUnlessDefined(const std::string &key, const std::string &value) { 
    150129                        if (!exists(key)) set(key, value); 
    151130                } 
    152131 
    153                 ///     Set the value of a key to a specific type. 
    154132                template <typename T> 
    155133                void setUnlessDefined(const std::string &key, const T &value) { 
    156                         if (!exists(key)) set(key, crash::to_string(value)); 
    157                 } 
    158  
    159                 /**     Return a prefixed set as a new PropertyMap<>. 
    160                         eg. Given the following properties.. 
    161  
    162                                 user.athomas.name = Alec Thomas 
    163                                 user.athomas.contact = mail:alec@swapoff.org 
    164  
    165                         This code.. 
    166  
    167                                 PropertMap props = conf.prefixToPropertyMap("user.athomas."); 
    168  
    169                         Would yield the following PropertyMap.. 
    170  
    171                                 name = Alec Thomas 
    172                                 contact = mail:alec@swapoff.org 
    173                 */ 
    174                 PropertyMap prefixToPropertyMap(const std::string &prefix); 
    175  
    176                 ///     Load a property map from a configuration file 
    177                 void load(const std::string &file); 
    178  
    179                 ///     Mark all keys as dirty/not dirty 
    180                 void vacuum(bool state = true); 
    181                 ///     Mark a key as dirty/not dirty 
    182                 void vacuum(const std::string &key, bool state = true); 
    183                 ///     Return the dirty flag for a key 
    184                 bool dirty(const std::string &key); 
    185                 ///     Return list of dirty keys 
    186                 Keys dirty(); 
    187  
     134                        if (!exists(key)) set(key, value); 
     135                } 
    188136        protected : 
    189                 PropertyMap *mparentpm; 
    190                 KeyValues mkv; 
     137                AbstractPropertyMap() {} 
     138}; 
     139 
     140/** 
     141        PropertyMap maintains a set of properties mapped by key strings. 
     142*/ 
     143class PropertyMap : public AbstractPropertyMap { 
     144        public : 
     145                PropertyMap() {} 
     146                ~PropertyMap() { 
     147                        for (Properties::iterator i = mproperties.begin(); i != mproperties.end(); ++i) 
     148                                delete i->second; 
     149                } 
     150 
     151                ///     A PropertyMap can be a copy of an AbstractPropertyMap 
     152                PropertyMap(const AbstractPropertyMap &copy) { 
     153                Keys keys = copy.keys(); 
     154 
     155                        for (Keys::iterator i = keys.begin(); i != keys.end(); ++i) 
     156                                set(*i, copy.vget(*i)); 
     157                } 
     158                ///     A PropertyMap can be a copy of an AbstractPropertyMap 
     159                PropertyMap &operator = (const AbstractPropertyMap &copy) { 
     160                Keys keys = copy.keys(); 
     161 
     162                        for (Properties::iterator i = mproperties.begin(); i != mproperties.end(); ++i) 
     163                                delete i->second; 
     164                        mproperties.clear(); 
     165                        for (Keys::iterator i = keys.begin(); i != keys.end(); ++i) 
     166                                set(*i, copy.vget(*i)); 
     167                        return *this; 
     168                } 
     169 
     170                virtual unsigned size() const { 
     171                        return mproperties.size(); 
     172                } 
     173 
     174                std::string vget(const std::string &key) const { 
     175                Properties::const_iterator i = mproperties.find(key); 
     176 
     177                        if (i == mproperties.end()) 
     178                                throw Exception("no property named '" + key + "'"); 
     179                        return i->second->get(); 
     180                } 
     181                bool exists(const std::string &key) const { 
     182                        return mproperties.find(key) != mproperties.end(); 
     183                } 
     184                void pset(const std::string &key, AbstractProperty *property) { 
     185                Properties::iterator i = mproperties.find(key); 
     186 
     187                        if (i != mproperties.end()) { 
     188                                delete i->second; 
     189                                i->second = property; 
     190                        } else 
     191                                mproperties[key] = property; 
     192                } 
     193                void vset(const std::string &key, const std::string &value) { 
     194                Properties::iterator i = mproperties.find(key); 
     195 
     196                        if (i == mproperties.end()) 
     197                                mproperties[key] = new ValueProperty(value); 
     198                        else 
     199                                i->second->set(value); 
     200 
     201                } 
     202                virtual void unset(const std::string &key) { 
     203                        mproperties.erase(key); 
     204                } 
     205                virtual Keys keys() const { 
     206                Keys out; 
     207 
     208                        for (Properties::const_iterator i = mproperties.begin(); i != mproperties.end(); ++i) 
     209                                out.push_back(i->first); 
     210                        return out; 
     211                } 
     212                ///     Retrieve list of keys matching glob 
     213                virtual Keys keys(const std::string &glob) const { 
     214                Keys out; 
     215 
     216                        for (Properties::const_iterator i = mproperties.begin(); i != mproperties.end(); ++i) 
     217                                if (crash::glob(glob, i->first)) 
     218                                        out.push_back(i->first); 
     219                        return out; 
     220                } 
     221 
     222                void clear() { 
     223                        for (Properties::iterator i = mproperties.begin(); i != mproperties.end(); ++i) 
     224                                delete i->second; 
     225                        mproperties.clear(); 
     226                } 
     227 
     228        private : 
     229                typedef std::map<std::string, AbstractProperty*> Properties; 
     230 
     231                Properties mproperties; 
     232}; 
     233 
     234/** 
     235        The behaviours of COWPropertyMap are thus: 
     236 
     237        * Property reads transparently traverse up the hierarchy, returning the 
     238          first key value that matches. 
     239        * Setting a property will create a new property in the current map, 
     240          "overlaying" properties higher up the hierarchy. 
     241        * Actual values are stored in a "store" map. 
     242        * Backing store can be null. In this situation, "flatten" 
     243 
     244        Essentially, this behaviour implements a copy-on-write PropertyMap. 
     245*/ 
     246class COWPropertyMap : public AbstractPropertyMap { 
     247        public : 
     248                ///     Construct a new COW property map. store is owned by the object. 
     249                COWPropertyMap(AbstractPropertyMap *backing = 0, AbstractPropertyMap *store = new PropertyMap()) : mbacking(backing), mstore(store) {} 
     250                ~COWPropertyMap() { 
     251                        delete mstore; 
     252                } 
     253 
     254                std::string vget(const std::string &key) const { 
     255                        if (mstore->exists(key)) 
     256                                return mstore->vget(key); 
     257                        if (mbacking) 
     258                                return mbacking->vget(key); 
     259                        throw Exception("no property named '" + key + "'"); 
     260                } 
     261                bool exists(const std::string &key) const { 
     262                        if (mstore->exists(key)) 
     263                                return true; 
     264                        if (mbacking) 
     265                                return mbacking->exists(key); 
     266                        return false; 
     267                } 
     268                void pset(const std::string &key, AbstractProperty *property) { 
     269                        mstore->pset(key, property); 
     270                } 
     271                void vset(const std::string &key, const std::string &value) { 
     272                        mstore->vset(key, value); 
     273                } 
     274                void unset(const std::string &key) { 
     275                        mstore->unset(key); 
     276                        if (mbacking) 
     277                                mbacking->unset(key); 
     278                } 
     279                /*      This is probably not the most efficient. It basically extracts 
     280                        both sets of keys, inserts them into a set then converts the set 
     281                        into a vector before returning it. */ 
     282                Keys keys() const; 
     283                Keys keys(const std::string &glob) const; 
     284 
     285                /*      TODO: Make an efficient size() method for COWPropertyMap 
     286                        unsigned size() const; */ 
     287 
     288                void clear() { 
     289                        mstore->clear(); 
     290                        if (mbacking) 
     291                                mbacking->clear(); 
     292                } 
     293 
     294                ///     Return list of COWed keys 
     295                Keys dirtyKeys() const { 
     296                        return mstore->keys(); 
     297                } 
     298                ///     Return list of COWed keys 
     299                Keys dirtyKeys(const std::string &glob) const { 
     300                        return mstore->keys(glob); 
     301                } 
     302 
     303                unsigned dirty() const { 
     304                        return mstore->size(); 
     305                } 
     306 
     307                bool dirty(const std::string &key) const { 
     308                        return mstore->exists(key); 
     309                } 
     310 
     311                ///     Merge COWed keys into backing. 
     312                void commit() { 
     313                Keys keys = mstore->keys(); 
     314 
     315                        if (!mbacking)