/*
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 ) ;
/** Returns whether it was succesful or not*/
friend bool operator < < ( std : : 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 > > ( 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 ;
//@}
/**
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