/*
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
#include
namespace ousia {
namespace RttiTypes {
extern const Rtti Document;
extern const Rtti Ontology;
extern const Rtti Typesystem;
}
struct XmlStandaloneEnvironment : public StandaloneEnvironment {
OsxmlParser parser;
FileLocator fileLocator;
XmlStandaloneEnvironment(ConcreteLogger &logger)
: StandaloneEnvironment(logger)
{
fileLocator.addDefaultSearchPaths();
fileLocator.addUnittestSearchPath("osxmlparser");
registry.registerDefaultExtensions();
registry.registerParser({"text/vnd.ousia.osml+xml"},
{&RttiTypes::Node}, &parser);
registry.registerResourceLocator(&fileLocator);
}
};
static TerminalLogger logger(std::cerr, true);
TEST(OsxmlParser, mismatchedTag)
{
XmlStandaloneEnvironment env(logger);
env.parse("mismatchedTag.osxml", "", "", RttiSet{&RttiTypes::Document});
ASSERT_TRUE(logger.hasError());
}
static void checkAttributes(Handle expected,
Handle desc)
{
if (expected == nullptr) {
ASSERT_TRUE(desc->getAttributesDescriptor()->getAttributes().empty());
} else {
ASSERT_EQ(expected->getName(),
desc->getAttributesDescriptor()->getName());
auto &attrs_exp = expected->getAttributes();
auto &attrs = desc->getAttributesDescriptor()->getAttributes();
ASSERT_EQ(attrs_exp.size(), attrs.size());
for (size_t i = 0; i < attrs_exp.size(); i++) {
ASSERT_EQ(attrs_exp[i]->getName(), attrs[i]->getName());
ASSERT_EQ(attrs_exp[i]->getType(), attrs[i]->getType());
ASSERT_EQ(attrs_exp[i]->isOptional(), attrs[i]->isOptional());
ASSERT_EQ(attrs_exp[i]->getDefaultValue(),
attrs[i]->getDefaultValue());
}
}
}
static void checkStructuredClass(
Handle n, const std::string &name, Handle ontology,
Variant cardinality = Cardinality::any(),
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(ontology, sc->getParent());
ASSERT_EQ(cardinality, sc->getCardinality());
ASSERT_EQ(transparent, sc->isTransparent());
ASSERT_EQ(root, sc->hasRootPermission());
checkAttributes(attributesDescriptor, sc);
}
static Rooted checkStructuredClass(
const std::string &resolve, const std::string &name, Handle ontology,
Variant cardinality = Cardinality::any(),
Handle attributesDescriptor = nullptr,
Handle superclass = nullptr, bool transparent = false,
bool root = false)
{
auto res = ontology->resolve(&RttiTypes::StructuredClass, resolve);
if (res.size() != 1) {
throw OusiaException("resolution error!");
}
Handle sc = res[0].node.cast();
checkStructuredClass(sc, name, ontology, cardinality, attributesDescriptor,
superclass, transparent, root);
return sc;
}
static void checkAnnotationClass(
Handle n, const std::string &name, Handle ontology,
Handle attributesDescriptor = nullptr)
{
ASSERT_FALSE(n == nullptr);
Handle ac = n.cast();
ASSERT_FALSE(ac == nullptr);
ASSERT_EQ(name, ac->getName());
ASSERT_EQ(ontology, ac->getParent());
checkAttributes(attributesDescriptor, ac);
}
static Rooted checkAnnotationClass(
const std::string &resolve, const std::string &name, Handle ontology,
Handle attributesDescriptor = nullptr)
{
auto res = ontology->resolve(&RttiTypes::AnnotationClass, resolve);
if (res.size() != 1) {
throw OusiaException("resolution error!");
}
Handle ac = res[0].node.cast();
checkAnnotationClass(ac, name, ontology, 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(primitiveType != nullptr, field->isPrimitive());
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 = "",
FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE,
Handle primitiveType = nullptr, bool optional = false)
{
auto res = desc->resolve(&RttiTypes::FieldDescriptor, name);
ASSERT_EQ(1U, res.size());
checkFieldDescriptor(res[0].node, name, parent, children, type,
primitiveType, optional);
}
static void checkFieldDescriptor(
Handle desc, NodeVector children,
const std::string &name = "",
FieldDescriptor::FieldType type = FieldDescriptor::FieldType::TREE,
Handle primitiveType = nullptr, bool optional = false)
{
checkFieldDescriptor(desc, desc, children, name, type, primitiveType,
optional);
}
TEST(OsxmlParser, ontologyParsing)
{
XmlStandaloneEnvironment env(logger);
Rooted book_ontology_node =
env.parse("book_ontology.osxml", "", "", RttiSet{&RttiTypes::Ontology});
ASSERT_FALSE(book_ontology_node == nullptr);
ASSERT_FALSE(logger.hasError());
// check the ontology node.
Rooted book_ontology = book_ontology_node.cast();
ASSERT_EQ("book", book_ontology->getName());
// get the book struct node.
Cardinality single;
single.merge({1});
Rooted book = checkStructuredClass(
"book", "book", book_ontology, single, nullptr, nullptr, false, true);
// get the chapter struct node.
Rooted chapter =
checkStructuredClass("chapter", "chapter", book_ontology);
Rooted section =
checkStructuredClass("section", "section", book_ontology);
Rooted subsection =
checkStructuredClass("subsection", "subsection", book_ontology);
Rooted paragraph =
checkStructuredClass("paragraph", "paragraph", book_ontology,
Cardinality::any(), nullptr, nullptr, true, false);
Rooted text =
checkStructuredClass("text", "text", book_ontology, Cardinality::any(),
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, {}, "", FieldDescriptor::FieldType::TREE,
env.project->getSystemTypesystem()->getStringType(), false);
// check parent handling using the headings ontology.
Rooted headings_ontology_node =
env.parse("headings_ontology.osxml", "", "", RttiSet{&RttiTypes::Ontology});
ASSERT_FALSE(headings_ontology_node == nullptr);
ASSERT_FALSE(logger.hasError());
Rooted headings_ontology = headings_ontology_node.cast();
// now there should be a heading struct.
Rooted heading =
checkStructuredClass("heading", "heading", headings_ontology, single,
nullptr, nullptr, true, false);
// which should be a reference to the paragraph descriptor.
checkFieldDescriptor(heading, paragraph, {text});
// and each struct in the book ontology (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 ontology.
Rooted comments_ontology_node =
env.parse("comments_ontology.osxml", "", "", RttiSet{&RttiTypes::Ontology});
ASSERT_FALSE(comments_ontology_node == nullptr);
ASSERT_FALSE(logger.hasError());
Rooted comments_ontology = comments_ontology_node.cast();
// now we should be able to find a comment annotation.
Rooted comment_anno =
checkAnnotationClass("comment", "comment", comments_ontology);
// as well as a comment struct
Rooted comment =
checkStructuredClass("comment", "comment", comments_ontology);
// and a reply struct
Rooted reply =
checkStructuredClass("reply", "reply", comments_ontology);
// check the fields for each of them.
{
std::vector> descs{comment_anno, comment, reply};
for (auto &d : descs) {
checkFieldDescriptor(d, {paragraph}, "content",
FieldDescriptor::FieldType::TREE, 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});
}
static void checkStructuredEntity(
Handle s, Handle expectedParent, Handle strct,
const Variant::mapType &expectedAttributes = Variant::mapType{},
const std::string &expectedName = "")
{
ASSERT_FALSE(s == nullptr);
ASSERT_TRUE(s->isa(&RttiTypes::StructuredEntity));
Rooted entity = s.cast();
ASSERT_EQ(expectedParent, entity->getParent());
ASSERT_EQ(strct, entity->getDescriptor());
ASSERT_EQ(expectedAttributes, entity->getAttributes());
ASSERT_EQ(expectedName, entity->getName());
}
static void checkStructuredEntity(
Handle s, Handle expectedParent, Handle doc,
const std::string &className,
const Variant::mapType &expectedAttributes = Variant::mapType{},
const std::string &expectedName = "")
{
auto res = doc->resolve(&RttiTypes::StructuredClass, className);
if (res.size() != 1) {
throw OusiaException("resolution error!");
}
Handle sc = res[0].node.cast();
checkStructuredEntity(s, expectedParent, sc, expectedAttributes,
expectedName);
}
static void checkText(Handle p, Handle expectedParent,
Handle doc, Variant expected)
{
checkStructuredEntity(p, expectedParent, doc, "paragraph");
Rooted par = p.cast();
ASSERT_EQ(1U, par->getField().size());
checkStructuredEntity(par->getField()[0], par, doc, "text");
Rooted text = par->getField()[0].cast();
ASSERT_EQ(1U, text->getField().size());
Handle d = text->getField()[0];
ASSERT_FALSE(d == nullptr);
ASSERT_TRUE(d->isa(&RttiTypes::DocumentPrimitive));
Rooted prim = d.cast();
ASSERT_EQ(text, prim->getParent());
ASSERT_EQ(expected, prim->getContent());
}
TEST(OsxmlParser, documentParsing)
{
logger.reset();
XmlStandaloneEnvironment env(logger);
Rooted book_document_node =
env.parse("simple_book.osxml", "", "", RttiSet{&RttiTypes::Document});
ASSERT_FALSE(book_document_node == nullptr);
ASSERT_TRUE(book_document_node->isa(&RttiTypes::Document));
Rooted doc = book_document_node.cast();
ASSERT_TRUE(doc->validate(logger));
checkStructuredEntity(doc->getRoot(), doc, doc, "book");
{
Rooted book = doc->getRoot();
ASSERT_EQ(2U, book->getField().size());
checkText(book->getField()[0], book, doc,
"This might be some introductory text or a dedication.");
checkStructuredEntity(book->getField()[1], book, doc, "chapter",
Variant::mapType{}, "myFirstChapter");
{
Rooted chapter =
book->getField()[1].cast();
ASSERT_EQ(3U, chapter->getField().size());
checkText(chapter->getField()[0], chapter, doc,
"Here we might have an introduction to the chapter.");
checkStructuredEntity(chapter->getField()[1], chapter, doc,
"section", Variant::mapType{},
"myFirstSection");
{
Rooted section =
chapter->getField()[1].cast();
ASSERT_EQ(1U, section->getField().size());
checkText(section->getField()[0], section, doc,
"Here we might find the actual section content.");
}
checkStructuredEntity(chapter->getField()[2], chapter, doc,
"section", Variant::mapType{},
"mySndSection");
{
Rooted section =
chapter->getField()[2].cast();
ASSERT_EQ(1U, section->getField().size());
checkText(section->getField()[0], section, doc,
"Here we might find the actual section content.");
}
}
}
}
TEST(OsxmlParser, emptyNamedField){
logger.reset();
XmlStandaloneEnvironment env(logger);
Rooted book_document_node =
env.parse("empty_named_field.osxml", "", "", RttiSet{&RttiTypes::Document});
ASSERT_FALSE(logger.hasError());
ASSERT_FALSE(book_document_node == nullptr);
ASSERT_TRUE(book_document_node->isa(&RttiTypes::Document));
// check the document content.
Rooted doc = book_document_node.cast();
ASSERT_TRUE(doc->validate(logger));
checkStructuredEntity(doc->getRoot(), doc, doc, "a");
ASSERT_EQ(1U, doc->getRoot()->getDescriptor()->getFieldDescriptors().size());
ASSERT_TRUE(doc->getRoot()->getField(0).empty());
}
}