check node number limits

master
Marc Wäckerlin 15 years ago
parent f9389d1082
commit bbd3b9781d
  1. 53
      src/xml-cxx/xml.hxx
  2. 120
      src/xml.cxx
  3. 41
      test/xml_test.cxx

@ -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: "<<name
<<"\nnumber of nodes: "<<num
<<"\nminimuml number: "<<min
<<"\nmaximum number: "<<max)).str(),
t, is, tag, 0) {
stream_error("wrong number of child nodes\nname of child: "+name
+"\nnumber of nodes: "+conv(num)
+"\nminimuml number: "+conv(min)
+"\nmaximum number: "+conv(max), t, is) {
}
private:
static std::string conv(unsigned long i) throw() {
std::stringstream ss;
ss<<i;
return ss.str();
}
};
@ -309,6 +322,8 @@ namespace xml {
operator unsigned long() const throw();
operator List() const throw();
List toList(const std::string& separators=" \t\n\r") const throw();
std::string front(const std::string& separators=" \t\n\r") const
throw(empty_attribute_list);
private:
Value(); // not implemented, key must always be given
};
@ -358,6 +373,10 @@ namespace xml {
virtual Node& set(const Attributes& o) throw();
Node& clear() throw ();
std::string name() const throw();
Node& min(size_type m) throw();
size_type min() const throw();
Node& max(size_type m) throw();
size_type max() const throw();
bool isChild() const throw();
Node& parent() const throw(no_parent);
bool hasAttr(const std::string& name) const throw();
@ -398,14 +417,16 @@ namespace xml {
Contents _contents;
std::string _name;
Node* _parent;
typedef std::pair<Node::size_type, Node::size_type> 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<Node> 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<Node> 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<Node> checkChildren(const xml::Node& tpl,
std::auto_ptr<Node> 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;

@ -10,6 +10,7 @@
#include <sstream>
#include <cstdlib>
#include <cassert>
#include <iomanip>
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**** "<<X<<std::endl
#define LOG(X) std::clog<<__PRETTY_FUNCTION__<<"\t**** "<<X<<std::endl
namespace xml {
@ -105,6 +106,10 @@ namespace xml {
std::istream& is, const Tag& tag, char c) throw():
exception(reason, t), _pos(is.tellg()), _tag(new Tag(tag)), _char(c) {
}
stream_error::stream_error(const std::string& reason, const Node& t,
std::istream& is) throw():
exception(reason, t), _pos(is.tellg()), _tag(0), _char(0) {
}
stream_error::~stream_error() throw() {
delete _tag;
}
@ -117,12 +122,14 @@ namespace xml {
if (_char)
ss<<"\nactual character: "<<(_char>31?_char:'?')
<<" (ascii="<<(int)_char<<")";
ss<<"\ntag type: "<<(_tag->type==START?"START"
:(_tag->type==END?"END"
:(_tag->type==EMPTY?"EMPTY"
:"???? <UNKNOWN>")));
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"
:"???? <UNKNOWN>")));
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<Node> String::clone() const throw() {
return std::auto_ptr<Node>(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()<<i).str()) {
dynamic_cast<std::stringstream&>(std::stringstream()<<i).str(),
min, max) {
}
std::auto_ptr<Node> UnsignedInteger::clone() const throw() {
return std::auto_ptr<Node>(new UnsignedInteger(*this));
@ -482,7 +518,10 @@ namespace xml {
//----------------------------------------------------------------------------
Factory::Factory(const Node& t) throw():
_template(xml::Node("<xml::start>")<<t), _line(0) {}
_template(xml::Node("<xml::start>")<<t), _line(0) {
_template[0].min(1);
_template[0].max(1);
}
const Node& Factory::operator*() const throw() {
return _template[0];
}
@ -500,21 +539,6 @@ namespace xml {
if (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<<res.attributes;
switch (res.type) {
case END: throw wrong_end_tag(*node, is, res);
case EMPTY:
return node; // empty
case START:
return read(is, _template[res.name]);
//! @todo store instead of ignore
case SPECIAL: break;
}
}*/
} catch (exception& e) {
e.line(_line);
throw;
@ -545,22 +569,37 @@ namespace xml {
if (res.name!=node.name()) throw wrong_end_tag(node, is, res);
} return result;
case EMPTY: {
*result<<(node[res.name].clone()->clear()<<res.attributes);
std::auto_ptr<Node> current(node[res.name].clone());
current->clear()<<res.attributes;
*result<<*checkChildren(node[res.name], current, is);
} break;
case START: {
++_open;
*result<<(*read(is, node[res.name])<<res.attributes);
*result<<(*checkChildren(node[res.name], read(is, node[res.name]), is)
<<res.attributes);
} break;
case SPECIAL: break; //! @todo ignored could be stored
}
}
}
std::auto_ptr<Node> Factory::checkChildren(const xml::Node& tpl,
std::auto_ptr<Node> node,
std::istream& is) const
throw(wrong_node_number) {
for (Node::size_type i(0); i<tpl.children(); ++i)
if (tpl[i].min()>0 && node->list(tpl[i].name()).size()<tpl[i].min()
|| 0<tpl[i].max() && tpl[i].max()<node->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);

@ -265,32 +265,49 @@ class ComplexTest: public CppUnit::TestFixture {
} {
std::stringstream file("<base one two three></base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::illegal_attribute);
} {
std::stringstream file("<base one/>");
CPPUNIT_ASSERT_NO_THROW(factory.read(file));
} {
std::stringstream file("<base one=\"abc def ghi\"/>");
CPPUNIT_ASSERT_EQUAL(std::string("abc"),
(*factory.read(file)).attribute("one").front());
} {
std::stringstream file("<base one=\"abc def ghi\"/>");
CPPUNIT_ASSERT_EQUAL((size_t)3,
(*factory.read(file)).attribute("one").toList()
.size());
} {
std::stringstream file("<base one=\"abc\"/>");
CPPUNIT_ASSERT_EQUAL(std::string("abc"),
(*factory.read(file)).attribute("one").front());
} {
std::stringstream file("<base one/>");
CPPUNIT_ASSERT_EQUAL(std::string(""),
(*factory.read(file)).attribute("one").front());
}
}
void ranges() {
xml::Factory factory(xml::Node("base", 2, 2)
<<xml::Node("mandatory", 1)
xml::Factory factory(xml::Node("base")
<<xml::Node("mandatory", 1, 1)
<<xml::Node("optional", 0, 1));
{
std::stringstream file("<base><mandatory></mandatory></base>"
"<base><mandatory/><optional/></base>");
std::stringstream file("<base><mandatory/><optional/></base>");
CPPUNIT_ASSERT_NO_THROW(factory.read(file));
} /*{
std::stringstream file("<base><mandatory></mandatory></base>"
"<base><mandatory/><optional/></base>");
} {
std::stringstream file("<base><optional/></base>");
CPPUNIT_ASSERT_THROW(factory.read(file),
xml::wrong_node_number);
} {
std::stringstream file("<base><mandatory></mandatory></base>"
"<base><mandatory/><optional/></base>");
std::stringstream file("<base><mandatory/><optional/>"
"<optional/></base>");
CPPUNIT_ASSERT_THROW(factory.read(file),
xml::wrong_node_number);
} {
std::stringstream file("<base><mandatory></mandatory></base>"
"<base><mandatory/><optional/></base>");
std::stringstream file("<base><mandatory/><mandatory/></base>");
CPPUNIT_ASSERT_THROW(factory.read(file),
xml::wrong_node_number);
}*/
}
}
CPPUNIT_TEST_SUITE(ComplexTest);
CPPUNIT_TEST(nodes);

Loading…
Cancel
Save