/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /** @file * * @author Reinhard Pfau * * @copyright © Copyright 2007-2008 by Intra2net AG */ #ifndef __I2N_CONFIGDATA_HPP__ #define __I2N_CONFIGDATA_HPP__ #include #include #include #include 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 KeyListType; typedef std::vector ValueListType; typedef std::map 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 > { typedef KeyValueData > inherited; public: typedef KeyValueData 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 inherited::getValueRef(key); } // eo operator [](const groupKey&) const GroupDataType& operator[](const GroupKeyType& key) const { return inherited::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 inherited::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