C++ class for reading and writing XML structures. No need for a C++ code parser or special pre compiler. Specify a schema entirly in native C++. The schema is verified when XML is read and exceptions are thrown when the XML to be parse is invalid.

458 lines
18 KiB

16 years ago
/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#include <xml-cxx/xml.hxx>
16 years ago
#include <cppunit/TestFixture.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/XmlOutputter.h>
#include <fstream>
16 years ago
#include <memory>
class NodeTest: public CppUnit::TestFixture {
public:
void constructorName() {
xml::Node t("test");
CPPUNIT_ASSERT_EQUAL(std::string("test"), t.name());
}
void clone() {
xml::Node t("test");
std::auto_ptr<xml::Node> p(t.clone());
CPPUNIT_ASSERT_EQUAL(std::string("test"), p->name());
}
void shift() {
xml::Node t("test");
t<<xml::Node("yxz");
CPPUNIT_ASSERT_EQUAL(std::string("yxz"), t["yxz"].name());
}
void out() {
xml::Node t("test");
{ std::stringstream ss;
t.out(ss);
CPPUNIT_ASSERT_EQUAL(std::string("<test/>"), ss.str());
}
{ std::stringstream ss;
t<<xml::Node("ABC");
t.out(ss);
CPPUNIT_ASSERT_EQUAL(std::string("<test>\n\t<ABC/>\n</test>"),
ss.str());
}
}
void operatorParenses() {
xml::Node t("test");
t<<xml::Node("yxz");
CPPUNIT_ASSERT_EQUAL(true, t("yxz"));
CPPUNIT_ASSERT_EQUAL(false, t("zxy"));
}
void operatorBrackets() {
xml::Node t("test");
t<<xml::Node("yxz");
CPPUNIT_ASSERT_EQUAL(std::string("yxz"), t["yxz"].name());
CPPUNIT_ASSERT_THROW(t["zxy"], xml::access_error);
}
void in() {
//! @todo
}
void text() {
xml::Node t("test");
CPPUNIT_ASSERT_THROW(t.text("Hallo Welt"), xml::tag_expected);
}
void textOut() {
xml::Node t("test");
t<<xml::String("yxc").text("Hello")<<xml::String("dfg").text("World");
CPPUNIT_ASSERT_EQUAL(std::string("HelloWorld"), t.text());
}
void dereference() {
xml::Node t("test");
t<<xml::Node("yxc")<<xml::Node("dfg");
CPPUNIT_ASSERT_EQUAL(t.text(), *t);
}
void assign() {
xml::Node t("test");
CPPUNIT_ASSERT_THROW((t="Hallo Welt"), xml::tag_expected);
}
CPPUNIT_TEST_SUITE(NodeTest);
CPPUNIT_TEST(constructorName);
CPPUNIT_TEST(clone);
CPPUNIT_TEST(operatorBrackets);
CPPUNIT_TEST(shift);
CPPUNIT_TEST(out);
CPPUNIT_TEST(operatorParenses);
CPPUNIT_TEST(in);
CPPUNIT_TEST(text);
CPPUNIT_TEST(textOut);
CPPUNIT_TEST(dereference);
CPPUNIT_TEST(assign);
CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(NodeTest);
class StringTest: public CppUnit::TestFixture {
public:
void constructorName() {
xml::String t("test");
CPPUNIT_ASSERT_EQUAL(std::string("test"), t.name());
}
void clone() {
xml::String t("test");
std::auto_ptr<xml::Node> p(t.clone());
CPPUNIT_ASSERT_EQUAL(std::string("test"), p->name());
}
void shift() {
xml::String t("test");
CPPUNIT_ASSERT_THROW(t<<xml::String("yxz"), xml::cannot_have_children);
}
void out() {
xml::String t("test");
{ std::stringstream ss;
t.out(ss);
CPPUNIT_ASSERT_EQUAL(std::string("<test/>"), ss.str());
}
{ std::stringstream ss;
(t="ABC").out(ss);
CPPUNIT_ASSERT_EQUAL(std::string("<test>ABC</test>"),
ss.str());
}
}
void operatorParenses() {
xml::String t("test");
CPPUNIT_ASSERT_EQUAL(false, t("zxy"));
}
void operatorBrackets() {
xml::String t("test");
CPPUNIT_ASSERT_THROW(t[std::string("zxy")], xml::access_error);
16 years ago
}
void text() {
xml::String t("test");
t.text("Hallo Welt");
CPPUNIT_ASSERT_EQUAL(std::string("Hallo Welt"), t.text());
}
void textOut() {
xml::String t("test");
t="yxc";
CPPUNIT_ASSERT_EQUAL(std::string("yxc"), t.text());
}
void dereference() {
xml::String t("test");
t="dfg";
CPPUNIT_ASSERT_EQUAL(t.text(), *t);
}
void assign() {
xml::String t1("test"), t2(t1);
t1="Hallo Welt";
t2.text("Hallo Welt");
CPPUNIT_ASSERT_EQUAL(t1.text(), t2.text());
}
CPPUNIT_TEST_SUITE(StringTest);
CPPUNIT_TEST(constructorName);
CPPUNIT_TEST(clone);
CPPUNIT_TEST(operatorBrackets);
CPPUNIT_TEST(shift);
CPPUNIT_TEST(out);
CPPUNIT_TEST(operatorParenses);
CPPUNIT_TEST(text);
CPPUNIT_TEST(textOut);
CPPUNIT_TEST(dereference);
CPPUNIT_TEST(assign);
CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(StringTest);
class ComplexTest: public CppUnit::TestFixture {
public:
void nodes() {
xml::Factory factory(xml::Node("base")
<<(xml::Node("child").attr("a", xml::optional)
<<xml::String("childofchild")
<<xml::UnsignedInteger("number"))
<<xml::Node("otherchild"));
std::stringstream file1;
file1<<"<!DOCTYPE xyz>"<<std::endl
<<"<?xml 1.0 encoding=\"utf8\"?>"<<std::endl
<<"<base>"<<std::endl
<<"<child a=\"b\"><childofchild/><childofchild/><childofchild>"
<<"xxx</childofchild><number>13</number><number/><number>"
<<" 42 </number><number> </number></child>"
<<"<child a=\"b\"/>"
<<"< otherchild >< / otherchild >< otherchild / >"<<std::endl
<<"</base>";
std::auto_ptr<xml::Node> node(factory.read(file1)); // should work
CPPUNIT_ASSERT_EQUAL((size_t)2, node->list("child").size());
CPPUNIT_ASSERT_EQUAL((size_t)3, (*node)[0].list("childofchild").size());
CPPUNIT_ASSERT_EQUAL((size_t)4, (*node)[0].list("number").size());
CPPUNIT_ASSERT_EQUAL((size_t)0, (*node)[1].list("childofchild").size());
CPPUNIT_ASSERT_EQUAL((size_t)2, node->list("otherchild").size());
CPPUNIT_ASSERT_EQUAL(std::string("xxx"), *(*node)["child"][2]);
CPPUNIT_ASSERT_EQUAL(std::string("13"), *(*node)["child"][3]);
CPPUNIT_ASSERT_EQUAL(std::string("0"), *(*node)["child"][4]);
CPPUNIT_ASSERT_EQUAL(std::string("42"), *(*node)["child"][5]);
CPPUNIT_ASSERT_EQUAL(std::string("0"), *(*node)["child"][6]);
std::stringstream file2;
file2<<"<!DOCTYPE xyz>"<<std::endl
<<"<?xml 1.0 encoding=\"utf8\"?>"<<std::endl
<<"<base>"<<std::endl
<<"<child><childofchild/><exception><childofchild/><childofchild>"
<<"xxx</childofchild></child>"
<<"<child/>"
<<"< otherchild >< / otherchild >< otherchild / >"<<std::endl
<<"</base>";
CPPUNIT_ASSERT_THROW(factory.read(file2), xml::wrong_start_tag);
{
std::stringstream file("<base></xyz>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::wrong_end_tag);
} {
std::stringstream file("<xyz></xyz>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::wrong_start_tag);
} {
std::stringstream file("<xyz/>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::wrong_start_tag);
} {
std::stringstream file("base");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::tag_expected);
} {
std::stringstream file("<base>hallo</base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::tag_expected);
} {
std::stringstream file
("<base><child><number>x</number></child></base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::type_mismatch);
} {
std::stringstream file
("<base><child><number>xyz</number></child></base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::type_mismatch);
} {
std::stringstream file("<base><child></child/></base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::second_slash_in_tag);
} {
std::stringstream file("<base><child><child/a></base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::character_after_slash);
} {
std::stringstream file("<base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::missing_end_tag);
} {
std::stringstream file("<base><child>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::missing_end_tag);
} {
std::stringstream file;
CPPUNIT_ASSERT_THROW(factory.read(file), xml::tag_expected);
} {
std::stringstream file("<base><child a=b></base>");
CPPUNIT_ASSERT_THROW(factory.read(file),
xml::attribute_value_not_quoted);
} {
std::stringstream file("<base><child a=\"b\" a=\"b\"></base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::duplicate_attribute);
} {
std::stringstream file("<base><child></child a=\"b\"></base>");
CPPUNIT_ASSERT_THROW(factory.read(file), xml::attributes_in_end_tag);
}
}
void attributes() {
xml::Factory factory(xml::Node("base")
.attr("one", xml::mandatory)
.attr("two", xml::optional)
<<(xml::Node("child")
<<xml::String("childofchild")
<<xml::UnsignedInteger("number"))
<<xml::Node("otherchild"));
{
std::stringstream file("<base></base>");
CPPUNIT_ASSERT_THROW(factory.read(file),
xml::mandatory_attribute_missing);
} {
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")
<<xml::Node("mandatory", 1, 1)
<<xml::Node("optional", 0, 1));
{
std::stringstream file("<base><mandatory/><optional/></base>");
CPPUNIT_ASSERT_NO_THROW(factory.read(file));
} {
std::stringstream file("<base><optional/></base>");
CPPUNIT_ASSERT_THROW(factory.read(file),
xml::wrong_node_number);
} {
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>");
CPPUNIT_ASSERT_THROW(factory.read(file),
xml::wrong_node_number);
}
}
CPPUNIT_TEST_SUITE(ComplexTest);
CPPUNIT_TEST(nodes);
CPPUNIT_TEST(attributes);
CPPUNIT_TEST(ranges);
CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(ComplexTest);
16 years ago
class FunTest: public CppUnit::TestFixture {
public:
void playing() {
std::string contents("<html>\n\t<head>\n\t\t<title/>\n\t</head>\n"
"\t<body>\n\t\t<h1 class=\"main\">Title</h1>"
"\n\t\t<p>First Paragraf.</p>\n\t\t<p/>\n"
"\t\t<h2/>\n\t\t<p/>\n\t\t<p/>\n\t</body>\n</html>");
xml::Attributes attr;
attr["class"]="main";
xml::Node test(xml::Node("html") // example
<<(xml::Node("head")
<<xml::String("title"))
<<(xml::Node("body")
<<(xml::String("h1")
<<(xml::Attributes()
<<(xml::Attr("class")="main")))
<<xml::String("p")
<<xml::String("p")
<<xml::String("h2")
<<xml::String("p")
<<xml::String("p")));
test["body"]["h1"] = "Title";
test["body"]["p"] = "First Paragraf.";
CPPUNIT_ASSERT_EQUAL(std::string("main"),
test["body"]["h1"].attr("class"));
std::stringstream ss;
test.out(ss);
CPPUNIT_ASSERT_EQUAL(contents, ss.str());
xml::Factory factory(xml::Node("html") // template
<<(xml::Node("head")
<<xml::String("title"))
<<(xml::Node("body")
<<xml::String("h1").attr("class", xml::optional)
16 years ago
<<xml::String("h2")
<<xml::String("p")));
std::auto_ptr<xml::Node> read(factory.read(ss)); // read back the example
std::stringstream ss2;
read->out(ss2);
CPPUNIT_ASSERT_EQUAL(contents, ss2.str());
}
void project() {
xml::Factory applications(xml::Node("applications")
<<(xml::Node("application")
.attr("id", xml::mandatory)
.attr("os", xml::optional)
<<xml::String("title")
<<xml::String("icon")
<<xml::String("info")
<<(xml::String("prog")
.attr("os", xml::optional))
<<(xml::Node("args")
.attr("os", xml::optional)
<<xml::String("arg"))
<<(xml::Node("env")
.attr("os", xml::optional)
<<(xml::Node("var")
.attr("name", xml::mandatory)
.attr("value", xml::optional)))
<<(xml::Node("runtime")
.attr("os", xml::optional)
<<(xml::Node("copy")
.attr("from", xml::mandatory)
.attr("to", xml::mandatory)
.attr("os", xml::optional)))
<<(xml::Node("buildtime")
<<(xml::Node("copy")
.attr("from", xml::mandatory)
.attr("to", xml::mandatory)))));
xml::Factory edition(xml::Node("edition")
<<xml::String("userfriendly-name")
<<xml::String("update-url")
<<xml::String("startpage")
<<xml::String("startsplash")
<<xml::String("endsplash")
<<(xml::String("termination")
.attr("terminate-on-media-removal", xml::optional)
.attr("terminate-children", xml::optional)
.attr("cleanup-files", xml::optional))
<<(xml::Node("startactions")
.attr("os", xml::optional)
<<(xml::Node("application")
.attr("id", xml::mandatory)
.attr("os", xml::optional)
<<xml::String("splash")
<<xml::String("type")))
<<(xml::Node("stopactions")
.attr("os", xml::optional)
<<(xml::Node("application")
.attr("id", xml::mandatory)
.attr("os", xml::optional)
<<xml::String("splash")))
<<(xml::Node("tree")
<<(xml::String("application")
.attr("id", xml::mandatory)
.attr("os", xml::optional))
<<(xml::Node("folder")
.attr("open", xml::optional)
.attr("os", xml::optional)
<<xml::String("title")
<<xml::String("icon")
<<xml::String("info")
<<(xml::String("application")
.attr("id", xml::mandatory)
.attr("os", xml::optional))))
<<(xml::Node("toolbar")
.attr("os", xml::optional)
<<(xml::String("application")
.attr("id", xml::mandatory)
.attr("os", xml::optional)))
<<(xml::Node("tray")
.attr("os", xml::optional)
<<xml::String("icon")
<<(xml::String("application")
.attr("id", xml::mandatory)
.attr("os", xml::optional))));
/*std::cout<<std::endl
<<*applications<<std::endl
<<*edition<<std::endl;*/
16 years ago
}
CPPUNIT_TEST_SUITE(FunTest);
CPPUNIT_TEST(playing);
CPPUNIT_TEST(project);
CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(FunTest);
int main(int argc, char** argv) try {
std::ofstream ofs((*argv+std::string(".xml")).c_str());
16 years ago
CppUnit::TextUi::TestRunner runner;
runner.setOutputter(new CppUnit::XmlOutputter(&runner.result(), ofs));
16 years ago
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
return runner.run() ? 0 : 1;
} catch (std::exception& e) {
std::cerr<<"***Exception: "<<e.what()<<std::endl;
return 1;
}