diff options
-rw-r--r-- | CMakeLists.txt | 51 | ||||
-rw-r--r-- | src/plugins/xml/XmlOutput.cpp | 116 | ||||
-rw-r--r-- | src/plugins/xml/XmlOutput.hpp | 20 | ||||
-rw-r--r-- | test/core/model/TestAdvanced.hpp | 12 | ||||
-rw-r--r-- | test/plugins/xml/XmlOutputTest.cpp | 111 |
5 files changed, 253 insertions, 57 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b310a2..15e2f32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -319,16 +319,6 @@ IF(TEST) ousia_core ) - ADD_EXECUTABLE(ousia_test_filesystem - test/plugins/filesystem/FileLocatorTest - ) - - TARGET_LINK_LIBRARIES(ousia_test_filesystem - ${GTEST_LIBRARIES} - ousia_core - ousia_filesystem - ) - # ADD_EXECUTABLE(ousia_test_css # test/plugins/css/Tokenizer # test/plugins/css/CodeTokenizerTest @@ -341,6 +331,16 @@ IF(TEST) # ousia_css # ) + ADD_EXECUTABLE(ousia_test_filesystem + test/plugins/filesystem/FileLocatorTest + ) + + TARGET_LINK_LIBRARIES(ousia_test_filesystem + ${GTEST_LIBRARIES} + ousia_core + ousia_filesystem + ) + ADD_EXECUTABLE(ousia_test_html test/plugins/html/DemoOutputTest ) @@ -351,6 +351,16 @@ IF(TEST) ousia_html ) +# ADD_EXECUTABLE(ousia_test_mozjs +# test/plugins/mozjs/MozJsScriptEngineTest +# ) + +# TARGET_LINK_LIBRARIES(ousia_test_mozjs +# ${GTEST_LIBRARIES} +# ousia_core +# ousia_mozjs +# ) + ADD_EXECUTABLE(ousia_test_osml test/formats/osml/OsmlParserTest test/formats/osml/OsmlStreamParserTest @@ -375,24 +385,25 @@ IF(TEST) ousia_filesystem ) -# ADD_EXECUTABLE(ousia_test_mozjs -# test/plugins/mozjs/MozJsScriptEngineTest -# ) + ADD_EXECUTABLE(ousia_test_xml + test/plugins/xml/XmlOutputTest + ) -# TARGET_LINK_LIBRARIES(ousia_test_mozjs -# ${GTEST_LIBRARIES} -# ousia_core -# ousia_mozjs -# ) + TARGET_LINK_LIBRARIES(ousia_test_xml + ${GTEST_LIBRARIES} + ousia_core + ousia_xml + ) # Register the unit tests ADD_TEST(ousia_test_core ousia_test_core) - ADD_TEST(ousia_test_filesystem ousia_test_filesystem) # ADD_TEST(ousia_test_css ousia_test_css) + ADD_TEST(ousia_test_filesystem ousia_test_filesystem) ADD_TEST(ousia_test_html ousia_test_html) +# ADD_TEST(ousia_test_mozjs ousia_test_mozjs) ADD_TEST(ousia_test_osml ousia_test_osml) ADD_TEST(ousia_test_osxml ousia_test_osxml) -# ADD_TEST(ousia_test_mozjs ousia_test_mozjs) + ADD_TEST(ousia_test_xml ousia_test_xml) ENDIF() ################################################################################ diff --git a/src/plugins/xml/XmlOutput.cpp b/src/plugins/xml/XmlOutput.cpp index e17b4ab..37d95ec 100644 --- a/src/plugins/xml/XmlOutput.cpp +++ b/src/plugins/xml/XmlOutput.cpp @@ -55,14 +55,16 @@ void XmlTransformer::writeXml(Handle<Document> doc, std::ostream &out, createImportElement(document, d, resourceManager, "domain"); if (import != nullptr) { document->addChild(import); + // add the import as namespace information to the document node as + // well. + document->getAttributes().emplace( + std::string("xmlns:") + d->getName(), + import->getAttributes()["src"]); } else { logger.warning(std::string( - "The location of domain \")" + d->getName() + + "The location of domain \"" + d->getName() + "\" could not be retrieved using the given ResourceManager.")); } - // add the import as namespace information to the document node as well. - document->getAttributes().emplace(std::string("xmlns:") + d->getName(), - import->getAttributes()["src"]); } // write imports for all referenced typesystems. for (auto t : doc->getTypesystems()) { @@ -72,7 +74,7 @@ void XmlTransformer::writeXml(Handle<Document> doc, std::ostream &out, document->addChild(import); } else { logger.warning(std::string( - "The location of typesystem \")" + t->getName() + + "The location of typesystem \"" + t->getName() + "\" could not be retrieved using the given ResourceManager.")); } } @@ -96,46 +98,50 @@ static std::string toString(Variant v, bool pretty) } } -Rooted<Element> XmlTransformer::transformStructuredEntity( - Handle<Element> parent, Handle<StructuredEntity> s, Logger &logger, - bool pretty) -{ - Manager &mgr = parent->getManager(); - // TODO: Is this the right handling? - // copy the attributes. - Variant attrs = s->getAttributes(); +std::map<std::string, std::string> XmlTransformer::transformAttributes( + DocumentEntity *entity, Logger &logger, bool pretty) +{ // copy the attributes. + Variant attrs = entity->getAttributes(); // build them. - s->getDescriptor()->getAttributesDescriptor()->build(attrs, logger); + entity->getDescriptor()->getAttributesDescriptor()->build(attrs, logger); // get the array representation. Variant::arrayType attrArr = attrs.asArray(); // transform them to string key-value pairs. NodeVector<Attribute> as = - s->getDescriptor()->getAttributesDescriptor()->getAttributes(); + entity->getDescriptor()->getAttributesDescriptor()->getAttributes(); std::map<std::string, std::string> xmlAttrs; for (size_t a = 0; a < as.size(); a++) { xmlAttrs.emplace(as[a]->getName(), toString(attrArr[a], pretty)); } + return xmlAttrs; +} + +void XmlTransformer::addNameAttribute(Handle<ousia::Node> n, + std::map<std::string, std::string> &attrs) +{ // copy the name attribute. - if (!s->getName().empty()) { - xmlAttrs.emplace("name", s->getName()); + if (!n->getName().empty()) { + attrs.emplace("name", n->getName()); } +} - // create the XML element itself. - Rooted<Element> elem{ - new Element{mgr, parent, s->getDescriptor()->getName(), xmlAttrs, - s->getDescriptor()->getParent().cast<Domain>()->getName()}}; - // then transform the fields. +void XmlTransformer::transformChildren(DocumentEntity *parentEntity, + Handle<Element> parent, Logger &logger, + bool pretty) +{ + Manager &mgr = parent->getManager(); NodeVector<FieldDescriptor> fieldDescs = - s->getDescriptor()->getFieldDescriptors(); + parentEntity->getDescriptor()->getFieldDescriptors(); for (size_t f = 0; f < fieldDescs.size(); f++) { - NodeVector<StructureNode> field = s->getField(f); + NodeVector<StructureNode> field = parentEntity->getField(f); Rooted<FieldDescriptor> fieldDesc = fieldDescs[f]; // if this is not the default field create an intermediate node for it. - Rooted<Element> par = elem; + Rooted<Element> par = parent; if (fieldDesc->getFieldType() != FieldDescriptor::FieldType::TREE && !fieldDesc->isPrimitive()) { - par = Rooted<Element>{new Element(mgr, elem, fieldDesc->getName())}; - elem->addChild(par); + par = + Rooted<Element>{new Element(mgr, parent, fieldDesc->getName())}; + parent->addChild(par); } for (auto c : field) { // transform each child. @@ -146,13 +152,67 @@ Rooted<Element> XmlTransformer::transformStructuredEntity( } else if (c->isa(&RttiTypes::DocumentPrimitive)) { child = transformPrimitive(par, c.cast<DocumentPrimitive>(), logger, pretty); + } else { + child = transformAnchor(par, c.cast<Anchor>(), logger, pretty); } - // TODO: Handle Anchors if (child != nullptr) { par->addChild(child); } } } +} + +Rooted<Element> XmlTransformer::transformStructuredEntity( + Handle<Element> parent, Handle<StructuredEntity> s, Logger &logger, + bool pretty) +{ + Manager &mgr = parent->getManager(); + // transform the attributes. + auto attrs = transformAttributes(s.get(), logger, pretty); + addNameAttribute(s, attrs); + // create the XML element itself. + Rooted<Element> elem{ + new Element{mgr, parent, s->getDescriptor()->getName(), + transformAttributes(s.get(), logger, pretty), + s->getDescriptor()->getParent().cast<Domain>()->getName()}}; + // then transform the children. + transformChildren(s.get(), elem, logger, pretty); + return elem; +} + +Rooted<Element> XmlTransformer::transformAnchor(Handle<Element> parent, + Handle<Anchor> a, + Logger &logger, bool pretty) +{ + Rooted<Element> elem; + if (a->isStart()) { + // if this is the start anchor we append all the additional information + // of the annotation here. + // transform the attributes. + auto attrs = + transformAttributes(a->getAnnotation().get(), logger, pretty); + addNameAttribute(a->getAnnotation(), attrs); + + elem = Rooted<Element>{new Element( + parent->getManager(), parent, + a->getAnnotation()->getDescriptor()->getName(), attrs, "a:start")}; + // and handle the children. + transformChildren(a->getAnnotation().get(), elem, logger, pretty); + } else if (a->isEnd()) { + /* + * in principle !a->isStart() should imply a->isEnd() but if no + * annotation is set both is false, so we check it to be sure. + * In case of an end anchor we just create an empty element with the + * annotation name. + */ + std::map<std::string, std::string> attrs; + addNameAttribute(a->getAnnotation(), attrs); + elem = Rooted<Element>{new Element( + parent->getManager(), parent, + a->getAnnotation()->getDescriptor()->getName(), attrs, "a:end")}; + } else { + logger.warning("Ignoring disconnected Anchor", *a); + } return elem; } diff --git a/src/plugins/xml/XmlOutput.hpp b/src/plugins/xml/XmlOutput.hpp index 2bb4190..24f2d49 100644 --- a/src/plugins/xml/XmlOutput.hpp +++ b/src/plugins/xml/XmlOutput.hpp @@ -37,13 +37,25 @@ namespace xml { class XmlTransformer { private: + std::map<std::string, std::string> transformAttributes( + DocumentEntity *entity, Logger &logger, bool pretty); + + void addNameAttribute(Handle<ousia::Node> n, + std::map<std::string, std::string> &attrs); + + void transformChildren(DocumentEntity *parentEntity, Handle<Element> parent, + Logger &logger, bool pretty); + Rooted<Element> transformStructuredEntity(Handle<Element> parent, Handle<StructuredEntity> s, Logger &logger, bool pretty); + Rooted<Element> transformAnchor(Handle<Element> parent, Handle<Anchor> a, + Logger &logger, bool pretty); + Rooted<Text> transformPrimitive(Handle<Element> parent, - Handle<DocumentPrimitive> p, - Logger &logger, bool pretty); + Handle<DocumentPrimitive> p, Logger &logger, + bool pretty); public: /** @@ -62,8 +74,8 @@ public: * @param pretty is a flag that manipulates whether newlines and tabs are * used. */ - void writeXml(Handle<Document> doc, std::ostream &out, Logger &logger,ResourceManager& resMgr, - bool pretty); + void writeXml(Handle<Document> doc, std::ostream &out, Logger &logger, + ResourceManager &resMgr, bool pretty); }; } } diff --git a/test/core/model/TestAdvanced.hpp b/test/core/model/TestAdvanced.hpp index 8e81554..71379d2 100644 --- a/test/core/model/TestAdvanced.hpp +++ b/test/core/model/TestAdvanced.hpp @@ -65,8 +65,10 @@ static Rooted<Domain> constructHeadingDomain(Manager &mgr, "paragraph"}; for (auto &s : secclasses) { Rooted<StructuredClass> desc = resolveDescriptor(bookDomain, s); - Rooted<FieldDescriptor> heading_field = desc->createFieldDescriptor( - logger, FieldDescriptor::FieldType::SUBTREE, "heading", true).first; + Rooted<FieldDescriptor> heading_field = + desc->createFieldDescriptor(logger, + FieldDescriptor::FieldType::SUBTREE, + "heading", true).first; heading_field->addChild(heading); } return domain; @@ -139,7 +141,7 @@ static bool addHeading(Logger &logger, Handle<Document> doc, { // Add the heading. Rooted<StructuredEntity> heading = buildStructuredEntity( - doc, logger, parent, {"heading"}, "heading", {}, ""); + doc, logger, parent, {"heading"}, "heading", Variant::mapType{}, ""); if (heading.isNull()) { return false; } @@ -193,7 +195,7 @@ static Rooted<Document> constructAdvancedDocument(Manager &mgr, Logger &logger, // Add the heading. { Rooted<StructuredEntity> heading = buildStructuredEntity( - doc, logger, book, {"heading"}, "heading", {}, ""); + doc, logger, book, {"heading"}, "heading", Variant::mapType{}, ""); if (heading.isNull()) { return {nullptr}; } @@ -311,4 +313,4 @@ static Rooted<Document> constructAdvancedDocument(Manager &mgr, Logger &logger, } } -#endif /* _TEST_DOCUMENT_HPP_ */ +#endif /* _TEST_DOCUMENT_HPP_ */
\ No newline at end of file diff --git a/test/plugins/xml/XmlOutputTest.cpp b/test/plugins/xml/XmlOutputTest.cpp new file mode 100644 index 0000000..403078d --- /dev/null +++ b/test/plugins/xml/XmlOutputTest.cpp @@ -0,0 +1,111 @@ +/* + 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/>. +*/ + +#include <gtest/gtest.h> + +#include <iostream> +#include <sstream> + +#include <plugins/xml/XmlOutput.hpp> + +#include <core/common/Rtti.hpp> +#include <core/frontend/TerminalLogger.hpp> +#include <core/model/Document.hpp> +#include <core/model/Domain.hpp> + +#include <core/model/TestAdvanced.hpp> +#include <core/model/TestDomain.hpp> + +namespace ousia { +namespace xml { + +TEST(DemoHTMLTransformer, writeHTML) +{ + // Construct Manager + TerminalLogger logger{std::cerr, true}; + Manager mgr{1}; + Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; + // Get the domains. + Rooted<Domain> bookDom = constructBookDomain(mgr, sys, logger); + Rooted<Domain> headingDom = + constructHeadingDomain(mgr, sys, bookDom, logger); + Rooted<Domain> listDom = constructListDomain(mgr, sys, bookDom, logger); + Rooted<Domain> emDom = constructEmphasisDomain(mgr, sys, logger); + // Construct the document. + Rooted<Document> doc = constructAdvancedDocument( + mgr, logger, bookDom, headingDom, listDom, emDom); + ASSERT_TRUE(doc != nullptr); + + // we can only do a rough check here. + ResourceManager dummy; + XmlTransformer transformer; + std::stringstream out; + transformer.writeXml(doc, out, logger, dummy, true); + const std::string res = out.str(); + ASSERT_FALSE(res == ""); + ASSERT_TRUE(res.find("Was ist Aufklärung?") != std::string::npos); + ASSERT_TRUE(res.find( + "Aufklärung ist der Ausgang des Menschen aus seiner " + "selbstverschuldeten Unmündigkeit!") != std::string::npos); + ASSERT_TRUE(res.find("Sapere aude!") != std::string::npos); +} + +TEST(DemoHTMLTransformer, AnnotationProcessing) +{ + // Construct Manager + TerminalLogger logger{std::cerr, true}; + Manager mgr{1}; + Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; + // Get the domains. + Rooted<Domain> bookDom = constructBookDomain(mgr, sys, logger); + Rooted<Domain> emDom = constructEmphasisDomain(mgr, sys, logger); + // Construct a document only containing overlapping annotations. + // it has the form: <em>bla<strong>blub</em>bla</strong> + Rooted<Document> doc{new Document(mgr, "annotations.oxd")}; + doc->referenceDomains({bookDom, emDom}); + Rooted<StructuredEntity> book = + buildRootStructuredEntity(doc, logger, {"book"}); + ASSERT_TRUE(book != nullptr); + Rooted<StructuredEntity> p = + buildStructuredEntity(doc, logger, book, {"paragraph"}); + ASSERT_TRUE(p != nullptr); + Rooted<Anchor> em_start{new Anchor(mgr, p)}; + ASSERT_TRUE(addText(logger, doc, p, "bla")); + Rooted<Anchor> strong_start{new Anchor(mgr, p)}; + ASSERT_TRUE(addText(logger, doc, p, "blub")); + Rooted<Anchor> em_end{new Anchor(mgr, p)}; + ASSERT_TRUE(addText(logger, doc, p, "bla")); + Rooted<Anchor> strong_end{new Anchor(mgr, p)}; + buildAnnotationEntity(doc, logger, {"emphasized"}, em_start, em_end); + buildAnnotationEntity(doc, logger, {"strong"}, strong_start, strong_end); + + // Check serialization. + ResourceManager dummy; + XmlTransformer transformer; + std::stringstream out; + transformer.writeXml(doc, out, logger, dummy, false); + const std::string res = out.str(); + // In HTML the overlapping structure must be serialized as follows: + ASSERT_TRUE( + res.find( + "<a:start:emphasized/><book:text>bla</book:text><a:start:strong/" + "><book:text>blub</book:text><a:end:emphasized/" + "><book:text>bla</book:text><a:end:strong/>") != std::string::npos); +} +} +}
\ No newline at end of file |