You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
13 KiB
359 lines
13 KiB
14 years ago
|
/*
|
||
|
Copyright (c) 2009 Maurice Bos and Nick Overdijk
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
* Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
* Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
* The names of the authors may not be used to endorse or promote
|
||
|
products derived from this software without specific prior written
|
||
|
permission.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
- Maurice Bos (maurice@bosbyte.nl)
|
||
|
- Nick Overdijk (nick@dotsimplicity.net)
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <fstream>
|
||
|
#include <string>
|
||
|
#include <sstream>
|
||
|
#include <map>
|
||
|
#include <stdexcept>
|
||
|
|
||
|
#ifndef STFU_HPP
|
||
|
#define STFU_HPP
|
||
|
|
||
|
namespace stfu {
|
||
|
|
||
|
const static std::string not_found("search->No such child/value");
|
||
|
|
||
|
/*! \class node
|
||
|
* \brief A node in an STF tree.
|
||
|
* \author Maurice Bos
|
||
|
* \author Nick Overdijk
|
||
|
* \version 1.0
|
||
|
* \date 2009-04-25
|
||
|
*
|
||
|
* When you read an STF file through a node (with read() for example), this node will be the root node, without a name. See the examples for more information.
|
||
|
*/
|
||
|
class node {
|
||
|
|
||
|
/** Overloaded ostream's operator<< */
|
||
|
friend std::ostream& operator<< (std::ostream& out, const node& root);
|
||
|
|
||
|
/** Acts like write(), but looks like this: "filename" << node */
|
||
|
friend bool operator<< (const char* filename, const node& root);
|
||
|
|
||
|
/** Overloaded istream's operator>> */
|
||
|
friend std::istream& operator>> (std::istream& in, node& root);
|
||
|
|
||
|
/** Acts like read(), but looks like this: "filename" >> node */
|
||
|
friend bool operator>> (const char* filename, node& root);
|
||
|
|
||
|
public:
|
||
|
//@{
|
||
|
/** The values and children belonging to this node. To add, remove, do whatever: you CAN use these variables directly. To use indexing, use the member functions below*/
|
||
|
std::multimap<std::string, std::string> values;
|
||
|
std::multimap<std::string, node> children;
|
||
|
//@}
|
||
|
|
||
|
node() : values(), children() {}
|
||
|
|
||
|
/**
|
||
|
Clears the whole node recursively.
|
||
|
*/
|
||
|
void clear() {
|
||
|
values.clear();
|
||
|
children.clear();
|
||
|
}
|
||
|
/**
|
||
|
Gets the std::string value from a variable
|
||
|
\param name The name of the value you wish to retrieve
|
||
|
\param index If there are more values with the same name, they are indexed. Here you can supply its indexnumber.
|
||
|
\return The retrieved value
|
||
|
*/
|
||
|
std::string& value(const std::string& name, size_t index = 0);
|
||
|
|
||
|
/**
|
||
|
Same as value(), but for unnamed values
|
||
|
\note same as value("", index)
|
||
|
\param index If there are more unnamed values, they are indexed. Here you can supply its indexnumber.
|
||
|
\return Same as value
|
||
|
*/
|
||
|
std::string& value(size_t index) {
|
||
|
return value("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Creates and adds a value.
|
||
|
\param name The name of the value to be created
|
||
|
\return A reference to the value of the created value.
|
||
|
*/
|
||
|
std::string& addValue(const std::string& name = "");
|
||
|
|
||
|
/**
|
||
|
Const function for const objects to get a value. It's NOT possible to call value() on constant objects for value() isn't const.
|
||
|
\param name Name of the value to be retrieved
|
||
|
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a const std::string& to the value of value with the name and index specified
|
||
|
*/
|
||
|
const std::string& getValue(const std::string& name, size_t index) const throw(std::out_of_range);
|
||
|
|
||
|
/**
|
||
|
Same as getValue() const, but for unnamed values
|
||
|
\note same as getValue("", index) const
|
||
|
\param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a const std::string& to the value of value with the name and index specified
|
||
|
*/
|
||
|
const std::string& getValue(size_t index) const throw(std::out_of_range) {
|
||
|
return getValue("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Same as getValue() const, but for non-const objects. The returned std::string& can safely be modified.
|
||
|
\param name Name of the value to be retrieved
|
||
|
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a std::string& to the value of value with the name and index specified
|
||
|
*/
|
||
|
std::string& getValue(const std::string& name, size_t index = 0) throw(std::out_of_range);
|
||
|
|
||
|
/**
|
||
|
Same as getValue(), but for unnamed values
|
||
|
\note same as getValue("", index)
|
||
|
\param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a std::string& to the value of value with the name and index specified
|
||
|
*/
|
||
|
std::string& getValue(size_t index) throw(std::out_of_range) {
|
||
|
return getValue("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Removes a value. Surprise huh?
|
||
|
\param name Name of the value to be removed
|
||
|
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
*/
|
||
|
void removeValue(const std::string& name, size_t index = 0) throw(std::out_of_range);
|
||
|
|
||
|
/**
|
||
|
Removes an unnamed value.
|
||
|
\note same as removeValue("", index);
|
||
|
\param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber.
|
||
|
*/
|
||
|
void removeValue(size_t index) throw(std::out_of_range) {
|
||
|
removeValue("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Renames a value.
|
||
|
\param oldName Name of the value to be renamed
|
||
|
\param newName The name that the oldName-value should have
|
||
|
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
*/
|
||
|
void renameValue(const std::string& oldName, const std::string& newName, size_t index = 0);
|
||
|
|
||
|
/**
|
||
|
Changes, adds or retrieves node
|
||
|
\param name The name of the child you wish to retrieve, change or add.
|
||
|
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
\return The retrieved node
|
||
|
|
||
|
If this index number is > the number of variables with that name, a new variable with that name is created with index = current number of same name variables + 1.
|
||
|
So say you have 4 variables named "tree", you call child("tree", 10), another tree gets made with index 5, NOT 10.
|
||
|
*/
|
||
|
node& child(const std::string& name, size_t index = 0);
|
||
|
|
||
|
/**
|
||
|
Same as child(), but for unnamed children.
|
||
|
\note same as child("", index)
|
||
|
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
|
||
|
\return The retrieved node
|
||
|
*/
|
||
|
node& child(size_t index = 0) {
|
||
|
return child("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Guarranteed to add a child
|
||
|
\param name Name for the child
|
||
|
\return A reference to the created node
|
||
|
*/
|
||
|
node& addChild(const std::string& name = "");
|
||
|
|
||
|
/**
|
||
|
As addChild(name), but copies data from newChild as well.
|
||
|
\param name Name for the child
|
||
|
\param newChild Data to copy from the child.
|
||
|
\return A reference to the created node
|
||
|
*/
|
||
|
node& addChild(const std::string& name, node& newChild);
|
||
|
|
||
|
/**
|
||
|
As addChild(name, newChild), but without name, for unnamed children
|
||
|
\note same as addChild("", newChild);
|
||
|
\param newChild Data to copy from the child.
|
||
|
\return A reference to the created node
|
||
|
*/
|
||
|
node& addChild(node& newChild) {
|
||
|
return addChild("", newChild);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Const function for const objects to get a node. It's NOT possible to call child() for this, for child() isn't const.
|
||
|
\param name Name of the child to be retrieved
|
||
|
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a const node& to the node with the name and index specified
|
||
|
*/
|
||
|
const node& getChild(const std::string& name, size_t index = 0) const throw(std::out_of_range);
|
||
|
|
||
|
/**
|
||
|
Const function for const objects to get an unnamed const node&.
|
||
|
\note same as getChild("", index) const;
|
||
|
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a const node& to the node with the name and index specified
|
||
|
*/
|
||
|
const node& getChild(size_t index = 0) const throw(std::out_of_range) {
|
||
|
return getChild("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Same as getChild() const, but for non-const objects. The returned child & can be modified
|
||
|
\param name Name of the child to be retrieved
|
||
|
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a node& to the node with the name and index specified
|
||
|
*/
|
||
|
node& getChild(const std::string& name, size_t index = 0) throw(std::out_of_range);
|
||
|
|
||
|
/**
|
||
|
Same as getChild() const, but for non-const objects.
|
||
|
\note same as getChild("", index);
|
||
|
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
|
||
|
\return Returns a node& to the node with the name and index specified
|
||
|
*/
|
||
|
node& getChild(size_t index = 0) throw(std::out_of_range) {
|
||
|
return getChild("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Removes a child. Surprise huh?
|
||
|
\param name Name of the child to be removed
|
||
|
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
*/
|
||
|
void removeChild(const std::string& name, size_t index = 0) throw(std::out_of_range);
|
||
|
|
||
|
/**
|
||
|
As removeChild() for unnamed children.
|
||
|
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
|
||
|
*/
|
||
|
void removeChild(size_t index = 0) throw(std::out_of_range) {
|
||
|
removeChild("", index);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Renames a child.
|
||
|
\param oldName Name of the child to be renamed
|
||
|
\param newName The name that the oldName-child should have
|
||
|
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
|
||
|
*/
|
||
|
void renameChild(const std::string& oldName, const std::string& newName, size_t index = 0);
|
||
|
|
||
|
|
||
|
/**
|
||
|
Reads the STF from an istream
|
||
|
|
||
|
\return Returns whether it was succesful
|
||
|
*/
|
||
|
bool read(std::istream& in);
|
||
|
|
||
|
/**
|
||
|
Reads the STF from a file
|
||
|
|
||
|
\return Returns whether it was succesful
|
||
|
*/
|
||
|
bool read(const char* filename);
|
||
|
|
||
|
/**
|
||
|
Writes the STF to an ostream with optional indentation
|
||
|
\param out ostream to write to
|
||
|
\param depth how much indentation to start with
|
||
|
\param indent What std::string to use as indenation
|
||
|
\return Returns whether it was succesful
|
||
|
*/
|
||
|
bool write(std::ostream& out, size_t depth = 0, std::string indent = "\t") const;
|
||
|
|
||
|
/**
|
||
|
Writes to a file. Simply first opens a file and passes that ostream to itself.
|
||
|
\param filename File to write to
|
||
|
\return Returns whether it was succesful
|
||
|
*/
|
||
|
bool write(const char* filename) const;
|
||
|
|
||
|
private:
|
||
|
char streamRead(std::istream& in,std::string& out, const std::string& delim);
|
||
|
char streamRead(std::istream& in, std::string& out, const char delim);
|
||
|
char streamSkip(std::istream& in, const std::string& delim);
|
||
|
|
||
|
/*
|
||
|
returns a T2&, not a const T2&. The functions getValue/getChild make sure this will be done correctly for const objects
|
||
|
this function can NOT use get_indexed_it, because that function can't be const:
|
||
|
const_iterators can not be transformed into iterators, and the iterator is needed for erase(), which wants an iterator.
|
||
|
*/
|
||
|
template <typename T1, typename T2>
|
||
|
T2& get_indexed(const std::multimap<T1, T2> &container, const T1& key, size_t index = 0) const throw(std::out_of_range) {
|
||
|
size_t count = container.count(key);
|
||
|
|
||
|
if(count != 0 && count-1 >= index) {
|
||
|
typename std::multimap<T1, T2>::const_iterator it = container.find(key);
|
||
|
while(index--) it++;
|
||
|
return const_cast<T2&>(it->second);
|
||
|
} else {
|
||
|
throw std::out_of_range((std::string)"get_indexed->"+"Element " + key + " doesn't exist!");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// template <typename T1, typename T2>
|
||
|
// typename multimap<T1, T2>::iterator get_indexed_it(multimap<T1, T2> &container, const T1 &key, size_t index = 0) throw (out_of_range) {
|
||
|
// size_t count = container.count(key);
|
||
|
//
|
||
|
// if (count != 0 && count-1 >= index) {
|
||
|
// typename multimap<T1, T2>::iterator it = container.find(key);
|
||
|
// while (index--) it++;
|
||
|
// return it;
|
||
|
// } else {
|
||
|
// throw out_of_range((std::string)"get_indexed_it->"+"Element " + key + "doesn't exist!");
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
template <typename Container, typename T>
|
||
|
typename Container::iterator get_indexed_it(Container& container, const T& key, size_t index = 0)
|
||
|
throw(std::out_of_range) {
|
||
|
typename Container::iterator it = container.find(key);
|
||
|
while(index-- && it != container.end() && it->first==key) it++;
|
||
|
if(it == container.end()) throw std::out_of_range("get_indexed_it(Container&, const T&, size_t)");
|
||
|
return it;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
typedef std::pair<std::string, std::string> value;
|
||
|
typedef std::pair<std::string, node> child;
|
||
|
|
||
|
typedef std::multimap<std::string, std::string>::iterator valueiterator;
|
||
|
typedef std::multimap<std::string, node>::iterator childiterator;
|
||
|
}
|
||
|
|
||
|
#endif // STFU_HPP
|