/* 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 #include "stf.hpp" using namespace std; using stfu::node; using stfu::child; using stfu::value; ostream &stfu::operator<< (ostream &out, const node &root) { root.write(out); return out; } bool stfu:: operator<< (const char *filename, const node &root){ return root.write(filename); } bool stfu::operator<< (ofstream &out, const node &root) { return root.write(out); } bool stfu::operator>> (istream &in, node &root) { return root.read(in); } bool stfu::operator>> (const char *filename, node &root) { return root.read(filename); } const string &node::getValue(const string &name, size_t index) const throw (out_of_range) { return get_indexed(values, name, index); } /*Function is const, but shouldn't be called on const objects since it returns a nonconst-reference to a member*/ string &node::getValue(const string &name, size_t index) throw (out_of_range) { return get_indexed(values, name, index); } string &node::addValue(const string &name) { return values.insert(pair(name, string()))->second; } string &node::value(const string &name, size_t index) { try { return getValue(name, index); } catch (out_of_range &e) { //it doesn't exist: create it return addValue(name); } } void node::removeValue(const string &name, size_t index) throw (out_of_range) { values.erase(get_indexed_it(values, name, index)); return; } void node::renameValue(const string &oldName, const string &newName, size_t index) { addValue(newName) = value(oldName, index); removeValue(oldName, index); } const node &node::getChild(const string &name, size_t index) const throw (out_of_range) { return get_indexed(children, name, index); } node &node::getChild(const string &name, size_t index) throw (out_of_range) { return get_indexed(children, name, index); } node &node::addChild(const string &name) { return children.insert(pair(name, node()))->second; } node &node::addChild(const string &name, node &newNode) { return children.insert(pair(name, newNode))->second; } node &node::child(const string &name, size_t index) { //if there's no such child, add one try { return getChild(name, index); } catch (out_of_range &e) { //it doesn't exist: create it return addChild(name); } } void node::renameChild(const string &oldName, const string &newName, size_t index) { node copy = child(oldName, index); removeChild(oldName, index); addChild(newName) = copy; } void node::removeChild(const string &name, size_t index) throw (out_of_range) { children.erase(get_indexed_it(children, name, index)); return; } bool node::read(const char *filename) { ifstream f(filename); if (!f.good()) return false; bool success = read(f); f.close(); return success; } bool node::read(istream &in) { while (1) { in >> ws; //Skip whitespace //if end of node is reached, return if (in.peek() == '}') { in.ignore(); return true; } if (in.eof()) { return true; //End of the file is reached } string name; char type; // '{' or '"' streamRead(in, name, ':'); //Read name (all chars before ':') type = streamSkip(in,"\"{"); //Skip everything until '{' or '"' switch (type) { //in case of value case '"': { string value; while (1) { if (streamRead(in, value, '"') == 0) { //Read to the closing-" return false; } if (in.peek() == '"') { in.ignore(); value += '"'; continue; } else { break; } } this->values.insert(pair(name, value)); break; } //in case of child case '{': { node sub; if (!sub.read(in)) { //Recursively read the subnode return false; } this->children.insert(pair(name,sub)); break; } default: return false; } } } /*Writes to a file using it's overloaded self*/ bool node::write(const char *filename) const { ofstream f(filename); if (!f.good()) { return false; } bool success = write(f); f.close(); return success; } // TODO (Nick#1#): Make write() not put unnamed values on a new line, children are okay. bool node::write(ostream &out, size_t depth, string indent) const { string indentation; for (size_t i = 0; i < depth; i++) { indentation += indent; } for (multimap::const_iterator value_it = values.begin(); value_it != values.end(); value_it++) { //Escape all the '"' by adding a second '"' string value(value_it->second); size_t found = value.find('"'); //while there are more ", insert second "s while (found != value.npos) { value.insert(found, 1, '"'); found = value.find('"', found+2); } out << indentation << value_it->first << ": \"" << value << '"' << endl; } for (multimap::const_iterator child_it = children.begin(); child_it != children.end(); child_it++) { out << indentation << child_it->first << ": {" << endl; child_it->second.write(out, depth+1); out << indentation << '}' << endl; } return true; } char node::streamSkip(istream &in, const string &delimiters) { char cur; //Return if the current char is part of delimiters[] while (in >> noskipws >> cur) { if (delimiters.find_first_of(cur) != delimiters.npos) return cur; } return 0; } char node::streamRead(istream &in, string &out, const string &delimiters) { char cur; //Return if the current char is part of delimiters[] while (in >> noskipws >> cur) { if (delimiters.find(cur) != delimiters.npos) return cur; out += cur; } return 0; } char node::streamRead(istream &in, string &out, const char delimiter) { char cur; //Return if the current char is delimiter while (in >> noskipws >> cur) { if (delimiter == cur) return cur; out += cur; } return 0; }