/* 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 #include #include #include #include #ifndef STFU_HPP #define STFU_HPP namespace stfu { using namespace std; const static 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 ostream &operator<< (ostream &out, const node &root); /** Returns whether it was succesful or not*/ friend bool operator<< (ofstream &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 bool operator>> (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*/ multimap values; multimap children; //@} /** Clears the whole node recursively. */ void clear() { values.clear(); children.clear(); } /** Gets the 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 */ string &value(const 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 */ 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. */ string &addValue(const 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 string& to the value of value with the name and index specified */ const string &getValue(const string &name, size_t index) const throw (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 string& to the value of value with the name and index specified */ const string &getValue(size_t index) const throw (out_of_range){ return getValue("", index); } /** Same as getValue() const, but for non-const objects. The returned 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 string& to the value of value with the name and index specified */ string &getValue(const string &name, size_t index = 0) throw (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 string& to the value of value with the name and index specified */ string &getValue(size_t index) throw (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 string &name, size_t index = 0) throw (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 (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 string &oldName, const 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 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 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 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 string &name, size_t index = 0) const throw (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 (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 string &name, size_t index = 0) throw (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 (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 string &name, size_t index = 0) throw (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 (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 string &oldName, const string &newName, size_t index = 0); /** Reads the STF from an istream \return Returns whether it was succesful */ bool read(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 string to use as indenation \return Returns whether it was succesful */ bool write(ostream &out, size_t depth = 0, 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(istream &in,string &out, const string &delim); char streamRead(istream &in, string &out, const char delim); char streamSkip(istream &in, const 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 T2& get_indexed(const multimap &container, const T1 &key, size_t index = 0) const throw (out_of_range) { size_t count = container.count(key); if (count != 0 && count-1 >= index) { typename multimap::const_iterator it = container.find(key); while (index--) it++; return const_cast(it->second); } else { throw out_of_range((string)"get_indexed->"+"Element " + key + "doesn't exist!"); } } // template // typename multimap::iterator get_indexed_it(multimap &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::iterator it = container.find(key); // while (index--) it++; // return it; // } else { // throw out_of_range((string)"get_indexed_it->"+"Element " + key + "doesn't exist!"); // } // } template typename Container::iterator get_indexed_it(Container& container, const T& key, size_t index = 0) throw (out_of_range) { typename Container::iterator it = container.find(key); while (index-- && it != container.end() && it->first==key) it++; if (it == container.end()) throw out_of_range("get_indexed_it(Container&, const T&, size_t)"); return it; } }; typedef pair value; typedef pair child; typedef multimap::iterator valueiterator; typedef multimap::iterator childiterator; } #endif // STFU_HPP