check node number limits
This commit is contained in:
@@ -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;
|
||||
|
120
src/xml.cxx
120
src/xml.cxx
@@ -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);
|
||||
|
Reference in New Issue
Block a user