From bbd3b9781d5848f9be3ec3e1089de676ec2ef777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=A4ckerlin?= Date: Thu, 9 Apr 2009 10:53:03 +0000 Subject: [PATCH] check node number limits --- src/xml-cxx/xml.hxx | 53 +++++++++++++------ src/xml.cxx | 120 +++++++++++++++++++++++++++++--------------- test/xml_test.cxx | 41 ++++++++++----- 3 files changed, 148 insertions(+), 66 deletions(-) diff --git a/src/xml-cxx/xml.hxx b/src/xml-cxx/xml.hxx index 2305fec..30c9f4b 100644 --- a/src/xml-cxx/xml.hxx +++ b/src/xml-cxx/xml.hxx @@ -118,6 +118,14 @@ namespace xml { Node* _node; }; //---------------------------------------------------------------------------- + class empty_attribute_list: public exception { + public: + empty_attribute_list(const std::string& name) throw(): + exception("list of attribute is empty access to first element failed" + "\nattribute: "+name) { + } + }; + //---------------------------------------------------------------------------- class no_parent: public exception { public: no_parent(const Node& t) throw(): exception("node has no parent", t) {} @@ -175,6 +183,8 @@ namespace xml { public: stream_error(const std::string& reason, const Node& t, std::istream& is, const Tag& tag, char c=0) throw(); + stream_error(const std::string& reason, const Node& t, + std::istream& is) throw(); ~stream_error() throw(); const char* what() const throw(); private: @@ -264,16 +274,19 @@ namespace xml { class wrong_node_number: public stream_error { public: wrong_node_number(const Node& t, std::istream& is, - const Tag& tag, const std::string& name, + const std::string& name, unsigned long num, unsigned long min, unsigned long max) throw(): - stream_error(((std::stringstream&) - (std::stringstream() - <<"wrong number of child nodes\nname of child: "< Limits; - Limits _limits; + size_type _min; + size_type _max; }; //---------------------------------------------------------------------------- class String: public Node { public: - String(std::string name, const std::string& text = std::string()) throw(); + String(std::string name, size_type min=0, size_type max=0) throw(); + String(std::string name, const std::string& text, + size_type min=0, size_type max=0) throw(); virtual std::auto_ptr clone() const throw(); virtual ~String() throw() {} virtual std::string text() const throw(); @@ -422,7 +443,8 @@ namespace xml { //---------------------------------------------------------------------------- class UnsignedInteger: public String { public: - UnsignedInteger(std::string name, unsigned long i = 0) throw(); + UnsignedInteger(std::string name, unsigned long i=0, + size_type min=0, size_type max=0) throw(); virtual std::auto_ptr clone() const throw(); virtual ~UnsignedInteger() throw() {} virtual UnsignedInteger& text(const std::string& txt) @@ -456,12 +478,15 @@ namespace xml { attributes_in_end_tag, illegal_attribute, mandatory_attribute_missing, wrong_node_number); + std::auto_ptr checkChildren(const xml::Node& tpl, + std::auto_ptr node, + std::istream& is) const + throw(wrong_node_number); Tag tag(std::istream& is, const Node& position) throw(second_slash_in_tag, wrong_start_tag, character_after_slash, missing_end_tag, attributes_in_end_tag, tag_expected, attribute_value_not_quoted, access_error, duplicate_attribute, - illegal_attribute, mandatory_attribute_missing, - wrong_node_number); + illegal_attribute, mandatory_attribute_missing); Node _template; unsigned long _line; long _open; diff --git a/src/xml.cxx b/src/xml.cxx index 94a0dbf..f38a72f 100644 --- a/src/xml.cxx +++ b/src/xml.cxx @@ -10,6 +10,7 @@ #include #include +#include #include class MethodTrace { public: @@ -33,7 +34,7 @@ class MethodTrace { }; unsigned int MethodTrace::_level(0); #define TRACE MethodTrace XXX_METHOD(this, __PRETTY_FUNCTION__) -#define LOG(X) std::cout<<__PRETTY_FUNCTION__<<"\t**** "<31?_char:'?') <<" (ascii="<<(int)_char<<")"; - ss<<"\ntag type: "<<(_tag->type==START?"START" - :(_tag->type==END?"END" - :(_tag->type==EMPTY?"EMPTY" - :"???? "))); - if (_tag->name.size()) ss<<"\nnode name read: "<<_tag->name; - if (_tag->text.size()) ss<<"\ncontained text: "<<_tag->text; + if (_tag) { + ss<<"\ntag type: "<<(_tag->type==START?"START" + :(_tag->type==END?"END" + :(_tag->type==EMPTY?"EMPTY" + :"???? "))); + if (_tag->name.size()) ss<<"\nnode name read: "<<_tag->name; + if (_tag->text.size()) ss<<"\ncontained text: "<<_tag->text; + } w = ss.str(); } return w.c_str(); @@ -193,6 +200,12 @@ namespace xml { l.push_back(second.substr(it, pos-it)); return l; } + std::string Attributes::Value::front(const std::string& separators) const + throw(empty_attribute_list) { + List l(toList(separators)); + if (!l.size()) throw empty_attribute_list(first); + return l.front(); + } //---------------------------------------------------------------------------- //! Empty attribute list Attributes::Attributes() throw() {} @@ -219,12 +232,13 @@ namespace xml { //! Nodes must be given a name. /*! Unnamed nodes are not allowed, therefore there's no default constructor. */ - Node::Node(std::string name, size_type min, size_type max) throw(): - _name(name), _parent(0), _limits(min, max) { + Node::Node(std::string name, + Node::size_type min, Node::size_type max) throw(): + _name(name), _parent(0), _min(min), _max(max) { } Node::Node(const Node& o) throw(): _attributes(o._attributes), _name(o.name()), _parent(0), - _limits(o._limits) { + _min(o._min), _max(o._max) { for (Contents::const_iterator it(o._contents.begin()); it!=o._contents.end(); ++it) _contents.push_back((*it)->clone(this).release()); @@ -233,7 +247,8 @@ namespace xml { _attributes=o._attributes; _name = o.name(); _parent = 0; - _limits = o._limits; + _min = o._min; + _max = o._max; for (Contents::const_iterator it(o._contents.begin()); it!=o._contents.end(); ++it) _contents.push_back((*it)->clone(this).release()); @@ -296,6 +311,20 @@ namespace xml { std::string Node::name() const throw() { return _name; } + Node& Node::min(Node::size_type m) throw() { + _min = m; + return *this; + } + Node::size_type Node::min() const throw() { + return _min; + } + Node& Node::max(Node::size_type m) throw() { + _max = m; + return *this; + } + Node::size_type Node::max() const throw() { + return _max; + } bool Node::isChild() const throw() { return _parent; } @@ -336,8 +365,8 @@ namespace xml { return _attributes; } Node& Node::limits(size_type min, size_type max) throw() { - _limits.first = min; - _limits.second = max; + _min = min; + _min = max; return *this; } Node::List Node::list(const std::string& name) const throw() { @@ -405,8 +434,13 @@ namespace xml { } //---------------------------------------------------------------------------- - String::String(std::string name, const std::string& text) throw(): - Node(name), _text(text) { + String::String(std::string name, + Node::size_type min, Node::size_type max) throw(): + Node(name, min, max) { + } + String::String(std::string name, const std::string& text, + Node::size_type min, Node::size_type max) throw(): + Node(name, min, max), _text(text) { } std::auto_ptr String::clone() const throw() { return std::auto_ptr(new String(*this)); @@ -443,9 +477,11 @@ namespace xml { } //---------------------------------------------------------------------------- - UnsignedInteger::UnsignedInteger(std::string name, unsigned long i) throw(): + UnsignedInteger::UnsignedInteger(std::string name, unsigned long i, + size_type min, size_type max) throw(): String(name, - dynamic_cast(std::stringstream()<(std::stringstream()< UnsignedInteger::clone() const throw() { return std::auto_ptr(new UnsignedInteger(*this)); @@ -482,7 +518,10 @@ namespace xml { //---------------------------------------------------------------------------- Factory::Factory(const Node& t) throw(): - _template(xml::Node("")<")<children()==0) throw tag_expected(_template[0], _template[0].name()); return (*node)[0].clone(); - /*node->clear(); - Tag res; - while (true) { - res = tag(is, _template); - *node<clear()< current(node[res.name].clone()); + current->clear()< Factory::checkChildren(const xml::Node& tpl, + std::auto_ptr node, + std::istream& is) const + throw(wrong_node_number) { + for (Node::size_type i(0); i0 && node->list(tpl[i].name()).size()list(tpl[i].name()).size()) + throw wrong_node_number(*node, is, tpl[i].name(), + node->list(tpl[i].name()).size(), + tpl[i].min(), tpl[i].max()); + return node; + } Tag Factory::tag(std::istream& is, const Node& position) throw(second_slash_in_tag, character_after_slash, tag_expected, missing_end_tag, attribute_value_not_quoted, access_error, duplicate_attribute, attributes_in_end_tag, illegal_attribute, mandatory_attribute_missing, - wrong_start_tag, wrong_node_number) { + wrong_start_tag) { char c(0); Tag tag((Tag){"", START, "", Attributes(), "", false}); while (is && is.get(c) && ws(c)); // skip ws @@ -596,7 +635,8 @@ namespace xml { if (nameRead) { // read attribute if (tag.type!=START) throw attributes_in_end_tag(position, is, tag); std::string attrname(1, c), attrvalue; - while (is && is.get(c) && c!='=' && c!='>' && !ws(c)) attrname+=c; + while (is && is.get(c) && c!='=' && c!='>' && c!='/' + && !ws(c)) attrname+=c; while (ws(c) && is && is.get(c)); // skip ws, search '=' if (c=='=') { // get the value while (is && is.get(c) && ws(c)); // skip ws @@ -629,9 +669,9 @@ namespace xml { else throw tag_expected(position, tag.text); } - for (Attributes::const_iterator it - (position[tag.name].attributes().begin()); - it!=position[tag.name].attributes().end(); ++it) + const xml::Node& node(position[tag.name]); + for (Attributes::const_iterator it(node.attributes().begin()); + it!=node.attributes().end(); ++it) if (tag.attributes.find(it->first)==tag.attributes.end()) { if (it->second=="xml::mandatory") throw mandatory_attribute_missing(position, is, tag, it->first); diff --git a/test/xml_test.cxx b/test/xml_test.cxx index e82f6ae..dece04d 100644 --- a/test/xml_test.cxx +++ b/test/xml_test.cxx @@ -265,32 +265,49 @@ class ComplexTest: public CppUnit::TestFixture { } { std::stringstream file(""); CPPUNIT_ASSERT_THROW(factory.read(file), xml::illegal_attribute); + } { + std::stringstream file(""); + CPPUNIT_ASSERT_NO_THROW(factory.read(file)); + } { + std::stringstream file(""); + CPPUNIT_ASSERT_EQUAL(std::string("abc"), + (*factory.read(file)).attribute("one").front()); + } { + std::stringstream file(""); + CPPUNIT_ASSERT_EQUAL((size_t)3, + (*factory.read(file)).attribute("one").toList() + .size()); + } { + std::stringstream file(""); + CPPUNIT_ASSERT_EQUAL(std::string("abc"), + (*factory.read(file)).attribute("one").front()); + } { + std::stringstream file(""); + CPPUNIT_ASSERT_EQUAL(std::string(""), + (*factory.read(file)).attribute("one").front()); } } void ranges() { - xml::Factory factory(xml::Node("base", 2, 2) - <" - ""); + std::stringstream file(""); CPPUNIT_ASSERT_NO_THROW(factory.read(file)); - } /*{ - std::stringstream file("" - ""); + } { + std::stringstream file(""); CPPUNIT_ASSERT_THROW(factory.read(file), xml::wrong_node_number); } { - std::stringstream file("" - ""); + std::stringstream file("" + ""); CPPUNIT_ASSERT_THROW(factory.read(file), xml::wrong_node_number); } { - std::stringstream file("" - ""); + std::stringstream file(""); CPPUNIT_ASSERT_THROW(factory.read(file), xml::wrong_node_number); - }*/ + } } CPPUNIT_TEST_SUITE(ComplexTest); CPPUNIT_TEST(nodes);