summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/XML.cpp29
-rw-r--r--src/core/XML.hpp7
-rw-r--r--src/core/model/Document.cpp52
-rw-r--r--src/core/model/Document.hpp58
-rw-r--r--src/core/model/Domain.hpp7
-rw-r--r--src/plugins/html/DemoOutput.cpp131
-rw-r--r--src/plugins/html/DemoOutput.hpp32
-rw-r--r--test/core/model/TestAdvanced.hpp346
-rw-r--r--test/core/model/TestDomain.hpp12
-rw-r--r--test/plugins/html/DemoOutputTest.cpp17
10 files changed, 627 insertions, 64 deletions
diff --git a/src/core/XML.cpp b/src/core/XML.cpp
index 038cb86..7f03b35 100644
--- a/src/core/XML.cpp
+++ b/src/core/XML.cpp
@@ -4,12 +4,16 @@
namespace ousia {
namespace xml {
-void Node::serialize(std::ostream& out){
+void Node::serialize(std::ostream &out, const std::string &doctype)
+{
out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ if (doctype != "") {
+ out << doctype << "\n";
+ }
doSerialize(out, 0);
}
-void Element::doSerialize(std::ostream& out, unsigned int tabdepth)
+void Element::doSerialize(std::ostream &out, unsigned int tabdepth)
{
for (unsigned int t = 0; t < tabdepth; t++) {
out << '\t';
@@ -18,17 +22,22 @@ void Element::doSerialize(std::ostream& out, unsigned int tabdepth)
for (auto &a : attributes) {
out << ' ' << a.first << "=\"" << a.second << '\"';
}
- out << ">\n";
- for (auto &n : children) {
- n->doSerialize(out, tabdepth + 1);
- }
- for (unsigned int t = 0; t < tabdepth; t++) {
- out << '\t';
+ // if we have no children, we close the tag immediately.
+ if (children.size() == 0) {
+ out << "/>\n";
+ } else {
+ out << ">\n";
+ for (auto &n : children) {
+ n->doSerialize(out, tabdepth + 1);
+ }
+ for (unsigned int t = 0; t < tabdepth; t++) {
+ out << '\t';
+ }
+ out << "</" << name << ">\n";
}
- out << "</" << name << ">\n";
}
-void Text::doSerialize(std::ostream& out, unsigned int tabdepth)
+void Text::doSerialize(std::ostream &out, unsigned int tabdepth)
{
for (unsigned int t = 0; t < tabdepth; t++) {
out << '\t';
diff --git a/src/core/XML.hpp b/src/core/XML.hpp
index 9ca124a..51ef6fd 100644
--- a/src/core/XML.hpp
+++ b/src/core/XML.hpp
@@ -64,9 +64,12 @@ public:
/**
* This method writes an XML prolog and the XML representing the current
* node, including all children, to the given output stream.
- * @param out is the output stream the serialized data shall be written to.
+ * @param out is the output stream the serialized data shall be
+ * written to.
+ * @param doctype enables you to add a prefix after the XML prolog
+ * specifying the doctype.
*/
- void serialize(std::ostream &out);
+ void serialize(std::ostream &out, const std::string & doctype = "");
/**
* This method just writes the XML representation of this node to the
* output stream, without the XML prolog.
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp
index e43337f..945fb3e 100644
--- a/src/core/model/Document.cpp
+++ b/src/core/model/Document.cpp
@@ -153,6 +153,9 @@ Rooted<StructuredEntity> StructuredEntity::buildEntity(
return {nullptr};
}
// append the new entity to the right field.
+ if (!parent->hasField(fieldName)) {
+ return {nullptr};
+ }
NodeVector<StructuredEntity> &field = parent->getField(fieldName);
field.push_back(entity);
@@ -176,13 +179,60 @@ Rooted<DocumentPrimitive> DocumentPrimitive::buildEntity(
return {nullptr};
}
// append the new entity to the right field.
+ if (!parent->hasField(fieldName)) {
+ return {nullptr};
+ }
NodeVector<StructuredEntity> &field = parent->getField(fieldName);
field.push_back(entity);
-
// and return it.
return entity;
}
+Rooted<AnnotationEntity::Anchor> AnnotationEntity::buildAnchor(
+ Handle<DocumentEntity> parent, std::string id, const std::string &fieldName)
+{
+ // If the parent is not set, we can not build the anchor.
+ if (parent == nullptr) {
+ return {nullptr};
+ }
+ // Then construct the Anchor itself
+ Rooted<Anchor> anchor{
+ new AnnotationEntity::Anchor(parent->getManager(), parent, id)};
+ // append the new entity to the right field.
+ if (!parent->hasField(fieldName)) {
+ return {nullptr};
+ }
+ NodeVector<StructuredEntity> &field = parent->getField(fieldName);
+ field.push_back(anchor);
+ // and return it.
+ return anchor;
+}
+
+Rooted<AnnotationEntity> AnnotationEntity::buildEntity(
+ Handle<Document> parent, std::vector<Handle<Domain>> domains,
+ const std::string &className, Handle<AnnotationEntity::Anchor> start,
+ Handle<AnnotationEntity::Anchor> end, Variant attributes, std::string name)
+{
+ // If the parent is not set, we can not build the AnnotationEntity.
+ if (parent == nullptr) {
+ return {nullptr};
+ }
+ // If we can not find the correct descriptor, we can not build the entity
+ // either.
+ Rooted<StructuredClass> descriptor = resolveDescriptor(domains, className);
+ if (descriptor == nullptr) {
+ return {nullptr};
+ }
+ // Then construct the AnnotationEntity itself
+ Rooted<AnnotationEntity> anno{
+ new AnnotationEntity(parent->getManager(), parent, descriptor,
+ attributes, start, end, name)};
+ // append the new entity to the document
+ parent->getAnnotations().push_back(anno);
+ // and return it.
+ return anno;
+}
+
/* Type registrations */
}
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp
index 7523962..993df9e 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -207,21 +207,15 @@ public:
* information please refer to the header documentation above.
*/
class StructuredEntity : public DocumentEntity {
-private:
- NodeVector<AnnotationEntity> annotations;
-
public:
StructuredEntity(Manager &mgr, Handle<Node> parent,
Handle<StructuredClass> descriptor, Variant attributes,
std::string name = "")
: DocumentEntity(mgr, parent, descriptor, std::move(attributes),
- std::move(name)),
- annotations(this)
+ std::move(name))
{
}
- NodeVector<AnnotationEntity> &getAnnotations() { return annotations; }
-
/**
* This builds the root StructuredEntity for the given document. It
* automatically appends the newly build entity to the given document.
@@ -343,12 +337,11 @@ public:
public:
/**
* @param mgr is the Manager instance.
- * @param name is the Anchor id.
* @param parent is the parent of this Anchor in the Structure Tree (!),
* not the AnnotationEntity that references this Anchor.
+ * @param name is the Anchor id.
*/
- Anchor(Manager &mgr, Handle<StructuredEntity> parent,
- std::string name = "")
+ Anchor(Manager &mgr, Handle<DocumentEntity> parent, std::string name)
: StructuredEntity(mgr, parent, nullptr, Variant(), std::move(name))
{
}
@@ -372,6 +365,45 @@ public:
Rooted<Anchor> getStart() { return start; }
Rooted<Anchor> getEnd() { return end; }
+
+ /**
+ * This builds an Anchor as child of the given DocumentEntity. It
+ * automatically appends the newly build Anchor to its parent.
+ *
+ * @param parent is the parent DocumentEntity. The newly constructed
+ * Anchor will automatically be appended to it.
+ * @param id is the id of this Anchor.
+ * @param fieldName is the name of the field where the newly constructed
+ * Anchor shall be appended.
+ *
+ * @return the newly created Anchor or a nullptr if some
+ * input handle was empty.
+ */
+ static Rooted<Anchor> buildAnchor(Handle<DocumentEntity> parent,
+ std::string id,
+ const std::string &fieldName = "");
+ /**
+ * This builds an AnnotationEntity as child of the given DocumentEntity. It
+ * automatically appends the newly build entity to its parent.
+ *
+ * @param parent is the document the newly constructed AnnotationEntity
+ * will be appended to.
+ * @param domains are the domains that are used to find the
+ * AnnotationClass for the new node. The domains will be
+ * searched in the given order.
+ * @param className is the name of the AnnotationClass.
+ * @param attributes are the attributes of the new node in terms of a Struct
+ * variant (empty per default).
+ * @param name is the name of this AnnotationEntity (empty per
+ * default).
+ * @return the newly created AnnotationEntity or a nullptr if some
+ * input handle was empty or the given domains did not
+ * contain a AnnotationClass with the given name.
+ */
+ static Rooted<AnnotationEntity> buildEntity(Handle<Document> parent, std::vector<Handle<Domain>> domains,
+ const std::string &className,
+ Handle<Anchor> start, Handle<Anchor> end,
+ Variant attributes = Variant(), std::string name = "");
};
/**
@@ -382,17 +414,21 @@ class Document : public Node {
private:
// TODO: Might there be several roots? E.g. metadata?
Owned<StructuredEntity> root;
+ NodeVector<AnnotationEntity> annotations;
public:
Document(Manager &mgr, std::string name)
// TODO: Can a document have a parent?
- : Node(mgr, std::move(name), nullptr)
+ : Node(mgr, std::move(name), nullptr),
+ annotations(this)
{
}
void setRoot(Handle<StructuredEntity> root) { this->root = acquire(root); };
Rooted<StructuredEntity> getRoot() const { return root; }
+
+ NodeVector<AnnotationEntity> getAnnotations() { return annotations; }
};
}
diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp
index 18ebfb4..7412ef4 100644
--- a/src/core/model/Domain.hpp
+++ b/src/core/model/Domain.hpp
@@ -521,6 +521,13 @@ public:
* This class has no special properties and is in essence just a Descriptor.
*/
class AnnotationClass : public Descriptor {
+public:
+ AnnotationClass(Manager &mgr, std::string name, Handle<Domain> domain,
+ // TODO: What would be a wise default value for attributes?
+ Handle<StructType> attributesDescriptor)
+ : Descriptor(mgr, std::move(name), domain, attributesDescriptor)
+ {
+ }
};
/**
diff --git a/src/plugins/html/DemoOutput.cpp b/src/plugins/html/DemoOutput.cpp
index 035ba25..92ff88c 100644
--- a/src/plugins/html/DemoOutput.cpp
+++ b/src/plugins/html/DemoOutput.cpp
@@ -16,6 +16,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <stack>
+
+
#include <core/common/Exceptions.hpp>
#include <core/common/Rtti.hpp>
#include <core/common/Variant.hpp>
@@ -30,10 +33,18 @@ void DemoHTMLTransformer::writeHTML(Handle<model::Document> doc,
{
Manager &mgr = doc->getManager();
// Create an XML object tree for the document first.
- Rooted<xml::Element> html{new xml::Element{mgr, "html"}};
+ Rooted<xml::Element> html{new xml::Element{
+ mgr, "html", {{"xlmns", "http://www.w3.org/1999/xhtml"}}}};
// add the head Element
Rooted<xml::Element> head{new xml::Element{mgr, "head"}};
html->children.push_back(head);
+ // add the meta element.
+ Rooted<xml::Element> meta{
+ new xml::Element{mgr,
+ "meta",
+ {{"http-equiv", "Content-Type"},
+ {"content", "text/html; charset=utf-8"}}}};
+ head->children.push_back(meta);
// add the title Element with Text
Rooted<xml::Element> title{new xml::Element{mgr, "title"}};
head->children.push_back(title);
@@ -45,31 +56,42 @@ void DemoHTMLTransformer::writeHTML(Handle<model::Document> doc,
// So far was the "preamble". No we have to get to the document content.
+ // build the start and end map for annotation processing.
+ AnnoMap startMap;
+ AnnoMap endMap;
+ for (auto &a : doc->getAnnotations()) {
+ // we assume uniquely IDed annotations, which should be checked in the
+ // validation process.
+ startMap.emplace(a->getStart()->getName(), a);
+ endMap.emplace(a->getEnd()->getName(), a);
+ }
+
// extract the book root node.
Rooted<model::StructuredEntity> root = doc->getRoot();
if (root->getDescriptor()->getName() != "book") {
throw OusiaException("The given documents root is no book node!");
}
// transform the book node.
- Rooted<xml::Element> book = transformSection(root);
+ Rooted<xml::Element> book = transformSection(root, startMap, endMap);
// add it as child to the body node.
body->children.push_back(book);
// After the content has been transformed, we serialize it.
- html->serialize(out);
+ html->serialize(
+ out,
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
+ "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
}
/**
* This is just for easier internal handling.
*/
-enum class SectionType { BOOK, CHAPTER, SECTION, SUBSECTION, NONE };
+enum class SectionType { BOOK, SECTION, SUBSECTION, NONE };
SectionType getSectionType(const std::string &name)
{
if (name == "book") {
return SectionType::BOOK;
- } else if (name == "chapter") {
- return SectionType::CHAPTER;
} else if (name == "section") {
return SectionType::SECTION;
} else if (name == "subsection") {
@@ -79,7 +101,8 @@ SectionType getSectionType(const std::string &name)
}
}
-Rooted<xml::Element> DemoHTMLTransformer::transformSection(Handle<model::StructuredEntity> section)
+Rooted<xml::Element> DemoHTMLTransformer::transformSection(
+ Handle<model::StructuredEntity> section, AnnoMap &startMap, AnnoMap &endMap)
{
Manager &mgr = section->getManager();
// check the section type.
@@ -93,7 +116,8 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(Handle<model::Structu
Rooted<xml::Element> sec{
new xml::Element{mgr, "div", {{"class", secclass}}}};
// check if we have a heading.
- if (section->hasField("heading")) {
+ if (section->hasField("heading") &&
+ section->getField("heading").size() > 0) {
Rooted<model::StructuredEntity> heading =
section->getField("heading")[0];
std::string headingclass;
@@ -101,14 +125,11 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(Handle<model::Structu
case SectionType::BOOK:
headingclass = "h1";
break;
- case SectionType::CHAPTER:
- headingclass = "h2";
- break;
case SectionType::SECTION:
- headingclass = "h3";
+ headingclass = "h2";
break;
case SectionType::SUBSECTION:
- headingclass = "h4";
+ headingclass = "h3";
break;
case SectionType::NONE:
// this can not happen;
@@ -117,7 +138,8 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(Handle<model::Structu
Rooted<xml::Element> h{new xml::Element{mgr, headingclass}};
sec->children.push_back(h);
// extract the heading text, enveloped in a paragraph Element.
- Rooted<xml::Element> h_content = transformParagraph(heading);
+ Rooted<xml::Element> h_content =
+ transformParagraph(heading, startMap, endMap);
// We omit the paragraph Element and add the children directly to the
// heading Element
for (auto &n : h_content->children) {
@@ -138,12 +160,11 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(Handle<model::Structu
const std::string childDescriptorName = n->getDescriptor()->getName();
Rooted<xml::Element> child;
if (childDescriptorName == "paragraph") {
- child = transformParagraph(n);
- // TODO: Implement
- // } else if(childDescriptorName == "ul"){
- // writeList(n, out);
+ child = transformParagraph(n, startMap, endMap);
+ } else if (childDescriptorName == "ul" || childDescriptorName == "ol") {
+ child = transformList(n, startMap, endMap);
} else {
- child = transformSection(n);
+ child = transformSection(n, startMap, endMap);
}
if (!child.isNull()) {
sec->children.push_back(child);
@@ -152,29 +173,90 @@ Rooted<xml::Element> DemoHTMLTransformer::transformSection(Handle<model::Structu
return sec;
}
-Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(Handle<model::StructuredEntity> par)
+Rooted<xml::Element> DemoHTMLTransformer::transformList(
+ Handle<model::StructuredEntity> list, AnnoMap &startMap, AnnoMap &endMap)
+{
+ Manager &mgr = list->getManager();
+ // create the list Element, which is either ul or ol (depends on descriptor)
+ std::string listclass = list->getDescriptor()->getName();
+ Rooted<xml::Element> l{new xml::Element{mgr, listclass}};
+ // iterate through list items.
+ for (auto &item : list->getField()) {
+ std::string itDescrName = item->getDescriptor()->getName();
+ if (itDescrName == "item") {
+ // create the list item.
+ Rooted<xml::Element> li{new xml::Element{mgr, "li"}};
+ l->children.push_back(li);
+ // extract the item text, enveloped in a paragraph Element.
+ Rooted<xml::Element> li_content =
+ transformParagraph(item, startMap, endMap);
+ // We omit the paragraph Element and add the children directly to
+ // the list item
+ for (auto &n : li_content->children) {
+ li->children.push_back(n);
+ }
+ }
+ }
+ return l;
+}
+
+typedef model::AnnotationEntity::Anchor Anchor;
+typedef std::stack<Rooted<model::AnnotationEntity>> AnnoStack;
+
+Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(
+ Handle<model::StructuredEntity> par, AnnoMap &startMap, AnnoMap &endMap)
{
Manager &mgr = par->getManager();
- // create the p xml::Element
+ // create the p Element
Rooted<xml::Element> p{new xml::Element{mgr, "p"}};
// check if we have a heading.
- if (par->hasField("heading")) {
+ if (par->hasField("heading") && par->getField("heading").size() > 0) {
Rooted<model::StructuredEntity> heading = par->getField("heading")[0];
// put the heading in a strong xml::Element.
Rooted<xml::Element> strong{new xml::Element{mgr, "strong"}};
p->children.push_back(strong);
// extract the heading text, enveloped in a paragraph Element.
- Rooted<xml::Element> h_content = transformParagraph(heading);
+ Rooted<xml::Element> h_content =
+ transformParagraph(heading, startMap, endMap);
// We omit the paragraph Element and add the children directly to the
// heading Element
for (auto &n : h_content->children) {
strong->children.push_back(n);
}
}
-
+
// transform paragraph children to XML as well
for (auto &n : par->getField()) {
+ if (n->isa(typeOf<Anchor>())) {
+ //TODO: This needs some more brain work.
+// // check if this is a start Anchor.
+// auto it = startMap.find(n->getName());
+// if(it != startMap.end()){
+// // if we have a start Anchor, we put another AnnotationEntity
+// // on top the stack.
+// opened.push(it->second);
+// // and we create an open tag.
+//
+// continue;
+// }
+// // check if this is an end Anchor.
+// auto it = endMap.find(n->getName());
+// if(it != endMap.end()){
+// /*
+// * Now it gets somewhat interesting: We have to close all
+// * tags that started after the one that is closed now and
+// * re-open them afterwards. So we create a lokal stack to
+// * temporarily store all AnnotationEntities that need to
+// * be re-opened.
+// */
+// AnnoStack tmp;
+// Rooted<
+// while(!opened.empty() && )
+// }
+//
+ continue;
+ }
std::string childDescriptorName = n->getDescriptor()->getName();
if (childDescriptorName == "text") {
Handle<model::DocumentPrimitive> primitive =
@@ -185,7 +267,6 @@ Rooted<xml::Element> DemoHTMLTransformer::transformParagraph(Handle<model::Struc
p->children.push_back(
new xml::Text(mgr, primitive->getContent().asString()));
}
- // TODO: Handle non-text content
}
return p;
}
diff --git a/src/plugins/html/DemoOutput.hpp b/src/plugins/html/DemoOutput.hpp
index 70a5daa..e08ec2b 100644
--- a/src/plugins/html/DemoOutput.hpp
+++ b/src/plugins/html/DemoOutput.hpp
@@ -30,6 +30,7 @@
#ifndef _OUSIA_HTML_DEMO_OUTPUT_HPP_
#define _OUSIA_HTML_DEMO_OUTPUT_HPP_
+#include <map>
#include <ostream>
#include <core/model/Document.hpp>
@@ -38,22 +39,31 @@
namespace ousia {
namespace html {
+typedef std::map<std::string, Rooted<model::AnnotationEntity>> AnnoMap;
+
class DemoHTMLTransformer {
private:
/**
- * These methods are called recursively to transform a document to an XML
- * tree.
+ * This transforms a section-like entity, namely book, section
+ * and subsection, to an XHTML element, including its header. For the
+ * children of the default field the respective transform function is
+ * called recursively.
*/
- Rooted<xml::Element> transformSection(Handle<model::StructuredEntity> sec);
- Rooted<xml::Element> transformParagraph(Handle<model::StructuredEntity> par);
+ Rooted<xml::Element> transformSection(Handle<model::StructuredEntity> sec,
+ AnnoMap& startMap, AnnoMap& endMap);
/**
- * This method is to be called recursively to write a list to HTML.
- * TODO: Implement
+ * This transforms a list entity, namely ul and ol to an XHTML element.
+ * For each item, the transformParagraph function is called.
*/
-// void writeList(Handle<StructuredEntity> sec, std::ostream& out,
-// int tabdepth);
-
- //TODO: Implement emphasis.
+ Rooted<xml::Element> transformList(Handle<model::StructuredEntity> list,
+ AnnoMap& startMap, AnnoMap& endMap);
+ /**
+ * This transforms a paragraph-like entity, namely heading, item and
+ * paragraph, to an XHTML element including the text and the anchors
+ * contained. For anchor handling we require the AnnoMaps.
+ */
+ Rooted<xml::Element> transformParagraph(Handle<model::StructuredEntity> par,
+ AnnoMap& startMap, AnnoMap& endMap);
public:
/**
@@ -74,7 +84,7 @@ public:
* and lists domains but no other.
* @param out is the output stream the data shall be written to.
*/
- void writeHTML(Handle<model::Document> doc, std::ostream& out);
+ void writeHTML(Handle<model::Document> doc, std::ostream &out);
};
}
}
diff --git a/test/core/model/TestAdvanced.hpp b/test/core/model/TestAdvanced.hpp
new file mode 100644
index 0000000..5af6003
--- /dev/null
+++ b/test/core/model/TestAdvanced.hpp
@@ -0,0 +1,346 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _MODEL_TEST_DOCUMENT_HPP_
+#define _MODEL_TEST_DOCUMENT_HPP_
+
+#include <core/model/Document.hpp>
+#include <core/model/Domain.hpp>
+#include <core/model/Typesystem.hpp>
+
+namespace ousia {
+namespace model {
+
+static Rooted<StructuredClass> resolveDescriptor(Handle<Domain> domain,
+ const std::string &className)
+{
+ // use the actual resolve method.
+ std::vector<Rooted<Managed>> resolved = domain->resolve(className);
+ // take the first valid result.
+ for (auto &r : resolved) {
+ if (r->isa(typeOf<StructuredClass>())) {
+ return r.cast<StructuredClass>();
+ }
+ }
+ // if no valid result exists, return nullptr.
+ return {nullptr};
+}
+
+/**
+ * This constructs the "heading" domain given the book domain.
+ */
+static Rooted<Domain> constructHeadingDomain(Manager &mgr,
+ Handle<SystemTypesystem> sys,
+ Handle<Domain> bookDomain,
+ Logger &logger)
+{
+ // set up domain node.
+ Rooted<Domain> domain{new Domain(mgr, sys, "headings")};
+ // set up cardinality (every section may have at most one heading).
+ Cardinality card;
+ card.merge({0, 1});
+ // set up heading StructuredClass.
+ Rooted<StructuredClass> heading{new StructuredClass(
+ mgr, "heading", domain, card, {nullptr}, {nullptr}, true)};
+ // as field we actually want to refer to the field of paragraph.
+ Rooted<StructuredClass> p = resolveDescriptor(bookDomain, "paragraph");
+ heading->getFieldDescriptors().push_back(p->getFieldDescriptors()[0]);
+ // add the class to the domain.
+ domain->getStructureClasses().push_back(heading);
+ // create a new field for headings in each section type.
+ std::vector<std::string> secclasses{"book", "section", "subsection",
+ "paragraph"};
+ for (auto &s : secclasses) {
+ Rooted<StructuredClass> desc = resolveDescriptor(bookDomain, s);
+ Rooted<FieldDescriptor> heading_field{new FieldDescriptor(
+ mgr, desc, FieldDescriptor::FieldType::SUBTREE, "heading")};
+ heading_field->getChildren().push_back(heading);
+ desc->getFieldDescriptors().push_back(heading_field);
+ }
+ return domain;
+}
+
+/**
+ * This constructs the "list" domain given the book domain.
+ */
+static Rooted<Domain> constructListDomain(Manager &mgr,
+ Handle<SystemTypesystem> sys,
+ Handle<Domain> bookDomain,
+ Logger &logger)
+{
+ // set up domain node.
+ Rooted<Domain> domain{new Domain(mgr, sys, "list")};
+ // set up cardinality
+ Cardinality any;
+ any.merge(Range<size_t>::typeRangeFrom(0));
+ // get book.paragraph
+ Rooted<StructuredClass> p = resolveDescriptor(bookDomain, "paragraph");
+ // set up item StructuredClass;
+ Rooted<StructuredClass> item{new StructuredClass(
+ mgr, "item", domain, any, {nullptr}, {nullptr}, false)};
+ domain->getStructureClasses().push_back(item);
+ // as field we actually want to refer to the field of paragraph.
+ item->getFieldDescriptors().push_back(p->getFieldDescriptors()[0]);
+ // set up list StructuredClasses.
+ std::vector<std::string> listTypes{"ol", "ul"};
+ for (auto &listType : listTypes) {
+ Rooted<StructuredClass> list{new StructuredClass(
+ mgr, listType, domain, any, {nullptr}, p, false)};
+ Rooted<FieldDescriptor> list_field{new FieldDescriptor(mgr, list)};
+ list_field->getChildren().push_back(item);
+ list->getFieldDescriptors().push_back(list_field);
+ domain->getStructureClasses().push_back(list);
+ }
+ return domain;
+}
+
+/**
+ * This constructs the "emphasis" domain.
+ */
+static Rooted<Domain> constructEmphasisDomain(Manager &mgr,
+ Handle<SystemTypesystem> sys,
+ Logger &logger)
+{
+ // set up domain node.
+ Rooted<Domain> domain{new Domain(mgr, sys, "emphasis")};
+ // create AnnotationClasses
+ Rooted<AnnotationClass> em{
+ new AnnotationClass(mgr, "emphasized", domain, {nullptr})};
+ domain->getAnnotationClasses().push_back(em);
+ Rooted<AnnotationClass> strong{
+ new AnnotationClass(mgr, "strong", domain, {nullptr})};
+ domain->getAnnotationClasses().push_back(strong);
+ return domain;
+}
+
+static bool addText(Handle<StructuredEntity> parent,
+ std::vector<Handle<Domain>> &doms,
+ const std::string &content)
+{
+ // Add its text.
+ Rooted<StructuredEntity> text =
+ StructuredEntity::buildEntity(parent, doms, "text");
+ if (text.isNull()) {
+ return false;
+ }
+ // And its primitive content
+ Variant content_var{content.c_str()};
+ Rooted<DocumentPrimitive> primitive =
+ DocumentPrimitive::buildEntity(text, content_var, "content");
+ if (primitive.isNull()) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool addHeading(Handle<StructuredEntity> parent,
+ std::vector<Handle<Domain>> &doms,
+ const std::string &text)
+{
+ // Add the heading.
+ Rooted<StructuredEntity> heading = StructuredEntity::buildEntity(
+ parent, doms, "heading", "heading", {}, "");
+ if (heading.isNull()) {
+ return false;
+ }
+ // Add its text.
+ if (!addText(heading, doms, text)) {
+ return false;
+ }
+ return true;
+}
+
+static int annoIdx = 1;
+
+// Only works for non-overlapping annotations!
+static bool addAnnotation(Handle<Document> doc, Handle<StructuredEntity> parent,
+ std::vector<Handle<Domain>> &doms,
+ const std::string &text, const std::string &annoClass)
+{
+ Rooted<AnnotationEntity::Anchor> start =
+ AnnotationEntity::buildAnchor(parent, std::to_string(annoIdx++));
+ if (start.isNull()) {
+ return false;
+ }
+ if (!addText(parent, doms, text)) {
+ return false;
+ }
+ Rooted<AnnotationEntity::Anchor> end =
+ AnnotationEntity::buildAnchor(parent, std::to_string(annoIdx++));
+ if (end.isNull()) {
+ return false;
+ }
+ Rooted<AnnotationEntity> anno =
+ AnnotationEntity::buildEntity(doc, doms, annoClass, start, end);
+ if (anno.isNull()) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * This constructs a more advanced book document using not only the book
+ * domain but also headings, emphasis and lists.
+ * TODO: insert emphasis and lists.
+ */
+static Rooted<Document> constructAdvancedDocument(Manager &mgr,
+ Rooted<Domain> bookDom,
+ Rooted<Domain> headingDom,
+ Rooted<Domain> listDom)
+{
+ std::vector<Handle<Domain>> doms{bookDom, headingDom, listDom};
+
+ // Start with the (empty) document.
+ Rooted<Document> doc{new Document(mgr, "kant_was_ist_aufklaerung.oxd")};
+
+ // Add the root.
+ Rooted<StructuredEntity> book =
+ StructuredEntity::buildRootEntity(doc, doms, "book");
+ if (book.isNull()) {
+ return {nullptr};
+ }
+
+ // Add the heading.
+ {
+ Rooted<StructuredEntity> heading = StructuredEntity::buildEntity(
+ book, doms, "heading", "heading", {}, "");
+ if (heading.isNull()) {
+ return {nullptr};
+ }
+ if (!addText(heading, doms, "Beantwortung der Frage: ")) {
+ return {nullptr};
+ }
+ if (!addAnnotation(doc, heading, doms, "Was ist Aufklärung?",
+ "emphasized")) {
+ return {nullptr};
+ }
+ }
+
+ // Add the main section.
+ Rooted<StructuredEntity> sec =
+ StructuredEntity::buildEntity(book, doms, "section");
+ if (sec.isNull()) {
+ return {nullptr};
+ }
+
+ // Add the heading.
+ if (!addHeading(sec, doms, "Was ist Aufklärung?")) {
+ return {nullptr};
+ }
+
+ // Add paragraph with main text.
+ {
+ Rooted<StructuredEntity> p =
+ StructuredEntity::buildEntity(sec, doms, "paragraph");
+ if (p.isNull()) {
+ return {nullptr};
+ }
+ // Add its text.
+ {
+ if (!addAnnotation(doc, p, doms,
+ "Aufklärung ist der Ausgang des Menschen aus "
+ "seiner selbstverschuldeten Unmündigkeit",
+ "strong")) {
+ return {nullptr};
+ }
+ if (!addAnnotation(doc, p, doms, "Unmündigkeit",
+ "emphasized")) {
+ return {nullptr};
+ }
+ if (!addText(p, doms,
+ "ist das Unvermögen, sich seines Verstandes ohne "
+ "Leitung eines anderen zu bedienen. ")) {
+ return {nullptr};
+ }
+ if (!addAnnotation(doc, p, doms, "Selbstverschuldet",
+ "emphasized")) {
+ return {nullptr};
+ }
+ if (!addText(p, doms,
+ " ist diese Unmündigkeit, wenn die Ursache derselben "
+ "nicht am Mangel des Verstandes, sondern der "
+ "Entschließung und des Mutes liegt, sich seiner ohne "
+ "Leitung eines andern zu bedienen.")) {
+ return {nullptr};
+ }
+ if (!addAnnotation(doc, p, doms,
+ "Sapere aude! Habe Mut, dich deines eigenen "
+ "Verstandes zu bedienen!",
+ "emphasized")) {
+ return {nullptr};
+ }
+ if (!addText(p, doms,
+ " ist also der Wahlspruch der Aufklärung.")) {
+ return {nullptr};
+ }
+ }
+ }
+
+ // Add the "Lesarten" section
+ Rooted<StructuredEntity> lesarten =
+ StructuredEntity::buildEntity(book, doms, "section");
+ if (lesarten.isNull()) {
+ return {nullptr};
+ }
+ // Add the heading.
+ if (!addHeading(lesarten, doms, "Lesarten")) {
+ return {nullptr};
+ }
+ // Add list with citations
+ {
+ // TODO: We need to restrict this to the list domain. Otherwise
+ // this leads to resolve errors for some reason.
+ Rooted<StructuredEntity> ul =
+ StructuredEntity::buildEntity(lesarten, {listDom}, "ul");
+ if (ul.isNull()) {
+ return {nullptr};
+ }
+ std::vector<std::string> citations{
+ "Berlinische Monatsschrift. Dezember-Heft 1784. S. 481–494.",
+ "Kant. Kleine Schriften. Neuwied 1793. Haupt. 8o. S. 34–50.",
+ "I. Kant. Zerstreute Aufsätze. Frankfurt und Leipzig 1793. 8o. S. "
+ "25–37.",
+ "I. Kant. Sämmtliche kleine Schriften. 4 Bände. 1797–98. 8o. "
+ "Königsberg u. Leipzig (Voigt, Jena). Nachdruck. Bd. III, S. "
+ "159–172.",
+ " I. Kant's vermischte Schriften. 3 Bände. Halle 1799. "
+ "(Tieftrunk). Bd. II. S. 687–700.",
+ "Kant. Vorzügliche kleine Schriften und Aufsätze, hrsg. mit Noten "
+ "von F. Ch. Starke. 2 Bände. Leipzig 1833 und Quedlinburg 1838. "
+ "Bd. I, S. 75–84."};
+ for (auto &cit : citations) {
+ // TODO: This needs to be restricted as well.
+ Rooted<StructuredEntity> item =
+ StructuredEntity::buildEntity(ul, {listDom}, "item");
+ if (item.isNull()) {
+ return {nullptr};
+ }
+ if (!addText(item, doms, cit)) {
+ return {nullptr};
+ }
+ }
+ }
+
+ return doc;
+}
+}
+}
+
+#endif /* _TEST_DOCUMENT_HPP_ */
+
diff --git a/test/core/model/TestDomain.hpp b/test/core/model/TestDomain.hpp
index 54e79ee..aa3096d 100644
--- a/test/core/model/TestDomain.hpp
+++ b/test/core/model/TestDomain.hpp
@@ -69,6 +69,18 @@ static Rooted<Domain> constructBookDomain(Manager &mgr,
new FieldDescriptor(mgr, paragraph)};
paragraph->getFieldDescriptors().push_back(paragraph_field);
+ // We append "subsection" to section.
+ Rooted<StructuredClass> subsection{
+ new StructuredClass(mgr, "subsection", domain, any)};
+ section_field->getChildren().push_back(subsection);
+ domain->getStructureClasses().push_back(subsection);
+ // And the field of it.
+ Rooted<FieldDescriptor> subsection_field{
+ new FieldDescriptor(mgr, subsection)};
+ subsection->getFieldDescriptors().push_back(subsection_field);
+ // and we add the paragraph to subsections fields
+ subsection_field->getChildren().push_back(paragraph);
+
// Finally we add the "text" node, which is transparent as well.
Rooted<StructuredClass> text{new StructuredClass(
mgr, "text", domain, any, {nullptr}, {nullptr}, true)};
diff --git a/test/plugins/html/DemoOutputTest.cpp b/test/plugins/html/DemoOutputTest.cpp
index 6123c79..36b53b4 100644
--- a/test/plugins/html/DemoOutputTest.cpp
+++ b/test/plugins/html/DemoOutputTest.cpp
@@ -25,6 +25,7 @@
#include <core/model/Document.hpp>
#include <core/model/Domain.hpp>
+#include <core/model/TestAdvanced.hpp>
#include <core/model/TestDocument.hpp>
#include <core/model/TestDomain.hpp>
@@ -37,17 +38,25 @@ TEST(DemoHTMLTransformer, writeHTML)
Logger logger;
Manager mgr{1};
Rooted<model::SystemTypesystem> sys{new model::SystemTypesystem(mgr)};
- // Get the domain.
- Rooted<model::Domain> domain = constructBookDomain(mgr, sys, logger);
+ // Get the domains.
+ Rooted<model::Domain> bookDom =
+ model::constructBookDomain(mgr, sys, logger);
+ Rooted<model::Domain> headingDom =
+ model::constructHeadingDomain(mgr, sys, bookDom, logger);
+ Rooted<model::Domain> listDom =
+ model::constructListDomain(mgr, sys, bookDom, logger);
+ Rooted<model::Domain> emDom =
+ model::constructEmphasisDomain(mgr, sys, logger);
// Construct the document.
- Rooted<model::Document> doc = model::constructBookDocument(mgr, domain);
+ Rooted<model::Document> doc =
+ model::constructAdvancedDocument(mgr, bookDom, headingDom, listDom);
#ifdef MANAGER_GRAPHVIZ_EXPORT
// dump the manager state
mgr.exportGraphviz("bookDocument.dot");
#endif
- // print it
+ // TODO: change this. Don't use printouts
DemoHTMLTransformer transformer;
transformer.writeHTML(doc, std::cout);
}