/*
Ousía
Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace ousia {
namespace RttiTypes {
extern const Rtti Document;
extern const Rtti Domain;
extern const Rtti Typesystem;
}
struct XmlStandaloneEnvironment : public StandaloneEnvironment {
XmlParser xmlParser;
FileLocator fileLocator;
XmlStandaloneEnvironment(ConcreteLogger &logger)
: StandaloneEnvironment(logger)
{
fileLocator.addDefaultSearchPaths();
fileLocator.addUnittestSearchPath("xmlparser");
registry.registerDefaultExtensions();
registry.registerParser({"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"},
{&RttiTypes::Node}, &xmlParser);
registry.registerResourceLocator(&fileLocator);
}
};
static TerminalLogger logger(std::cerr, true);
TEST(XmlParser, mismatchedTag)
{
XmlStandaloneEnvironment env(logger);
env.parse("mismatchedTag.oxm", "", "", RttiSet{&RttiTypes::Document});
ASSERT_TRUE(logger.hasError());
}
TEST(XmlParser, generic)
{
XmlStandaloneEnvironment env(logger);
env.parse("generic.oxm", "", "", RttiSet{&RttiTypes::Node});
#ifdef MANAGER_GRAPHVIZ_EXPORT
env.manager.exportGraphviz("xmlDocument.dot");
#endif
}
static void checkStructuredClass(
Handle n, const std::string &name, Handle domain,
Variant cardinality = AnyCardinality,
Handle attributesDescriptor = nullptr,
Handle superclass = nullptr, bool transparent = false,
bool root = false)
{
ASSERT_FALSE(n == nullptr);
Handle sc = n.cast();
ASSERT_FALSE(sc == nullptr);
ASSERT_EQ(name, sc->getName());
ASSERT_EQ(domain, sc->getParent());
ASSERT_EQ(cardinality, sc->getCardinality());
ASSERT_EQ(transparent, sc->isTransparent());
ASSERT_EQ(root, sc->hasRootPermission());
ASSERT_EQ(attributesDescriptor, sc->getAttributesDescriptor());
}
static Rooted checkStructuredClass(
const std::string &resolve, const std::string &name, Handle domain,
Variant cardinality = AnyCardinality,
Handle attributesDescriptor = nullptr,
Handle superclass = nullptr, bool transparent = false,
bool root = false)
{
auto res = domain->resolve(RttiTypes::StructuredClass, resolve);
if (res.size() != 1) {
throw OusiaException("resolution error!");
}
Handle sc = res[0].node.cast();
checkStructuredClass(sc, name, domain, cardinality, attributesDescriptor,
superclass, transparent, root);
return sc;
}
static void checkAnnotationClass(
Handle n, const std::string &name, Handle domain,
Handle attributesDescriptor = nullptr)
{
ASSERT_FALSE(n == nullptr);
Handle ac = n.cast();
ASSERT_FALSE(ac == nullptr);
ASSERT_EQ(name, ac->getName());
ASSERT_EQ(domain, ac->getParent());
ASSERT_EQ(attributesDescriptor, ac->getAttributesDescriptor());
}
static Rooted checkAnnotationClass(
const std::string &resolve, const std::string &name, Handle domain,
Handle attributesDescriptor = nullptr)
{
auto res = domain->resolve(RttiTypes::AnnotationClass, resolve);
if (res.size() != 1) {
throw OusiaException("resolution error!");
}
Handle ac = res[0].node.cast();
checkAnnotationClass(ac, name, domain, attributesDescriptor);
return ac;
}
static void checkFieldDescriptor(
Handle n, const std::string &name, Handle parent,
NodeVector children,
FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE,
Handle primitiveType = nullptr, bool optional = false)
{
ASSERT_FALSE(n == nullptr);
Handle field = n.cast();
ASSERT_FALSE(field.isNull());
ASSERT_EQ(name, field->getName());
ASSERT_EQ(parent, field->getParent());
ASSERT_EQ(type, field->getFieldType());
ASSERT_EQ(primitiveType, field->getPrimitiveType());
ASSERT_EQ(optional, field->isOptional());
// check the children.
ASSERT_EQ(children.size(), field->getChildren().size());
for (unsigned int c = 0; c < children.size(); c++) {
ASSERT_EQ(children[c], field->getChildren()[c]);
}
}
static void checkFieldDescriptor(
Handle desc, Handle parent,
NodeVector children,
const std::string &name = DEFAULT_FIELD_NAME,
FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE,
Handle primitiveType = nullptr, bool optional = false)
{
auto res = desc->resolve(RttiTypes::FieldDescriptor, name);
ASSERT_EQ(1, res.size());
checkFieldDescriptor(res[0].node, name, parent, children, type,
primitiveType, optional);
}
static void checkFieldDescriptor(
Handle desc, NodeVector children,
const std::string &name = DEFAULT_FIELD_NAME,
FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE,
Handle primitiveType = nullptr, bool optional = false)
{
checkFieldDescriptor(desc, desc, children, name, type, primitiveType,
optional);
}
TEST(XmlParser, domainParsing)
{
XmlStandaloneEnvironment env(logger);
Rooted book_domain_node =
env.parse("book_domain.oxm", "", "", RttiSet{&RttiTypes::Domain});
ASSERT_FALSE(book_domain_node == nullptr);
ASSERT_FALSE(logger.hasError());
// check the domain node.
Rooted book_domain = book_domain_node.cast();
ASSERT_EQ("book", book_domain->getName());
// get the book struct node.
Cardinality single;
single.merge({1});
Rooted book = checkStructuredClass(
"book", "book", book_domain, single, nullptr, nullptr, false, true);
// get the chapter struct node.
Rooted chapter =
checkStructuredClass("chapter", "chapter", book_domain);
Rooted section =
checkStructuredClass("section", "section", book_domain);
Rooted subsection =
checkStructuredClass("subsection", "subsection", book_domain);
Rooted paragraph =
checkStructuredClass("paragraph", "paragraph", book_domain,
AnyCardinality, nullptr, nullptr, true, false);
Rooted text =
checkStructuredClass("text", "text", book_domain, AnyCardinality,
nullptr, nullptr, true, false);
// check the FieldDescriptors.
checkFieldDescriptor(book, {chapter, paragraph});
checkFieldDescriptor(chapter, {section, paragraph});
checkFieldDescriptor(section, {subsection, paragraph});
checkFieldDescriptor(subsection, {paragraph});
checkFieldDescriptor(paragraph, {text});
checkFieldDescriptor(
text, {}, "content", FieldDescriptor::FieldType::PRIMITIVE,
env.project->getSystemTypesystem()->getStringType(), false);
// check parent handling using the headings domain.
Rooted headings_domain_node =
env.parse("headings_domain.oxm", "", "", RttiSet{&RttiTypes::Domain});
ASSERT_FALSE(headings_domain_node == nullptr);
ASSERT_FALSE(logger.hasError());
Rooted headings_domain = headings_domain_node.cast();
// now there should be a heading struct.
Rooted heading =
checkStructuredClass("heading", "heading", headings_domain, single,
nullptr, nullptr, true, false);
// which should be a reference to the paragraph descriptor.
checkFieldDescriptor(heading, paragraph, {text});
// and each struct in the book domain (except for text) should have a
// heading field now.
checkFieldDescriptor(book, {heading}, "heading",
FieldDescriptor::FieldType::SUBTREE, nullptr, true);
checkFieldDescriptor(chapter, {heading}, "heading",
FieldDescriptor::FieldType::SUBTREE, nullptr, true);
checkFieldDescriptor(section, {heading}, "heading",
FieldDescriptor::FieldType::SUBTREE, nullptr, true);
checkFieldDescriptor(subsection, {heading}, "heading",
FieldDescriptor::FieldType::SUBTREE, nullptr, true);
checkFieldDescriptor(paragraph, {heading}, "heading",
FieldDescriptor::FieldType::SUBTREE, nullptr, true);
// check annotation handling using the comments domain.
Rooted comments_domain_node =
env.parse("comments_domain.oxm", "", "", RttiSet{&RttiTypes::Domain});
ASSERT_FALSE(comments_domain_node == nullptr);
ASSERT_FALSE(logger.hasError());
Rooted comments_domain = comments_domain_node.cast();
// now we should be able to find a comment annotation.
Rooted comment_anno =
checkAnnotationClass("comment", "comment", comments_domain);
// as well as a comment struct
Rooted comment =
checkStructuredClass("comment", "comment", comments_domain);
// and a reply struct
Rooted reply =
checkStructuredClass("reply", "reply", comments_domain);
// check the fields for each of them.
{
std::vector> descs{comment_anno, comment, reply};
for (auto &d : descs) {
checkFieldDescriptor(d, {paragraph}, "content",
FieldDescriptor::FieldType::SUBTREE, nullptr,
false);
checkFieldDescriptor(d, {reply}, "replies",
FieldDescriptor::FieldType::SUBTREE, nullptr,
false);
}
}
// paragraph should have comment as child now as well.
checkFieldDescriptor(paragraph, {text, comment});
// as should heading, because it references the paragraph default field.
checkFieldDescriptor(heading, paragraph, {text, comment});
}
}