--- /dev/null
+/** @file
+ *
+ * @author Reinhard Pfau \<Reinhard.Pfau@intra2net.com\>
+ *
+ * @copyright © Copyright 2007-2008 by Intra2net AG
+ * @license commercial
+ *
+ * info@intra2net.com
+ */
+
+#ifndef __I2N_CONFIGDATA_HPP__
+#define __I2N_CONFIGDATA_HPP__
+
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <sstream>
+
+
+namespace I2n
+{
+
+
+
+/**
+ * key-value data storage with some additional features.
+ * This class is meant to hold data from config files.
+ *
+ * Basic ideas:
+ *
+ * - the keys are remembered in the order they are put into the list
+ * - each key can hold multiple values
+ * - the values for each key are remembered in the order they were added
+ * - if a single value for a key is requested than the last value is returned.
+ * .
+ * @tparam KeyType the type for the keys
+ * @tparam ValueType the type for the values.
+ */
+template <
+ class KeyType,
+ class ValueType
+>
+class KeyValueData
+{
+ public:
+
+ typedef std::vector<KeyType> KeyListType;
+ typedef std::vector<ValueType> ValueListType;
+ typedef std::map<KeyType, ValueListType> StorageType;
+
+ public:
+
+ /**
+ * @brief adaptor class for accessing values within the list for a given key.
+ *
+ * This is returned by KeyValueList::operator[](const std::string&) for
+ * more convenient access.
+ *
+ * This class is designed to be used in transient contexts.
+ */
+ class ValueAdaptor
+ {
+ // make parent class my friend since the constructors are private!
+ friend class KeyValueData;
+
+ public:
+
+ /**
+ * cast operator for ValueType.
+ * @return the (last) value for the key
+ */
+ operator ValueType () const
+ {
+ return m_kv.getValue(m_key);
+ } // eo operator ValueType() const
+
+
+ /**
+ * cast operator for ValueListType.
+ * @return the value list for the key.
+ */
+ operator ValueListType () const
+ {
+ return m_kv.getValues(m_key);
+ } // eo operator ValueListType() const
+
+
+ /**
+ * performs setValue().
+ * @param value the value which should be set for the key.
+ * @return reference to this (for chaining).
+ */
+ ValueAdaptor& operator = (const ValueType& value)
+ {
+ m_kv.setValue(m_key,value);
+ return *this;
+ } // eo operator =(const ValueType&)
+
+
+ /**
+ * performs setValues()
+ * @param values the new values (/value list) which should be set for the key.
+ * @return reference to this (for chaining).
+ */
+ ValueAdaptor& operator = (const ValueListType& values)
+ {
+ m_kv.setValues(m_key, values);
+ return *this;
+ } // eo operator =(const ValueListType&);
+
+
+ /**
+ * performs addValue().
+ * @param value the value which should be added to the value list for the key,
+ * @return reference to this (for chaining).
+ */
+ ValueAdaptor& operator += (const ValueType& value)
+ {
+ m_kv.addValue(m_key,value);
+ return *this;
+ } // eo operator +=(const ValueType&)
+
+
+ /**
+ * performs addValues().
+ * @param values the new values (/value list) which should be added to the value list for the key.
+ * @return reference to this (for chaining).
+ */
+ ValueAdaptor& operator += (const ValueListType& values)
+ {
+ m_kv.addValues(m_key, values);
+ return *this;
+ } // eo operator +=(const ValueListType)
+
+
+ private:
+
+ ValueAdaptor(KeyValueData& kv, const KeyType& key)
+ : m_kv(kv)
+ , m_key(key)
+ {
+ }
+
+ ValueAdaptor(const ValueAdaptor& other)
+ : m_kv(other.m_kv)
+ , m_key(other.m_key)
+ {
+ }
+
+ ValueAdaptor& operator =(const ValueAdaptor&);
+
+ private:
+ KeyValueData& m_kv;
+ KeyType m_key;
+ }; // eo class ValueAdaptor
+
+ public:
+
+ KeyValueData()
+ {
+ } // eo KeyValueData
+
+
+
+ ~KeyValueData()
+ {
+ } // eo ~KeyValueData
+
+
+
+ /**
+ * set a value for a key.
+ * This erases an existing value list for that key and set's a new (single) value.
+ * @param key the key.
+ * @param value the value.
+ */
+ KeyValueData& setValue(const KeyType& key, const ValueType& value)
+ {
+ typename StorageType::iterator it= m_storage.find(key);
+ if (it == m_storage.end())
+ {
+ m_key_list.push_back(key);
+ m_storage[key].push_back(value);
+ }
+ else
+ {
+ it->second.clear();
+ it->second.push_back(value);
+ }
+ return *this;
+ } // eo setValue(const KeyType&,const ValueType&)
+
+
+
+ /**
+ * set a new value list for a key.
+ * Erases the key when the new value list is empty.
+ * Else the value list for the key is replaced with the new values.
+ * @param key the key
+ * @param values the new value list.
+ */
+ KeyValueData& setValues(const KeyType& key, const ValueListType& values)
+ {
+ typename StorageType::iterator it= m_storage.find(key);
+ if (it == m_storage.end())
+ {
+ m_key_list.push_back(key);
+ m_storage[key]= values;
+ }
+ else
+ {
+ if (values.empty())
+ {
+ deleteKey(key);
+ }
+ else
+ {
+ it->second= values;
+ }
+ }
+ return *this;
+ } // eo setValues(const KeyType&,const ValueListType&)
+
+
+
+ /**
+ * adds a new value to the value list of a key.
+ *
+ * @param key the key
+ * @param value the new value
+ */
+ KeyValueData& addValue(const KeyType& key, const ValueType& value)
+ {
+ typename StorageType::iterator it= m_storage.find(key);
+ if (it == m_storage.end())
+ {
+ m_key_list.push_back(key);
+ m_storage[key].push_back(value);
+ }
+ else
+ {
+ it->second.push_back(value);
+ }
+ return *this;
+ } // eo addValue(const KeyType&,const ValueType&)
+
+
+
+ /**
+ * adds new values to the value list of a key.
+ *
+ * @param key the key
+ * @param values the new value list
+ */
+ KeyValueData& addValues(const KeyType& key, const ValueListType& values)
+ {
+ typename StorageType::iterator it= m_storage.find(key);
+ if (it == m_storage.end())
+ {
+ m_key_list.push_back(key);
+ m_storage[key]= values;
+ }
+ else
+ {
+ std::copy(values.begin(), values.end(), std::back_inserter(it->second));
+ }
+ return *this;
+ } // eo addValues(const KeyType&,const ValueListType&)
+
+
+
+ /**
+ * @brief removes a value from a value list of a key.
+ * @param key the key.
+ * @param value the value which should be removed (once).
+ * @return self reference.
+ * @note only the first occurance of the value is removed!
+ */
+ KeyValueData& removeValue(const KeyType& key, const ValueType& value)
+ {
+ typename StorageType::iterator it= m_storage.find(key);
+ if (it != m_storage.end())
+ {
+ for( typename ValueListType::iterator it_val= it->second.begin();
+ it_val != it->second.end();
+ ++it_val)
+ {
+ if ( *it_val == value)
+ {
+ it->second.erase(it_val);
+ break;
+ }
+ }
+ if (it->second.empty())
+ {
+ deleteKey( key );
+ }
+ }
+ return *this;
+ } // eo removeValue
+
+
+ /**
+ * retrieve the (single) value for a key.
+ * If the value list of the key has multiple values, the last one returned.
+ * @param key the key
+ * @param[out] value the resulting value if found
+ * @return @a true iff the key has a value (i,e, the key exits).
+ */
+ bool getValue(const KeyType& key, ValueType& value) const
+ {
+ typename StorageType::const_iterator it= m_storage.find(key);
+ if (it == m_storage.end())
+ {
+ return false;
+ }
+ else
+ {
+ value= it->second.back();
+ return true;
+ }
+ } // eo getValue(const KeyType&, ValueType&)
+
+
+
+ /**
+ * retrieve the value list for a key.
+ * @param key the key
+ * @param[out] values the resulting value list if found
+ * @return @a true iff the key has values (i,e, the key exits).
+ */
+ bool getValues(const KeyType& key, ValueListType& values) const
+ {
+ typename StorageType::const_iterator it= m_storage.find(key);
+ if (it == m_storage.end())
+ {
+ return false;
+ }
+ else
+ {
+ values= it->second;
+ return true;
+ }
+ } // eo getValues(const KeyType&, ValueListType&)
+
+
+
+ /**
+ * retrieve the (single) value for a key.
+ * If the value list of the key has multiple values, the last one returned.
+ * @param key the key
+ * @return the value of the key (default contructed value if key not found)
+ */
+ ValueType getValue(const KeyType& key) const
+ {
+ ValueType value;
+ getValue( key, value);
+ return value;
+ } // eo getValue(const KeyType&)
+
+
+
+ /**
+ * retrieve the value list for a key.
+ * @param key the key
+ * @return the value of the key (empty if key not found)
+ */
+ ValueListType getValues(const KeyType& key) const
+ {
+ ValueListType values;
+ getValues( key, values);
+ return values;
+ } // eo getValues(const KeyType&)
+
+
+
+ /**
+ * retrieve the (single) value for a key.
+ * If the value list of the key has multiple values, the last one returned.
+ * If the key wasn't found (i.e. no value) then the default value is returned.
+ * @param key the key
+ * @param default_value the value which is returned if the key wasn't found.
+ * @return the value of the key (default_value if key not found)
+ */
+ ValueType getValueOrDefault(const KeyType& key, const ValueType& default_value) const
+ {
+ ValueType value;
+ if (getValue( key, value))
+ {
+ return value;
+ }
+ return default_value;
+ } // eo getValueOrDefault(const KeyType&,const ValueType&)
+
+
+
+ /**
+ * removes a key (and it's values) from the list.
+ * @param key the key.
+ */
+ void deleteKey(const KeyType& key)
+ {
+ typename StorageType::iterator it= m_storage.find(key);
+ if (it == m_storage.end())
+ {
+ return;
+ }
+ m_storage.erase(it);
+ typename KeyListType::iterator itk= std::find(m_key_list.begin(), m_key_list.end(), key);
+ if (itk != m_key_list.end())
+ {
+ m_key_list.erase(itk);
+ }
+ } // eo deleteKey(const KeyType&)
+
+
+
+ /**
+ * returns if a key exists in the list.
+ * @param key the key which should be looked for.
+ * @return @a true if the key is in the list.
+ */
+ bool hasKey(const KeyType& key) const
+ {
+ return (m_storage.find(key) != m_storage.end());
+ } // eo hasKey(const KeyType&)
+
+
+
+ /**
+ * delivers the list of keys.
+ * @param[out] result_list the key list
+ */
+ void getKeys(KeyListType& result_list) const
+ {
+ result_list= m_key_list;
+ } // eo getKeys(KeyListType&) const
+
+
+
+ /**
+ * deliviers the list of keys.
+ * @return the list of keys.
+ */
+ KeyListType getKeys() const
+ {
+ return m_key_list;
+ } // eo getKeys() const
+
+
+
+ /**
+ * returns if the list is empty (i.e. has no keys)
+ * @return @a true iff the list has no keys (and values).
+ */
+ bool empty() const
+ {
+ return m_key_list.empty();
+ } // eo empty
+
+
+
+ /**
+ * @brief index operator for more readable access.
+ * @param key the key which should be indexed.
+ * @return an instance of ValueAdaptor initialized with the key.
+ */
+ ValueAdaptor operator [] (const KeyType& key)
+ {
+ return ValueAdaptor(*this, key);
+ } // eo operator[](const KeyType&)
+
+
+ /**
+ * @brief returns if the current iunstance is equal to another one (of the same type).
+ * @param rhs the orher instance to compare with.
+ * @return @a true iff the two instances are equal.
+ */
+ bool operator==( const KeyValueData& rhs)
+ {
+ return m_key_list == rhs.m_key_list and m_storage == rhs.m_storage;
+ } // eo operator==(const KeyValueData&)
+
+
+ /**
+ * @brief clears the list.
+ *
+ * Well, it just deletes all entries.
+ */
+ void clear()
+ {
+ m_storage.clear();
+ m_key_list.clear();
+ } // eo clear
+
+
+ /**
+ * @brief exchanges the internal data with another instance.
+ * @param other
+ */
+ void swap(KeyValueData& other)
+ {
+ if (this != &other)
+ {
+ m_storage.swap( other.m_storage );
+ m_key_list.swap( other.m_key_list );
+ }
+ } // eo swap(KeyValueData&)
+
+
+ protected:
+
+ /**
+ * returns a reference to the last value of the value list for a given key; creating it if the key doesn't
+ * already exist.
+ * This method is only for derived classes to optimize accessing of the values.
+ * @param key the key for which the reference is needed.
+ * @return reference to the last value of the value list for the key.
+ */
+ ValueType& getValueRef(const KeyType& key)
+ {
+ typename KeyListType::iterator it=
+ std::find( m_key_list.begin(), m_key_list.end(), key );
+ if (it == m_key_list.end())
+ {
+ m_key_list.push_back(key);
+ }
+ if (m_storage[key].empty())
+ {
+ ValueType v;
+ m_storage[key].push_back( v );
+ }
+ return m_storage[key].back();
+ } // eo getValueRef(const KeyType&)
+
+
+ /**
+ * @brief returns a reference to the last value of the value list for a given key.
+ * @param key the key for which the reference is needed.
+ * @return reference to the last value of the value list for the key or to internal
+ * value if no values are existing.
+ *
+ * This method is only for derived classes to optimize accessing of the values.
+ *
+ * @note The method returns a reference to an internal value if no approbiate value
+ * exists! It's up to the derived class to deal with that.
+ */
+ const ValueType& getConstValueRef(const KeyType& key) const
+ {
+ static ValueType empty_value;
+ typename StorageType::const_iterator storage_it= m_storage.find(key);
+ if (storage_it == m_storage.end()
+ or storage_it->second.empty() )
+ {
+ return empty_value;
+ }
+ return storage_it->second.back();
+ } // eo getConstValueRef(const KeyType&)
+
+
+ /**
+ * @brief returns a reference to the value list for a given key.
+ * @param key the key for which the reference is needed.
+ * @return reference to the value list for the key.
+ *
+ * This method is only for derived classes to optimize accessing of the values.
+ *
+ * @note The method returns a reference to an internal value if no approbiate value
+ * exists! It's up to the derived class to deal with that.
+ */
+ const ValueListType& getConstValuesRef(const KeyType& key) const
+ {
+ static ValueListType empty_value;
+ typename StorageType::const_iterator storage_it= m_storage.find(key);
+ if ( storage_it == m_storage.end() )
+ {
+ return empty_value;
+ }
+ return storage_it->second;
+ } // eo getConstValuesRef(const KeyType&)
+
+
+
+ private:
+
+ StorageType m_storage;
+ KeyListType m_key_list;
+
+}; // eo KeyValueData
+
+
+
+
+/**
+ * grouped key value list.
+ * This (template) class provides storage for groups whose values are key-value lists.
+ * Basically it's a key-value list, wohse values are another key-value list.
+ *
+ * If used with std::string for all key types, it provides a storage suitable for holding they
+ * (for example) the content of INI style config files.
+ *
+ * @param GroupKeyType type for the group keys.
+ * @param KeyType type for the keys (within a group)
+ * @param ValueType type for the values (within a group)
+ */
+template<
+ class GroupKeyType,
+ class KeyType,
+ class ValueType
+>
+class GroupedKeyValueData
+: public KeyValueData<GroupKeyType, KeyValueData<KeyType,ValueType> >
+{
+ typedef KeyValueData<GroupKeyType, KeyValueData<KeyType,ValueType> > inherited;
+
+ public:
+
+ typedef KeyValueData<KeyType,ValueType> GroupDataType;
+
+ public:
+
+ GroupedKeyValueData()
+ {
+ }
+
+
+ ~GroupedKeyValueData()
+ {
+ }
+
+
+ /*
+ ** overridden methods:
+ */
+
+ /**
+ * add (new) group data to a group.
+ * This differs from KeyValueData::addValue in a way that it merges the group data into
+ * an existing group instead of appending a new data group.
+ * @param group_key the key of the group.
+ * @param value the new data which should be added to the group.
+ */
+ void addValue(const GroupKeyType& group_key, const GroupDataType& value)
+ {
+ if (value.empty())
+ {
+ return;
+ }
+ GroupDataType& group_data= getValueRef(group_key);
+
+ typename GroupDataType::KeyListType keys = value.getKeys();
+ for(typename GroupDataType::KeyListType::const_iterator it_key= keys.begin();
+ it_key != keys.end();
+ ++it_key)
+ {
+ typename GroupDataType::ValueListType values= value.getValues(*it_key);
+ if (values.empty()) continue;
+ group_data.addValues( *it_key, value.getValues(*it_key) );
+ }
+ } // eo addValue(const GroupKey&,const GroupData&)
+
+
+ /**
+ * set new group values.
+ * This differs from KeyValueData::setValues in a way that it merges all values to a single
+ * data group.
+ * @param group_key the key of the group.
+ * @param values the list of data grups which are merged together to the new group value.
+ */
+ void setValues(const GroupKeyType& group_key, const typename inherited::ValueListType& values)
+ {
+ if (values.empty())
+ {
+ inherited::deleteKey(group_key);
+ return;
+ }
+ // note: since (the overridden method) addValue() already does what we need,
+ // we just clear the data and iterate over the new values list:
+ getValueRef(group_key).clear();
+ for(typename inherited::ValueListType::const_iterator it= values.begin();
+ it != values.end();
+ ++it)
+ {
+ addValue(group_key, *it);
+ }
+ if ( getValueRef(group_key).empty())
+ {
+ deleteKey(group_key);
+ }
+ } // eo setValues(const GroupKeyType&,const ValueListType&)
+
+
+ /**
+ * add new group values.
+ * This differs from KeyValueList::addValues in a way that the new values are merged into
+ * a single data groupo.
+ * @param group_key
+ * @param values
+ */
+ void addValues(const GroupKeyType& group_key, const typename inherited::ValueListType& values)
+ {
+ if (values.empty())
+ {
+ return;
+ }
+ // like in setValues(); we just use our new addValue() to do the job:
+ for(typename inherited::ValueListType::const_iterator it= values.begin();
+ it != values.end();
+ ++it)
+ {
+ addValue(group_key, *it);
+ }
+ } // eo addValues(const GroupKeyType&,const ValueListType&)
+
+
+
+ /*
+ ** additional methods:
+ */
+
+
+ GroupDataType& operator[](const GroupKeyType& key)
+ {
+ return getValueRef(key);
+ } // eo operator [](const groupKey&)
+
+
+ const GroupDataType& operator[](const GroupKeyType& key) const
+ {
+ return getConstValueRef(key);
+ } // eo operator [](const groupKey&) const
+
+
+ /**
+ * check if a group exists (alias for hasKey(const GroupKey&) const).
+ * @param group_key the key og the group which is looked for.
+ * @return @a true iff the group exists.
+ */
+ bool hasGroup(const GroupKeyType& group_key) const
+ {
+ return inherited::hasKey(group_key);
+ } // eo hasGroup(const GroupKeyType&) const
+
+
+ /**
+ * check if a key within a group exists.
+ * @param group_key the group which should be looked in.
+ * @param key the key which should be tested.
+ * @return @a true iff the group exists and the key exists within that group.
+ */
+ bool hasKey(const GroupKeyType& group_key, const KeyType& key) const
+ {
+ if (!hasGroup(group_key))
+ {
+ return false;
+ }
+ return getConstValueRef(group_key).hasKey(key);
+ } // eo hasKey(const GroupKeyType&,const KeyType&) const
+
+
+
+}; // eo GroupedKeyValueData
+
+
+
+/*
+** typedef the most common case; suitable for holding the content of
+** INI style config files.
+*/
+
+
+typedef GroupedKeyValueData<
+ std::string,
+ std::string,
+ std::string
+> ConfigData;
+
+typedef ConfigData::GroupDataType ConfigSectionData;
+
+
+
+} // eo namespace I2n
+
+
+
+#endif