From a7019614896fdd3e29b9a28f6a8cfd2c1b365983 Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Sun, 1 Mar 2015 20:47:25 +0100 Subject: Renamed domain to ontology. --- CMakeLists.txt | 6 +- data/domain/bibliography.osxml | 42 - data/domain/book.osxml | 34 - data/domain/comments.osxml | 40 - data/domain/headings.osxml | 33 - data/domain/lists.osxml | 26 - data/domain/meta.osxml | 50 - data/ontology/bibliography.osxml | 42 + data/ontology/book.osxml | 34 + data/ontology/comments.osxml | 40 + data/ontology/headings.osxml | 33 + data/ontology/lists.osxml | 26 + data/ontology/meta.osxml | 50 + src/cli/Main.cpp | 8 +- src/core/model/Document.cpp | 8 +- src/core/model/Document.hpp | 58 +- src/core/model/Domain.cpp | 981 ---------------- src/core/model/Domain.hpp | 1203 -------------------- src/core/model/Node.hpp | 4 +- src/core/model/Ontology.cpp | 981 ++++++++++++++++ src/core/model/Ontology.hpp | 1203 ++++++++++++++++++++ src/core/model/Project.cpp | 6 +- src/core/model/Project.hpp | 14 +- src/core/model/RootNode.hpp | 2 +- src/core/parser/ParserScope.cpp | 2 +- src/core/parser/stack/DocumentHandler.cpp | 2 +- src/core/parser/stack/DocumentHandler.hpp | 2 +- src/core/parser/stack/DomainHandler.cpp | 417 ------- src/core/parser/stack/DomainHandler.hpp | 257 ----- src/core/parser/stack/GenericParserStates.cpp | 26 +- src/core/parser/stack/ImportIncludeHandler.cpp | 6 +- src/core/parser/stack/OntologyHandler.cpp | 417 +++++++ src/core/parser/stack/OntologyHandler.hpp | 257 +++++ src/core/parser/stack/TypesystemHandler.cpp | 16 +- src/core/resource/Resource.cpp | 2 +- src/core/resource/Resource.hpp | 4 +- src/core/resource/ResourceRequest.cpp | 4 +- src/formats/osml/OsmlParser.cpp | 2 +- src/plugins/filesystem/FileLocator.cpp | 4 +- src/plugins/filesystem/FileLocator.hpp | 2 +- src/plugins/html/DemoOutput.hpp | 8 +- src/plugins/xml/XmlOutput.cpp | 10 +- src/plugins/xml/XmlOutput.hpp | 4 +- test/core/RegistryTest.cpp | 4 +- test/core/frontend/TerminalLoggerTest.cpp | 4 +- test/core/model/DocumentTest.cpp | 42 +- test/core/model/DomainTest.cpp | 689 ----------- test/core/model/OntologyTest.cpp | 689 +++++++++++ test/core/model/TestAdvanced.hpp | 68 +- test/core/model/TestDocument.hpp | 8 +- test/core/model/TestDocumentBuilder.hpp | 12 +- test/core/model/TestDomain.hpp | 93 -- test/core/model/TestOntology.hpp | 93 ++ test/core/resource/ResourceRequestTest.cpp | 52 +- test/formats/osml/OsmlParserTest.cpp | 18 +- test/formats/osxml/OsxmlParserTest.cpp | 80 +- test/plugins/filesystem/FileLocatorTest.cpp | 22 +- test/plugins/html/DemoOutputTest.cpp | 24 +- test/plugins/xml/XmlOutputTest.cpp | 34 +- testdata/osmlparser/empty_domain.osml | 2 - testdata/osmlparser/empty_ontology.osml | 2 + testdata/osmlparser/explicit_fields.osml | 2 +- testdata/osmlparser/include_recursive_root.osml | 2 +- testdata/osmlparser/include_root.osml | 2 +- testdata/osmlparser/inline_domain.osml | 14 - testdata/osmlparser/inline_ontology.osml | 14 + testdata/osmlparser/invalid_explicit_fields.osml | 2 +- .../osmlparser/rollback_on_invalid_element.osml | 2 +- testdata/osmlparser/struct_with_no_field.osml | 2 +- testdata/osmlparser/structure_inheritance.osml | 2 +- testdata/osxmlparser/book_domain.osxml | 98 -- testdata/osxmlparser/book_ontology.osxml | 98 ++ testdata/osxmlparser/comments_domain.osxml | 40 - testdata/osxmlparser/comments_ontology.osxml | 40 + testdata/osxmlparser/complex_book.osxml | 12 +- testdata/osxmlparser/headings_domain.osxml | 33 - testdata/osxmlparser/headings_ontology.osxml | 33 + testdata/osxmlparser/simple_book.osxml | 4 +- 78 files changed, 4351 insertions(+), 4351 deletions(-) delete mode 100644 data/domain/bibliography.osxml delete mode 100644 data/domain/book.osxml delete mode 100644 data/domain/comments.osxml delete mode 100644 data/domain/headings.osxml delete mode 100644 data/domain/lists.osxml delete mode 100644 data/domain/meta.osxml create mode 100644 data/ontology/bibliography.osxml create mode 100644 data/ontology/book.osxml create mode 100644 data/ontology/comments.osxml create mode 100644 data/ontology/headings.osxml create mode 100644 data/ontology/lists.osxml create mode 100644 data/ontology/meta.osxml delete mode 100644 src/core/model/Domain.cpp delete mode 100644 src/core/model/Domain.hpp create mode 100644 src/core/model/Ontology.cpp create mode 100644 src/core/model/Ontology.hpp delete mode 100644 src/core/parser/stack/DomainHandler.cpp delete mode 100644 src/core/parser/stack/DomainHandler.hpp create mode 100644 src/core/parser/stack/OntologyHandler.cpp create mode 100644 src/core/parser/stack/OntologyHandler.hpp delete mode 100644 test/core/model/DomainTest.cpp create mode 100644 test/core/model/OntologyTest.cpp delete mode 100644 test/core/model/TestDomain.hpp create mode 100644 test/core/model/TestOntology.hpp delete mode 100644 testdata/osmlparser/empty_domain.osml create mode 100644 testdata/osmlparser/empty_ontology.osml delete mode 100644 testdata/osmlparser/inline_domain.osml create mode 100644 testdata/osmlparser/inline_ontology.osml delete mode 100644 testdata/osxmlparser/book_domain.osxml create mode 100644 testdata/osxmlparser/book_ontology.osxml delete mode 100644 testdata/osxmlparser/comments_domain.osxml create mode 100644 testdata/osxmlparser/comments_ontology.osxml delete mode 100644 testdata/osxmlparser/headings_domain.osxml create mode 100644 testdata/osxmlparser/headings_ontology.osxml diff --git a/CMakeLists.txt b/CMakeLists.txt index ea5c3aa..72273d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,7 +169,7 @@ ADD_LIBRARY(ousia_core src/core/managed/Managed src/core/managed/Manager src/core/model/Document - src/core/model/Domain + src/core/model/Ontology src/core/model/Index src/core/model/Node src/core/model/Project @@ -181,7 +181,7 @@ ADD_LIBRARY(ousia_core src/core/parser/ParserScope src/core/parser/stack/Callbacks src/core/parser/stack/DocumentHandler - src/core/parser/stack/DomainHandler + src/core/parser/stack/OntologyHandler src/core/parser/stack/GenericParserStates src/core/parser/stack/Handler src/core/parser/stack/ImportIncludeHandler @@ -316,7 +316,7 @@ IF(TEST) test/core/managed/ManagedTest test/core/managed/ManagerTest test/core/managed/VariantObjectTest - test/core/model/DomainTest + test/core/model/OntologyTest test/core/model/DocumentTest test/core/model/IndexTest test/core/model/NodeTest diff --git a/data/domain/bibliography.osxml b/data/domain/bibliography.osxml deleted file mode 100644 index 5953e5f..0000000 --- a/data/domain/bibliography.osxml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/domain/book.osxml b/data/domain/book.osxml deleted file mode 100644 index 8ec60ed..0000000 --- a/data/domain/book.osxml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/domain/comments.osxml b/data/domain/comments.osxml deleted file mode 100644 index cb19bd4..0000000 --- a/data/domain/comments.osxml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/domain/headings.osxml b/data/domain/headings.osxml deleted file mode 100644 index 055a204..0000000 --- a/data/domain/headings.osxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/domain/lists.osxml b/data/domain/lists.osxml deleted file mode 100644 index 66e77cb..0000000 --- a/data/domain/lists.osxml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/domain/meta.osxml b/data/domain/meta.osxml deleted file mode 100644 index b595f39..0000000 --- a/data/domain/meta.osxml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/ontology/bibliography.osxml b/data/ontology/bibliography.osxml new file mode 100644 index 0000000..e133a2b --- /dev/null +++ b/data/ontology/bibliography.osxml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/ontology/book.osxml b/data/ontology/book.osxml new file mode 100644 index 0000000..41dc788 --- /dev/null +++ b/data/ontology/book.osxml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/ontology/comments.osxml b/data/ontology/comments.osxml new file mode 100644 index 0000000..621fe1c --- /dev/null +++ b/data/ontology/comments.osxml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/ontology/headings.osxml b/data/ontology/headings.osxml new file mode 100644 index 0000000..1319306 --- /dev/null +++ b/data/ontology/headings.osxml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/ontology/lists.osxml b/data/ontology/lists.osxml new file mode 100644 index 0000000..a177cd4 --- /dev/null +++ b/data/ontology/lists.osxml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/ontology/meta.osxml b/data/ontology/meta.osxml new file mode 100644 index 0000000..6b25305 --- /dev/null +++ b/data/ontology/meta.osxml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cli/Main.cpp b/src/cli/Main.cpp index d03735d..2fe0585 100644 --- a/src/cli/Main.cpp +++ b/src/cli/Main.cpp @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include #include @@ -120,7 +120,7 @@ int main(int argc, char **argv) "The input document file name")( "include,I", po::value>(), "Include paths, where resources like the input document " - "or additional domains, typesystems, etc. might be " + "or additional ontologies, typesystems, etc. might be " "found.")( "output,o", po::value(&outputPath), "The output file name. Per default the input file name will be used.")( @@ -234,11 +234,11 @@ int main(int argc, char **argv) OsxmlParser osxmlParser; registry.registerParser( {"text/vnd.ousia.osml"}, - {&RttiTypes::Document, &RttiTypes::Domain, &RttiTypes::Typesystem}, + {&RttiTypes::Document, &RttiTypes::Ontology, &RttiTypes::Typesystem}, &osmlParser); registry.registerParser( {"text/vnd.ousia.osml+xml"}, - {&RttiTypes::Document, &RttiTypes::Domain, &RttiTypes::Typesystem}, + {&RttiTypes::Document, &RttiTypes::Ontology, &RttiTypes::Typesystem}, &osxmlParser); registry.registerResourceLocator(&fileLocator); diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index 2fcd20d..fc39348 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -723,7 +723,7 @@ void Document::doResolve(ResolutionState &state) if (root != nullptr) { continueResolveCompositum(root, state); } - continueResolveReferences(domains, state); + continueResolveReferences(ontologies, state); continueResolveReferences(typesystems, state); } @@ -761,8 +761,8 @@ bool Document::doValidate(Logger &logger) const void Document::doReference(Handle node) { - if (node->isa(&RttiTypes::Domain)) { - referenceDomain(node.cast()); + if (node->isa(&RttiTypes::Ontology)) { + referenceOntology(node.cast()); } if (node->isa(&RttiTypes::Typesystem)) { referenceTypesystem(node.cast()); @@ -771,7 +771,7 @@ void Document::doReference(Handle node) RttiSet Document::doGetReferenceTypes() const { - return RttiSet{&RttiTypes::Domain, &RttiTypes::Typesystem}; + return RttiSet{&RttiTypes::Ontology, &RttiTypes::Typesystem}; } Rooted Document::createRootStructuredEntity( diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index a5e40ce..dc0f73f 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -22,29 +22,29 @@ * This header contains the class hierarchy of actual document classes. A graph * of connected instances of these nodes is a "Document". How the different * DocumentEntity instances may be connected within the graph is subject to the - * specification in the respective Domain(s) (see also the Domain.hpp). + * specification in the respective Ontology(s) (see also the Ontology.hpp). * * A Document, from top to bottom, consists of "Document" instance, * which "owns" the structural root node of the in-document graph. This might - * for example be a "book" node of the "book" domain. That root node in turn has + * for example be a "book" node of the "book" ontology. That root node in turn has * structure nodes as children, which in turn may have children. This * constitutes a Structure Tree. Additionally annotations may be attached to * Structure Nodes, effectively resulting in a Document Graph instead of a * Document Tree (other references may introduce cycles as well). * - * Consider this XML representation of a document using the "book" domain: + * Consider this XML representation of a document using the "book" ontology: * * \code{.xml} * * - * - * + * + * * * * * This might be some introductory text or a dedication. Ideally, of * course, such elements would be semantically specified as such in - * additional domains (or in this one). + * additional ontologies (or in this one). * * Here we might have an introduction to the chapter, including some * overview of the chapters structure. @@ -63,9 +63,9 @@ * \endcode * * As can be seen the StructureEntities inherently follow a tree structure that - * is restricted by the implicit context free grammar of the "book" Domain + * is restricted by the implicit context free grammar of the "book" Ontology * definition (e.g. it is not allowed to have a "book" node inside a "section"; - * refer to te Domain.hpp for more information). + * refer to te Ontology.hpp for more information). * * Another interesting fact is the special place of AnnotationEntities: They are * Defined by start and end Anchors in the text. Note that this allows for @@ -114,7 +114,7 @@ #include #include "Node.hpp" -#include "Domain.hpp" +#include "Ontology.hpp" #include "RootNode.hpp" #include "Typesystem.hpp" @@ -131,7 +131,7 @@ class Anchor; /** * A DocumentEntity is the common superclass for StructuredEntities and - * AnnotationEntities. Similarly to DescriptorEntity in the Domain.hpp it + * AnnotationEntities. Similarly to DescriptorEntity in the Ontology.hpp it * defines that each node in the Document graph may have attributes (in form * of a struct Variant), and fields. * The fields here are a vector of vectors. The first vector implements all @@ -215,7 +215,7 @@ public: * If the name is unknown an exception is thrown. * * @param fieldName is the name of a field as specified in the - * FieldDescriptor in the Domain description. + * FieldDescriptor in the Ontology description. * @return a NodeVector of all StructuredEntities in that field. */ const NodeVector &getField( @@ -243,7 +243,7 @@ public: * If the index is out of bounds an exception is thrown. * * @param idx is the index of a field as specified in the - * FieldDescriptor in the Domain description. + * FieldDescriptor in the Ontology description. * @return a NodeVector of all StructuredEntities in that field. */ const NodeVector &getField(const size_t &idx) const; @@ -257,7 +257,7 @@ public: * * @param s is the StructureNode that shall be added. * @param fieldIdx is the index of a field as specified in the - * FieldDescriptor in the Domain description. + * FieldDescriptor in the Ontology description. */ void addStructureNode(Handle s, const size_t &fieldIdx); /** @@ -271,7 +271,7 @@ public: * * @param s is the StructureNode that shall be added. * @param fieldName is the name of a field as specified in the - * FieldDescriptor in the Domain description. + * FieldDescriptor in the Ontology description. */ void addStructureNode(Handle s, const std::string &fieldName = DEFAULT_FIELD_NAME); @@ -287,7 +287,7 @@ public: * * @param ss are the StructureNodes that shall be added. * @param fieldName is the name of a field as specified in the - * FieldDescriptor in the Domain description. + * FieldDescriptor in the Ontology description. */ void addStructureNodes(const std::vector> &ss, const std::string &fieldName = DEFAULT_FIELD_NAME); @@ -298,7 +298,7 @@ public: * * @param s is the StructureNode that shall be removed. * @param fieldIdx is the index of a field as specified in the - * FieldDescriptor in the Domain description. + * FieldDescriptor in the Ontology description. * @return true if this StructureNode was a child here and false if * if was not found. */ @@ -313,7 +313,7 @@ public: * * @param s is the StructureNode that shall be removed. * @param fieldName is the name of a field as specified in the - * FieldDescriptor in the Domain description. + * FieldDescriptor in the Ontology description. * @return true if this StructureNode was a child here and false if * if was not found. */ @@ -866,7 +866,7 @@ public: /** * A Document is mainly a wrapper for the Root structure node of the Document - * Graph. It also references the domains that have been used within this + * Graph. It also references the ontologies that have been used within this * document and the AnnotationEntities that span over Anchors in this Document. */ class Document : public RootNode { @@ -874,7 +874,7 @@ private: // TODO: Might there be several roots? E.g. metadata? Owned root; NodeVector annotations; - NodeVector domains; + NodeVector ontologies; NodeVector typesystems; protected: @@ -893,7 +893,7 @@ public: Document(Manager &mgr, std::string name) : RootNode(mgr, std::move(name), nullptr), annotations(this), - domains(this), + ontologies(this), typesystems(this) { } @@ -987,30 +987,30 @@ public: std::string name = ""); /** - * Returns a const reference to the NodeVector of Domains that are used + * Returns a const reference to the NodeVector of ontologies that are used * within this Document. * - * @return a const reference to the NodeVector of Domains that are used + * @return a const reference to the NodeVector of ontologies that are used * within this Document. */ - const NodeVector &getDomains() const { return domains; } + const NodeVector &getOntologys() const { return ontologies; } /** - * Adds a Domain reference to this Document. + * Adds a Ontology reference to this Document. */ - void referenceDomain(Handle d) + void referenceOntology(Handle d) { invalidate(); - domains.push_back(d); + ontologies.push_back(d); } /** - * Adds multiple Domain references to this Document. + * Adds multiple Ontology references to this Document. */ - void referenceDomains(const std::vector> &d) + void referenceOntologys(const std::vector> &d) { invalidate(); - domains.insert(domains.end(), d.begin(), d.end()); + ontologies.insert(ontologies.end(), d.begin(), d.end()); } /** diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp deleted file mode 100644 index 8255401..0000000 --- a/src/core/model/Domain.cpp +++ /dev/null @@ -1,981 +0,0 @@ -/* - 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 "Domain.hpp" - -namespace ousia { - -/* Helper Functions */ - -struct PathState { - std::shared_ptr pred; - Node *node; - size_t length; - - PathState(std::shared_ptr pred, Node *node) - : pred(pred), node(node) - { - if (pred == nullptr) { - length = 1; - } else { - length = pred->length + 1; - } - } -}; - -static void constructPath(std::shared_ptr state, - NodeVector &vec) -{ - if (state->pred != nullptr) { - constructPath(state->pred, vec); - } - vec.push_back(state->node); -} - -static NodeVector pathTo(const Node *start, Logger &logger, - Handle target, bool &success) -{ - success = false; - // shortest path. - NodeVector shortest; - // state queue for breadth-first search. - std::queue> states; - if (start->isa(&RttiTypes::Descriptor)) { - const Descriptor *desc = static_cast(start); - // initially put every field descriptor on the queue. - NodeVector fields = desc->getFieldDescriptors(); - - for (auto fd : fields) { - if (fd == target) { - // if we have found the target directly, return without search. - success = true; - return shortest; - } - if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { - states.push(std::make_shared(nullptr, fd.get())); - } - } - } else { - const FieldDescriptor *field = - static_cast(start); - // initially put every child and its subclasses to the queue. - for (auto c : field->getChildrenWithSubclasses()) { - // if we have found the target directly, return without search. - if (c == target) { - success = true; - return shortest; - } - if (c->isTransparent()) { - states.push(std::make_shared(nullptr, c.get())); - } - } - } - // set of visited nodes. - std::unordered_set visited; - while (!states.empty()) { - std::shared_ptr current = states.front(); - states.pop(); - // do not proceed if this node was already visited. - if (!visited.insert(current->node).second) { - continue; - } - // also do not proceed if we can't get better than the current shortest - // path anymore. - if (!shortest.empty() && current->length > shortest.size()) { - continue; - } - - bool fin = false; - if (current->node->isa(&RttiTypes::StructuredClass)) { - const StructuredClass *strct = - static_cast(current->node); - - // look through all fields. - NodeVector fields = strct->getFieldDescriptors(); - for (auto fd : fields) { - // if we found our target, break off the search in this branch. - if (fd == target) { - fin = true; - continue; - } - // only continue in the TREE field. - if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { - states.push(std::make_shared(current, fd.get())); - } - } - } else { - // otherwise this is a FieldDescriptor. - const FieldDescriptor *field = - static_cast(current->node); - // and we proceed by visiting all permitted children. - for (auto c : field->getChildrenWithSubclasses()) { - // if we found our target, break off the search in this branch. - if (c == target) { - fin = true; - continue; - } - // We only allow to continue our path via transparent children. - if (c->isTransparent()) { - states.push(std::make_shared(current, c.get())); - } - } - } - // check if we are finished. - if (fin) { - success = true; - // if so we look if we found a shorter path than the current minimum - if (shortest.empty() || current->length < shortest.size()) { - NodeVector newPath; - constructPath(current, newPath); - shortest = newPath; - } else if (current->length == shortest.size()) { - // if the length is the same the result is ambigous and we log - // an error. - NodeVector newPath; - constructPath(current, newPath); - logger.error( - std::string("Can not unambigously create a path from \"") + - start->getName() + "\" to \"" + target->getName() + "\"."); - logger.note("Dismissed the path:", SourceLocation{}, - MessageMode::NO_CONTEXT); - for (auto n : newPath) { - logger.note(n->getName()); - } - } - } - } - return shortest; -} - -template -static NodeVector collect(const Node *start, F match) -{ - // result - NodeVector res; - // queue for breadth-first search of graph. - std::queue> q; - // put the initial node on the stack. - q.push(const_cast(start)); - // set of visited nodes. - std::unordered_set visited; - while (!q.empty()) { - Rooted n = q.front(); - q.pop(); - // do not proceed if this node was already visited. - if (!visited.insert(n.get()).second) { - continue; - } - - if (n->isa(&RttiTypes::StructuredClass)) { - Rooted strct = n.cast(); - - // look through all fields. - NodeVector fields = strct->getFieldDescriptors(); - for (auto fd : fields) { - // note matches. - if (match(fd)) { - res.push_back(fd); - } - // only continue in the TREE field. - if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { - q.push(fd); - } - } - } else { - // otherwise this is a FieldDescriptor. - Rooted field = n.cast(); - // and we proceed by visiting all permitted children. - for (auto c : field->getChildrenWithSubclasses()) { - // note matches. - if (match(c)) { - res.push_back(c); - } - // We only continue our search via transparent children. - if (c->isTransparent()) { - q.push(c); - } - } - } - } - return res; -} - -/* Class FieldDescriptor */ - -FieldDescriptor::FieldDescriptor(Manager &mgr, Handle primitiveType, - Handle parent, FieldType fieldType, - std::string name, bool optional) - : Node(mgr, std::move(name), parent), - children(this), - fieldType(fieldType), - primitiveType(acquire(primitiveType)), - optional(optional), - primitive(true) -{ -} - -FieldDescriptor::FieldDescriptor(Manager &mgr, Handle parent, - FieldType fieldType, std::string name, - bool optional) - : Node(mgr, std::move(name), parent), - children(this), - fieldType(fieldType), - optional(optional), - primitive(false) -{ -} - -bool FieldDescriptor::doValidate(Logger &logger) const -{ - bool valid = true; - // check parent type - if (getParent() == nullptr) { - logger.error(std::string("Field \"") + getName() + "\" has no parent!", - *this); - valid = false; - } else if (!getParent()->isa(&RttiTypes::Descriptor)) { - logger.error(std::string("The parent of Field \"") + getName() + - "\" is not a descriptor!", - *this); - valid = false; - } - // check name - if (getName().empty()) { - if (fieldType != FieldType::TREE) { - logger.error(std::string("Field \"") + getName() + - "\" is not the main field but has an empty name!", - *this); - valid = false; - } - } else { - valid = valid & validateName(logger); - } - - // check consistency of FieldType with the rest of the FieldDescriptor. - if (primitive) { - if (children.size() > 0) { - logger.error(std::string("Field \"") + getName() + - "\" is supposed to be primitive but has " - "registered child classes!", - *this); - valid = false; - } - if (primitiveType == nullptr) { - logger.error(std::string("Field \"") + getName() + - "\" is supposed to be primitive but has " - "no primitive type!", - *this); - valid = false; - } - } else { - if (primitiveType != nullptr) { - logger.error(std::string("Field \"") + getName() + - "\" is supposed to be non-primitive but has " - "a primitive type!", - *this); - valid = false; - } - // if this is not a primitive field we require at least one child. - if (children.empty()) { - logger.error(std::string("Field \"") + getName() + - "\" is non primitive but does not allow children!", - *this); - valid = false; - } - } - /* - * we are not allowed to call the validation functions of each child because - * this might lead to cycles. What we should do, however, is to check if - * there are duplicates. - */ - std::set names; - for (Handle c : getChildrenWithSubclasses()) { - if (!names.insert(c->getName()).second) { - logger.error(std::string("Field \"") + getName() + - "\" had multiple children with the name \"" + - c->getName() + "\"", - *this); - valid = false; - } - } - - return valid; -} - -static void gatherSubclasses( - std::unordered_set& visited, - NodeVector &res, Handle strct) -{ - // this check is to prevent cycles. - if (!visited.insert(strct.get()).second) { - return; - } - for (auto sub : strct->getSubclasses()) { - // this check is to prevent cycles. - if(visited.count(sub.get())){ - continue; - } - res.push_back(sub); - gatherSubclasses(visited, res, sub); - } -} - -NodeVector FieldDescriptor::getChildrenWithSubclasses() const -{ - std::unordered_set visited; - NodeVector res; - for (auto c : children) { - res.push_back(c); - gatherSubclasses(visited, res, c); - } - return res; -} - -bool FieldDescriptor::removeChild(Handle c) -{ - auto it = children.find(c); - if (it != children.end()) { - invalidate(); - children.erase(it); - return true; - } - return false; -} - -std::pair, bool> FieldDescriptor::pathTo( - Handle childDescriptor, Logger &logger) const -{ - bool success = false; - NodeVector path = - ousia::pathTo(this, logger, childDescriptor, success); - return std::make_pair(path, success); -} -NodeVector FieldDescriptor::pathTo(Handle field, - Logger &logger) const -{ - bool success = false; - return ousia::pathTo(this, logger, field, success); -} -NodeVector FieldDescriptor::getDefaultFields() const -{ - // TODO: In principle a cast would be nicer here, but for now we copy. - NodeVector nodes = collect(this, [](Handle n) { - if (!n->isa(&RttiTypes::FieldDescriptor)) { - return false; - } - Handle f = n.cast(); - return f->getFieldType() == FieldDescriptor::FieldType::TREE && - f->isPrimitive(); - }); - NodeVector res; - for (auto n : nodes) { - res.push_back(n.cast()); - } - return res; -} - -/* Class Descriptor */ - -void Descriptor::doResolve(ResolutionState &state) -{ - const NodeVector &attributes = - attributesDescriptor->getAttributes(); - continueResolveComposita(attributes, attributes.getIndex(), state); - continueResolveComposita(fieldDescriptors, fieldDescriptors.getIndex(), - state); -} - -bool Descriptor::doValidate(Logger &logger) const -{ - bool valid = true; - // check parent type - if (getParent() == nullptr) { - logger.error( - std::string("Descriptor \"") + getName() + "\" has no parent!", - *this); - valid = false; - } else if (!getParent()->isa(&RttiTypes::Domain)) { - logger.error(std::string("The parent of Descriptor \"") + getName() + - "\" is not a Domain!", - *this); - valid = false; - } - // check name - if (getName().empty()) { - logger.error("The name of this Descriptor is empty!", *this); - valid = false; - } else { - valid = valid & validateName(logger); - } - // ensure that no attribute with the key "name" exists. - if (attributesDescriptor == nullptr) { - logger.error(std::string("Descriptor \"") + getName() + - "\" has no Attribute specification!"); - valid = false; - } else { - if (attributesDescriptor->hasAttribute("name")) { - logger.error( - std::string("Descriptor \"") + getName() + - "\" has an attribute \"name\" which is a reserved word!"); - valid = false; - } - valid = valid & attributesDescriptor->validate(logger); - } - // check that only one FieldDescriptor is of type TREE. - auto fds = Descriptor::getFieldDescriptors(); - bool hasTREE = false; - for (auto fd : fds) { - if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { - if (!hasTREE) { - hasTREE = true; - } else { - logger.error( - std::string("Descriptor \"") + getName() + - "\" has multiple TREE fields, which is not permitted", - *fd); - valid = false; - break; - } - } - } - - // check attributes and the FieldDescriptors - return valid & continueValidationCheckDuplicates(fds, logger); -} - -NodeVector Descriptor::pathTo(Handle target, - Logger &logger) const -{ - bool success = false; - return ousia::pathTo(this, logger, target, success); -} - -std::pair, bool> Descriptor::pathTo( - Handle field, Logger &logger) const -{ - bool success = false; - NodeVector path = ousia::pathTo(this, logger, field, success); - return std::make_pair(path, success); -} - -NodeVector Descriptor::getDefaultFields() const -{ - // TODO: In principle a cast would be nicer here, but for now we copy. - NodeVector nodes = collect(this, [](Handle n) { - if (!n->isa(&RttiTypes::FieldDescriptor)) { - return false; - } - Handle f = n.cast(); - return f->getFieldType() == FieldDescriptor::FieldType::TREE && - f->isPrimitive(); - }); - NodeVector res; - for (auto n : nodes) { - res.push_back(n.cast()); - } - return res; -} - -NodeVector Descriptor::getPermittedChildren() const -{ - // TODO: In principle a cast would be nicer here, but for now we copy. - NodeVector nodes = collect(this, [](Handle n) { - return n->isa(&RttiTypes::StructuredClass); - }); - NodeVector res; - for (auto n : nodes) { - res.push_back(n.cast()); - } - return res; -} - -static ssize_t getFieldDescriptorIndex(const NodeVector &fds, - const std::string &name) -{ - if (fds.empty()) { - return -1; - } - - if (name == DEFAULT_FIELD_NAME) { - if (fds.back()->getFieldType() == FieldDescriptor::FieldType::TREE) { - return fds.size() - 1; - } else { - /* The last field has to be the TREE field. If the last field does - * not have the FieldType TREE no TREE-field exists at all. So we - * return -1. - */ - return -1; - } - } - - for (size_t f = 0; f < fds.size(); f++) { - if (fds[f]->getName() == name) { - return f; - } - } - return -1; -} - -ssize_t Descriptor::getFieldDescriptorIndex(const std::string &name) const -{ - NodeVector fds = getFieldDescriptors(); - return ousia::getFieldDescriptorIndex(fds, name); -} - -ssize_t Descriptor::getFieldDescriptorIndex(Handle fd) const -{ - size_t f = 0; - for (auto &fd2 : getFieldDescriptors()) { - if (fd == fd2) { - return f; - } - f++; - } - return -1; -} - -Rooted Descriptor::getFieldDescriptor( - const std::string &name) const -{ - NodeVector fds = getFieldDescriptors(); - ssize_t idx = ousia::getFieldDescriptorIndex(fds, name); - if (idx != -1) { - return fds[idx]; - } else { - return nullptr; - } -} - -bool Descriptor::addAndSortFieldDescriptor(Handle fd, - Logger &logger) -{ - // only add it if we need to. - auto fds = getFieldDescriptors(); - if (fds.find(fd) == fds.end()) { - invalidate(); - // check if the previous field is a tree field already. - if (!fieldDescriptors.empty() && - fieldDescriptors.back()->getFieldType() == - FieldDescriptor::FieldType::TREE && - fd->getFieldType() != FieldDescriptor::FieldType::TREE) { - // if so we add the new field before the TREE field. - fieldDescriptors.insert(fieldDescriptors.end() - 1, fd); - return true; - } else { - fieldDescriptors.push_back(fd); - } - } - return false; -} - -bool Descriptor::addFieldDescriptor(Handle fd, Logger &logger) -{ - if (fd->getParent() == nullptr) { - fd->setParent(this); - } - return addAndSortFieldDescriptor(fd, logger); -} - -bool Descriptor::moveFieldDescriptor(Handle fd, Logger &logger) -{ - bool sorted = addAndSortFieldDescriptor(fd, logger); - Handle par = fd->getParent(); - if (par != this) { - if (par != nullptr) { - // remove the FieldDescriptor from the old parent. - par.cast()->removeFieldDescriptor(fd); - } - fd->setParent(this); - } - return sorted; -} - -bool Descriptor::copyFieldDescriptor(Handle fd, Logger &logger) -{ - Rooted copy; - if (fd->isPrimitive()) { - copy = Rooted{new FieldDescriptor( - getManager(), fd->getPrimitiveType(), this, fd->getFieldType(), - fd->getName(), fd->isOptional())}; - } else { - /* - * In case of non-primitive FieldDescriptors we also want to copy the - * child references. - */ - copy = Rooted{ - new FieldDescriptor(getManager(), this, fd->getFieldType(), - fd->getName(), fd->isOptional())}; - for (auto c : fd->getChildren()) { - copy->addChild(c); - } - } - return addFieldDescriptor(copy, logger); -} - -bool Descriptor::removeFieldDescriptor(Handle fd) -{ - auto it = fieldDescriptors.find(fd); - if (it != fieldDescriptors.end()) { - invalidate(); - fieldDescriptors.erase(it); - fd->setParent(nullptr); - return true; - } - return false; -} - -std::pair, bool> -Descriptor::createPrimitiveFieldDescriptor(Handle primitiveType, - Logger &logger, - FieldDescriptor::FieldType fieldType, - std::string name, bool optional) -{ - Rooted fd{new FieldDescriptor(getManager(), primitiveType, - this, fieldType, - std::move(name), optional)}; - bool sorted = addFieldDescriptor(fd, logger); - return std::make_pair(fd, sorted); -} - -std::pair, bool> Descriptor::createFieldDescriptor( - Logger &logger, FieldDescriptor::FieldType fieldType, std::string name, - bool optional) -{ - Rooted fd{new FieldDescriptor( - getManager(), this, fieldType, std::move(name), optional)}; - bool sorted = addFieldDescriptor(fd, logger); - return std::make_pair(fd, sorted); -} - -/* Class StructuredClass */ - -StructuredClass::StructuredClass(Manager &mgr, std::string name, - Handle domain, Variant cardinality, - Handle superclass, - bool transparent, bool root) - : Descriptor(mgr, std::move(name), domain), - cardinality(cardinality), - superclass(acquire(superclass)), - subclasses(this), - transparent(transparent), - root(root) -{ - ExceptionLogger logger; - if (superclass != nullptr) { - superclass->addSubclass(this, logger); - } - if (domain != nullptr) { - domain->addStructuredClass(this); - } -} - -bool StructuredClass::doValidate(Logger &logger) const -{ - bool valid = true; - // check if all registered subclasses have this StructuredClass as parent. - for (Handle sub : subclasses) { - if (sub->getSuperclass() != this) { - logger.error(std::string("Struct \"") + sub->getName() + - "\" is registered as subclass of \"" + getName() + - "\" but does not have it as superclass!", - *this); - valid = false; - } - } - // check the cardinality. - if (!cardinality.isCardinality()) { - logger.error(cardinality.toString() + " is not a cardinality!", *this); - valid = false; - } - // check the validity of this superclass. - if (superclass != nullptr) { - valid = valid & superclass->validate(logger); - } - // check the validity as a Descriptor. - /* - * Note that we do not check the validity of all subclasses. This is because - * it will lead to cycles as the subclasses would call validate on their - * superclass, which is this one. - */ - return valid & Descriptor::doValidate(logger); -} - -void StructuredClass::setSuperclass(Handle sup, Logger &logger) -{ - if (superclass == sup) { - return; - } - // remove this subclass from the old superclass. - if (superclass != nullptr) { - superclass->removeSubclass(this, logger); - } - // set the new superclass - superclass = acquire(sup); - invalidate(); - // add this class as new subclass of the new superclass. - if (sup != nullptr) { - sup->addSubclass(this, logger); - // set the attribute descriptor supertype - getAttributesDescriptor()->setParentStructure( - sup->getAttributesDescriptor(), logger); - } else { - getAttributesDescriptor()->setParentStructure(nullptr, logger); - } -} - -bool StructuredClass::isSubclassOf(Handle c) const -{ - if (c == nullptr || superclass == nullptr) { - return false; - } - if (c == superclass) { - return true; - } - return superclass->isSubclassOf(c); -} - -void StructuredClass::addSubclass(Handle sc, Logger &logger) -{ - if (sc == nullptr) { - return; - } - // check if we already have that class. - if (subclasses.find(sc) == subclasses.end()) { - invalidate(); - subclasses.push_back(sc); - } - sc->setSuperclass(this, logger); -} - -void StructuredClass::removeSubclass(Handle sc, Logger &logger) -{ - // if we don't have this subclass we can return directly. - if (sc == nullptr) { - return; - } - auto it = subclasses.find(sc); - if (it == subclasses.end()) { - return; - } - // otherwise we have to erase it. - invalidate(); - subclasses.erase(it); - sc->setSuperclass(nullptr, logger); -} - -Rooted StructuredClass::gatherFieldDescriptors( - NodeVector ¤t, - std::unordered_set &visited, - std::set &overriddenFields, bool hasTREE) const -{ - // this check is to prevent cycles of inheritance to mess up this function. - if (!visited.insert(this).second) { - return nullptr; - } - Rooted mainField; - NodeVector tmp; - // first gather the non-overridden fields. - for (auto f : Descriptor::getFieldDescriptors()) { - if (overriddenFields.insert(f->getName()).second) { - bool isTREE = f->getFieldType() == FieldDescriptor::FieldType::TREE; - if (!isTREE) { - tmp.push_back(f); - } else { - if (!hasTREE) { - hasTREE = true; - mainField = f; - } - } - } - } - // append all non-overridden superclass fields. - - if (superclass != nullptr) { - Rooted super_main_field = - superclass->gatherFieldDescriptors(current, visited, - overriddenFields, hasTREE); - if (!hasTREE) { - mainField = super_main_field; - } - } - // then append all subtree fields of this level. - current.insert(current.end(), tmp.begin(), tmp.end()); - // and return the main field. - return mainField; -} - -NodeVector StructuredClass::getFieldDescriptors() const -{ - // in this case we return a NodeVector of Rooted entries without owner. - NodeVector vec; - std::unordered_set visited; - std::set overriddenFields; - Rooted mainField = - gatherFieldDescriptors(vec, visited, overriddenFields, false); - if (mainField != nullptr) { - vec.push_back(mainField); - } - return vec; -} - -/* Class AnnotationClass */ - -AnnotationClass::AnnotationClass(Manager &mgr, std::string name, - Handle domain) - : Descriptor(mgr, std::move(name), domain) -{ - if (domain != nullptr) { - domain->addAnnotationClass(this); - } -} - -/* Class Domain */ - -void Domain::doResolve(ResolutionState &state) -{ - continueResolveComposita(structuredClasses, structuredClasses.getIndex(), - state); - continueResolveComposita(annotationClasses, annotationClasses.getIndex(), - state); - continueResolveReferences(typesystems, state); - continueResolveReferences(domains, state); -} - -bool Domain::doValidate(Logger &logger) const -{ - // check validity of name, of StructuredClasses, of AnnotationClasses and - // TypeSystems. - return validateName(logger) & - continueValidationCheckDuplicates(structuredClasses, logger) & - continueValidationCheckDuplicates(annotationClasses, logger) & - continueValidationCheckDuplicates(typesystems, logger); -} - -void Domain::doReference(Handle node) -{ - if (node->isa(&RttiTypes::Typesystem)) { - referenceTypesystem(node.cast()); - } - if (node->isa(&RttiTypes::Domain)) { - referenceDomain(node.cast()); - } -} - -RttiSet Domain::doGetReferenceTypes() const -{ - return RttiSet{&RttiTypes::Domain, &RttiTypes::Typesystem}; -} - -void Domain::addStructuredClass(Handle s) -{ - // only add it if we need to. - if (structuredClasses.find(s) == structuredClasses.end()) { - invalidate(); - structuredClasses.push_back(s); - } - Handle par = s->getParent(); - if (par != this) { - if (par != nullptr) { - // remove the StructuredClass from the old parent. - par.cast()->removeStructuredClass(s); - } - s->setParent(this); - } -} - -bool Domain::removeStructuredClass(Handle s) -{ - auto it = structuredClasses.find(s); - if (it != structuredClasses.end()) { - invalidate(); - structuredClasses.erase(it); - s->setParent(nullptr); - return true; - } - return false; -} - -Rooted Domain::createStructuredClass( - std::string name, Variant cardinality, Handle superclass, - bool transparent, bool root) -{ - return Rooted{new StructuredClass( - getManager(), std::move(name), this, cardinality, superclass, - std::move(transparent), std::move(root))}; -} - -void Domain::addAnnotationClass(Handle a) -{ - // only add it if we need to. - if (annotationClasses.find(a) == annotationClasses.end()) { - invalidate(); - annotationClasses.push_back(a); - } - Handle par = a->getParent(); - if (par != this) { - if (par != nullptr) { - // remove the StructuredClass from the old parent. - par.cast()->removeAnnotationClass(a); - } - a->setParent(this); - } -} - -bool Domain::removeAnnotationClass(Handle a) -{ - auto it = annotationClasses.find(a); - if (it != annotationClasses.end()) { - invalidate(); - annotationClasses.erase(it); - a->setParent(nullptr); - return true; - } - return false; -} - -Rooted Domain::createAnnotationClass(std::string name) -{ - return Rooted{ - new AnnotationClass(getManager(), std::move(name), this)}; -} - -/* Type registrations */ - -namespace RttiTypes { -const Rtti FieldDescriptor = - RttiBuilder("FieldDescriptor").parent(&Node); -const Rtti Descriptor = - RttiBuilder("Descriptor").parent(&Node); -const Rtti StructuredClass = - RttiBuilder("StructuredClass") - .parent(&Descriptor) - .composedOf(&FieldDescriptor); -const Rtti AnnotationClass = - RttiBuilder("AnnotationClass").parent(&Descriptor); -const Rtti Domain = RttiBuilder("Domain") - .parent(&RootNode) - .composedOf({&StructuredClass, &AnnotationClass}); -} -} \ No newline at end of file diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp deleted file mode 100644 index 7e10d91..0000000 --- a/src/core/model/Domain.hpp +++ /dev/null @@ -1,1203 +0,0 @@ -/* - 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 . -*/ - -/** - * @file Domain.hpp - * - * This header contains the class hierarchy of descriptor classes for domains. - * Properly connected instances of these classes with a Domain node as root - * describe a semantic Domain in a formal way. It specifies the allowed (tree) - * structure of a document by means of StructuredClasses as well as the allowed - * Annotations by means of AnnotationClasses. - * - * The Structure Description contained in the hierarchy of StructuredClasses is - * equivalent to a context free grammar of a special form. We introduce the - * terms "StructuredClass" and "FieldDescriptor". - * On the top level you would start with a StructuredClass, say "book", which - * in turn might contain two FieldDescriptors, one for the meta data of ones - * book and one for the actual structure. Consider the following XML: - * - * \code{.xml} - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * \endcode - * - * Note that we define one field as the TREE (meaning the main or default - * document structure) and one mearly as SUBTREE, relating to supporting - * information. You are not allowed to define more than one field of type - * "TREE". - * - * The translation to a context free grammar is as follows: - * - * \code{.txt} - * BOOK := BOOK_TREE - * BOOK_TREE := CHAPTER BOOK_TREE | PARAGRAPH BOOK_TREE | epsilon - * CHAPTER := CHAPTER_TREE - * CHAPTER_TREE := SECTION CHAPTER_TREE | PARAGRAPH CHAPTER_TREE | epsilon - * SECTION :=
SECTION_TREE
- * SECTION_TREE := SUBSECTION SECTION_TREE | PARAGRAPH SECTION_TREE | - * epsilon - * SUBSECTION := SUBSECTION_TREE - * SUBSECTION_TREE := PARAGRAPH SUBSECTION_TREE | epsilon - * PARAGRAPH := PARAGRAPH_CONTENT - * PARAGRAPH_CONTENT := string - * \endcode - * - * Note that this translation recurs to further nonterminals like SECTION but - * necessarily produces one "book" terminal. Also note that, in principle, - * this grammar translation allows for arbitrarily many children instances of - * the proper StructuredClass. This can be regulated by the "cardinality" - * property of a StructuredClass. - * - * It is possible to add further fields, like we would in the "headings" domain - * to add titles to our structure. - * - * \code{.xml} - * - * - * - * - * - * - * ... - * - * - * - * \endcode - * - * This would change the context free grammar as follows: - * - * \code{.txt} - * BOOK := HEADING BOOK_TREE - * HEADING := PARAGRAPH - * \endcode - * - * AnnotationClasses on the other hand do not specify a context free grammar. - * They merely specify what kinds of Annotations are allowed within this domain - * and which fields or attributes they have. Note that Annotations are allowed - * to define structured children that manifest e.g. meta information of that - * Annotation. An example for that would be the "comment" domain: - * - * \code{.xml} - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * \endcode - * - * Here we have comment annotations, which have a reply tree as sub structure. - * - * @author Benjamin Paaßen (bpaassen@techfak.uni-bielefeld.de) - */ - -#ifndef _OUSIA_MODEL_DOMAIN_HPP_ -#define _OUSIA_MODEL_DOMAIN_HPP_ - -#include -#include - -#include "Node.hpp" -#include "RootNode.hpp" -#include "Typesystem.hpp" - -namespace ousia { - -// Forward declarations -class Rtti; -class Descriptor; -class StructuredClass; -class Domain; - -/** - * Magic field name used to identify the default field. The default field is - * either the tree field or the only subtree field. - */ -static const std::string DEFAULT_FIELD_NAME = "$default"; - -/** - * As mentioned in the description above a FieldDescriptor specifies the - * StructuredClasses that are allowed as children of a StructuredClass or - * AnnotationClass. A field may also be primitive, which means that a proper - * instance of the respective StructuredClass or AnnotationClass must provide - * accordingly typed content without further descending in the Structure - * Hierarchy. - * - * As an example consider the "text" StructuredClass, which might allow - * the actual text content. Here is the according XML: - * - * \code{.xml} - * - * - * - * \endcode - * - */ -class FieldDescriptor : public Node { - friend Descriptor; - -public: - /** - * This enum class contains all possible FieldTypes, meaning either the - * main structure beneath this Descriptor (TREE) or supporting structure - * (SUBTREE) - * - * Note that there may be only one TREE field in a descriptor. - */ - enum class FieldType { TREE, SUBTREE }; - -private: - NodeVector children; - FieldType fieldType; - Owned primitiveType; - bool optional; - bool primitive; - -protected: - bool doValidate(Logger &logger) const override; - -public: - /** - * This is the constructor for primitive fields. - * - * @param mgr is the global Manager instance. - * @param parent is a handle of the Descriptor node that has this - * FieldDescriptor. - * @param primitiveType is a handle to some Type in some Typesystem of which - * one instance is allowed to fill this field. - * @param name is the name of this field. - * @param optional should be set to 'false' is this field needs to be - * filled in order for an instance of the parent - * Descriptor to be valid. - */ - FieldDescriptor(Manager &mgr, Handle primitiveType, - Handle parent, - FieldType fieldType = FieldType::TREE, - std::string name = "", bool optional = false); - - /** - * This is the constructor for non-primitive fields. You have to provide - * children here later on. - * - * @param mgr is the global Manager instance. - * @param parent is a handle of the Descriptor node that has this - * FieldDescriptor. - * @param fieldType is the FieldType of this FieldDescriptor, either - * TREE for the main or default structure or SUBTREE - * for supporting structures. - * @param name is the name of this field. - * @param optional should be set to 'false' is this field needs to be - * filled in order for an instance of the parent - * Descriptor to be valid. - */ - FieldDescriptor(Manager &mgr, Handle parent = nullptr, - FieldType fieldType = FieldType::TREE, - std::string name = "", bool optional = false); - - /** - * Returns a const reference to the NodeVector of StructuredClasses whose - * instances are allowed as children in the StructureTree of instances of - * this field. - * - * @return a const reference to the NodeVector of StructuredClasses whose - * instances are allowed as children in the StructureTree of instances of - * this field. - */ - const NodeVector &getChildren() const { return children; } - - /** - * Returns all StructuredClasses whose instances are allowed as children in - * the Structure Tree of instances of this field including subclasses of - * children, which are allowed directly. - * - * @return all StructuredClasses whose instances are allowed as children in - * the Structure Tree of instances of this field including subclasses of - * children, which are allowed directly. - */ - NodeVector getChildrenWithSubclasses() const; - - /** - * Adds a StructuredClass whose instances shall be allowed as children in - * the StructureTree of instances of this field. - */ - void addChild(Handle c) - { - invalidate(); - children.push_back(c); - } - - /** - * Adds multiple StructuredClasses whose instances shall be allowed as - * children in the StructureTree of instances of this field. - */ - void addChildren(const std::vector> &cs) - { - invalidate(); - children.insert(children.end(), cs.begin(), cs.end()); - } - - /** - * Removes the given StructuredClass from the list of children of this - * FieldDescriptor. - * - * @param c some StructuredClass that shan't be a child of this - * FieldDescriptor anymore. - * @return true if the FieldDescriptor contained this child and false if it - * did not. - */ - bool removeChild(Handle c); - - /** - * Returns the type of this field (not to be confused with the primitive - * type of this field). - * - * @return the type of this field. - */ - FieldType getFieldType() const { return fieldType; } - /** - * Sets the type of this field (not to be confused with the primitive type - * of this field). - * - * @param ft is the new type of this field. - */ - void setFieldType(const FieldType &ft) - { - invalidate(); - fieldType = ft; - } - - /** - * Returns if this field is primitive. - * - * @return true if and only if this field is primitive. - */ - bool isPrimitive() const { return primitive; } - - /** - * Returns the primitive type of this field, which is only allowed to be - * set if the type of this field is PRIMITIVE. - * - * @return the primitive type of this field. - */ - Rooted getPrimitiveType() const { return primitiveType; } - - /** - * Sets the primitive type of this field, which is only allowed to be - * set if the type of this field is PRIMITIVE. - * - * @param t is the new primitive type of this field- - */ - void setPrimitiveType(Handle t) - { - invalidate(); - primitiveType = acquire(t); - } - - /** - * Returns true if and only if this field is optional. - * - * @return true if and only if this field is optional. - */ - bool isOptional() const { return optional; } - - /** - * Specifies whether this field shall be optional. - * - * @param o should be true if and only if this field should be optional. - */ - void setOptional(bool o) - { - invalidate(); - optional = std::move(o); - } - - /** - * This tries to construct the shortest possible path of this Descriptor - * to the given child Descriptor. Note that this method has the problem that - * an empty return path does NOT strictly imply that no such path could - * be constructed: We also return an empty vector if the given - * Descriptor is a direct child. Therefore we also return a bool value - * indicating that the path is valid or not. - * - * Implicitly this does a breadth-first search on the graph of - * StructuredClasses that are transparent. It also takes care of cycles. - * - * @param childDescriptor is a supposedly valid child Descriptor of this - * Descriptor. - * @return a tuple containing a path of FieldDescriptors and - * StructuredClasses between this Descriptor and the - * input Descriptor and a bool value indicating if - * the construction was successful. - * - */ - std::pair, bool> pathTo( - Handle childDescriptor, Logger &logger) const; - /** - * This tries to construct the shortest possible path of this Descriptor - * to the given FieldDescriptor. Note that this method has the problem that - * an empty return path does NOT strictly imply that no such path could - * be constructed: We also return an empty vector if the given - * FieldDescriptor is a direct child. Therefore we also return a bool value - * indicating that the path is valid or not. - * - * - * Implicitly this does a breadth-first search on the graph of - * StructuredClasses that are transparent. It also takes care of cycles. - * - * @param field is a FieldDescriptor that may be allowed as child of this - * Descriptor. - * @return a path of FieldDescriptors and StructuredClasses between - * this Descriptor and the input FieldDescriptor or an empty - * vector if no such path could be constructed. - */ - NodeVector pathTo(Handle field, - Logger &logger) const; - - /** - * Returns a vector of all TREE fields that are allowed as structure tree - * children of an instance of this Descriptor. This also makes use of - * transparency. - * The list is sorted by the number of transparent elements that have to be - * constructed to arrive at the respective FieldDescriptor. - * - * @return a vector of all TREE fields that are allowed as structure tree - * children of an instance of this Descriptor. - */ - NodeVector getDefaultFields() const; -}; - -/** - * This is a super class for StructuredClasses and AnnotationClasses and is, - * in itself, not supposed to be instantiated. It defines that both, Annotations - * and StructuredEntities, may have attributes and fields. For more information - * on fields please have a look at the header documentation as well as the - * documentation of the FieldDescriptor class. - * - * Attributes are primitive content stored in a key-value fashion. Therefore - * the attribute specification of a descriptor is done by referencing an - * appropriate StructType that contains all permitted keys and value types. - * - * In XML terms the difference between primitive fields and attributes can be - * explained as the difference between node attributes and node children. - * Consider the XML - * - * \code{.xml} - * - * value - * - * \endcode - * - * key="value" inside the A-node would be an attribute, while value - * would be a primitive field. While equivalent in XML the semantics are - * different: An attribute describes indeed attributes, features of one single - * node whereas a primitive field describes the _content_ of a node. - * - */ -class Descriptor : public Node { - friend FieldDescriptor; - -private: - Owned attributesDescriptor; - NodeVector fieldDescriptors; - - bool addAndSortFieldDescriptor(Handle fd, Logger &logger); - -protected: - void doResolve(ResolutionState &state) override; - - bool doValidate(Logger &logger) const override; - -public: - Descriptor(Manager &mgr, std::string name, Handle domain) - : Node(mgr, std::move(name), domain), - attributesDescriptor(acquire(new StructType(mgr, "", nullptr))), - fieldDescriptors(this) - { - } - - /** - * Returns a reference to the StructType that specifies the attribute keys - * as well as value domains for this Descriptor. - * - * @return a reference to the StructType that specifies the attribute keys - * as well as value domains for this Descriptor. - */ - Rooted getAttributesDescriptor() const - { - return attributesDescriptor; - } - - /** - * Returns the NodeVector of all FieldDescriptors of this Descriptor. - * - * @return the NodeVector of all FieldDescriptors of this Descriptor. - */ - virtual NodeVector getFieldDescriptors() const - { - return fieldDescriptors; - } - - /** - * Returns the index of the FieldDescriptor with the given name or -1 if no - * such FieldDescriptor was found. - * - * @param name the name of a FieldDescriptor. - - * @return the index of the FieldDescriptor with the given name or -1 if - * no such FieldDescriptor was found. - */ - ssize_t getFieldDescriptorIndex( - const std::string &name = DEFAULT_FIELD_NAME) const; - /** - * Returns the index of the given FieldDescriptor or -1 of the given - * FieldDescriptor is not registered at this Descriptor. - * - * @param fd a FieldDescriptor. - - * @return the index of the given FieldDescriptor or -1 of the given - * FieldDescriptor is not registered at this Descriptor. - */ - ssize_t getFieldDescriptorIndex(Handle fd) const; - /** - * Returns the FieldDescriptor with the given name. - * - * @param name the name of a FieldDescriptor. - - * @return the FieldDescriptor with the given name or a nullptr if no - * such FieldDescriptor was found. - */ - Rooted getFieldDescriptor( - const std::string &name = DEFAULT_FIELD_NAME) const; - - /** - * This returns true if this Descriptor has a FieldDescriptor with the - * given name. - * - * @param name the name of a FieldDescriptor. - - * @return true if this Descriptor has a FieldDescriptor with the given - * name - */ - bool hasField(const std::string &fieldName = DEFAULT_FIELD_NAME) const - { - return getFieldDescriptorIndex(fieldName) != -1; - } - - /** - * Adds the given FieldDescriptor to this Descriptor. This also sets the - * parent of the given FieldDescriptor if it is not set yet. - * - * @param fd is a FieldDescriptor. - * @return returns true if the given FieldDescriptor was not added at the - * end one place before because a TREE field already existed and - * the TREE field has to be at the end. - */ - bool addFieldDescriptor(Handle fd, Logger &logger); - - /** - * Adds the given FieldDescriptor to this Descriptor. This also sets the - * parent of the given FieldDescriptor if it is not set to this Descriptor - * already and removes it from the old parent Descriptor. - * - * @param fd is a FieldDescriptor. - * @return returns true if the given FieldDescriptor was not added at the - * end one place before because a TREE field already existed and - * the TREE field has to be at the end. - */ - bool moveFieldDescriptor(Handle fd, Logger &logger); - - /** - * Copies a FieldDescriptor that belongs to another Descriptor to this - * Descriptor. - * - * @param fd some FieldDescriptor belonging to another Descriptor. - * @return returns true if the given FieldDescriptor was not added at the - * end one place before because a TREE field already existed and - * the TREE field has to be at the end. - */ - bool copyFieldDescriptor(Handle fd, Logger &logger); - - /** - * Removes the given FieldDescriptor from this Descriptor. This also sets - * the parent of the given FieldDescriptor to null. - * - * @param fd is a FieldDescriptor. - * @return true if the FieldDescriptor was removed and false if this - * Descriptor did not have the given FieldDescriptor as child. - */ - bool removeFieldDescriptor(Handle fd); - - /** - * This creates a new primitive FieldDescriptor and adds it to this - * Descriptor. - * - * @param primitiveType is a handle to some Type in some Typesystem of which - * one instance is allowed to fill this field. - * @param name is the name of this field. - * @param optional should be set to 'false' is this field needs to be - * filled in order for an instance of the parent - * Descriptor to be valid. - * - * @return the newly created FieldDescriptor and a bool - * indicating whether the order of FieldDescriptors had - * to be changed for the TREE field to be in the last - * spot. - */ - std::pair, bool> createPrimitiveFieldDescriptor( - Handle primitiveType, Logger &logger, - FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, - std::string name = "", bool optional = false); - - /** - * This creates a new primitive FieldDescriptor and adds it to this - * Descriptor. - * - * @param fieldType is the FieldType of this FieldDescriptor, either - * TREE for the main or default structure or SUBTREE - * for supporting structures. - * @param name is the name of this field. - * @param optional should be set to 'false' is this field needs to be - * filled in order for an instance of the parent - * Descriptor to be valid. - * - * @return the newly created FieldDescriptor and a bool - * indicating whether the order of FieldDescriptors had - * to be changed for the TREE field to be in the last - * spot. - */ - std::pair, bool> createFieldDescriptor( - Logger &logger, - FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, - std::string name = "", bool optional = false); - - /** - * This tries to construct the shortest possible path of this Descriptor - * to the given child Descriptor. As an example consider the book domain - * from above. - * - * First consider the call book->pathTo(chapter). This is an easy example: - * Our path just contains a reference to the default field of book, because - * a chapter may be directly added to the main field of book. - * - * Second consider the call book->pathTo(text). This is somewhat more - * complicated, but it is still a valid request, because we can construct - * the path: {book_main_field, paragraph, paragraph_main_field}. - * This is only valid because paragraph is transparent. - * - * What about the call book->pathTo(section)? This will lead to an empty - * return path (= invalid). We could, of course, in principle construct - * a path between book and section (via chapter), but chapter is not - * transparent. Therefore that path is not allowed. - * - * Implicitly this does a breadth-first search on the graph of - * StructuredClasses that are transparent. It also takes care of cycles. - * - * @param childDescriptor is a supposedly valid child Descriptor of this - * Descriptor. - * @return either a path of FieldDescriptors and - * StructuredClasses between this Descriptor and - * the input StructuredClass or an empty vector if - * no such path can be constructed. - * - */ - NodeVector pathTo(Handle childDescriptor, - Logger &logger) const; - /** - * This tries to construct the shortest possible path of this Descriptor - * to the given FieldDescriptor. Note that this method has the problem that - * an empty return path does NOT strictly imply that no such path could - * be constructed: We also return an empty vector if the given - * FieldDescriptor is a direct child. Therefore we also return a bool value - * indicating that the path is valid or not. - * - * - * Implicitly this does a breadth-first search on the graph of - * StructuredClasses that are transparent. It also takes care of cycles. - * - * @param field is a FieldDescriptor that may be allowed as child of this - * Descriptor. - * @return returns a tuple containing a path of FieldDescriptors and - * StructuredClasses between this Descriptor and the input - * FieldDescriptor and a bool value indicating if the - * construction was successful. - */ - std::pair, bool> pathTo(Handle field, - Logger &logger) const; - - /** - * Returns a vector of all TREE fields that are allowed as structure tree - * children of an instance of this Descriptor. This also makes use of - * transparency. - * The list is sorted by the number of transparent elements that have to be - * constructed to arrive at the respective FieldDescriptor. - * - * @return a vector of all TREE fields that are allowed as structure tree - * children of an instance of this Descriptor. - */ - NodeVector getDefaultFields() const; - - /** - * Returns a vector of all StructuredClasses that are allowed as children - * of an instance of this Descriptor in the structure tree. This also makes - * use of transparency. - * The list is sorted by the number of transparent elements that have to be - * constructed to arrive at the respective FieldDescriptor. - * - * @return a vector of all StructuredClasses that are allowed as children - * of an instance of this Descriptor in the structure tree. - */ - NodeVector getPermittedChildren() const; -}; -/* - * TODO: We should discuss Cardinalities one more time. Is it smart to define - * cardinalities independent of context? Should we not have at least have the - * possibility to define it context-dependently? - */ - -/** - * A StructuredClass specifies nodes in the StructureTree of a document that - * implements this domain. For more information on the StructureTree please - * consult the Header documentation above. - * - * Note that a StructuredClass may "invade" an existing Domain description by - * defining itself as a viable child in one existing field. Consider the - * example of the "heading" domain from the header documentation again: - * - * \code{.xml} - * - * - * - * - * - * - * - * - * - * - * ... - * - * - * - * - * - * - * \endcode - * - * The "parent" construct allows to "invade" another domain. - * - * This does indeed interfere with an existing domain and one must carefully - * craft such parent references to not create undesired side effects. However - * they provide the most convenient mechanism to extend existing domains - * without having to rewrite them. - * - * Another important factor is the 'transparent' flag. Transparent - * StructureClasses may be implicitly constructed in the document graph. - * If we go back to our example a user would (without transparency) have to - * explicitly declare: - * - * \code{.xml} - * - *
- * Text. - *
- *
- * \endcode - * - * But in our mind the document - * - * \code{.xml} - * - *
- * Text. - *
- *
- * \endcode - * - * Is already sufficiently specific. We can infer that a paragraph should be - * wrapped around "Text.". Therefore we set the 'transparent' flag of the - * "paragraph" StructuredClass to true. Please note that such inferences - * become increasingly complicated when children of transparent - * StructuredClasses are allowed to be transparent as well. So use with care. - * - * Finally we allow StructuredClasses to inherit attributes of other - * StructuredClasses. Inheritance also implies that instance of the inheriting - * class can be used wherever an instance of the inherited class is allowed. - * Inheritance therefore also goes for fields. - */ -class StructuredClass : public Descriptor { - friend Domain; - -private: - const Variant cardinality; - Owned superclass; - NodeVector subclasses; - bool transparent; - bool root; - - /** - * Helper method for getFieldDescriptors. - */ - Rooted gatherFieldDescriptors( - NodeVector ¤t, - std::unordered_set &visited, - std::set &overriddenFields, bool hasTREE) const; - -protected: - bool doValidate(Logger &logger) const override; - -public: - /** - * The constructor for a StructuredClass. - * - * @param mgr is the current Manager. - * @param name is the name of the StructuredClass. - * @param domain is the Domain this StructuredClass belongs - * to. - * @param cardinality specifies how often an element of this type - * may occur at a specific point in the - * StructureTree. For example: A document should - * have at least one author. This is set to * - * per default, meaning that any number of - * of instances is valid, including zero. - * @param superclass references a parent StructuredClass. Please - * look for more information on inheritance in - * the class documentation above. The default is - * a null reference, meaning no super class. - * The constructor automatically registers this - * class as a subclass at the super class. - * @param transparent specifies whether this StructuredClass is - * transparent. For more information on - * transparency please refer to the class - * documentation above. The default is false. - * @param root specifies whether this StructuredClass is - * allowed to be at the root of a Document. - */ - StructuredClass(Manager &mgr, std::string name, - Handle domain = nullptr, - Variant cardinality = Cardinality::any(), - Handle superclass = nullptr, - bool transparent = false, bool root = false); - - /** - * Returns the Cardinality of this StructuredClass (as a RangeSet). - * - * @return the Cardinality of this StructuredClass (as a RangeSet). - */ - const Variant &getCardinality() const { return cardinality; } - - /** - * Returns the superclass of this StructuredClass. This is not the same as - * the parents in the Structure Tree! - * - * @return the superclass of this StructuredClass. - */ - Rooted getSuperclass() const { return superclass; } - - /** - * Sets the superclass of this StructuredClass. This is not the same as - * the parents in the Structure Tree! - * - * This will also register this class as a subclass at the given superclass - * and unregister it at the previous superclass. - * - * It will also set the parent for this Descriptors AttributesDescriptor. - * - * @param sup some StructuredClass that shall be the new superclass of - * this StructuredClass. - * @param logger is some logger. Errors during setting the parent for this - * Descriptors AttributesDescriptor will be written into this - * logger. - */ - void setSuperclass(Handle sup, Logger &logger); - - /** - * Returns true if this class is a subclass of the given class. It does not - * return true if the other class is equal to the given class. - * - * @param c is another class that might or might not be a superclass of this - * one - * @return true if this class is a subclass of the given class. - * - */ - bool isSubclassOf(Handle c) const; - - /** - * Returns the StructuredClasses that are subclasses of this class. This - * is the inverted version of isa, meaning: each class c that has a isa - * relationship to this class is part of the returned vector. - * - * Note that the order of subclasses is not strictly defined. - * - * You are not allowed to add subclasses directly to the vector. When you - * construct a new StructuredClass with a non-empty isa-handle it will - * automatically register as subclass at the super class. - * - * @return the StructuredClasses that are subclasses of this class. - */ - const NodeVector &getSubclasses() const - { - return subclasses; - } - - /** - * Adds a subclass to this StructuredClass. This also calls setSuperclass - * on the given subclass. - * - * @param sc is some StructuredClass. - * @param logger is some logger. Errors during setting the parent for the - * new subclasses AttributesDescriptor will be written into - * this logger. - */ - void addSubclass(Handle sc, Logger &logger); - - /** - * Removes a subclass from this StructuredClass. This also calls - * setSuperclass(nullptr) on the given subclass. - * - * @param sc is some StructuredClass. - * @param logger is some logger. Errors during setting the parent for the - * removed subclasses AttributesDescriptor will be written - * into this logger. - */ - void removeSubclass(Handle sc, Logger &logger); - - /** - * Returns a NodeVector of all FieldDescriptors of - * this StructuredClass. This also merges the FieldDescriptors directly - * belonging to this StructuredClass with all FieldDescritptors of its - * Superclass (and so on recurvively). The order of field descriptors is - * as follows: - * 1.) non-overridden SUBTREE FieldDescriptors of super classes. - * 2.) SUBTREE FieldDescriptors of this class. - * 3.) TREE FieldDescriptor (either inherited from super class or direct) - * - * @return a NodeVector of all FieldDescriptors of this StructuredClass. - */ - NodeVector getFieldDescriptors() const override; - - bool isTransparent() const { return transparent; } - - void setTransparent(bool t) - { - invalidate(); - transparent = std::move(t); - } - - bool hasRootPermission() const { return root; } - - void setRootPermission(bool r) - { - invalidate(); - root = std::move(r); - } -}; - -/** - * An AnnotationClass defines allowed Annotations. For more information on - * Annotations please refer to the Document.hpp. - * - * This class has no special properties and is in essence just a Descriptor. - */ -class AnnotationClass : public Descriptor { - friend Domain; - -public: - /** - * The constructor for a new AnnotationClass. Note that you have to add - * the FieldDescriptors to it later on. - * - * @param mgr is the Manager instance. - * @param name is a name for this AnnotationClass that will - * be used for later references to this - * AnnotationClass. - * @param domain is the Domain this AnnotationClass belongs - * to. - */ - AnnotationClass(Manager &mgr, std::string name, Handle domain); -}; - -/** - * A Domain node specifies which StructuredClasses and which AnnotationClasses - * are part of this domain. TODO: Do we want to be able to restrict Annotations - * to certain Structures? - */ -class Domain : public RootNode { - friend StructuredClass; - friend AnnotationClass; - -private: - NodeVector structuredClasses; - NodeVector annotationClasses; - NodeVector typesystems; - NodeVector domains; - -protected: - void doResolve(ResolutionState &state) override; - bool doValidate(Logger &logger) const override; - void doReference(Handle node) override; - RttiSet doGetReferenceTypes() const override; - -public: - /** - * The constructor for a new domain. Note that this is an empty Domain and - * still has to be filled with StructuredClasses and AnnotationClasses. - * - * @param mgr is the Manager instance. - * @param name is a name for this domain which will be used for later - * references to this Domain. - */ - Domain(Manager &mgr, std::string name = "") - : RootNode(mgr, std::move(name), nullptr), - structuredClasses(this), - annotationClasses(this), - typesystems(this), - domains(this) - { - } - - /** - * The constructor for a new domain. Note that this is an empty Domain and - * still has to be filled with StructuredClasses and AnnotationClasses. - * - * @param mgr is the Manager instance. - * @param sys is the SystemTypesystem instance. - * @param name is a name for this domain which will be used for later - * references to this Domain. - */ - Domain(Manager &mgr, Handle sys, std::string name = "") - : Domain(mgr, std::move(name)) - { - referenceTypesystem(sys); - } - - /** - * Creates a new Domain and returns it. - * - * @param mgr is the Manager instance. - * @param name is a name for this domain which will be used for later - * references to this Domain. - */ - static Rooted createEmptyDomain(Manager &mgr, std::string name) - { - return Rooted{new Domain(mgr, std::move(name))}; - } - - /** - * Returns a const reference to the NodeVector of StructuredClasses that are - * part of this Domain. - * - * @return a const reference to the NodeVector of StructuredClasses that are - * part of this Domain. - */ - const NodeVector &getStructureClasses() const - { - return structuredClasses; - } - /** - * Adds a StructuredClass to this domain. This also sets the parent of the - * given StructuredClass if it is not set to this Domain already and removes - * it from the old Domain. - * - * @param s is some StructuredClass. - */ - void addStructuredClass(Handle s); - - /** - * Removes a StructuredClass from this domain. This also sets the parent of - * the given StructuredClass to null. - * - * @param s is some StructuredClass. - * @return true if the given StructuredClass was removed and false if this - * Domain did not have the given StructuredClass as child. - */ - bool removeStructuredClass(Handle s); - - /** - * This creates a new StructuredClass and appends it to this Domain. - * - * @param name is the name of the StructuredClass. - * @param cardinality specifies how often an element of this type - * may occur at a specific point in the - * StructureTree. For example: A document should - * have at least one author. This is set to * - * per default, meaning that any number of - * of instances is valid, including zero. - * @param superclass references a parent StructuredClass. Please - * look for more information on inheritance in - * the class documentation above. The default is - * a null reference, meaning no super class. - * The constructor automatically registers this - * class as a subclass at the super class. - * @param transparent specifies whether this StructuredClass is - * transparent. For more information on - * transparency please refer to the class - * documentation above. The default is false. - * @param root specifies whether this StructuredClass is - * allowed to be at the root of a Document. - * - * @return the newly created StructuredClass. - */ - Rooted createStructuredClass( - std::string name, Variant cardinality = Cardinality::any(), - Handle superclass = nullptr, bool transparent = false, - bool root = false); - - /** - * Returns a const reference to the NodeVector of AnnotationClasses that are - * part of this Domain. - * - * @return a const reference to the NodeVector of AnnotationClasses that are - * part of this Domain. - */ - const NodeVector &getAnnotationClasses() const - { - return annotationClasses; - } - /** - * Adds an AnnotationClass to this domain. This also sets the parent of the - * given AnnotationClass if it is not set to this Domain already and removes - * it from the old Domain. - * - * @param a is some AnnotationClass. - */ - void addAnnotationClass(Handle a); - - /** - * Removes a AnnotationClass from this domain. This also sets the parent of - * the given AnnotationClass to null. - * - * @param a is some AnnotationClass. - * @return true if the given AnnotationClass was removed and false if this - * Domain did not have the given AnnotationClass as child. - */ - bool removeAnnotationClass(Handle a); - - /** - * This creates a new AnnotationClass and appends it to this Domain. - * - * @param name is a name for this AnnotationClass that will - * be used for later references to this - * AnnotationClass. - */ - Rooted createAnnotationClass(std::string name); - - /** - * Returns a const reference to the NodeVector of TypeSystems that are - * references in this Domain. - * - * @return a const reference to the NodeVector of TypeSystems that are - * references in this Domain. - */ - const NodeVector &getTypesystems() const { return typesystems; } - - /** - * Adds a Typesystem reference to this Domain. - */ - void referenceTypesystem(Handle t) { typesystems.push_back(t); } - - /** - * Adds multiple Typesystem references to this Domain. - */ - void referenceTypesystems(const std::vector> &ts) - { - typesystems.insert(typesystems.end(), ts.begin(), ts.end()); - } - - /** - * Adds a Domain reference to this Domain. - */ - void referenceDomain(Handle d) { domains.push_back(d); } - - /** - * Adds multiple Domain references to this Domain. - */ - void referenceDomains(const std::vector> &ds) - { - domains.insert(domains.end(), ds.begin(), ds.end()); - } -}; - -namespace RttiTypes { - -extern const Rtti FieldDescriptor; -extern const Rtti Descriptor; -extern const Rtti StructuredClass; -extern const Rtti AnnotationClass; -extern const Rtti Domain; -} -} - -#endif /* _OUSIA_MODEL_DOMAIN_HPP_ */ diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index fe8db16..0567e67 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -88,7 +88,7 @@ struct ResolutionResult { /** * Root node of the subtree in which the node was found. This e.g. points to - * the Domain in which a Structure was defined or the Typesystem in which a + * the Ontology in which a Structure was defined or the Typesystem in which a * Type was defined. May be nullptr. */ Rooted resolutionRoot; @@ -119,7 +119,7 @@ class ResolutionState; /** * The Node class builds the base class for any Node within the DOM graph. A - * node may either be a descriptive node (such as a domain description etc.) + * node may either be a descriptive node (such as a ontology description etc.) * or a document element. Each node is identified by acharacteristic name and * a parent element. Note that the node name is not required to be unique. Nodes * without parent are considered root nodes. diff --git a/src/core/model/Ontology.cpp b/src/core/model/Ontology.cpp new file mode 100644 index 0000000..8829139 --- /dev/null +++ b/src/core/model/Ontology.cpp @@ -0,0 +1,981 @@ +/* + 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 "Ontology.hpp" + +namespace ousia { + +/* Helper Functions */ + +struct PathState { + std::shared_ptr pred; + Node *node; + size_t length; + + PathState(std::shared_ptr pred, Node *node) + : pred(pred), node(node) + { + if (pred == nullptr) { + length = 1; + } else { + length = pred->length + 1; + } + } +}; + +static void constructPath(std::shared_ptr state, + NodeVector &vec) +{ + if (state->pred != nullptr) { + constructPath(state->pred, vec); + } + vec.push_back(state->node); +} + +static NodeVector pathTo(const Node *start, Logger &logger, + Handle target, bool &success) +{ + success = false; + // shortest path. + NodeVector shortest; + // state queue for breadth-first search. + std::queue> states; + if (start->isa(&RttiTypes::Descriptor)) { + const Descriptor *desc = static_cast(start); + // initially put every field descriptor on the queue. + NodeVector fields = desc->getFieldDescriptors(); + + for (auto fd : fields) { + if (fd == target) { + // if we have found the target directly, return without search. + success = true; + return shortest; + } + if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { + states.push(std::make_shared(nullptr, fd.get())); + } + } + } else { + const FieldDescriptor *field = + static_cast(start); + // initially put every child and its subclasses to the queue. + for (auto c : field->getChildrenWithSubclasses()) { + // if we have found the target directly, return without search. + if (c == target) { + success = true; + return shortest; + } + if (c->isTransparent()) { + states.push(std::make_shared(nullptr, c.get())); + } + } + } + // set of visited nodes. + std::unordered_set visited; + while (!states.empty()) { + std::shared_ptr current = states.front(); + states.pop(); + // do not proceed if this node was already visited. + if (!visited.insert(current->node).second) { + continue; + } + // also do not proceed if we can't get better than the current shortest + // path anymore. + if (!shortest.empty() && current->length > shortest.size()) { + continue; + } + + bool fin = false; + if (current->node->isa(&RttiTypes::StructuredClass)) { + const StructuredClass *strct = + static_cast(current->node); + + // look through all fields. + NodeVector fields = strct->getFieldDescriptors(); + for (auto fd : fields) { + // if we found our target, break off the search in this branch. + if (fd == target) { + fin = true; + continue; + } + // only continue in the TREE field. + if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { + states.push(std::make_shared(current, fd.get())); + } + } + } else { + // otherwise this is a FieldDescriptor. + const FieldDescriptor *field = + static_cast(current->node); + // and we proceed by visiting all permitted children. + for (auto c : field->getChildrenWithSubclasses()) { + // if we found our target, break off the search in this branch. + if (c == target) { + fin = true; + continue; + } + // We only allow to continue our path via transparent children. + if (c->isTransparent()) { + states.push(std::make_shared(current, c.get())); + } + } + } + // check if we are finished. + if (fin) { + success = true; + // if so we look if we found a shorter path than the current minimum + if (shortest.empty() || current->length < shortest.size()) { + NodeVector newPath; + constructPath(current, newPath); + shortest = newPath; + } else if (current->length == shortest.size()) { + // if the length is the same the result is ambigous and we log + // an error. + NodeVector newPath; + constructPath(current, newPath); + logger.error( + std::string("Can not unambigously create a path from \"") + + start->getName() + "\" to \"" + target->getName() + "\"."); + logger.note("Dismissed the path:", SourceLocation{}, + MessageMode::NO_CONTEXT); + for (auto n : newPath) { + logger.note(n->getName()); + } + } + } + } + return shortest; +} + +template +static NodeVector collect(const Node *start, F match) +{ + // result + NodeVector res; + // queue for breadth-first search of graph. + std::queue> q; + // put the initial node on the stack. + q.push(const_cast(start)); + // set of visited nodes. + std::unordered_set visited; + while (!q.empty()) { + Rooted n = q.front(); + q.pop(); + // do not proceed if this node was already visited. + if (!visited.insert(n.get()).second) { + continue; + } + + if (n->isa(&RttiTypes::StructuredClass)) { + Rooted strct = n.cast(); + + // look through all fields. + NodeVector fields = strct->getFieldDescriptors(); + for (auto fd : fields) { + // note matches. + if (match(fd)) { + res.push_back(fd); + } + // only continue in the TREE field. + if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { + q.push(fd); + } + } + } else { + // otherwise this is a FieldDescriptor. + Rooted field = n.cast(); + // and we proceed by visiting all permitted children. + for (auto c : field->getChildrenWithSubclasses()) { + // note matches. + if (match(c)) { + res.push_back(c); + } + // We only continue our search via transparent children. + if (c->isTransparent()) { + q.push(c); + } + } + } + } + return res; +} + +/* Class FieldDescriptor */ + +FieldDescriptor::FieldDescriptor(Manager &mgr, Handle primitiveType, + Handle parent, FieldType fieldType, + std::string name, bool optional) + : Node(mgr, std::move(name), parent), + children(this), + fieldType(fieldType), + primitiveType(acquire(primitiveType)), + optional(optional), + primitive(true) +{ +} + +FieldDescriptor::FieldDescriptor(Manager &mgr, Handle parent, + FieldType fieldType, std::string name, + bool optional) + : Node(mgr, std::move(name), parent), + children(this), + fieldType(fieldType), + optional(optional), + primitive(false) +{ +} + +bool FieldDescriptor::doValidate(Logger &logger) const +{ + bool valid = true; + // check parent type + if (getParent() == nullptr) { + logger.error(std::string("Field \"") + getName() + "\" has no parent!", + *this); + valid = false; + } else if (!getParent()->isa(&RttiTypes::Descriptor)) { + logger.error(std::string("The parent of Field \"") + getName() + + "\" is not a descriptor!", + *this); + valid = false; + } + // check name + if (getName().empty()) { + if (fieldType != FieldType::TREE) { + logger.error(std::string("Field \"") + getName() + + "\" is not the main field but has an empty name!", + *this); + valid = false; + } + } else { + valid = valid & validateName(logger); + } + + // check consistency of FieldType with the rest of the FieldDescriptor. + if (primitive) { + if (children.size() > 0) { + logger.error(std::string("Field \"") + getName() + + "\" is supposed to be primitive but has " + "registered child classes!", + *this); + valid = false; + } + if (primitiveType == nullptr) { + logger.error(std::string("Field \"") + getName() + + "\" is supposed to be primitive but has " + "no primitive type!", + *this); + valid = false; + } + } else { + if (primitiveType != nullptr) { + logger.error(std::string("Field \"") + getName() + + "\" is supposed to be non-primitive but has " + "a primitive type!", + *this); + valid = false; + } + // if this is not a primitive field we require at least one child. + if (children.empty()) { + logger.error(std::string("Field \"") + getName() + + "\" is non primitive but does not allow children!", + *this); + valid = false; + } + } + /* + * we are not allowed to call the validation functions of each child because + * this might lead to cycles. What we should do, however, is to check if + * there are duplicates. + */ + std::set names; + for (Handle c : getChildrenWithSubclasses()) { + if (!names.insert(c->getName()).second) { + logger.error(std::string("Field \"") + getName() + + "\" had multiple children with the name \"" + + c->getName() + "\"", + *this); + valid = false; + } + } + + return valid; +} + +static void gatherSubclasses( + std::unordered_set& visited, + NodeVector &res, Handle strct) +{ + // this check is to prevent cycles. + if (!visited.insert(strct.get()).second) { + return; + } + for (auto sub : strct->getSubclasses()) { + // this check is to prevent cycles. + if(visited.count(sub.get())){ + continue; + } + res.push_back(sub); + gatherSubclasses(visited, res, sub); + } +} + +NodeVector FieldDescriptor::getChildrenWithSubclasses() const +{ + std::unordered_set visited; + NodeVector res; + for (auto c : children) { + res.push_back(c); + gatherSubclasses(visited, res, c); + } + return res; +} + +bool FieldDescriptor::removeChild(Handle c) +{ + auto it = children.find(c); + if (it != children.end()) { + invalidate(); + children.erase(it); + return true; + } + return false; +} + +std::pair, bool> FieldDescriptor::pathTo( + Handle childDescriptor, Logger &logger) const +{ + bool success = false; + NodeVector path = + ousia::pathTo(this, logger, childDescriptor, success); + return std::make_pair(path, success); +} +NodeVector FieldDescriptor::pathTo(Handle field, + Logger &logger) const +{ + bool success = false; + return ousia::pathTo(this, logger, field, success); +} +NodeVector FieldDescriptor::getDefaultFields() const +{ + // TODO: In principle a cast would be nicer here, but for now we copy. + NodeVector nodes = collect(this, [](Handle n) { + if (!n->isa(&RttiTypes::FieldDescriptor)) { + return false; + } + Handle f = n.cast(); + return f->getFieldType() == FieldDescriptor::FieldType::TREE && + f->isPrimitive(); + }); + NodeVector res; + for (auto n : nodes) { + res.push_back(n.cast()); + } + return res; +} + +/* Class Descriptor */ + +void Descriptor::doResolve(ResolutionState &state) +{ + const NodeVector &attributes = + attributesDescriptor->getAttributes(); + continueResolveComposita(attributes, attributes.getIndex(), state); + continueResolveComposita(fieldDescriptors, fieldDescriptors.getIndex(), + state); +} + +bool Descriptor::doValidate(Logger &logger) const +{ + bool valid = true; + // check parent type + if (getParent() == nullptr) { + logger.error( + std::string("Descriptor \"") + getName() + "\" has no parent!", + *this); + valid = false; + } else if (!getParent()->isa(&RttiTypes::Ontology)) { + logger.error(std::string("The parent of Descriptor \"") + getName() + + "\" is not a Ontology!", + *this); + valid = false; + } + // check name + if (getName().empty()) { + logger.error("The name of this Descriptor is empty!", *this); + valid = false; + } else { + valid = valid & validateName(logger); + } + // ensure that no attribute with the key "name" exists. + if (attributesDescriptor == nullptr) { + logger.error(std::string("Descriptor \"") + getName() + + "\" has no Attribute specification!"); + valid = false; + } else { + if (attributesDescriptor->hasAttribute("name")) { + logger.error( + std::string("Descriptor \"") + getName() + + "\" has an attribute \"name\" which is a reserved word!"); + valid = false; + } + valid = valid & attributesDescriptor->validate(logger); + } + // check that only one FieldDescriptor is of type TREE. + auto fds = Descriptor::getFieldDescriptors(); + bool hasTREE = false; + for (auto fd : fds) { + if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { + if (!hasTREE) { + hasTREE = true; + } else { + logger.error( + std::string("Descriptor \"") + getName() + + "\" has multiple TREE fields, which is not permitted", + *fd); + valid = false; + break; + } + } + } + + // check attributes and the FieldDescriptors + return valid & continueValidationCheckDuplicates(fds, logger); +} + +NodeVector Descriptor::pathTo(Handle target, + Logger &logger) const +{ + bool success = false; + return ousia::pathTo(this, logger, target, success); +} + +std::pair, bool> Descriptor::pathTo( + Handle field, Logger &logger) const +{ + bool success = false; + NodeVector path = ousia::pathTo(this, logger, field, success); + return std::make_pair(path, success); +} + +NodeVector Descriptor::getDefaultFields() const +{ + // TODO: In principle a cast would be nicer here, but for now we copy. + NodeVector nodes = collect(this, [](Handle n) { + if (!n->isa(&RttiTypes::FieldDescriptor)) { + return false; + } + Handle f = n.cast(); + return f->getFieldType() == FieldDescriptor::FieldType::TREE && + f->isPrimitive(); + }); + NodeVector res; + for (auto n : nodes) { + res.push_back(n.cast()); + } + return res; +} + +NodeVector Descriptor::getPermittedChildren() const +{ + // TODO: In principle a cast would be nicer here, but for now we copy. + NodeVector nodes = collect(this, [](Handle n) { + return n->isa(&RttiTypes::StructuredClass); + }); + NodeVector res; + for (auto n : nodes) { + res.push_back(n.cast()); + } + return res; +} + +static ssize_t getFieldDescriptorIndex(const NodeVector &fds, + const std::string &name) +{ + if (fds.empty()) { + return -1; + } + + if (name == DEFAULT_FIELD_NAME) { + if (fds.back()->getFieldType() == FieldDescriptor::FieldType::TREE) { + return fds.size() - 1; + } else { + /* The last field has to be the TREE field. If the last field does + * not have the FieldType TREE no TREE-field exists at all. So we + * return -1. + */ + return -1; + } + } + + for (size_t f = 0; f < fds.size(); f++) { + if (fds[f]->getName() == name) { + return f; + } + } + return -1; +} + +ssize_t Descriptor::getFieldDescriptorIndex(const std::string &name) const +{ + NodeVector fds = getFieldDescriptors(); + return ousia::getFieldDescriptorIndex(fds, name); +} + +ssize_t Descriptor::getFieldDescriptorIndex(Handle fd) const +{ + size_t f = 0; + for (auto &fd2 : getFieldDescriptors()) { + if (fd == fd2) { + return f; + } + f++; + } + return -1; +} + +Rooted Descriptor::getFieldDescriptor( + const std::string &name) const +{ + NodeVector fds = getFieldDescriptors(); + ssize_t idx = ousia::getFieldDescriptorIndex(fds, name); + if (idx != -1) { + return fds[idx]; + } else { + return nullptr; + } +} + +bool Descriptor::addAndSortFieldDescriptor(Handle fd, + Logger &logger) +{ + // only add it if we need to. + auto fds = getFieldDescriptors(); + if (fds.find(fd) == fds.end()) { + invalidate(); + // check if the previous field is a tree field already. + if (!fieldDescriptors.empty() && + fieldDescriptors.back()->getFieldType() == + FieldDescriptor::FieldType::TREE && + fd->getFieldType() != FieldDescriptor::FieldType::TREE) { + // if so we add the new field before the TREE field. + fieldDescriptors.insert(fieldDescriptors.end() - 1, fd); + return true; + } else { + fieldDescriptors.push_back(fd); + } + } + return false; +} + +bool Descriptor::addFieldDescriptor(Handle fd, Logger &logger) +{ + if (fd->getParent() == nullptr) { + fd->setParent(this); + } + return addAndSortFieldDescriptor(fd, logger); +} + +bool Descriptor::moveFieldDescriptor(Handle fd, Logger &logger) +{ + bool sorted = addAndSortFieldDescriptor(fd, logger); + Handle par = fd->getParent(); + if (par != this) { + if (par != nullptr) { + // remove the FieldDescriptor from the old parent. + par.cast()->removeFieldDescriptor(fd); + } + fd->setParent(this); + } + return sorted; +} + +bool Descriptor::copyFieldDescriptor(Handle fd, Logger &logger) +{ + Rooted copy; + if (fd->isPrimitive()) { + copy = Rooted{new FieldDescriptor( + getManager(), fd->getPrimitiveType(), this, fd->getFieldType(), + fd->getName(), fd->isOptional())}; + } else { + /* + * In case of non-primitive FieldDescriptors we also want to copy the + * child references. + */ + copy = Rooted{ + new FieldDescriptor(getManager(), this, fd->getFieldType(), + fd->getName(), fd->isOptional())}; + for (auto c : fd->getChildren()) { + copy->addChild(c); + } + } + return addFieldDescriptor(copy, logger); +} + +bool Descriptor::removeFieldDescriptor(Handle fd) +{ + auto it = fieldDescriptors.find(fd); + if (it != fieldDescriptors.end()) { + invalidate(); + fieldDescriptors.erase(it); + fd->setParent(nullptr); + return true; + } + return false; +} + +std::pair, bool> +Descriptor::createPrimitiveFieldDescriptor(Handle primitiveType, + Logger &logger, + FieldDescriptor::FieldType fieldType, + std::string name, bool optional) +{ + Rooted fd{new FieldDescriptor(getManager(), primitiveType, + this, fieldType, + std::move(name), optional)}; + bool sorted = addFieldDescriptor(fd, logger); + return std::make_pair(fd, sorted); +} + +std::pair, bool> Descriptor::createFieldDescriptor( + Logger &logger, FieldDescriptor::FieldType fieldType, std::string name, + bool optional) +{ + Rooted fd{new FieldDescriptor( + getManager(), this, fieldType, std::move(name), optional)}; + bool sorted = addFieldDescriptor(fd, logger); + return std::make_pair(fd, sorted); +} + +/* Class StructuredClass */ + +StructuredClass::StructuredClass(Manager &mgr, std::string name, + Handle ontology, Variant cardinality, + Handle superclass, + bool transparent, bool root) + : Descriptor(mgr, std::move(name), ontology), + cardinality(cardinality), + superclass(acquire(superclass)), + subclasses(this), + transparent(transparent), + root(root) +{ + ExceptionLogger logger; + if (superclass != nullptr) { + superclass->addSubclass(this, logger); + } + if (ontology != nullptr) { + ontology->addStructuredClass(this); + } +} + +bool StructuredClass::doValidate(Logger &logger) const +{ + bool valid = true; + // check if all registered subclasses have this StructuredClass as parent. + for (Handle sub : subclasses) { + if (sub->getSuperclass() != this) { + logger.error(std::string("Struct \"") + sub->getName() + + "\" is registered as subclass of \"" + getName() + + "\" but does not have it as superclass!", + *this); + valid = false; + } + } + // check the cardinality. + if (!cardinality.isCardinality()) { + logger.error(cardinality.toString() + " is not a cardinality!", *this); + valid = false; + } + // check the validity of this superclass. + if (superclass != nullptr) { + valid = valid & superclass->validate(logger); + } + // check the validity as a Descriptor. + /* + * Note that we do not check the validity of all subclasses. This is because + * it will lead to cycles as the subclasses would call validate on their + * superclass, which is this one. + */ + return valid & Descriptor::doValidate(logger); +} + +void StructuredClass::setSuperclass(Handle sup, Logger &logger) +{ + if (superclass == sup) { + return; + } + // remove this subclass from the old superclass. + if (superclass != nullptr) { + superclass->removeSubclass(this, logger); + } + // set the new superclass + superclass = acquire(sup); + invalidate(); + // add this class as new subclass of the new superclass. + if (sup != nullptr) { + sup->addSubclass(this, logger); + // set the attribute descriptor supertype + getAttributesDescriptor()->setParentStructure( + sup->getAttributesDescriptor(), logger); + } else { + getAttributesDescriptor()->setParentStructure(nullptr, logger); + } +} + +bool StructuredClass::isSubclassOf(Handle c) const +{ + if (c == nullptr || superclass == nullptr) { + return false; + } + if (c == superclass) { + return true; + } + return superclass->isSubclassOf(c); +} + +void StructuredClass::addSubclass(Handle sc, Logger &logger) +{ + if (sc == nullptr) { + return; + } + // check if we already have that class. + if (subclasses.find(sc) == subclasses.end()) { + invalidate(); + subclasses.push_back(sc); + } + sc->setSuperclass(this, logger); +} + +void StructuredClass::removeSubclass(Handle sc, Logger &logger) +{ + // if we don't have this subclass we can return directly. + if (sc == nullptr) { + return; + } + auto it = subclasses.find(sc); + if (it == subclasses.end()) { + return; + } + // otherwise we have to erase it. + invalidate(); + subclasses.erase(it); + sc->setSuperclass(nullptr, logger); +} + +Rooted StructuredClass::gatherFieldDescriptors( + NodeVector ¤t, + std::unordered_set &visited, + std::set &overriddenFields, bool hasTREE) const +{ + // this check is to prevent cycles of inheritance to mess up this function. + if (!visited.insert(this).second) { + return nullptr; + } + Rooted mainField; + NodeVector tmp; + // first gather the non-overridden fields. + for (auto f : Descriptor::getFieldDescriptors()) { + if (overriddenFields.insert(f->getName()).second) { + bool isTREE = f->getFieldType() == FieldDescriptor::FieldType::TREE; + if (!isTREE) { + tmp.push_back(f); + } else { + if (!hasTREE) { + hasTREE = true; + mainField = f; + } + } + } + } + // append all non-overridden superclass fields. + + if (superclass != nullptr) { + Rooted super_main_field = + superclass->gatherFieldDescriptors(current, visited, + overriddenFields, hasTREE); + if (!hasTREE) { + mainField = super_main_field; + } + } + // then append all subtree fields of this level. + current.insert(current.end(), tmp.begin(), tmp.end()); + // and return the main field. + return mainField; +} + +NodeVector StructuredClass::getFieldDescriptors() const +{ + // in this case we return a NodeVector of Rooted entries without owner. + NodeVector vec; + std::unordered_set visited; + std::set overriddenFields; + Rooted mainField = + gatherFieldDescriptors(vec, visited, overriddenFields, false); + if (mainField != nullptr) { + vec.push_back(mainField); + } + return vec; +} + +/* Class AnnotationClass */ + +AnnotationClass::AnnotationClass(Manager &mgr, std::string name, + Handle ontology) + : Descriptor(mgr, std::move(name), ontology) +{ + if (ontology != nullptr) { + ontology->addAnnotationClass(this); + } +} + +/* Class Ontology */ + +void Ontology::doResolve(ResolutionState &state) +{ + continueResolveComposita(structuredClasses, structuredClasses.getIndex(), + state); + continueResolveComposita(annotationClasses, annotationClasses.getIndex(), + state); + continueResolveReferences(typesystems, state); + continueResolveReferences(ontologies, state); +} + +bool Ontology::doValidate(Logger &logger) const +{ + // check validity of name, of StructuredClasses, of AnnotationClasses and + // TypeSystems. + return validateName(logger) & + continueValidationCheckDuplicates(structuredClasses, logger) & + continueValidationCheckDuplicates(annotationClasses, logger) & + continueValidationCheckDuplicates(typesystems, logger); +} + +void Ontology::doReference(Handle node) +{ + if (node->isa(&RttiTypes::Typesystem)) { + referenceTypesystem(node.cast()); + } + if (node->isa(&RttiTypes::Ontology)) { + referenceOntology(node.cast()); + } +} + +RttiSet Ontology::doGetReferenceTypes() const +{ + return RttiSet{&RttiTypes::Ontology, &RttiTypes::Typesystem}; +} + +void Ontology::addStructuredClass(Handle s) +{ + // only add it if we need to. + if (structuredClasses.find(s) == structuredClasses.end()) { + invalidate(); + structuredClasses.push_back(s); + } + Handle par = s->getParent(); + if (par != this) { + if (par != nullptr) { + // remove the StructuredClass from the old parent. + par.cast()->removeStructuredClass(s); + } + s->setParent(this); + } +} + +bool Ontology::removeStructuredClass(Handle s) +{ + auto it = structuredClasses.find(s); + if (it != structuredClasses.end()) { + invalidate(); + structuredClasses.erase(it); + s->setParent(nullptr); + return true; + } + return false; +} + +Rooted Ontology::createStructuredClass( + std::string name, Variant cardinality, Handle superclass, + bool transparent, bool root) +{ + return Rooted{new StructuredClass( + getManager(), std::move(name), this, cardinality, superclass, + std::move(transparent), std::move(root))}; +} + +void Ontology::addAnnotationClass(Handle a) +{ + // only add it if we need to. + if (annotationClasses.find(a) == annotationClasses.end()) { + invalidate(); + annotationClasses.push_back(a); + } + Handle par = a->getParent(); + if (par != this) { + if (par != nullptr) { + // remove the StructuredClass from the old parent. + par.cast()->removeAnnotationClass(a); + } + a->setParent(this); + } +} + +bool Ontology::removeAnnotationClass(Handle a) +{ + auto it = annotationClasses.find(a); + if (it != annotationClasses.end()) { + invalidate(); + annotationClasses.erase(it); + a->setParent(nullptr); + return true; + } + return false; +} + +Rooted Ontology::createAnnotationClass(std::string name) +{ + return Rooted{ + new AnnotationClass(getManager(), std::move(name), this)}; +} + +/* Type registrations */ + +namespace RttiTypes { +const Rtti FieldDescriptor = + RttiBuilder("FieldDescriptor").parent(&Node); +const Rtti Descriptor = + RttiBuilder("Descriptor").parent(&Node); +const Rtti StructuredClass = + RttiBuilder("StructuredClass") + .parent(&Descriptor) + .composedOf(&FieldDescriptor); +const Rtti AnnotationClass = + RttiBuilder("AnnotationClass").parent(&Descriptor); +const Rtti Ontology = RttiBuilder("Ontology") + .parent(&RootNode) + .composedOf({&StructuredClass, &AnnotationClass}); +} +} \ No newline at end of file diff --git a/src/core/model/Ontology.hpp b/src/core/model/Ontology.hpp new file mode 100644 index 0000000..948caa5 --- /dev/null +++ b/src/core/model/Ontology.hpp @@ -0,0 +1,1203 @@ +/* + 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 . +*/ + +/** + * @file Ontology.hpp + * + * This header contains the class hierarchy of descriptor classes for ontologies. + * Properly connected instances of these classes with a Ontology node as root + * describe a semantic Ontology in a formal way. It specifies the allowed (tree) + * structure of a document by means of StructuredClasses as well as the allowed + * Annotations by means of AnnotationClasses. + * + * The Structure Description contained in the hierarchy of StructuredClasses is + * equivalent to a context free grammar of a special form. We introduce the + * terms "StructuredClass" and "FieldDescriptor". + * On the top level you would start with a StructuredClass, say "book", which + * in turn might contain two FieldDescriptors, one for the meta data of ones + * book and one for the actual structure. Consider the following XML: + * + * \code{.xml} + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * \endcode + * + * Note that we define one field as the TREE (meaning the main or default + * document structure) and one mearly as SUBTREE, relating to supporting + * information. You are not allowed to define more than one field of type + * "TREE". + * + * The translation to a context free grammar is as follows: + * + * \code{.txt} + * BOOK := BOOK_TREE + * BOOK_TREE := CHAPTER BOOK_TREE | PARAGRAPH BOOK_TREE | epsilon + * CHAPTER := CHAPTER_TREE + * CHAPTER_TREE := SECTION CHAPTER_TREE | PARAGRAPH CHAPTER_TREE | epsilon + * SECTION :=
SECTION_TREE
+ * SECTION_TREE := SUBSECTION SECTION_TREE | PARAGRAPH SECTION_TREE | + * epsilon + * SUBSECTION := SUBSECTION_TREE + * SUBSECTION_TREE := PARAGRAPH SUBSECTION_TREE | epsilon + * PARAGRAPH := PARAGRAPH_CONTENT + * PARAGRAPH_CONTENT := string + * \endcode + * + * Note that this translation recurs to further nonterminals like SECTION but + * necessarily produces one "book" terminal. Also note that, in principle, + * this grammar translation allows for arbitrarily many children instances of + * the proper StructuredClass. This can be regulated by the "cardinality" + * property of a StructuredClass. + * + * It is possible to add further fields, like we would in the "headings" ontology + * to add titles to our structure. + * + * \code{.xml} + * + * + * + * + * + * + * ... + * + * + * + * \endcode + * + * This would change the context free grammar as follows: + * + * \code{.txt} + * BOOK := HEADING BOOK_TREE + * HEADING := PARAGRAPH + * \endcode + * + * AnnotationClasses on the other hand do not specify a context free grammar. + * They merely specify what kinds of Annotations are allowed within this ontology + * and which fields or attributes they have. Note that Annotations are allowed + * to define structured children that manifest e.g. meta information of that + * Annotation. An example for that would be the "comment" ontology: + * + * \code{.xml} + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * \endcode + * + * Here we have comment annotations, which have a reply tree as sub structure. + * + * @author Benjamin Paaßen (bpaassen@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_MODEL_DOMAIN_HPP_ +#define _OUSIA_MODEL_DOMAIN_HPP_ + +#include +#include + +#include "Node.hpp" +#include "RootNode.hpp" +#include "Typesystem.hpp" + +namespace ousia { + +// Forward declarations +class Rtti; +class Descriptor; +class StructuredClass; +class Ontology; + +/** + * Magic field name used to identify the default field. The default field is + * either the tree field or the only subtree field. + */ +static const std::string DEFAULT_FIELD_NAME = "$default"; + +/** + * As mentioned in the description above a FieldDescriptor specifies the + * StructuredClasses that are allowed as children of a StructuredClass or + * AnnotationClass. A field may also be primitive, which means that a proper + * instance of the respective StructuredClass or AnnotationClass must provide + * accordingly typed content without further descending in the Structure + * Hierarchy. + * + * As an example consider the "text" StructuredClass, which might allow + * the actual text content. Here is the according XML: + * + * \code{.xml} + * + * + * + * \endcode + * + */ +class FieldDescriptor : public Node { + friend Descriptor; + +public: + /** + * This enum class contains all possible FieldTypes, meaning either the + * main structure beneath this Descriptor (TREE) or supporting structure + * (SUBTREE) + * + * Note that there may be only one TREE field in a descriptor. + */ + enum class FieldType { TREE, SUBTREE }; + +private: + NodeVector children; + FieldType fieldType; + Owned primitiveType; + bool optional; + bool primitive; + +protected: + bool doValidate(Logger &logger) const override; + +public: + /** + * This is the constructor for primitive fields. + * + * @param mgr is the global Manager instance. + * @param parent is a handle of the Descriptor node that has this + * FieldDescriptor. + * @param primitiveType is a handle to some Type in some Typesystem of which + * one instance is allowed to fill this field. + * @param name is the name of this field. + * @param optional should be set to 'false' is this field needs to be + * filled in order for an instance of the parent + * Descriptor to be valid. + */ + FieldDescriptor(Manager &mgr, Handle primitiveType, + Handle parent, + FieldType fieldType = FieldType::TREE, + std::string name = "", bool optional = false); + + /** + * This is the constructor for non-primitive fields. You have to provide + * children here later on. + * + * @param mgr is the global Manager instance. + * @param parent is a handle of the Descriptor node that has this + * FieldDescriptor. + * @param fieldType is the FieldType of this FieldDescriptor, either + * TREE for the main or default structure or SUBTREE + * for supporting structures. + * @param name is the name of this field. + * @param optional should be set to 'false' is this field needs to be + * filled in order for an instance of the parent + * Descriptor to be valid. + */ + FieldDescriptor(Manager &mgr, Handle parent = nullptr, + FieldType fieldType = FieldType::TREE, + std::string name = "", bool optional = false); + + /** + * Returns a const reference to the NodeVector of StructuredClasses whose + * instances are allowed as children in the StructureTree of instances of + * this field. + * + * @return a const reference to the NodeVector of StructuredClasses whose + * instances are allowed as children in the StructureTree of instances of + * this field. + */ + const NodeVector &getChildren() const { return children; } + + /** + * Returns all StructuredClasses whose instances are allowed as children in + * the Structure Tree of instances of this field including subclasses of + * children, which are allowed directly. + * + * @return all StructuredClasses whose instances are allowed as children in + * the Structure Tree of instances of this field including subclasses of + * children, which are allowed directly. + */ + NodeVector getChildrenWithSubclasses() const; + + /** + * Adds a StructuredClass whose instances shall be allowed as children in + * the StructureTree of instances of this field. + */ + void addChild(Handle c) + { + invalidate(); + children.push_back(c); + } + + /** + * Adds multiple StructuredClasses whose instances shall be allowed as + * children in the StructureTree of instances of this field. + */ + void addChildren(const std::vector> &cs) + { + invalidate(); + children.insert(children.end(), cs.begin(), cs.end()); + } + + /** + * Removes the given StructuredClass from the list of children of this + * FieldDescriptor. + * + * @param c some StructuredClass that shan't be a child of this + * FieldDescriptor anymore. + * @return true if the FieldDescriptor contained this child and false if it + * did not. + */ + bool removeChild(Handle c); + + /** + * Returns the type of this field (not to be confused with the primitive + * type of this field). + * + * @return the type of this field. + */ + FieldType getFieldType() const { return fieldType; } + /** + * Sets the type of this field (not to be confused with the primitive type + * of this field). + * + * @param ft is the new type of this field. + */ + void setFieldType(const FieldType &ft) + { + invalidate(); + fieldType = ft; + } + + /** + * Returns if this field is primitive. + * + * @return true if and only if this field is primitive. + */ + bool isPrimitive() const { return primitive; } + + /** + * Returns the primitive type of this field, which is only allowed to be + * set if the type of this field is PRIMITIVE. + * + * @return the primitive type of this field. + */ + Rooted getPrimitiveType() const { return primitiveType; } + + /** + * Sets the primitive type of this field, which is only allowed to be + * set if the type of this field is PRIMITIVE. + * + * @param t is the new primitive type of this field- + */ + void setPrimitiveType(Handle t) + { + invalidate(); + primitiveType = acquire(t); + } + + /** + * Returns true if and only if this field is optional. + * + * @return true if and only if this field is optional. + */ + bool isOptional() const { return optional; } + + /** + * Specifies whether this field shall be optional. + * + * @param o should be true if and only if this field should be optional. + */ + void setOptional(bool o) + { + invalidate(); + optional = std::move(o); + } + + /** + * This tries to construct the shortest possible path of this Descriptor + * to the given child Descriptor. Note that this method has the problem that + * an empty return path does NOT strictly imply that no such path could + * be constructed: We also return an empty vector if the given + * Descriptor is a direct child. Therefore we also return a bool value + * indicating that the path is valid or not. + * + * Implicitly this does a breadth-first search on the graph of + * StructuredClasses that are transparent. It also takes care of cycles. + * + * @param childDescriptor is a supposedly valid child Descriptor of this + * Descriptor. + * @return a tuple containing a path of FieldDescriptors and + * StructuredClasses between this Descriptor and the + * input Descriptor and a bool value indicating if + * the construction was successful. + * + */ + std::pair, bool> pathTo( + Handle childDescriptor, Logger &logger) const; + /** + * This tries to construct the shortest possible path of this Descriptor + * to the given FieldDescriptor. Note that this method has the problem that + * an empty return path does NOT strictly imply that no such path could + * be constructed: We also return an empty vector if the given + * FieldDescriptor is a direct child. Therefore we also return a bool value + * indicating that the path is valid or not. + * + * + * Implicitly this does a breadth-first search on the graph of + * StructuredClasses that are transparent. It also takes care of cycles. + * + * @param field is a FieldDescriptor that may be allowed as child of this + * Descriptor. + * @return a path of FieldDescriptors and StructuredClasses between + * this Descriptor and the input FieldDescriptor or an empty + * vector if no such path could be constructed. + */ + NodeVector pathTo(Handle field, + Logger &logger) const; + + /** + * Returns a vector of all TREE fields that are allowed as structure tree + * children of an instance of this Descriptor. This also makes use of + * transparency. + * The list is sorted by the number of transparent elements that have to be + * constructed to arrive at the respective FieldDescriptor. + * + * @return a vector of all TREE fields that are allowed as structure tree + * children of an instance of this Descriptor. + */ + NodeVector getDefaultFields() const; +}; + +/** + * This is a super class for StructuredClasses and AnnotationClasses and is, + * in itself, not supposed to be instantiated. It defines that both, Annotations + * and StructuredEntities, may have attributes and fields. For more information + * on fields please have a look at the header documentation as well as the + * documentation of the FieldDescriptor class. + * + * Attributes are primitive content stored in a key-value fashion. Therefore + * the attribute specification of a descriptor is done by referencing an + * appropriate StructType that contains all permitted keys and value types. + * + * In XML terms the difference between primitive fields and attributes can be + * explained as the difference between node attributes and node children. + * Consider the XML + * + * \code{.xml} + * + * value + * + * \endcode + * + * key="value" inside the A-node would be an attribute, while value + * would be a primitive field. While equivalent in XML the semantics are + * different: An attribute describes indeed attributes, features of one single + * node whereas a primitive field describes the _content_ of a node. + * + */ +class Descriptor : public Node { + friend FieldDescriptor; + +private: + Owned attributesDescriptor; + NodeVector fieldDescriptors; + + bool addAndSortFieldDescriptor(Handle fd, Logger &logger); + +protected: + void doResolve(ResolutionState &state) override; + + bool doValidate(Logger &logger) const override; + +public: + Descriptor(Manager &mgr, std::string name, Handle ontology) + : Node(mgr, std::move(name), ontology), + attributesDescriptor(acquire(new StructType(mgr, "", nullptr))), + fieldDescriptors(this) + { + } + + /** + * Returns a reference to the StructType that specifies the attribute keys + * as well as value ontologies for this Descriptor. + * + * @return a reference to the StructType that specifies the attribute keys + * as well as value ontologies for this Descriptor. + */ + Rooted getAttributesDescriptor() const + { + return attributesDescriptor; + } + + /** + * Returns the NodeVector of all FieldDescriptors of this Descriptor. + * + * @return the NodeVector of all FieldDescriptors of this Descriptor. + */ + virtual NodeVector getFieldDescriptors() const + { + return fieldDescriptors; + } + + /** + * Returns the index of the FieldDescriptor with the given name or -1 if no + * such FieldDescriptor was found. + * + * @param name the name of a FieldDescriptor. + + * @return the index of the FieldDescriptor with the given name or -1 if + * no such FieldDescriptor was found. + */ + ssize_t getFieldDescriptorIndex( + const std::string &name = DEFAULT_FIELD_NAME) const; + /** + * Returns the index of the given FieldDescriptor or -1 of the given + * FieldDescriptor is not registered at this Descriptor. + * + * @param fd a FieldDescriptor. + + * @return the index of the given FieldDescriptor or -1 of the given + * FieldDescriptor is not registered at this Descriptor. + */ + ssize_t getFieldDescriptorIndex(Handle fd) const; + /** + * Returns the FieldDescriptor with the given name. + * + * @param name the name of a FieldDescriptor. + + * @return the FieldDescriptor with the given name or a nullptr if no + * such FieldDescriptor was found. + */ + Rooted getFieldDescriptor( + const std::string &name = DEFAULT_FIELD_NAME) const; + + /** + * This returns true if this Descriptor has a FieldDescriptor with the + * given name. + * + * @param name the name of a FieldDescriptor. + + * @return true if this Descriptor has a FieldDescriptor with the given + * name + */ + bool hasField(const std::string &fieldName = DEFAULT_FIELD_NAME) const + { + return getFieldDescriptorIndex(fieldName) != -1; + } + + /** + * Adds the given FieldDescriptor to this Descriptor. This also sets the + * parent of the given FieldDescriptor if it is not set yet. + * + * @param fd is a FieldDescriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. + */ + bool addFieldDescriptor(Handle fd, Logger &logger); + + /** + * Adds the given FieldDescriptor to this Descriptor. This also sets the + * parent of the given FieldDescriptor if it is not set to this Descriptor + * already and removes it from the old parent Descriptor. + * + * @param fd is a FieldDescriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. + */ + bool moveFieldDescriptor(Handle fd, Logger &logger); + + /** + * Copies a FieldDescriptor that belongs to another Descriptor to this + * Descriptor. + * + * @param fd some FieldDescriptor belonging to another Descriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. + */ + bool copyFieldDescriptor(Handle fd, Logger &logger); + + /** + * Removes the given FieldDescriptor from this Descriptor. This also sets + * the parent of the given FieldDescriptor to null. + * + * @param fd is a FieldDescriptor. + * @return true if the FieldDescriptor was removed and false if this + * Descriptor did not have the given FieldDescriptor as child. + */ + bool removeFieldDescriptor(Handle fd); + + /** + * This creates a new primitive FieldDescriptor and adds it to this + * Descriptor. + * + * @param primitiveType is a handle to some Type in some Typesystem of which + * one instance is allowed to fill this field. + * @param name is the name of this field. + * @param optional should be set to 'false' is this field needs to be + * filled in order for an instance of the parent + * Descriptor to be valid. + * + * @return the newly created FieldDescriptor and a bool + * indicating whether the order of FieldDescriptors had + * to be changed for the TREE field to be in the last + * spot. + */ + std::pair, bool> createPrimitiveFieldDescriptor( + Handle primitiveType, Logger &logger, + FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, + std::string name = "", bool optional = false); + + /** + * This creates a new primitive FieldDescriptor and adds it to this + * Descriptor. + * + * @param fieldType is the FieldType of this FieldDescriptor, either + * TREE for the main or default structure or SUBTREE + * for supporting structures. + * @param name is the name of this field. + * @param optional should be set to 'false' is this field needs to be + * filled in order for an instance of the parent + * Descriptor to be valid. + * + * @return the newly created FieldDescriptor and a bool + * indicating whether the order of FieldDescriptors had + * to be changed for the TREE field to be in the last + * spot. + */ + std::pair, bool> createFieldDescriptor( + Logger &logger, + FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, + std::string name = "", bool optional = false); + + /** + * This tries to construct the shortest possible path of this Descriptor + * to the given child Descriptor. As an example consider the book ontology + * from above. + * + * First consider the call book->pathTo(chapter). This is an easy example: + * Our path just contains a reference to the default field of book, because + * a chapter may be directly added to the main field of book. + * + * Second consider the call book->pathTo(text). This is somewhat more + * complicated, but it is still a valid request, because we can construct + * the path: {book_main_field, paragraph, paragraph_main_field}. + * This is only valid because paragraph is transparent. + * + * What about the call book->pathTo(section)? This will lead to an empty + * return path (= invalid). We could, of course, in principle construct + * a path between book and section (via chapter), but chapter is not + * transparent. Therefore that path is not allowed. + * + * Implicitly this does a breadth-first search on the graph of + * StructuredClasses that are transparent. It also takes care of cycles. + * + * @param childDescriptor is a supposedly valid child Descriptor of this + * Descriptor. + * @return either a path of FieldDescriptors and + * StructuredClasses between this Descriptor and + * the input StructuredClass or an empty vector if + * no such path can be constructed. + * + */ + NodeVector pathTo(Handle childDescriptor, + Logger &logger) const; + /** + * This tries to construct the shortest possible path of this Descriptor + * to the given FieldDescriptor. Note that this method has the problem that + * an empty return path does NOT strictly imply that no such path could + * be constructed: We also return an empty vector if the given + * FieldDescriptor is a direct child. Therefore we also return a bool value + * indicating that the path is valid or not. + * + * + * Implicitly this does a breadth-first search on the graph of + * StructuredClasses that are transparent. It also takes care of cycles. + * + * @param field is a FieldDescriptor that may be allowed as child of this + * Descriptor. + * @return returns a tuple containing a path of FieldDescriptors and + * StructuredClasses between this Descriptor and the input + * FieldDescriptor and a bool value indicating if the + * construction was successful. + */ + std::pair, bool> pathTo(Handle field, + Logger &logger) const; + + /** + * Returns a vector of all TREE fields that are allowed as structure tree + * children of an instance of this Descriptor. This also makes use of + * transparency. + * The list is sorted by the number of transparent elements that have to be + * constructed to arrive at the respective FieldDescriptor. + * + * @return a vector of all TREE fields that are allowed as structure tree + * children of an instance of this Descriptor. + */ + NodeVector getDefaultFields() const; + + /** + * Returns a vector of all StructuredClasses that are allowed as children + * of an instance of this Descriptor in the structure tree. This also makes + * use of transparency. + * The list is sorted by the number of transparent elements that have to be + * constructed to arrive at the respective FieldDescriptor. + * + * @return a vector of all StructuredClasses that are allowed as children + * of an instance of this Descriptor in the structure tree. + */ + NodeVector getPermittedChildren() const; +}; +/* + * TODO: We should discuss Cardinalities one more time. Is it smart to define + * cardinalities independent of context? Should we not have at least have the + * possibility to define it context-dependently? + */ + +/** + * A StructuredClass specifies nodes in the StructureTree of a document that + * implements this ontology. For more information on the StructureTree please + * consult the Header documentation above. + * + * Note that a StructuredClass may "invade" an existing Ontology description by + * defining itself as a viable child in one existing field. Consider the + * example of the "heading" ontology from the header documentation again: + * + * \code{.xml} + * + * + * + * + * + * + * + * + * + * + * ... + * + * + * + * + * + * + * \endcode + * + * The "parent" construct allows to "invade" another ontology. + * + * This does indeed interfere with an existing ontology and one must carefully + * craft such parent references to not create undesired side effects. However + * they provide the most convenient mechanism to extend existing ontologies + * without having to rewrite them. + * + * Another important factor is the 'transparent' flag. Transparent + * StructureClasses may be implicitly constructed in the document graph. + * If we go back to our example a user would (without transparency) have to + * explicitly declare: + * + * \code{.xml} + * + *
+ * Text. + *
+ *
+ * \endcode + * + * But in our mind the document + * + * \code{.xml} + * + *
+ * Text. + *
+ *
+ * \endcode + * + * Is already sufficiently specific. We can infer that a paragraph should be + * wrapped around "Text.". Therefore we set the 'transparent' flag of the + * "paragraph" StructuredClass to true. Please note that such inferences + * become increasingly complicated when children of transparent + * StructuredClasses are allowed to be transparent as well. So use with care. + * + * Finally we allow StructuredClasses to inherit attributes of other + * StructuredClasses. Inheritance also implies that instance of the inheriting + * class can be used wherever an instance of the inherited class is allowed. + * Inheritance therefore also goes for fields. + */ +class StructuredClass : public Descriptor { + friend Ontology; + +private: + const Variant cardinality; + Owned superclass; + NodeVector subclasses; + bool transparent; + bool root; + + /** + * Helper method for getFieldDescriptors. + */ + Rooted gatherFieldDescriptors( + NodeVector ¤t, + std::unordered_set &visited, + std::set &overriddenFields, bool hasTREE) const; + +protected: + bool doValidate(Logger &logger) const override; + +public: + /** + * The constructor for a StructuredClass. + * + * @param mgr is the current Manager. + * @param name is the name of the StructuredClass. + * @param ontology is the Ontology this StructuredClass belongs + * to. + * @param cardinality specifies how often an element of this type + * may occur at a specific point in the + * StructureTree. For example: A document should + * have at least one author. This is set to * + * per default, meaning that any number of + * of instances is valid, including zero. + * @param superclass references a parent StructuredClass. Please + * look for more information on inheritance in + * the class documentation above. The default is + * a null reference, meaning no super class. + * The constructor automatically registers this + * class as a subclass at the super class. + * @param transparent specifies whether this StructuredClass is + * transparent. For more information on + * transparency please refer to the class + * documentation above. The default is false. + * @param root specifies whether this StructuredClass is + * allowed to be at the root of a Document. + */ + StructuredClass(Manager &mgr, std::string name, + Handle ontology = nullptr, + Variant cardinality = Cardinality::any(), + Handle superclass = nullptr, + bool transparent = false, bool root = false); + + /** + * Returns the Cardinality of this StructuredClass (as a RangeSet). + * + * @return the Cardinality of this StructuredClass (as a RangeSet). + */ + const Variant &getCardinality() const { return cardinality; } + + /** + * Returns the superclass of this StructuredClass. This is not the same as + * the parents in the Structure Tree! + * + * @return the superclass of this StructuredClass. + */ + Rooted getSuperclass() const { return superclass; } + + /** + * Sets the superclass of this StructuredClass. This is not the same as + * the parents in the Structure Tree! + * + * This will also register this class as a subclass at the given superclass + * and unregister it at the previous superclass. + * + * It will also set the parent for this Descriptors AttributesDescriptor. + * + * @param sup some StructuredClass that shall be the new superclass of + * this StructuredClass. + * @param logger is some logger. Errors during setting the parent for this + * Descriptors AttributesDescriptor will be written into this + * logger. + */ + void setSuperclass(Handle sup, Logger &logger); + + /** + * Returns true if this class is a subclass of the given class. It does not + * return true if the other class is equal to the given class. + * + * @param c is another class that might or might not be a superclass of this + * one + * @return true if this class is a subclass of the given class. + * + */ + bool isSubclassOf(Handle c) const; + + /** + * Returns the StructuredClasses that are subclasses of this class. This + * is the inverted version of isa, meaning: each class c that has a isa + * relationship to this class is part of the returned vector. + * + * Note that the order of subclasses is not strictly defined. + * + * You are not allowed to add subclasses directly to the vector. When you + * construct a new StructuredClass with a non-empty isa-handle it will + * automatically register as subclass at the super class. + * + * @return the StructuredClasses that are subclasses of this class. + */ + const NodeVector &getSubclasses() const + { + return subclasses; + } + + /** + * Adds a subclass to this StructuredClass. This also calls setSuperclass + * on the given subclass. + * + * @param sc is some StructuredClass. + * @param logger is some logger. Errors during setting the parent for the + * new subclasses AttributesDescriptor will be written into + * this logger. + */ + void addSubclass(Handle sc, Logger &logger); + + /** + * Removes a subclass from this StructuredClass. This also calls + * setSuperclass(nullptr) on the given subclass. + * + * @param sc is some StructuredClass. + * @param logger is some logger. Errors during setting the parent for the + * removed subclasses AttributesDescriptor will be written + * into this logger. + */ + void removeSubclass(Handle sc, Logger &logger); + + /** + * Returns a NodeVector of all FieldDescriptors of + * this StructuredClass. This also merges the FieldDescriptors directly + * belonging to this StructuredClass with all FieldDescritptors of its + * Superclass (and so on recurvively). The order of field descriptors is + * as follows: + * 1.) non-overridden SUBTREE FieldDescriptors of super classes. + * 2.) SUBTREE FieldDescriptors of this class. + * 3.) TREE FieldDescriptor (either inherited from super class or direct) + * + * @return a NodeVector of all FieldDescriptors of this StructuredClass. + */ + NodeVector getFieldDescriptors() const override; + + bool isTransparent() const { return transparent; } + + void setTransparent(bool t) + { + invalidate(); + transparent = std::move(t); + } + + bool hasRootPermission() const { return root; } + + void setRootPermission(bool r) + { + invalidate(); + root = std::move(r); + } +}; + +/** + * An AnnotationClass defines allowed Annotations. For more information on + * Annotations please refer to the Document.hpp. + * + * This class has no special properties and is in essence just a Descriptor. + */ +class AnnotationClass : public Descriptor { + friend Ontology; + +public: + /** + * The constructor for a new AnnotationClass. Note that you have to add + * the FieldDescriptors to it later on. + * + * @param mgr is the Manager instance. + * @param name is a name for this AnnotationClass that will + * be used for later references to this + * AnnotationClass. + * @param ontology is the Ontology this AnnotationClass belongs + * to. + */ + AnnotationClass(Manager &mgr, std::string name, Handle ontology); +}; + +/** + * A Ontology node specifies which StructuredClasses and which AnnotationClasses + * are part of this ontology. TODO: Do we want to be able to restrict Annotations + * to certain Structures? + */ +class Ontology : public RootNode { + friend StructuredClass; + friend AnnotationClass; + +private: + NodeVector structuredClasses; + NodeVector annotationClasses; + NodeVector typesystems; + NodeVector ontologies; + +protected: + void doResolve(ResolutionState &state) override; + bool doValidate(Logger &logger) const override; + void doReference(Handle node) override; + RttiSet doGetReferenceTypes() const override; + +public: + /** + * The constructor for a new ontology. Note that this is an empty Ontology and + * still has to be filled with StructuredClasses and AnnotationClasses. + * + * @param mgr is the Manager instance. + * @param name is a name for this ontology which will be used for later + * references to this Ontology. + */ + Ontology(Manager &mgr, std::string name = "") + : RootNode(mgr, std::move(name), nullptr), + structuredClasses(this), + annotationClasses(this), + typesystems(this), + ontologies(this) + { + } + + /** + * The constructor for a new ontology. Note that this is an empty Ontology and + * still has to be filled with StructuredClasses and AnnotationClasses. + * + * @param mgr is the Manager instance. + * @param sys is the SystemTypesystem instance. + * @param name is a name for this ontology which will be used for later + * references to this Ontology. + */ + Ontology(Manager &mgr, Handle sys, std::string name = "") + : Ontology(mgr, std::move(name)) + { + referenceTypesystem(sys); + } + + /** + * Creates a new Ontology and returns it. + * + * @param mgr is the Manager instance. + * @param name is a name for this ontology which will be used for later + * references to this Ontology. + */ + static Rooted createEmptyOntology(Manager &mgr, std::string name) + { + return Rooted{new Ontology(mgr, std::move(name))}; + } + + /** + * Returns a const reference to the NodeVector of StructuredClasses that are + * part of this Ontology. + * + * @return a const reference to the NodeVector of StructuredClasses that are + * part of this Ontology. + */ + const NodeVector &getStructureClasses() const + { + return structuredClasses; + } + /** + * Adds a StructuredClass to this ontology. This also sets the parent of the + * given StructuredClass if it is not set to this Ontology already and removes + * it from the old Ontology. + * + * @param s is some StructuredClass. + */ + void addStructuredClass(Handle s); + + /** + * Removes a StructuredClass from this ontology. This also sets the parent of + * the given StructuredClass to null. + * + * @param s is some StructuredClass. + * @return true if the given StructuredClass was removed and false if this + * Ontology did not have the given StructuredClass as child. + */ + bool removeStructuredClass(Handle s); + + /** + * This creates a new StructuredClass and appends it to this Ontology. + * + * @param name is the name of the StructuredClass. + * @param cardinality specifies how often an element of this type + * may occur at a specific point in the + * StructureTree. For example: A document should + * have at least one author. This is set to * + * per default, meaning that any number of + * of instances is valid, including zero. + * @param superclass references a parent StructuredClass. Please + * look for more information on inheritance in + * the class documentation above. The default is + * a null reference, meaning no super class. + * The constructor automatically registers this + * class as a subclass at the super class. + * @param transparent specifies whether this StructuredClass is + * transparent. For more information on + * transparency please refer to the class + * documentation above. The default is false. + * @param root specifies whether this StructuredClass is + * allowed to be at the root of a Document. + * + * @return the newly created StructuredClass. + */ + Rooted createStructuredClass( + std::string name, Variant cardinality = Cardinality::any(), + Handle superclass = nullptr, bool transparent = false, + bool root = false); + + /** + * Returns a const reference to the NodeVector of AnnotationClasses that are + * part of this Ontology. + * + * @return a const reference to the NodeVector of AnnotationClasses that are + * part of this Ontology. + */ + const NodeVector &getAnnotationClasses() const + { + return annotationClasses; + } + /** + * Adds an AnnotationClass to this ontology. This also sets the parent of the + * given AnnotationClass if it is not set to this Ontology already and removes + * it from the old Ontology. + * + * @param a is some AnnotationClass. + */ + void addAnnotationClass(Handle a); + + /** + * Removes a AnnotationClass from this ontology. This also sets the parent of + * the given AnnotationClass to null. + * + * @param a is some AnnotationClass. + * @return true if the given AnnotationClass was removed and false if this + * Ontology did not have the given AnnotationClass as child. + */ + bool removeAnnotationClass(Handle a); + + /** + * This creates a new AnnotationClass and appends it to this Ontology. + * + * @param name is a name for this AnnotationClass that will + * be used for later references to this + * AnnotationClass. + */ + Rooted createAnnotationClass(std::string name); + + /** + * Returns a const reference to the NodeVector of TypeSystems that are + * references in this Ontology. + * + * @return a const reference to the NodeVector of TypeSystems that are + * references in this Ontology. + */ + const NodeVector &getTypesystems() const { return typesystems; } + + /** + * Adds a Typesystem reference to this Ontology. + */ + void referenceTypesystem(Handle t) { typesystems.push_back(t); } + + /** + * Adds multiple Typesystem references to this Ontology. + */ + void referenceTypesystems(const std::vector> &ts) + { + typesystems.insert(typesystems.end(), ts.begin(), ts.end()); + } + + /** + * Adds a Ontology reference to this Ontology. + */ + void referenceOntology(Handle d) { ontologies.push_back(d); } + + /** + * Adds multiple Ontology references to this Ontology. + */ + void referenceOntologys(const std::vector> &ds) + { + ontologies.insert(ontologies.end(), ds.begin(), ds.end()); + } +}; + +namespace RttiTypes { + +extern const Rtti FieldDescriptor; +extern const Rtti Descriptor; +extern const Rtti StructuredClass; +extern const Rtti AnnotationClass; +extern const Rtti Ontology; +} +} + +#endif /* _OUSIA_MODEL_DOMAIN_HPP_ */ diff --git a/src/core/model/Project.cpp b/src/core/model/Project.cpp index f7dab8a..ecc0589 100644 --- a/src/core/model/Project.cpp +++ b/src/core/model/Project.cpp @@ -18,7 +18,7 @@ #include -#include "Domain.hpp" +#include "Ontology.hpp" #include "Document.hpp" #include "Project.hpp" #include "Typesystem.hpp" @@ -69,9 +69,9 @@ Rooted Project::createDocument(const std::string &name) return document; } -Rooted Project::createDomain(const std::string &name) +Rooted Project::createOntology(const std::string &name) { - return Rooted{new Domain(getManager(), systemTypesystem, name)}; + return Rooted{new Ontology(getManager(), systemTypesystem, name)}; } void Project::referenceDocument(Handle document) diff --git a/src/core/model/Project.hpp b/src/core/model/Project.hpp index 480609c..97c711c 100644 --- a/src/core/model/Project.hpp +++ b/src/core/model/Project.hpp @@ -20,7 +20,7 @@ * @file Project.hpp * * Contains the concept of the "Project" class which represents the entity into - * which domains, documents, typesystems and other resources are embedded. + * which ontologies, documents, typesystems and other resources are embedded. * * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) */ @@ -40,12 +40,12 @@ class Registry; class SystemTypesystem; class Typesystem; class Document; -class Domain; +class Ontology; /** * The Project class constitutes the top-level node in which a collection of * documents are stored. It also contains an instance of the SystemTypesystem - * and allows for simple creation of new Typesystem and Domain instances. + * and allows for simple creation of new Typesystem and Ontology instances. */ class Project : public RootNode { private: @@ -99,12 +99,12 @@ public: Rooted createDocument(const std::string &name); /** - * Returns a new domain with the given name and adds it to the list of - * domains. Provides a reference of the system typesystem to the domain. + * Returns a new ontology with the given name and adds it to the list of + * ontologies. Provides a reference of the system typesystem to the ontology. * - * @param name is the name of the domain that should be created. + * @param name is the name of the ontology that should be created. */ - Rooted createDomain(const std::string &name); + Rooted createOntology(const std::string &name); /** * Adds the given document to the list of documents in the project. diff --git a/src/core/model/RootNode.hpp b/src/core/model/RootNode.hpp index 173a6e4..d26917f 100644 --- a/src/core/model/RootNode.hpp +++ b/src/core/model/RootNode.hpp @@ -37,7 +37,7 @@ namespace ousia { /** * The RootNode class represents a Node that may be a Root node (such as - * Documents, Typesystems and Domains). Root nodes have the property, that the + * Documents, Typesystems and Ontologys). Root nodes have the property, that the * allow importing/referencing other Nodes. */ class RootNode : public Node { diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp index dabb03c..c46dc51 100644 --- a/src/core/parser/ParserScope.cpp +++ b/src/core/parser/ParserScope.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index bb04bd3..2144c34 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp index 862081c..44feb2b 100644 --- a/src/core/parser/stack/DocumentHandler.hpp +++ b/src/core/parser/stack/DocumentHandler.hpp @@ -46,7 +46,7 @@ namespace parser_stack { /** * The DocumentHandler class parses the "document" tag that is used to introduce * a new document. Note that this tag is not mandatory in osml files -- if the - * first command is not a typesystem, domain or any other declarative command, + * first command is not a typesystem, ontology or any other declarative command, * the DocumentHandler will be implicitly called. */ class DocumentHandler : public StaticHandler { diff --git a/src/core/parser/stack/DomainHandler.cpp b/src/core/parser/stack/DomainHandler.cpp deleted file mode 100644 index e86f893..0000000 --- a/src/core/parser/stack/DomainHandler.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - 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 "DocumentHandler.hpp" -#include "DomainHandler.hpp" -#include "State.hpp" -#include "TypesystemHandler.hpp" - -namespace ousia { -namespace parser_stack { - -/* DomainHandler */ - -bool DomainHandler::start(Variant::mapType &args) -{ - // Create the Domain node - Rooted domain = - context().getProject()->createDomain(args["name"].asString()); - domain->setLocation(location()); - - // If the domain is defined inside a document, add the reference to the - // document - Rooted document = scope().select(); - if (document != nullptr) { - document->reference(domain); - } - - // Push the typesystem onto the scope, set the POST_HEAD flag to true - scope().push(domain); - scope().setFlag(ParserFlag::POST_HEAD, false); - return true; -} - -void DomainHandler::end() { scope().pop(logger()); } - -/* DomainStructHandler */ - -bool DomainStructHandler::start(Variant::mapType &args) -{ - scope().setFlag(ParserFlag::POST_HEAD, true); - - Rooted domain = scope().selectOrThrow(); - - Rooted structuredClass = domain->createStructuredClass( - args["name"].asString(), args["cardinality"].asCardinality(), nullptr, - args["transparent"].asBool(), args["isRoot"].asBool()); - structuredClass->setLocation(location()); - - const std::string &isa = args["isa"].asString(); - if (!isa.empty()) { - scope().resolve( - isa, structuredClass, logger(), - [](Handle superclass, Handle structuredClass, - Logger &logger) { - if (superclass != nullptr) { - structuredClass.cast()->setSuperclass( - superclass.cast(), logger); - } - }); - } - - scope().push(structuredClass); - return true; -} - -void DomainStructHandler::end() { scope().pop(logger()); } - -/* DomainAnnotationHandler */ -bool DomainAnnotationHandler::start(Variant::mapType &args) -{ - scope().setFlag(ParserFlag::POST_HEAD, true); - - Rooted domain = scope().selectOrThrow(); - - Rooted annotationClass = - domain->createAnnotationClass(args["name"].asString()); - annotationClass->setLocation(location()); - - scope().push(annotationClass); - return true; -} - -void DomainAnnotationHandler::end() { scope().pop(logger()); } - -/* DomainAttributesHandler */ - -bool DomainAttributesHandler::start(Variant::mapType &args) -{ - // Fetch the current typesystem and create the struct node - Rooted parent = scope().selectOrThrow(); - - Rooted attrDesc = parent->getAttributesDescriptor(); - attrDesc->setLocation(location()); - - scope().push(attrDesc); - return true; -} - -void DomainAttributesHandler::end() { scope().pop(logger()); } - -/* DomainFieldHandler */ - -bool DomainFieldHandler::start(Variant::mapType &args) -{ - FieldDescriptor::FieldType type; - if (args["isSubtree"].asBool()) { - type = FieldDescriptor::FieldType::SUBTREE; - } else { - type = FieldDescriptor::FieldType::TREE; - } - - Rooted parent = scope().selectOrThrow(); - - auto res = parent->createFieldDescriptor( - logger(), type, args["name"].asString(), args["optional"].asBool()); - res.first->setLocation(location()); - if (res.second) { - logger().warning( - std::string("Field \"") + res.first->getName() + - "\" was declared after main field. The order of fields " - "was changed to make the main field the last field.", - *res.first); - } - - scope().push(res.first); - return true; -} - -void DomainFieldHandler::end() { scope().pop(logger()); } - -/* DomainFieldRefHandler */ - -bool DomainFieldRefHandler::start(Variant::mapType &args) -{ - Rooted parent = scope().selectOrThrow(); - - const std::string &name = args["ref"].asString(); - - auto loc = location(); - - scope().resolveFieldDescriptor(name, parent, logger(), - [loc](Handle field, - Handle parent, Logger &logger) { - if (field != nullptr) { - if (parent.cast()->addFieldDescriptor( - field.cast(), logger)) { - logger.warning( - std::string("Field \"") + field->getName() + - "\" was referenced after main field was declared. The " - "order of fields was changed to make the main field " - "the last field.", - loc); - } - } - }); - return true; -} - -void DomainFieldRefHandler::end() {} - -/* DomainPrimitiveHandler */ - -bool DomainPrimitiveHandler::start(Variant::mapType &args) -{ - Rooted parent = scope().selectOrThrow(); - - FieldDescriptor::FieldType fieldType; - if (args["isSubtree"].asBool()) { - fieldType = FieldDescriptor::FieldType::SUBTREE; - } else { - fieldType = FieldDescriptor::FieldType::TREE; - } - - auto res = parent->createPrimitiveFieldDescriptor( - new UnknownType(manager()), logger(), fieldType, - args["name"].asString(), args["optional"].asBool()); - res.first->setLocation(location()); - if (res.second) { - logger().warning( - std::string("Field \"") + res.first->getName() + - "\" was declared after main field. The order of fields " - "was changed to make the main field the last field.", - *res.first); - } - - const std::string &type = args["type"].asString(); - scope().resolveType(type, res.first, logger(), - [](Handle type, Handle field, - Logger &logger) { - if (type != nullptr) { - field.cast()->setPrimitiveType(type.cast()); - } - }); - - scope().push(res.first); - return true; -} - -void DomainPrimitiveHandler::end() { scope().pop(logger()); } - -/* DomainChildHandler */ - -bool DomainChildHandler::start(Variant::mapType &args) -{ - Rooted field = scope().selectOrThrow(); - - const std::string &ref = args["ref"].asString(); - scope().resolve( - ref, field, logger(), - [](Handle child, Handle field, Logger &logger) { - if (child != nullptr) { - field.cast()->addChild( - child.cast()); - } - }); - return true; -} - -/* DomainParentHandler */ - -bool DomainParentHandler::start(Variant::mapType &args) -{ - Rooted strct = scope().selectOrThrow(); - - Rooted parent{ - new DomainParent(strct->getManager(), args["ref"].asString(), strct)}; - parent->setLocation(location()); - scope().push(parent); - return true; -} - -void DomainParentHandler::end() { scope().pop(logger()); } - -/* DomainParentFieldHandler */ - -bool DomainParentFieldHandler::start(Variant::mapType &args) -{ - Rooted parentNameNode = scope().selectOrThrow(); - FieldDescriptor::FieldType type; - if (args["isSubtree"].asBool()) { - type = FieldDescriptor::FieldType::SUBTREE; - } else { - type = FieldDescriptor::FieldType::TREE; - } - - const std::string &name = args["name"].asString(); - const bool optional = args["optional"].asBool(); - Rooted strct = - parentNameNode->getParent().cast(); - - // resolve the parent, create the declared field and add the declared - // StructuredClass as child to it. - scope().resolve( - parentNameNode->getName(), strct, logger(), - [type, name, optional](Handle parent, Handle strct, - Logger &logger) { - if (parent != nullptr) { - Rooted field = - (parent.cast()->createFieldDescriptor( - logger, type, name, optional)).first; - field->addChild(strct.cast()); - } - }); - return true; -} - -/* DomainParentFieldRefHandler */ - -bool DomainParentFieldRefHandler::start(Variant::mapType &args) -{ - Rooted parentNameNode = scope().selectOrThrow(); - - const std::string &name = args["ref"].asString(); - Rooted strct = - parentNameNode->getParent().cast(); - auto loc = location(); - - // resolve the parent, get the referenced field and add the declared - // StructuredClass as child to it. - scope().resolve( - parentNameNode->getName(), strct, logger(), - [name, loc](Handle parent, Handle strct, Logger &logger) { - if (parent != nullptr) { - Rooted field = - parent.cast()->getFieldDescriptor(name); - if (field == nullptr) { - logger.error( - std::string("Could not find referenced field ") + name, - loc); - return; - } - field->addChild(strct.cast()); - } - }); - return true; -} - -namespace States { -const State Domain = StateBuilder() - .parents({&None, &Document}) - .createdNodeType(&RttiTypes::Domain) - .elementHandler(DomainHandler::create) - .arguments({Argument::String("name")}); - -const State DomainStruct = - StateBuilder() - .parent(&Domain) - .createdNodeType(&RttiTypes::StructuredClass) - .elementHandler(DomainStructHandler::create) - .arguments({Argument::String("name"), - Argument::Cardinality("cardinality", Cardinality::any()), - Argument::Bool("isRoot", false), - Argument::Bool("transparent", false), - Argument::String("isa", "")}); - -const State DomainAnnotation = - StateBuilder() - .parent(&Domain) - .createdNodeType(&RttiTypes::AnnotationClass) - .elementHandler(DomainAnnotationHandler::create) - .arguments({Argument::String("name")}); - -const State DomainAttributes = - StateBuilder() - .parents({&DomainStruct, &DomainAnnotation}) - .createdNodeType(&RttiTypes::StructType) - .elementHandler(DomainAttributesHandler::create) - .arguments({}); - -const State DomainAttribute = - StateBuilder() - .parent(&DomainAttributes) - .elementHandler(TypesystemStructFieldHandler::create) - .arguments({Argument::String("name"), Argument::String("type"), - Argument::Any("default", Variant::fromObject(nullptr))}); - -const State DomainField = StateBuilder() - .parents({&DomainStruct, &DomainAnnotation}) - .createdNodeType(&RttiTypes::FieldDescriptor) - .elementHandler(DomainFieldHandler::create) - .arguments({Argument::String("name", ""), - Argument::Bool("isSubtree", false), - Argument::Bool("optional", false)}); - -const State DomainFieldRef = - StateBuilder() - .parents({&DomainStruct, &DomainAnnotation}) - .createdNodeType(&RttiTypes::FieldDescriptor) - .elementHandler(DomainFieldRefHandler::create) - .arguments({Argument::String("ref", DEFAULT_FIELD_NAME)}); - -const State DomainStructPrimitive = - StateBuilder() - .parents({&DomainStruct, &DomainAnnotation}) - .createdNodeType(&RttiTypes::FieldDescriptor) - .elementHandler(DomainPrimitiveHandler::create) - .arguments( - {Argument::String("name", ""), Argument::Bool("isSubtree", false), - Argument::Bool("optional", false), Argument::String("type")}); - -const State DomainStructChild = StateBuilder() - .parent(&DomainField) - .elementHandler(DomainChildHandler::create) - .arguments({Argument::String("ref")}); - -const State DomainStructParent = - StateBuilder() - .parent(&DomainStruct) - .createdNodeType(&RttiTypes::DomainParent) - .elementHandler(DomainParentHandler::create) - .arguments({Argument::String("ref")}); - -const State DomainStructParentField = - StateBuilder() - .parent(&DomainStructParent) - .createdNodeType(&RttiTypes::FieldDescriptor) - .elementHandler(DomainParentFieldHandler::create) - .arguments({Argument::String("name", ""), - Argument::Bool("isSubtree", false), - Argument::Bool("optional", false)}); - -const State DomainStructParentFieldRef = - StateBuilder() - .parent(&DomainStructParent) - .createdNodeType(&RttiTypes::FieldDescriptor) - .elementHandler(DomainParentFieldRefHandler::create) - .arguments({Argument::String("ref", DEFAULT_FIELD_NAME)}); -} -} - -namespace RttiTypes { -const Rtti DomainParent = RttiBuilder( - "DomainParent").parent(&Node); -} -} diff --git a/src/core/parser/stack/DomainHandler.hpp b/src/core/parser/stack/DomainHandler.hpp deleted file mode 100644 index 76172d6..0000000 --- a/src/core/parser/stack/DomainHandler.hpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - 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 . -*/ - -/** - * @file DomainHandler.hpp - * - * Contains the Handler classes used for parsing Domain descriptors. This - * includes the "domain" tag and all describing tags below the "domain" tag. - * - * @author Benjamin Paaßen (bpaassen@techfak.uni-bielefeld.de) - */ - -#ifndef _OUSIA_DOMAIN_HANDLER_HPP_ -#define _OUSIA_DOMAIN_HANDLER_HPP_ - -#include -#include - -#include "Handler.hpp" - -namespace ousia { - -// Forward declarations -class Rtti; - -namespace parser_stack { - -// TODO: Documentation - -class DomainHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainHandler{handlerData}; - } -}; - -class DomainStructHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainStructHandler{handlerData}; - } -}; - -class DomainAnnotationHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainAnnotationHandler{handlerData}; - } -}; - -class DomainAttributesHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainAttributesHandler{handlerData}; - } -}; - -class DomainFieldHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainFieldHandler{handlerData}; - } -}; - -class DomainFieldRefHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainFieldRefHandler{handlerData}; - } -}; - -class DomainPrimitiveHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainPrimitiveHandler{handlerData}; - } -}; - -class DomainChildHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainChildHandler{handlerData}; - } -}; - -class DomainParent : public Node { -public: - using Node::Node; -}; - -class DomainParentHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - void end() override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainParentHandler{handlerData}; - } -}; - -class DomainParentFieldHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainParentFieldHandler{handlerData}; - } -}; - -class DomainParentFieldRefHandler : public StaticHandler { -public: - using StaticHandler::StaticHandler; - - bool start(Variant::mapType &args) override; - - static Handler *create(const HandlerData &handlerData) - { - return new DomainParentFieldRefHandler{handlerData}; - } -}; - -namespace States { -/** - * State representing a "domain" struct. - */ -extern const State Domain; - -/** - * State representing a "struct" tag within a domain description. - */ -extern const State DomainStruct; - -/** - * State representing an "annotation" tag within a domain description. - */ -extern const State DomainAnnotation; - -/** - * State representing an "attributes" tag within a structure or annotation. - */ -extern const State DomainAttributes; - -/** - * State representing an "attribute" tag within the "attributes". - */ -extern const State DomainAttribute; - -/** - * State representing a "field" tag within a structure or annotation. - */ -extern const State DomainField; - -/** - * State representing a "fieldref" tag within a structure or annotation. - */ -extern const State DomainFieldRef; - -/** - * State representing a "primitive" tag within a structure or annotation. - */ -extern const State DomainStructPrimitive; - -/** - * State representing a "child" tag within a structure or annotation. - */ -extern const State DomainStructChild; - -/** - * State representing a "parent" tag within a structure or annotation. - */ -extern const State DomainStructParent; - -/** - * State representing a "field" tag within a "parent" tag. - */ -extern const State DomainStructParentField; - -/** - * State representing a "fieldRef" tag within a "parent" tag. - */ -extern const State DomainStructParentFieldRef; -} -} - -namespace RttiTypes { -extern const Rtti DomainParent; -} -} -#endif diff --git a/src/core/parser/stack/GenericParserStates.cpp b/src/core/parser/stack/GenericParserStates.cpp index 69a6e0e..7287524 100644 --- a/src/core/parser/stack/GenericParserStates.cpp +++ b/src/core/parser/stack/GenericParserStates.cpp @@ -17,7 +17,7 @@ */ #include "DocumentHandler.hpp" -#include "DomainHandler.hpp" +#include "OntologyHandler.hpp" #include "GenericParserStates.hpp" #include "ImportIncludeHandler.hpp" #include "TypesystemHandler.hpp" @@ -28,18 +28,18 @@ namespace parser_stack { const std::multimap GenericParserStates{ {"document", &States::Document}, {"*", &States::DocumentChild}, - {"domain", &States::Domain}, - {"struct", &States::DomainStruct}, - {"annotation", &States::DomainAnnotation}, - {"attributes", &States::DomainAttributes}, - {"attribute", &States::DomainAttribute}, - {"field", &States::DomainField}, - {"fieldRef", &States::DomainFieldRef}, - {"primitive", &States::DomainStructPrimitive}, - {"childRef", &States::DomainStructChild}, - {"parentRef", &States::DomainStructParent}, - {"field", &States::DomainStructParentField}, - {"fieldRef", &States::DomainStructParentFieldRef}, + {"ontology", &States::Ontology}, + {"struct", &States::OntologyStruct}, + {"annotation", &States::OntologyAnnotation}, + {"attributes", &States::OntologyAttributes}, + {"attribute", &States::OntologyAttribute}, + {"field", &States::OntologyField}, + {"fieldRef", &States::OntologyFieldRef}, + {"primitive", &States::OntologyStructPrimitive}, + {"childRef", &States::OntologyStructChild}, + {"parentRef", &States::OntologyStructParent}, + {"field", &States::OntologyStructParentField}, + {"fieldRef", &States::OntologyStructParentFieldRef}, {"typesystem", &States::Typesystem}, {"enum", &States::TypesystemEnum}, {"entry", &States::TypesystemEnumEntry}, diff --git a/src/core/parser/stack/ImportIncludeHandler.cpp b/src/core/parser/stack/ImportIncludeHandler.cpp index d1ea97d..a6cbaea 100644 --- a/src/core/parser/stack/ImportIncludeHandler.cpp +++ b/src/core/parser/stack/ImportIncludeHandler.cpp @@ -20,7 +20,7 @@ #include #include -#include "DomainHandler.hpp" +#include "OntologyHandler.hpp" #include "DocumentHandler.hpp" #include "ImportIncludeHandler.hpp" #include "State.hpp" @@ -38,7 +38,7 @@ void ImportHandler::doHandle(const Variant &fieldData, Variant::mapType &args) Rooted leaf = scope().getLeaf(); if (leaf == nullptr || !leaf->isa(&RttiTypes::RootNode)) { logger().error( - "Import not supported here, must be inside a document, domain " + "Import not supported here, must be inside a document, ontology " "or typesystem command.", location()); return; @@ -66,7 +66,7 @@ void IncludeHandler::doHandle(const Variant &fieldData, Variant::mapType &args) namespace States { const State Import = StateBuilder() - .parents({&Document, &Typesystem, &Domain}) + .parents({&Document, &Typesystem, &Ontology}) .elementHandler(ImportHandler::create) .arguments({Argument::String("rel", ""), Argument::String("type", ""), Argument::String("src", "")}); diff --git a/src/core/parser/stack/OntologyHandler.cpp b/src/core/parser/stack/OntologyHandler.cpp new file mode 100644 index 0000000..8c0e4d9 --- /dev/null +++ b/src/core/parser/stack/OntologyHandler.cpp @@ -0,0 +1,417 @@ +/* + 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 "DocumentHandler.hpp" +#include "OntologyHandler.hpp" +#include "State.hpp" +#include "TypesystemHandler.hpp" + +namespace ousia { +namespace parser_stack { + +/* OntologyHandler */ + +bool OntologyHandler::start(Variant::mapType &args) +{ + // Create the Ontology node + Rooted ontology = + context().getProject()->createOntology(args["name"].asString()); + ontology->setLocation(location()); + + // If the ontology is defined inside a document, add the reference to the + // document + Rooted document = scope().select(); + if (document != nullptr) { + document->reference(ontology); + } + + // Push the typesystem onto the scope, set the POST_HEAD flag to true + scope().push(ontology); + scope().setFlag(ParserFlag::POST_HEAD, false); + return true; +} + +void OntologyHandler::end() { scope().pop(logger()); } + +/* OntologyStructHandler */ + +bool OntologyStructHandler::start(Variant::mapType &args) +{ + scope().setFlag(ParserFlag::POST_HEAD, true); + + Rooted ontology = scope().selectOrThrow(); + + Rooted structuredClass = ontology->createStructuredClass( + args["name"].asString(), args["cardinality"].asCardinality(), nullptr, + args["transparent"].asBool(), args["isRoot"].asBool()); + structuredClass->setLocation(location()); + + const std::string &isa = args["isa"].asString(); + if (!isa.empty()) { + scope().resolve( + isa, structuredClass, logger(), + [](Handle superclass, Handle structuredClass, + Logger &logger) { + if (superclass != nullptr) { + structuredClass.cast()->setSuperclass( + superclass.cast(), logger); + } + }); + } + + scope().push(structuredClass); + return true; +} + +void OntologyStructHandler::end() { scope().pop(logger()); } + +/* OntologyAnnotationHandler */ +bool OntologyAnnotationHandler::start(Variant::mapType &args) +{ + scope().setFlag(ParserFlag::POST_HEAD, true); + + Rooted ontology = scope().selectOrThrow(); + + Rooted annotationClass = + ontology->createAnnotationClass(args["name"].asString()); + annotationClass->setLocation(location()); + + scope().push(annotationClass); + return true; +} + +void OntologyAnnotationHandler::end() { scope().pop(logger()); } + +/* OntologyAttributesHandler */ + +bool OntologyAttributesHandler::start(Variant::mapType &args) +{ + // Fetch the current typesystem and create the struct node + Rooted parent = scope().selectOrThrow(); + + Rooted attrDesc = parent->getAttributesDescriptor(); + attrDesc->setLocation(location()); + + scope().push(attrDesc); + return true; +} + +void OntologyAttributesHandler::end() { scope().pop(logger()); } + +/* OntologyFieldHandler */ + +bool OntologyFieldHandler::start(Variant::mapType &args) +{ + FieldDescriptor::FieldType type; + if (args["isSubtree"].asBool()) { + type = FieldDescriptor::FieldType::SUBTREE; + } else { + type = FieldDescriptor::FieldType::TREE; + } + + Rooted parent = scope().selectOrThrow(); + + auto res = parent->createFieldDescriptor( + logger(), type, args["name"].asString(), args["optional"].asBool()); + res.first->setLocation(location()); + if (res.second) { + logger().warning( + std::string("Field \"") + res.first->getName() + + "\" was declared after main field. The order of fields " + "was changed to make the main field the last field.", + *res.first); + } + + scope().push(res.first); + return true; +} + +void OntologyFieldHandler::end() { scope().pop(logger()); } + +/* OntologyFieldRefHandler */ + +bool OntologyFieldRefHandler::start(Variant::mapType &args) +{ + Rooted parent = scope().selectOrThrow(); + + const std::string &name = args["ref"].asString(); + + auto loc = location(); + + scope().resolveFieldDescriptor(name, parent, logger(), + [loc](Handle field, + Handle parent, Logger &logger) { + if (field != nullptr) { + if (parent.cast()->addFieldDescriptor( + field.cast(), logger)) { + logger.warning( + std::string("Field \"") + field->getName() + + "\" was referenced after main field was declared. The " + "order of fields was changed to make the main field " + "the last field.", + loc); + } + } + }); + return true; +} + +void OntologyFieldRefHandler::end() {} + +/* OntologyPrimitiveHandler */ + +bool OntologyPrimitiveHandler::start(Variant::mapType &args) +{ + Rooted parent = scope().selectOrThrow(); + + FieldDescriptor::FieldType fieldType; + if (args["isSubtree"].asBool()) { + fieldType = FieldDescriptor::FieldType::SUBTREE; + } else { + fieldType = FieldDescriptor::FieldType::TREE; + } + + auto res = parent->createPrimitiveFieldDescriptor( + new UnknownType(manager()), logger(), fieldType, + args["name"].asString(), args["optional"].asBool()); + res.first->setLocation(location()); + if (res.second) { + logger().warning( + std::string("Field \"") + res.first->getName() + + "\" was declared after main field. The order of fields " + "was changed to make the main field the last field.", + *res.first); + } + + const std::string &type = args["type"].asString(); + scope().resolveType(type, res.first, logger(), + [](Handle type, Handle field, + Logger &logger) { + if (type != nullptr) { + field.cast()->setPrimitiveType(type.cast()); + } + }); + + scope().push(res.first); + return true; +} + +void OntologyPrimitiveHandler::end() { scope().pop(logger()); } + +/* OntologyChildHandler */ + +bool OntologyChildHandler::start(Variant::mapType &args) +{ + Rooted field = scope().selectOrThrow(); + + const std::string &ref = args["ref"].asString(); + scope().resolve( + ref, field, logger(), + [](Handle child, Handle field, Logger &logger) { + if (child != nullptr) { + field.cast()->addChild( + child.cast()); + } + }); + return true; +} + +/* OntologyParentHandler */ + +bool OntologyParentHandler::start(Variant::mapType &args) +{ + Rooted strct = scope().selectOrThrow(); + + Rooted parent{ + new OntologyParent(strct->getManager(), args["ref"].asString(), strct)}; + parent->setLocation(location()); + scope().push(parent); + return true; +} + +void OntologyParentHandler::end() { scope().pop(logger()); } + +/* OntologyParentFieldHandler */ + +bool OntologyParentFieldHandler::start(Variant::mapType &args) +{ + Rooted parentNameNode = scope().selectOrThrow(); + FieldDescriptor::FieldType type; + if (args["isSubtree"].asBool()) { + type = FieldDescriptor::FieldType::SUBTREE; + } else { + type = FieldDescriptor::FieldType::TREE; + } + + const std::string &name = args["name"].asString(); + const bool optional = args["optional"].asBool(); + Rooted strct = + parentNameNode->getParent().cast(); + + // resolve the parent, create the declared field and add the declared + // StructuredClass as child to it. + scope().resolve( + parentNameNode->getName(), strct, logger(), + [type, name, optional](Handle parent, Handle strct, + Logger &logger) { + if (parent != nullptr) { + Rooted field = + (parent.cast()->createFieldDescriptor( + logger, type, name, optional)).first; + field->addChild(strct.cast()); + } + }); + return true; +} + +/* OntologyParentFieldRefHandler */ + +bool OntologyParentFieldRefHandler::start(Variant::mapType &args) +{ + Rooted parentNameNode = scope().selectOrThrow(); + + const std::string &name = args["ref"].asString(); + Rooted strct = + parentNameNode->getParent().cast(); + auto loc = location(); + + // resolve the parent, get the referenced field and add the declared + // StructuredClass as child to it. + scope().resolve( + parentNameNode->getName(), strct, logger(), + [name, loc](Handle parent, Handle strct, Logger &logger) { + if (parent != nullptr) { + Rooted field = + parent.cast()->getFieldDescriptor(name); + if (field == nullptr) { + logger.error( + std::string("Could not find referenced field ") + name, + loc); + return; + } + field->addChild(strct.cast()); + } + }); + return true; +} + +namespace States { +const State Ontology = StateBuilder() + .parents({&None, &Document}) + .createdNodeType(&RttiTypes::Ontology) + .elementHandler(OntologyHandler::create) + .arguments({Argument::String("name")}); + +const State OntologyStruct = + StateBuilder() + .parent(&Ontology) + .createdNodeType(&RttiTypes::StructuredClass) + .elementHandler(OntologyStructHandler::create) + .arguments({Argument::String("name"), + Argument::Cardinality("cardinality", Cardinality::any()), + Argument::Bool("isRoot", false), + Argument::Bool("transparent", false), + Argument::String("isa", "")}); + +const State OntologyAnnotation = + StateBuilder() + .parent(&Ontology) + .createdNodeType(&RttiTypes::AnnotationClass) + .elementHandler(OntologyAnnotationHandler::create) + .arguments({Argument::String("name")}); + +const State OntologyAttributes = + StateBuilder() + .parents({&OntologyStruct, &OntologyAnnotation}) + .createdNodeType(&RttiTypes::StructType) + .elementHandler(OntologyAttributesHandler::create) + .arguments({}); + +const State OntologyAttribute = + StateBuilder() + .parent(&OntologyAttributes) + .elementHandler(TypesystemStructFieldHandler::create) + .arguments({Argument::String("name"), Argument::String("type"), + Argument::Any("default", Variant::fromObject(nullptr))}); + +const State OntologyField = StateBuilder() + .parents({&OntologyStruct, &OntologyAnnotation}) + .createdNodeType(&RttiTypes::FieldDescriptor) + .elementHandler(OntologyFieldHandler::create) + .arguments({Argument::String("name", ""), + Argument::Bool("isSubtree", false), + Argument::Bool("optional", false)}); + +const State OntologyFieldRef = + StateBuilder() + .parents({&OntologyStruct, &OntologyAnnotation}) + .createdNodeType(&RttiTypes::FieldDescriptor) + .elementHandler(OntologyFieldRefHandler::create) + .arguments({Argument::String("ref", DEFAULT_FIELD_NAME)}); + +const State OntologyStructPrimitive = + StateBuilder() + .parents({&OntologyStruct, &OntologyAnnotation}) + .createdNodeType(&RttiTypes::FieldDescriptor) + .elementHandler(OntologyPrimitiveHandler::create) + .arguments( + {Argument::String("name", ""), Argument::Bool("isSubtree", false), + Argument::Bool("optional", false), Argument::String("type")}); + +const State OntologyStructChild = StateBuilder() + .parent(&OntologyField) + .elementHandler(OntologyChildHandler::create) + .arguments({Argument::String("ref")}); + +const State OntologyStructParent = + StateBuilder() + .parent(&OntologyStruct) + .createdNodeType(&RttiTypes::OntologyParent) + .elementHandler(OntologyParentHandler::create) + .arguments({Argument::String("ref")}); + +const State OntologyStructParentField = + StateBuilder() + .parent(&OntologyStructParent) + .createdNodeType(&RttiTypes::FieldDescriptor) + .elementHandler(OntologyParentFieldHandler::create) + .arguments({Argument::String("name", ""), + Argument::Bool("isSubtree", false), + Argument::Bool("optional", false)}); + +const State OntologyStructParentFieldRef = + StateBuilder() + .parent(&OntologyStructParent) + .createdNodeType(&RttiTypes::FieldDescriptor) + .elementHandler(OntologyParentFieldRefHandler::create) + .arguments({Argument::String("ref", DEFAULT_FIELD_NAME)}); +} +} + +namespace RttiTypes { +const Rtti OntologyParent = RttiBuilder( + "OntologyParent").parent(&Node); +} +} diff --git a/src/core/parser/stack/OntologyHandler.hpp b/src/core/parser/stack/OntologyHandler.hpp new file mode 100644 index 0000000..caeacc7 --- /dev/null +++ b/src/core/parser/stack/OntologyHandler.hpp @@ -0,0 +1,257 @@ +/* + 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 . +*/ + +/** + * @file OntologyHandler.hpp + * + * Contains the Handler classes used for parsing Ontology descriptors. This + * includes the "ontology" tag and all describing tags below the "ontology" tag. + * + * @author Benjamin Paaßen (bpaassen@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_DOMAIN_HANDLER_HPP_ +#define _OUSIA_DOMAIN_HANDLER_HPP_ + +#include +#include + +#include "Handler.hpp" + +namespace ousia { + +// Forward declarations +class Rtti; + +namespace parser_stack { + +// TODO: Documentation + +class OntologyHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyHandler{handlerData}; + } +}; + +class OntologyStructHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyStructHandler{handlerData}; + } +}; + +class OntologyAnnotationHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyAnnotationHandler{handlerData}; + } +}; + +class OntologyAttributesHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyAttributesHandler{handlerData}; + } +}; + +class OntologyFieldHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyFieldHandler{handlerData}; + } +}; + +class OntologyFieldRefHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyFieldRefHandler{handlerData}; + } +}; + +class OntologyPrimitiveHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyPrimitiveHandler{handlerData}; + } +}; + +class OntologyChildHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyChildHandler{handlerData}; + } +}; + +class OntologyParent : public Node { +public: + using Node::Node; +}; + +class OntologyParentHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + void end() override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyParentHandler{handlerData}; + } +}; + +class OntologyParentFieldHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyParentFieldHandler{handlerData}; + } +}; + +class OntologyParentFieldRefHandler : public StaticHandler { +public: + using StaticHandler::StaticHandler; + + bool start(Variant::mapType &args) override; + + static Handler *create(const HandlerData &handlerData) + { + return new OntologyParentFieldRefHandler{handlerData}; + } +}; + +namespace States { +/** + * State representing a "ontology" struct. + */ +extern const State Ontology; + +/** + * State representing a "struct" tag within a ontology description. + */ +extern const State OntologyStruct; + +/** + * State representing an "annotation" tag within a ontology description. + */ +extern const State OntologyAnnotation; + +/** + * State representing an "attributes" tag within a structure or annotation. + */ +extern const State OntologyAttributes; + +/** + * State representing an "attribute" tag within the "attributes". + */ +extern const State OntologyAttribute; + +/** + * State representing a "field" tag within a structure or annotation. + */ +extern const State OntologyField; + +/** + * State representing a "fieldref" tag within a structure or annotation. + */ +extern const State OntologyFieldRef; + +/** + * State representing a "primitive" tag within a structure or annotation. + */ +extern const State OntologyStructPrimitive; + +/** + * State representing a "child" tag within a structure or annotation. + */ +extern const State OntologyStructChild; + +/** + * State representing a "parent" tag within a structure or annotation. + */ +extern const State OntologyStructParent; + +/** + * State representing a "field" tag within a "parent" tag. + */ +extern const State OntologyStructParentField; + +/** + * State representing a "fieldRef" tag within a "parent" tag. + */ +extern const State OntologyStructParentFieldRef; +} +} + +namespace RttiTypes { +extern const Rtti OntologyParent; +} +} +#endif diff --git a/src/core/parser/stack/TypesystemHandler.cpp b/src/core/parser/stack/TypesystemHandler.cpp index de8ee49..b62f684 100644 --- a/src/core/parser/stack/TypesystemHandler.cpp +++ b/src/core/parser/stack/TypesystemHandler.cpp @@ -17,13 +17,13 @@ */ #include -#include +#include #include #include #include #include "DocumentHandler.hpp" -#include "DomainHandler.hpp" +#include "OntologyHandler.hpp" #include "State.hpp" #include "TypesystemHandler.hpp" @@ -39,12 +39,12 @@ bool TypesystemHandler::start(Variant::mapType &args) context().getProject()->createTypesystem(args["name"].asString()); typesystem->setLocation(location()); - // If the typesystem is defined inside a domain, add a reference to the - // typesystem to the domain -- do the same with a document, if no domain + // If the typesystem is defined inside a ontology, add a reference to the + // typesystem to the ontology -- do the same with a document, if no ontology // is found - Rooted domain = scope().select(); - if (domain != nullptr) { - domain->reference(typesystem); + Rooted ontology = scope().select(); + if (ontology != nullptr) { + ontology->reference(typesystem); } else { Rooted document = scope().select(); if (document != nullptr) { @@ -190,7 +190,7 @@ bool TypesystemConstantHandler::start(Variant::mapType &args) namespace States { const State Typesystem = StateBuilder() - .parents({&None, &Domain, &Document}) + .parents({&None, &Ontology, &Document}) .createdNodeType(&RttiTypes::Typesystem) .elementHandler(TypesystemHandler::create) .arguments({Argument::String("name", "")}); diff --git a/src/core/resource/Resource.cpp b/src/core/resource/Resource.cpp index 4b9e496..7bcaf88 100644 --- a/src/core/resource/Resource.cpp +++ b/src/core/resource/Resource.cpp @@ -42,7 +42,7 @@ static std::unordered_map reverseMap static const std::unordered_map NAME_RESOURCE_TYPE_MAP{{"document", ResourceType::DOCUMENT}, - {"domain", ResourceType::DOMAIN_DESC}, + {"ontology", ResourceType::ONTOLOGY}, {"typesystem", ResourceType::TYPESYSTEM}, {"attributes", ResourceType::ATTRIBUTES}, {"stylesheet", ResourceType::STYLESHEET}, diff --git a/src/core/resource/Resource.hpp b/src/core/resource/Resource.hpp index 63ed591..548a255 100644 --- a/src/core/resource/Resource.hpp +++ b/src/core/resource/Resource.hpp @@ -48,9 +48,9 @@ enum class ResourceType { UNKNOWN, /** - * The resource contains a domain description. + * The resource contains a ontology description. */ - DOMAIN_DESC, + ONTOLOGY, /** * The resource contains a typesystem description. diff --git a/src/core/resource/ResourceRequest.cpp b/src/core/resource/ResourceRequest.cpp index f01a324..3c01e6a 100644 --- a/src/core/resource/ResourceRequest.cpp +++ b/src/core/resource/ResourceRequest.cpp @@ -33,7 +33,7 @@ namespace ousia { namespace RttiTypes { extern const Rtti Document; -extern const Rtti Domain; +extern const Rtti Ontology; extern const Rtti Node; extern const Rtti Typesystem; } @@ -43,7 +43,7 @@ extern const Rtti Typesystem; */ static const std::unordered_map RTTI_RESOURCE_TYPE_MAP{{&RttiTypes::Document, ResourceType::DOCUMENT}, - {&RttiTypes::Domain, ResourceType::DOMAIN_DESC}, + {&RttiTypes::Ontology, ResourceType::ONTOLOGY}, {&RttiTypes::Typesystem, ResourceType::TYPESYSTEM}}; /** diff --git a/src/formats/osml/OsmlParser.cpp b/src/formats/osml/OsmlParser.cpp index a24f091..16e7aa4 100644 --- a/src/formats/osml/OsmlParser.cpp +++ b/src/formats/osml/OsmlParser.cpp @@ -95,7 +95,7 @@ public: const std::string &cmd = parser.getCommandName().asString(); if (cmd != "typesystem" && cmd != "document" && - cmd != "domain") { + cmd != "ontology") { stack.command("document", Variant::mapType{}); } needsDocument = false; diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp index 2fd5483..994bbb9 100644 --- a/src/plugins/filesystem/FileLocator.cpp +++ b/src/plugins/filesystem/FileLocator.cpp @@ -103,8 +103,8 @@ void FileLocator::addDefaultSearchPaths(const std::string &relativeTo) // Add the search paths fs::path base{relativeTo}; addSearchPath(base.generic_string(), ResourceType::UNKNOWN); - addSearchPath((base / "domain").generic_string(), - ResourceType::DOMAIN_DESC); + addSearchPath((base / "ontology").generic_string(), + ResourceType::ONTOLOGY); addSearchPath((base / "typesystem").generic_string(), ResourceType::TYPESYSTEM); } diff --git a/src/plugins/filesystem/FileLocator.hpp b/src/plugins/filesystem/FileLocator.hpp index ad64db8..d1de6d0 100644 --- a/src/plugins/filesystem/FileLocator.hpp +++ b/src/plugins/filesystem/FileLocator.hpp @@ -116,7 +116,7 @@ public: *
  • The global application data directory used for make install * (default is /usr/local/share on UNIX)
  • * - * Resource type specific subdirectories (domain, typesytem, etc.) + * Resource type specific subdirectories (ontology, typesytem, etc.) * are automatically added to the aforementioned paths. */ void addDefaultSearchPaths(); diff --git a/src/plugins/html/DemoOutput.hpp b/src/plugins/html/DemoOutput.hpp index 4367202..b038a96 100644 --- a/src/plugins/html/DemoOutput.hpp +++ b/src/plugins/html/DemoOutput.hpp @@ -19,7 +19,7 @@ /** * @file DemoOutput.hpp * - * This implements a Demo HTML output for the following domains: + * This implements a Demo HTML output for the following ontologies: * * book * * headings * * emphasis @@ -67,18 +67,18 @@ public: * This writes a HTML representation of the given document to the given * output stream. Note that this method lacks the generality of our Ousia * approach with respect to two important points: - * 1.) It hardcodes the dependency to a certain set of domains in the C++ + * 1.) It hardcodes the dependency to a certain set of ontologies in the C++ * code. * 2.) It does not use the proposed pipeline of first copying the document * graph, then attaching style attributes and then transforming it to a * specific output format but does all of these steps at once. - * 3.) It does not use different transformers for the different domains but + * 3.) It does not use different transformers for the different ontologies but * does all transformations at once. * Therefore this is not an adequate model of our algorithms but only a * Demo. * * @param doc is a Document using concepts of the book, headings, - * emphasis and lists domains but no other. + * emphasis and lists ontologies but no other. * @param out is the output stream the data shall be written to. * @param pretty is a flag that manipulates whether newlines and tabs are * used. diff --git a/src/plugins/xml/XmlOutput.cpp b/src/plugins/xml/XmlOutput.cpp index ace7635..ffdecab 100644 --- a/src/plugins/xml/XmlOutput.cpp +++ b/src/plugins/xml/XmlOutput.cpp @@ -51,10 +51,10 @@ void XmlTransformer::writeXml(Handle doc, std::ostream &out, Manager &mgr = doc->getManager(); // the outermost tag is the document itself. Rooted document{new Element{mgr, {nullptr}, "document"}}; - // write imports for all referenced domains. - for (auto d : doc->getDomains()) { + // write imports for all referenced ontologies. + for (auto d : doc->getOntologys()) { Rooted import = - createImportElement(document, d, resourceManager, "domain"); + createImportElement(document, d, resourceManager, "ontology"); if (import != nullptr) { document->addChild(import); // add the import as namespace information to the document node as @@ -63,7 +63,7 @@ void XmlTransformer::writeXml(Handle doc, std::ostream &out, std::string("xmlns:") + d->getName(), d->getName()); } else { logger.warning(std::string( - "The location of domain \"" + d->getName() + + "The location of ontology \"" + d->getName() + "\" could not be retrieved using the given ResourceManager.")); } } @@ -199,7 +199,7 @@ Rooted XmlTransformer::transformStructuredEntity( Rooted elem{ new Element{mgr, parent, s->getDescriptor()->getName(), transformAttributes(s->getName(), s.get(), logger, pretty), - s->getDescriptor()->getParent().cast()->getName()}}; + s->getDescriptor()->getParent().cast()->getName()}}; // then transform the children. transformChildren(s.get(), elem, logger, pretty); return elem; diff --git a/src/plugins/xml/XmlOutput.hpp b/src/plugins/xml/XmlOutput.hpp index da406a7..55c1c67 100644 --- a/src/plugins/xml/XmlOutput.hpp +++ b/src/plugins/xml/XmlOutput.hpp @@ -62,7 +62,7 @@ public: /** * This writes an XML serialization of the given document to the given * output stream. The serialization is equivalent to the input XML format, - * safe for the domain references. TODO: Can we change this? If so: how? + * safe for the ontology references. TODO: Can we change this? If so: how? * Note, though, that the serialization will not exploit transparency. * TODO: Can we change that? * @@ -70,7 +70,7 @@ public: * @param out is the output stream the XML serialization of the document * shall be written to. * @param logger is the logger errors shall be written to. - * @param resMgr is the ResourceManager to locate the domains and + * @param resMgr is the ResourceManager to locate the ontologies and * typesystems that were imported in this document. * @param pretty is a flag that manipulates whether newlines and tabs are * used. diff --git a/test/core/RegistryTest.cpp b/test/core/RegistryTest.cpp index 4e8fc6a..b557169 100644 --- a/test/core/RegistryTest.cpp +++ b/test/core/RegistryTest.cpp @@ -100,9 +100,9 @@ TEST(Registry, locateResource) Resource res; ASSERT_TRUE( - registry.locateResource(res, "path", ResourceType::DOMAIN_DESC)); + registry.locateResource(res, "path", ResourceType::ONTOLOGY)); ASSERT_TRUE(res.isValid()); - ASSERT_EQ(ResourceType::DOMAIN_DESC, res.getType()); + ASSERT_EQ(ResourceType::ONTOLOGY, res.getType()); ASSERT_EQ("path", res.getLocation()); } } diff --git a/test/core/frontend/TerminalLoggerTest.cpp b/test/core/frontend/TerminalLoggerTest.cpp index 99e8f29..acc379d 100644 --- a/test/core/frontend/TerminalLoggerTest.cpp +++ b/test/core/frontend/TerminalLoggerTest.cpp @@ -35,8 +35,8 @@ struct Pos { }; static const std::string testStr = - "\\link[domain]{book}\n" // 1 - "\\link[domain]{meta}\n" // 2 + "\\link[ontology]{book}\n" // 1 + "\\link[ontology]{meta}\n" // 2 "\n" // 3 "\\meta{\n" // 4 "\t\\title{The Adventures Of Tom Sawyer}\n" // 5 diff --git a/test/core/model/DocumentTest.cpp b/test/core/model/DocumentTest.cpp index 1bb2356..8ed59f5 100644 --- a/test/core/model/DocumentTest.cpp +++ b/test/core/model/DocumentTest.cpp @@ -23,10 +23,10 @@ #include #include #include -#include +#include #include "TestDocument.hpp" -#include "TestDomain.hpp" +#include "TestOntology.hpp" namespace ousia { @@ -36,10 +36,10 @@ TEST(Document, construct) TerminalLogger logger{std::cerr, true}; Manager mgr{1}; Rooted sys{new SystemTypesystem(mgr)}; - // Get the domain. - Rooted domain = constructBookDomain(mgr, sys, logger); + // Get the ontology. + Rooted ontology = constructBookOntology(mgr, sys, logger); // Construct the document. - Rooted doc = constructBookDocument(mgr, logger, domain); + Rooted doc = constructBookDocument(mgr, logger, ontology); // Check the document content. ASSERT_FALSE(doc.isNull()); @@ -111,22 +111,22 @@ TEST(Document, construct) TEST(Document, validate) { - // Let's start with a trivial domain and a trivial document. + // Let's start with a trivial ontology and a trivial document. TerminalLogger logger{std::cerr, true}; Manager mgr{1}; Rooted sys{new SystemTypesystem(mgr)}; - Rooted domain{new Domain(mgr, sys, "trivial")}; + Rooted ontology{new Ontology(mgr, sys, "trivial")}; Cardinality single; single.merge({1}); // Set up the "root" StructuredClass. Rooted rootClass{new StructuredClass( - mgr, "root", domain, single, {nullptr}, false, true)}; + mgr, "root", ontology, single, {nullptr}, false, true)}; // set up a document for it. { // first an invalid one, which is empty. Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); ASSERT_FALSE(doc->validate(logger)); // then add a root, which should make it valid. @@ -138,7 +138,7 @@ TEST(Document, validate) { // A root with an invalid name, however, should make it invalid Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); Rooted root = buildRootStructuredEntity( doc, logger, {"root"}, {}, "my invalid root"); ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); @@ -150,7 +150,7 @@ TEST(Document, validate) rootClass->createFieldDescriptor(logger).first; // and add a child class for it. Rooted childClass{ - new StructuredClass(mgr, "child", domain, single)}; + new StructuredClass(mgr, "child", ontology, single)}; rootField->addChild(childClass); { /* @@ -158,7 +158,7 @@ TEST(Document, validate) * document should be invalid again. */ Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); Rooted root = buildRootStructuredEntity(doc, logger, {"root"}); ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); @@ -173,16 +173,16 @@ TEST(Document, validate) ASSERT_FALSE(doc->validate(logger)); } /* - * Add a further extension to the domain: We add a subclass to child. + * Add a further extension to the ontology: We add a subclass to child. */ Rooted childSubClass{ - new StructuredClass(mgr, "childSub", domain, single, childClass)}; + new StructuredClass(mgr, "childSub", ontology, single, childClass)}; { /* * A document with one instance of the Child subclass should be valid. */ Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); Rooted root = buildRootStructuredEntity(doc, logger, {"root"}); buildStructuredEntity(doc, logger, root, {"childSub"}); @@ -202,7 +202,7 @@ TEST(Document, validate) * invalid, because it has no children of itself. */ Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); Rooted root = buildRootStructuredEntity(doc, logger, {"root"}); buildStructuredEntity(doc, logger, root, {"childSub"}); @@ -223,7 +223,7 @@ TEST(Document, validate) * valid, because of the override. */ Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); Rooted root = buildRootStructuredEntity(doc, logger, {"root"}); buildStructuredEntity(doc, logger, root, {"childSub"}); @@ -241,7 +241,7 @@ TEST(Document, validate) * invalid again, because we are missing the primitive content. */ Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); Rooted root = buildRootStructuredEntity(doc, logger, {"root"}); Rooted child = @@ -259,14 +259,14 @@ TEST(Document, validate) ASSERT_TRUE(doc->validate(logger)); } - // Now add an Annotation class to the domain. - Rooted annoClass{new AnnotationClass(mgr, "anno", domain)}; + // Now add an Annotation class to the ontology. + Rooted annoClass{new AnnotationClass(mgr, "anno", ontology)}; { /* * Create a valid document in itself. */ Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(domain); + doc->referenceOntology(ontology); Rooted root = buildRootStructuredEntity(doc, logger, {"root"}); Rooted start{new Anchor(mgr, root)}; diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp deleted file mode 100644 index 6bbf26d..0000000 --- a/test/core/model/DomainTest.cpp +++ /dev/null @@ -1,689 +0,0 @@ -/* - 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 "TestDomain.hpp" - -namespace ousia { - -void assert_path(const ResolutionResult &res, const Rtti *expected_type, - std::vector expected_path) -{ - // Check class/type - ASSERT_TRUE(res.node->isa(expected_type)); - - // Check path - ASSERT_EQ(expected_path, res.node->path()); -} - -TEST(Domain, testDomainResolving) -{ - // Construct Manager - Logger logger; - Manager mgr{1}; - Rooted sys{new SystemTypesystem(mgr)}; - // Get the domain. - Rooted domain = constructBookDomain(mgr, sys, logger); - - std::vector res; - - // There is one domain called "book" - res = domain->resolve(&RttiTypes::Domain, "book"); - ASSERT_EQ(1U, res.size()); - assert_path(res[0], &RttiTypes::Domain, {"book"}); - - // There is one domain called "book" - res = domain->resolve(&RttiTypes::StructuredClass, "book"); - ASSERT_EQ(1U, res.size()); - assert_path(res[0], &RttiTypes::StructuredClass, {"book", "book"}); - - // If we explicitly ask for the "book, book" path, then only the - // StructuredClass should be returned. - res = domain->resolve(&RttiTypes::Domain, - std::vector{"book", "book"}); - ASSERT_EQ(0U, res.size()); - - res = domain->resolve(&RttiTypes::StructuredClass, - std::vector{"book", "book"}); - ASSERT_EQ(1U, res.size()); - - // If we ask for "section" the result should be unique as well. - res = domain->resolve(&RttiTypes::StructuredClass, "section"); - ASSERT_EQ(1U, res.size()); - assert_path(res[0], &RttiTypes::StructuredClass, {"book", "section"}); - - // If we ask for "paragraph" it is referenced two times in the Domain graph, - // but should be returned only once. - res = domain->resolve(&RttiTypes::StructuredClass, "paragraph"); - ASSERT_EQ(1U, res.size()); - assert_path(res[0], &RttiTypes::StructuredClass, {"book", "paragraph"}); -} - -// i use this wrapper due to the strange behaviour of GTEST. -static void assertFalse(bool b){ - ASSERT_FALSE(b); -} - -static Rooted createUnsortedPrimitiveField( - Handle strct, Handle type, Logger &logger, bool tree, - std::string name) -{ - FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::SUBTREE; - if (tree) { - fieldType = FieldDescriptor::FieldType::TREE; - } - - auto res = strct->createPrimitiveFieldDescriptor(type, logger, fieldType, - std::move(name)); - assertFalse(res.second); - return res.first; -} - -TEST(StructuredClass, getFieldDescriptors) -{ - /* - * We construct a case with the three levels: - * 1.) A has the SUBTREE fields a and b as well as a TREE field. - * 2.) B is a subclass of A and has the SUBTREE fields b and c as well as - * a TREE field. - * 3.) C is a subclass of B and has the SUBTREE field a. - * As a result we expect C to have none of As fields, the TREE field of B, - * and the SUBTREE fields a (of C) , b and c (of B). - */ - TerminalLogger logger{std::cout}; - Manager mgr{1}; - Rooted sys{new SystemTypesystem(mgr)}; - Rooted domain{new Domain(mgr, sys, "myDomain")}; - - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), nullptr, false, true)}; - Rooted A_a = createUnsortedPrimitiveField( - A, sys->getStringType(), logger, false, "a"); - Rooted A_b = createUnsortedPrimitiveField( - A, sys->getStringType(), logger, false, "b"); - Rooted A_main = createUnsortedPrimitiveField( - A, sys->getStringType(), logger, true, "somename"); - - Rooted B{new StructuredClass( - mgr, "B", domain, Cardinality::any(), A, false, true)}; - Rooted B_b = createUnsortedPrimitiveField( - B, sys->getStringType(), logger, false, "b"); - Rooted B_c = createUnsortedPrimitiveField( - B, sys->getStringType(), logger, false, "c"); - Rooted B_main = createUnsortedPrimitiveField( - B, sys->getStringType(), logger, true, "othername"); - - Rooted C{new StructuredClass( - mgr, "C", domain, Cardinality::any(), B, false, true)}; - Rooted C_a = createUnsortedPrimitiveField( - C, sys->getStringType(), logger, false, "a"); - - ASSERT_TRUE(domain->validate(logger)); - - // check all FieldDescriptors - { - NodeVector fds = A->getFieldDescriptors(); - ASSERT_EQ(3, fds.size()); - ASSERT_EQ(A_a, fds[0]); - ASSERT_EQ(A_b, fds[1]); - ASSERT_EQ(A_main, fds[2]); - } - { - NodeVector fds = B->getFieldDescriptors(); - ASSERT_EQ(4, fds.size()); - ASSERT_EQ(A_a, fds[0]); - ASSERT_EQ(B_b, fds[1]); - ASSERT_EQ(B_c, fds[2]); - ASSERT_EQ(B_main, fds[3]); - } - { - NodeVector fds = C->getFieldDescriptors(); - ASSERT_EQ(4, fds.size()); - ASSERT_EQ(B_b, fds[0]); - ASSERT_EQ(B_c, fds[1]); - // superclass fields come before subclass fields (except for the TREE - // field, which is always last). - ASSERT_EQ(C_a, fds[2]); - ASSERT_EQ(B_main, fds[3]); - } -} - - -TEST(StructuredClass, getFieldDescriptorsCycles) -{ - Logger logger; - Manager mgr{1}; - Rooted sys{new SystemTypesystem(mgr)}; - Rooted domain{new Domain(mgr, sys, "myDomain")}; - - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), nullptr, false, true)}; - A->addSubclass(A, logger); - Rooted A_a = createUnsortedPrimitiveField( - A, sys->getStringType(), logger, false, "a"); - ASSERT_FALSE(domain->validate(logger)); - // if we call getFieldDescriptors that should still return a valid result. - NodeVector fds = A->getFieldDescriptors(); - ASSERT_EQ(1, fds.size()); - ASSERT_EQ(A_a, fds[0]); -} - -Rooted getClass(const std::string name, Handle dom) -{ - std::vector res = - dom->resolve(&RttiTypes::StructuredClass, name); - return res[0].node.cast(); -} - -TEST(Descriptor, pathTo) -{ - // Start with some easy examples from the book domain. - TerminalLogger logger{std::cout}; - Manager mgr{1}; - Rooted sys{new SystemTypesystem(mgr)}; - // Get the domain. - Rooted domain = constructBookDomain(mgr, sys, logger); - - // get the book node and the section node. - Rooted book = getClass("book", domain); - Rooted section = getClass("section", domain); - // get the path in between. - NodeVector path = book->pathTo(section, logger); - ASSERT_EQ(1U, path.size()); - ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); - - // get the text node. - Rooted text = getClass("text", domain); - // get the path between book and text via paragraph. - path = book->pathTo(text, logger); - ASSERT_EQ(3U, path.size()); - ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); - ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); - ASSERT_EQ("paragraph", path[1]->getName()); - ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); - - // get the subsection node. - Rooted subsection = getClass("subsection", domain); - // try to get the path between book and subsection. - path = book->pathTo(subsection, logger); - // this should be impossible. - ASSERT_EQ(0U, path.size()); - - // try to construct the path between section and the text field. - auto res = section->pathTo(text->getFieldDescriptor(), logger); - ASSERT_TRUE(res.second); - path = res.first; - ASSERT_EQ(4U, path.size()); - ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); - ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); - ASSERT_EQ("paragraph", path[1]->getName()); - ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); - ASSERT_TRUE(path[3]->isa(&RttiTypes::StructuredClass)); - ASSERT_EQ("text", path[3]->getName()); -} - -TEST(Descriptor, pathToAdvanced) -{ - /* - * Now we build a really nasty domain with lots of transparency - * and inheritance. The basic idea is to have three paths from start to - * finish, where one is blocked by overriding fields and the longer valid - * one is found first such that it has to be replaced by the shorter one - * during the search. - * - * To achieve that we have the following structure: - * 1.) The start class inherits from A. - * 2.) A has B as child in the default field. - * 3.) B is transparent and has no children (but C as subclass) - * 4.) C is a subclass of B, transparent and has - * the target as child (shortest path). - * 5.) A has D as child in the default field. - * 6.) D is transparent has E as child in the default field. - * 7.) E is transparent and has target as child in the default field - * (longer path) - * - * So the path A_second_field, C, C_field should be returned. - */ - Manager mgr{1}; - TerminalLogger logger{std::cout}; - Rooted sys{new SystemTypesystem(mgr)}; - // Construct the domain - Rooted domain{new Domain(mgr, sys, "nasty")}; - - // Let's create the classes that we need first - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), {nullptr}, false, true)}; - - Rooted start{new StructuredClass( - mgr, "start", domain, Cardinality::any(), A, false, false)}; - - Rooted B{new StructuredClass( - mgr, "B", domain, Cardinality::any(), {nullptr}, true, false)}; - - Rooted C{new StructuredClass( - mgr, "C", domain, Cardinality::any(), B, true, false)}; - - Rooted D{new StructuredClass( - mgr, "D", domain, Cardinality::any(), {nullptr}, true, false)}; - - Rooted E{new StructuredClass( - mgr, "E", domain, Cardinality::any(), {nullptr}, true, false)}; - - Rooted target{ - new StructuredClass(mgr, "target", domain, Cardinality::any())}; - - // We create a field for A - Rooted A_field = A->createFieldDescriptor(logger).first; - A_field->addChild(B); - A_field->addChild(D); - - // We create no field for B - // One for C - Rooted C_field = C->createFieldDescriptor(logger).first; - C_field->addChild(target); - - // One for D - Rooted D_field = D->createFieldDescriptor(logger).first; - D_field->addChild(E); - - // One for E - Rooted E_field = E->createFieldDescriptor(logger).first; - E_field->addChild(target); - - ASSERT_TRUE(domain->validate(logger)); - -#ifdef MANAGER_GRAPHVIZ_EXPORT - // dump the manager state - mgr.exportGraphviz("nastyDomain.dot"); -#endif - - // and now we should be able to find the shortest path as suggested - NodeVector path = start->pathTo(target, logger); - ASSERT_EQ(3U, path.size()); - ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); - ASSERT_EQ("", path[0]->getName()); - ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); - ASSERT_EQ("C", path[1]->getName()); - ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); - ASSERT_EQ("", path[2]->getName()); -} - -TEST(Descriptor, pathToCycles) -{ - // build a domain with a cycle. - Manager mgr{1}; - Logger logger; - Rooted sys{new SystemTypesystem(mgr)}; - // Construct the domain - Rooted domain{new Domain(mgr, sys, "cycles")}; - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), {nullptr}, true, true)}; - A->addSubclass(A, logger); - ASSERT_FALSE(domain->validate(logger)); - Rooted B{new StructuredClass( - mgr, "B", domain, Cardinality::any(), {nullptr}, false, true)}; - Rooted A_field = A->createFieldDescriptor(logger).first; - A_field->addChild(B); - /* - * Now try to create the path from A to B. A direct path is possible but - * in the worst case this could also try to find shorter paths via an - * endless repition of A instances. - * As we cut the search tree at paths that are longer than our current - * optimum this should not happen, though. - */ - NodeVector path = A->pathTo(B, logger); - ASSERT_EQ(1, path.size()); - ASSERT_EQ(A_field, path[0]); -} - -TEST(Descriptor, getDefaultFields) -{ - // construct a domain with lots of default fields to test. - // start with a single structure class. - Manager mgr{1}; - TerminalLogger logger{std::cout}; - Rooted sys{new SystemTypesystem(mgr)}; - // Construct the domain - Rooted domain{new Domain(mgr, sys, "nasty")}; - - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), nullptr, false, true)}; - - // in this trivial case no field should be found. - ASSERT_TRUE(A->getDefaultFields().empty()); - - // create a field. - Rooted A_prim_field = - A->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; - // now we should find that. - auto fields = A->getDefaultFields(); - ASSERT_EQ(1U, fields.size()); - ASSERT_EQ(A_prim_field, fields[0]); - - // remove that field from A and add it to another class. - - Rooted B{new StructuredClass( - mgr, "B", domain, Cardinality::any(), nullptr, false, true)}; - - B->moveFieldDescriptor(A_prim_field, logger); - - // new we shouldn't find the field anymore. - ASSERT_TRUE(A->getDefaultFields().empty()); - - // but we should find it again if we set B as superclass of A. - A->setSuperclass(B, logger); - fields = A->getDefaultFields(); - ASSERT_EQ(1U, fields.size()); - ASSERT_EQ(A_prim_field, fields[0]); - - // and we should not be able to find it if we override the field. - Rooted A_field = A->createFieldDescriptor(logger).first; - ASSERT_TRUE(A->getDefaultFields().empty()); - - // add a transparent child class. - - Rooted C{new StructuredClass( - mgr, "C", domain, Cardinality::any(), nullptr, true, false)}; - A_field->addChild(C); - - // add a primitive field for it. - Rooted C_field = - C->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; - - // now we should find that. - fields = A->getDefaultFields(); - ASSERT_EQ(1U, fields.size()); - ASSERT_EQ(C_field, fields[0]); - - // add another transparent child class to A with a daughter class that has - // in turn a subclass with a primitive field. - Rooted D{new StructuredClass( - mgr, "D", domain, Cardinality::any(), nullptr, true, false)}; - A_field->addChild(D); - Rooted D_field = D->createFieldDescriptor(logger).first; - Rooted E{new StructuredClass( - mgr, "E", domain, Cardinality::any(), nullptr, true, false)}; - D_field->addChild(E); - Rooted F{new StructuredClass( - mgr, "E", domain, Cardinality::any(), E, true, false)}; - Rooted F_field = - F->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; - - // now we should find both primitive fields, but the C field first. - fields = A->getDefaultFields(); - ASSERT_EQ(2U, fields.size()); - ASSERT_EQ(C_field, fields[0]); - ASSERT_EQ(F_field, fields[1]); -} - -TEST(Descriptor, getDefaultFieldsCycles) -{ - // build a domain with a cycle. - Manager mgr{1}; - Logger logger; - Rooted sys{new SystemTypesystem(mgr)}; - // Construct the domain - Rooted domain{new Domain(mgr, sys, "cycles")}; - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), {nullptr}, true, true)}; - A->addSubclass(A, logger); - ASSERT_FALSE(domain->validate(logger)); - Rooted A_field = - A->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; - /* - * Now try to get the default fields of A. This should not lead to cycles - * if we correctly note all already visited nodes. - */ - NodeVector defaultFields = A->getDefaultFields(); - ASSERT_EQ(1, defaultFields.size()); - ASSERT_EQ(A_field, defaultFields[0]); -} - -TEST(Descriptor, getPermittedChildren) -{ - // analyze the book domain. - TerminalLogger logger{std::cout}; - Manager mgr{1}; - Rooted sys{new SystemTypesystem(mgr)}; - // Get the domain. - Rooted domain = constructBookDomain(mgr, sys, logger); - // get the relevant classes. - Rooted book = getClass("book", domain); - Rooted section = getClass("section", domain); - Rooted paragraph = getClass("paragraph", domain); - Rooted text = getClass("text", domain); - /* - * as permitted children we expect section, paragraph and text in exactly - * that order. section should be before paragraph because of declaration - * order and text should be last because it needs a transparent paragraph - * in between. - */ - NodeVector children = book->getPermittedChildren(); - ASSERT_EQ(3U, children.size()); - ASSERT_EQ(section, children[0]); - ASSERT_EQ(paragraph, children[1]); - ASSERT_EQ(text, children[2]); - - // Now we add a subclass to text. - Rooted sub{new StructuredClass( - mgr, "Subclass", domain, Cardinality::any(), text, true, false)}; - // And that should be in the result list as well now. - children = book->getPermittedChildren(); - ASSERT_EQ(4U, children.size()); - ASSERT_EQ(section, children[0]); - ASSERT_EQ(paragraph, children[1]); - ASSERT_EQ(text, children[2]); - ASSERT_EQ(sub, children[3]); -} - -TEST(Descriptor, getPermittedChildrenCycles) -{ - // build a domain with a cycle. - Manager mgr{1}; - Logger logger; - Rooted sys{new SystemTypesystem(mgr)}; - // Construct the domain - Rooted domain{new Domain(mgr, sys, "cycles")}; - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), {nullptr}, true, true)}; - A->addSubclass(A, logger); - ASSERT_FALSE(domain->validate(logger)); - Rooted A_field = A->createFieldDescriptor(logger).first; - // we make the cycle worse by adding A as child of itself. - A_field->addChild(A); - /* - * Now try to get the permitted children of A. This should not lead to - * cycles - * if we correctly note all already visited nodes. - */ - NodeVector children = A->getPermittedChildren(); - ASSERT_EQ(1, children.size()); - ASSERT_EQ(A, children[0]); -} - -TEST(StructuredClass, isSubclassOf) -{ - // create an inheritance hierarchy. - Manager mgr{1}; - Rooted sys{new SystemTypesystem(mgr)}; - Rooted domain{new Domain(mgr, sys, "inheritance")}; - Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), {nullptr}, false, true)}; - // first branch - Rooted B{ - new StructuredClass(mgr, "B", domain, Cardinality::any(), A)}; - Rooted C{ - new StructuredClass(mgr, "C", domain, Cardinality::any(), B)}; - // second branch - Rooted D{ - new StructuredClass(mgr, "D", domain, Cardinality::any(), A)}; - Rooted E{ - new StructuredClass(mgr, "E", domain, Cardinality::any(), D)}; - Rooted F{ - new StructuredClass(mgr, "F", domain, Cardinality::any(), D)}; - - // check function results - ASSERT_FALSE(A->isSubclassOf(A)); - ASSERT_FALSE(A->isSubclassOf(B)); - ASSERT_FALSE(A->isSubclassOf(C)); - ASSERT_FALSE(A->isSubclassOf(D)); - ASSERT_FALSE(A->isSubclassOf(E)); - ASSERT_FALSE(A->isSubclassOf(F)); - - ASSERT_TRUE(B->isSubclassOf(A)); - ASSERT_FALSE(B->isSubclassOf(B)); - ASSERT_FALSE(B->isSubclassOf(C)); - ASSERT_FALSE(B->isSubclassOf(D)); - ASSERT_FALSE(B->isSubclassOf(E)); - ASSERT_FALSE(B->isSubclassOf(F)); - - ASSERT_TRUE(C->isSubclassOf(A)); - ASSERT_TRUE(C->isSubclassOf(B)); - ASSERT_FALSE(C->isSubclassOf(C)); - ASSERT_FALSE(C->isSubclassOf(D)); - ASSERT_FALSE(C->isSubclassOf(E)); - ASSERT_FALSE(C->isSubclassOf(F)); - - ASSERT_TRUE(D->isSubclassOf(A)); - ASSERT_FALSE(D->isSubclassOf(B)); - ASSERT_FALSE(D->isSubclassOf(C)); - ASSERT_FALSE(D->isSubclassOf(D)); - ASSERT_FALSE(D->isSubclassOf(E)); - ASSERT_FALSE(D->isSubclassOf(F)); - - ASSERT_TRUE(E->isSubclassOf(A)); - ASSERT_FALSE(E->isSubclassOf(B)); - ASSERT_FALSE(E->isSubclassOf(C)); - ASSERT_TRUE(E->isSubclassOf(D)); - ASSERT_FALSE(E->isSubclassOf(E)); - ASSERT_FALSE(E->isSubclassOf(F)); - - ASSERT_TRUE(F->isSubclassOf(A)); - ASSERT_FALSE(F->isSubclassOf(B)); - ASSERT_FALSE(F->isSubclassOf(C)); - ASSERT_TRUE(F->isSubclassOf(D)); - ASSERT_FALSE(F->isSubclassOf(E)); - ASSERT_FALSE(F->isSubclassOf(F)); -} - -TEST(Domain, validate) -{ - TerminalLogger logger{std::cerr, true}; - Manager mgr{1}; - Rooted sys{new SystemTypesystem(mgr)}; - // start with an easy example: Our book domain should be valid. - { - Rooted domain = constructBookDomain(mgr, sys, logger); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - } - { - // Even easier: An empty domain should be valid. - Rooted domain{new Domain(mgr, sys, "domain")}; - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // if we add a StructureClass it should be valid still. - Rooted base{ - new StructuredClass(mgr, "myClass", domain)}; - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // if we tamper with the name, however, it shouldn't be valid anymore. - base->setName(""); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - base->setName("my class"); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - base->setName("myClass"); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // Let's add a primitive field (without a primitive type at first) - Rooted base_field = - base->createPrimitiveFieldDescriptor(nullptr, logger).first; - // this should not be valid. - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - // but it should be if we set the type. - base_field->setPrimitiveType(sys->getStringType()); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // add a subclass for our base class. - Rooted sub{new StructuredClass(mgr, "sub", domain)}; - // this should be valid in itself. - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // and still if we add a superclass. - sub->setSuperclass(base, logger); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // and still if we remove the subclass from the base class. - base->removeSubclass(sub, logger); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - ASSERT_EQ(nullptr, sub->getSuperclass()); - // and still if we re-add it. - base->addSubclass(sub, logger); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - ASSERT_EQ(base, sub->getSuperclass()); - // add a non-primitive field to the child class. - Rooted sub_field = - sub->createFieldDescriptor(logger).first; - // this should not be valid because we allow no children. - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - // we should also be able to add a child and make it valid. - sub_field->addChild(base); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // it should be invalid if we add it twice. - sub_field->addChild(base); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - // and valid again if we remove it once. - sub_field->removeChild(base); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // if we set a primitive type it should be invalid - sub_field->setPrimitiveType(sys->getStringType()); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - // and valid again if we unset it. - sub_field->setPrimitiveType(nullptr); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - // It should be invalid if we set another TREE field. - Rooted sub_field2 = - sub->createFieldDescriptor(logger, FieldDescriptor::FieldType::TREE, - "test", false).first; - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_FALSE(domain->validate(logger)); - // but valid again if we remove it - sub->removeFieldDescriptor(sub_field2); - ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); - ASSERT_TRUE(domain->validate(logger)); - } -} -} \ No newline at end of file diff --git a/test/core/model/OntologyTest.cpp b/test/core/model/OntologyTest.cpp new file mode 100644 index 0000000..764dcb4 --- /dev/null +++ b/test/core/model/OntologyTest.cpp @@ -0,0 +1,689 @@ +/* + 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 "TestOntology.hpp" + +namespace ousia { + +void assert_path(const ResolutionResult &res, const Rtti *expected_type, + std::vector expected_path) +{ + // Check class/type + ASSERT_TRUE(res.node->isa(expected_type)); + + // Check path + ASSERT_EQ(expected_path, res.node->path()); +} + +TEST(Ontology, testOntologyResolving) +{ + // Construct Manager + Logger logger; + Manager mgr{1}; + Rooted sys{new SystemTypesystem(mgr)}; + // Get the ontology. + Rooted ontology = constructBookOntology(mgr, sys, logger); + + std::vector res; + + // There is one ontology called "book" + res = ontology->resolve(&RttiTypes::Ontology, "book"); + ASSERT_EQ(1U, res.size()); + assert_path(res[0], &RttiTypes::Ontology, {"book"}); + + // There is one ontology called "book" + res = ontology->resolve(&RttiTypes::StructuredClass, "book"); + ASSERT_EQ(1U, res.size()); + assert_path(res[0], &RttiTypes::StructuredClass, {"book", "book"}); + + // If we explicitly ask for the "book, book" path, then only the + // StructuredClass should be returned. + res = ontology->resolve(&RttiTypes::Ontology, + std::vector{"book", "book"}); + ASSERT_EQ(0U, res.size()); + + res = ontology->resolve(&RttiTypes::StructuredClass, + std::vector{"book", "book"}); + ASSERT_EQ(1U, res.size()); + + // If we ask for "section" the result should be unique as well. + res = ontology->resolve(&RttiTypes::StructuredClass, "section"); + ASSERT_EQ(1U, res.size()); + assert_path(res[0], &RttiTypes::StructuredClass, {"book", "section"}); + + // If we ask for "paragraph" it is referenced two times in the Ontology graph, + // but should be returned only once. + res = ontology->resolve(&RttiTypes::StructuredClass, "paragraph"); + ASSERT_EQ(1U, res.size()); + assert_path(res[0], &RttiTypes::StructuredClass, {"book", "paragraph"}); +} + +// i use this wrapper due to the strange behaviour of GTEST. +static void assertFalse(bool b){ + ASSERT_FALSE(b); +} + +static Rooted createUnsortedPrimitiveField( + Handle strct, Handle type, Logger &logger, bool tree, + std::string name) +{ + FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::SUBTREE; + if (tree) { + fieldType = FieldDescriptor::FieldType::TREE; + } + + auto res = strct->createPrimitiveFieldDescriptor(type, logger, fieldType, + std::move(name)); + assertFalse(res.second); + return res.first; +} + +TEST(StructuredClass, getFieldDescriptors) +{ + /* + * We construct a case with the three levels: + * 1.) A has the SUBTREE fields a and b as well as a TREE field. + * 2.) B is a subclass of A and has the SUBTREE fields b and c as well as + * a TREE field. + * 3.) C is a subclass of B and has the SUBTREE field a. + * As a result we expect C to have none of As fields, the TREE field of B, + * and the SUBTREE fields a (of C) , b and c (of B). + */ + TerminalLogger logger{std::cout}; + Manager mgr{1}; + Rooted sys{new SystemTypesystem(mgr)}; + Rooted ontology{new Ontology(mgr, sys, "myOntology")}; + + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), nullptr, false, true)}; + Rooted A_a = createUnsortedPrimitiveField( + A, sys->getStringType(), logger, false, "a"); + Rooted A_b = createUnsortedPrimitiveField( + A, sys->getStringType(), logger, false, "b"); + Rooted A_main = createUnsortedPrimitiveField( + A, sys->getStringType(), logger, true, "somename"); + + Rooted B{new StructuredClass( + mgr, "B", ontology, Cardinality::any(), A, false, true)}; + Rooted B_b = createUnsortedPrimitiveField( + B, sys->getStringType(), logger, false, "b"); + Rooted B_c = createUnsortedPrimitiveField( + B, sys->getStringType(), logger, false, "c"); + Rooted B_main = createUnsortedPrimitiveField( + B, sys->getStringType(), logger, true, "othername"); + + Rooted C{new StructuredClass( + mgr, "C", ontology, Cardinality::any(), B, false, true)}; + Rooted C_a = createUnsortedPrimitiveField( + C, sys->getStringType(), logger, false, "a"); + + ASSERT_TRUE(ontology->validate(logger)); + + // check all FieldDescriptors + { + NodeVector fds = A->getFieldDescriptors(); + ASSERT_EQ(3, fds.size()); + ASSERT_EQ(A_a, fds[0]); + ASSERT_EQ(A_b, fds[1]); + ASSERT_EQ(A_main, fds[2]); + } + { + NodeVector fds = B->getFieldDescriptors(); + ASSERT_EQ(4, fds.size()); + ASSERT_EQ(A_a, fds[0]); + ASSERT_EQ(B_b, fds[1]); + ASSERT_EQ(B_c, fds[2]); + ASSERT_EQ(B_main, fds[3]); + } + { + NodeVector fds = C->getFieldDescriptors(); + ASSERT_EQ(4, fds.size()); + ASSERT_EQ(B_b, fds[0]); + ASSERT_EQ(B_c, fds[1]); + // superclass fields come before subclass fields (except for the TREE + // field, which is always last). + ASSERT_EQ(C_a, fds[2]); + ASSERT_EQ(B_main, fds[3]); + } +} + + +TEST(StructuredClass, getFieldDescriptorsCycles) +{ + Logger logger; + Manager mgr{1}; + Rooted sys{new SystemTypesystem(mgr)}; + Rooted ontology{new Ontology(mgr, sys, "myOntology")}; + + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), nullptr, false, true)}; + A->addSubclass(A, logger); + Rooted A_a = createUnsortedPrimitiveField( + A, sys->getStringType(), logger, false, "a"); + ASSERT_FALSE(ontology->validate(logger)); + // if we call getFieldDescriptors that should still return a valid result. + NodeVector fds = A->getFieldDescriptors(); + ASSERT_EQ(1, fds.size()); + ASSERT_EQ(A_a, fds[0]); +} + +Rooted getClass(const std::string name, Handle dom) +{ + std::vector res = + dom->resolve(&RttiTypes::StructuredClass, name); + return res[0].node.cast(); +} + +TEST(Descriptor, pathTo) +{ + // Start with some easy examples from the book ontology. + TerminalLogger logger{std::cout}; + Manager mgr{1}; + Rooted sys{new SystemTypesystem(mgr)}; + // Get the ontology. + Rooted ontology = constructBookOntology(mgr, sys, logger); + + // get the book node and the section node. + Rooted book = getClass("book", ontology); + Rooted section = getClass("section", ontology); + // get the path in between. + NodeVector path = book->pathTo(section, logger); + ASSERT_EQ(1U, path.size()); + ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); + + // get the text node. + Rooted text = getClass("text", ontology); + // get the path between book and text via paragraph. + path = book->pathTo(text, logger); + ASSERT_EQ(3U, path.size()); + ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); + ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); + ASSERT_EQ("paragraph", path[1]->getName()); + ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); + + // get the subsection node. + Rooted subsection = getClass("subsection", ontology); + // try to get the path between book and subsection. + path = book->pathTo(subsection, logger); + // this should be impossible. + ASSERT_EQ(0U, path.size()); + + // try to construct the path between section and the text field. + auto res = section->pathTo(text->getFieldDescriptor(), logger); + ASSERT_TRUE(res.second); + path = res.first; + ASSERT_EQ(4U, path.size()); + ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); + ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); + ASSERT_EQ("paragraph", path[1]->getName()); + ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); + ASSERT_TRUE(path[3]->isa(&RttiTypes::StructuredClass)); + ASSERT_EQ("text", path[3]->getName()); +} + +TEST(Descriptor, pathToAdvanced) +{ + /* + * Now we build a really nasty ontology with lots of transparency + * and inheritance. The basic idea is to have three paths from start to + * finish, where one is blocked by overriding fields and the longer valid + * one is found first such that it has to be replaced by the shorter one + * during the search. + * + * To achieve that we have the following structure: + * 1.) The start class inherits from A. + * 2.) A has B as child in the default field. + * 3.) B is transparent and has no children (but C as subclass) + * 4.) C is a subclass of B, transparent and has + * the target as child (shortest path). + * 5.) A has D as child in the default field. + * 6.) D is transparent has E as child in the default field. + * 7.) E is transparent and has target as child in the default field + * (longer path) + * + * So the path A_second_field, C, C_field should be returned. + */ + Manager mgr{1}; + TerminalLogger logger{std::cout}; + Rooted sys{new SystemTypesystem(mgr)}; + // Construct the ontology + Rooted ontology{new Ontology(mgr, sys, "nasty")}; + + // Let's create the classes that we need first + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), {nullptr}, false, true)}; + + Rooted start{new StructuredClass( + mgr, "start", ontology, Cardinality::any(), A, false, false)}; + + Rooted B{new StructuredClass( + mgr, "B", ontology, Cardinality::any(), {nullptr}, true, false)}; + + Rooted C{new StructuredClass( + mgr, "C", ontology, Cardinality::any(), B, true, false)}; + + Rooted D{new StructuredClass( + mgr, "D", ontology, Cardinality::any(), {nullptr}, true, false)}; + + Rooted E{new StructuredClass( + mgr, "E", ontology, Cardinality::any(), {nullptr}, true, false)}; + + Rooted target{ + new StructuredClass(mgr, "target", ontology, Cardinality::any())}; + + // We create a field for A + Rooted A_field = A->createFieldDescriptor(logger).first; + A_field->addChild(B); + A_field->addChild(D); + + // We create no field for B + // One for C + Rooted C_field = C->createFieldDescriptor(logger).first; + C_field->addChild(target); + + // One for D + Rooted D_field = D->createFieldDescriptor(logger).first; + D_field->addChild(E); + + // One for E + Rooted E_field = E->createFieldDescriptor(logger).first; + E_field->addChild(target); + + ASSERT_TRUE(ontology->validate(logger)); + +#ifdef MANAGER_GRAPHVIZ_EXPORT + // dump the manager state + mgr.exportGraphviz("nastyOntology.dot"); +#endif + + // and now we should be able to find the shortest path as suggested + NodeVector path = start->pathTo(target, logger); + ASSERT_EQ(3U, path.size()); + ASSERT_TRUE(path[0]->isa(&RttiTypes::FieldDescriptor)); + ASSERT_EQ("", path[0]->getName()); + ASSERT_TRUE(path[1]->isa(&RttiTypes::StructuredClass)); + ASSERT_EQ("C", path[1]->getName()); + ASSERT_TRUE(path[2]->isa(&RttiTypes::FieldDescriptor)); + ASSERT_EQ("", path[2]->getName()); +} + +TEST(Descriptor, pathToCycles) +{ + // build a ontology with a cycle. + Manager mgr{1}; + Logger logger; + Rooted sys{new SystemTypesystem(mgr)}; + // Construct the ontology + Rooted ontology{new Ontology(mgr, sys, "cycles")}; + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), {nullptr}, true, true)}; + A->addSubclass(A, logger); + ASSERT_FALSE(ontology->validate(logger)); + Rooted B{new StructuredClass( + mgr, "B", ontology, Cardinality::any(), {nullptr}, false, true)}; + Rooted A_field = A->createFieldDescriptor(logger).first; + A_field->addChild(B); + /* + * Now try to create the path from A to B. A direct path is possible but + * in the worst case this could also try to find shorter paths via an + * endless repition of A instances. + * As we cut the search tree at paths that are longer than our current + * optimum this should not happen, though. + */ + NodeVector path = A->pathTo(B, logger); + ASSERT_EQ(1, path.size()); + ASSERT_EQ(A_field, path[0]); +} + +TEST(Descriptor, getDefaultFields) +{ + // construct a ontology with lots of default fields to test. + // start with a single structure class. + Manager mgr{1}; + TerminalLogger logger{std::cout}; + Rooted sys{new SystemTypesystem(mgr)}; + // Construct the ontology + Rooted ontology{new Ontology(mgr, sys, "nasty")}; + + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), nullptr, false, true)}; + + // in this trivial case no field should be found. + ASSERT_TRUE(A->getDefaultFields().empty()); + + // create a field. + Rooted A_prim_field = + A->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; + // now we should find that. + auto fields = A->getDefaultFields(); + ASSERT_EQ(1U, fields.size()); + ASSERT_EQ(A_prim_field, fields[0]); + + // remove that field from A and add it to another class. + + Rooted B{new StructuredClass( + mgr, "B", ontology, Cardinality::any(), nullptr, false, true)}; + + B->moveFieldDescriptor(A_prim_field, logger); + + // new we shouldn't find the field anymore. + ASSERT_TRUE(A->getDefaultFields().empty()); + + // but we should find it again if we set B as superclass of A. + A->setSuperclass(B, logger); + fields = A->getDefaultFields(); + ASSERT_EQ(1U, fields.size()); + ASSERT_EQ(A_prim_field, fields[0]); + + // and we should not be able to find it if we override the field. + Rooted A_field = A->createFieldDescriptor(logger).first; + ASSERT_TRUE(A->getDefaultFields().empty()); + + // add a transparent child class. + + Rooted C{new StructuredClass( + mgr, "C", ontology, Cardinality::any(), nullptr, true, false)}; + A_field->addChild(C); + + // add a primitive field for it. + Rooted C_field = + C->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; + + // now we should find that. + fields = A->getDefaultFields(); + ASSERT_EQ(1U, fields.size()); + ASSERT_EQ(C_field, fields[0]); + + // add another transparent child class to A with a daughter class that has + // in turn a subclass with a primitive field. + Rooted D{new StructuredClass( + mgr, "D", ontology, Cardinality::any(), nullptr, true, false)}; + A_field->addChild(D); + Rooted D_field = D->createFieldDescriptor(logger).first; + Rooted E{new StructuredClass( + mgr, "E", ontology, Cardinality::any(), nullptr, true, false)}; + D_field->addChild(E); + Rooted F{new StructuredClass( + mgr, "E", ontology, Cardinality::any(), E, true, false)}; + Rooted F_field = + F->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; + + // now we should find both primitive fields, but the C field first. + fields = A->getDefaultFields(); + ASSERT_EQ(2U, fields.size()); + ASSERT_EQ(C_field, fields[0]); + ASSERT_EQ(F_field, fields[1]); +} + +TEST(Descriptor, getDefaultFieldsCycles) +{ + // build a ontology with a cycle. + Manager mgr{1}; + Logger logger; + Rooted sys{new SystemTypesystem(mgr)}; + // Construct the ontology + Rooted ontology{new Ontology(mgr, sys, "cycles")}; + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), {nullptr}, true, true)}; + A->addSubclass(A, logger); + ASSERT_FALSE(ontology->validate(logger)); + Rooted A_field = + A->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; + /* + * Now try to get the default fields of A. This should not lead to cycles + * if we correctly note all already visited nodes. + */ + NodeVector defaultFields = A->getDefaultFields(); + ASSERT_EQ(1, defaultFields.size()); + ASSERT_EQ(A_field, defaultFields[0]); +} + +TEST(Descriptor, getPermittedChildren) +{ + // analyze the book ontology. + TerminalLogger logger{std::cout}; + Manager mgr{1}; + Rooted sys{new SystemTypesystem(mgr)}; + // Get the ontology. + Rooted ontology = constructBookOntology(mgr, sys, logger); + // get the relevant classes. + Rooted book = getClass("book", ontology); + Rooted section = getClass("section", ontology); + Rooted paragraph = getClass("paragraph", ontology); + Rooted text = getClass("text", ontology); + /* + * as permitted children we expect section, paragraph and text in exactly + * that order. section should be before paragraph because of declaration + * order and text should be last because it needs a transparent paragraph + * in between. + */ + NodeVector children = book->getPermittedChildren(); + ASSERT_EQ(3U, children.size()); + ASSERT_EQ(section, children[0]); + ASSERT_EQ(paragraph, children[1]); + ASSERT_EQ(text, children[2]); + + // Now we add a subclass to text. + Rooted sub{new StructuredClass( + mgr, "Subclass", ontology, Cardinality::any(), text, true, false)}; + // And that should be in the result list as well now. + children = book->getPermittedChildren(); + ASSERT_EQ(4U, children.size()); + ASSERT_EQ(section, children[0]); + ASSERT_EQ(paragraph, children[1]); + ASSERT_EQ(text, children[2]); + ASSERT_EQ(sub, children[3]); +} + +TEST(Descriptor, getPermittedChildrenCycles) +{ + // build a ontology with a cycle. + Manager mgr{1}; + Logger logger; + Rooted sys{new SystemTypesystem(mgr)}; + // Construct the ontology + Rooted ontology{new Ontology(mgr, sys, "cycles")}; + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), {nullptr}, true, true)}; + A->addSubclass(A, logger); + ASSERT_FALSE(ontology->validate(logger)); + Rooted A_field = A->createFieldDescriptor(logger).first; + // we make the cycle worse by adding A as child of itself. + A_field->addChild(A); + /* + * Now try to get the permitted children of A. This should not lead to + * cycles + * if we correctly note all already visited nodes. + */ + NodeVector children = A->getPermittedChildren(); + ASSERT_EQ(1, children.size()); + ASSERT_EQ(A, children[0]); +} + +TEST(StructuredClass, isSubclassOf) +{ + // create an inheritance hierarchy. + Manager mgr{1}; + Rooted sys{new SystemTypesystem(mgr)}; + Rooted ontology{new Ontology(mgr, sys, "inheritance")}; + Rooted A{new StructuredClass( + mgr, "A", ontology, Cardinality::any(), {nullptr}, false, true)}; + // first branch + Rooted B{ + new StructuredClass(mgr, "B", ontology, Cardinality::any(), A)}; + Rooted C{ + new StructuredClass(mgr, "C", ontology, Cardinality::any(), B)}; + // second branch + Rooted D{ + new StructuredClass(mgr, "D", ontology, Cardinality::any(), A)}; + Rooted E{ + new StructuredClass(mgr, "E", ontology, Cardinality::any(), D)}; + Rooted F{ + new StructuredClass(mgr, "F", ontology, Cardinality::any(), D)}; + + // check function results + ASSERT_FALSE(A->isSubclassOf(A)); + ASSERT_FALSE(A->isSubclassOf(B)); + ASSERT_FALSE(A->isSubclassOf(C)); + ASSERT_FALSE(A->isSubclassOf(D)); + ASSERT_FALSE(A->isSubclassOf(E)); + ASSERT_FALSE(A->isSubclassOf(F)); + + ASSERT_TRUE(B->isSubclassOf(A)); + ASSERT_FALSE(B->isSubclassOf(B)); + ASSERT_FALSE(B->isSubclassOf(C)); + ASSERT_FALSE(B->isSubclassOf(D)); + ASSERT_FALSE(B->isSubclassOf(E)); + ASSERT_FALSE(B->isSubclassOf(F)); + + ASSERT_TRUE(C->isSubclassOf(A)); + ASSERT_TRUE(C->isSubclassOf(B)); + ASSERT_FALSE(C->isSubclassOf(C)); + ASSERT_FALSE(C->isSubclassOf(D)); + ASSERT_FALSE(C->isSubclassOf(E)); + ASSERT_FALSE(C->isSubclassOf(F)); + + ASSERT_TRUE(D->isSubclassOf(A)); + ASSERT_FALSE(D->isSubclassOf(B)); + ASSERT_FALSE(D->isSubclassOf(C)); + ASSERT_FALSE(D->isSubclassOf(D)); + ASSERT_FALSE(D->isSubclassOf(E)); + ASSERT_FALSE(D->isSubclassOf(F)); + + ASSERT_TRUE(E->isSubclassOf(A)); + ASSERT_FALSE(E->isSubclassOf(B)); + ASSERT_FALSE(E->isSubclassOf(C)); + ASSERT_TRUE(E->isSubclassOf(D)); + ASSERT_FALSE(E->isSubclassOf(E)); + ASSERT_FALSE(E->isSubclassOf(F)); + + ASSERT_TRUE(F->isSubclassOf(A)); + ASSERT_FALSE(F->isSubclassOf(B)); + ASSERT_FALSE(F->isSubclassOf(C)); + ASSERT_TRUE(F->isSubclassOf(D)); + ASSERT_FALSE(F->isSubclassOf(E)); + ASSERT_FALSE(F->isSubclassOf(F)); +} + +TEST(Ontology, validate) +{ + TerminalLogger logger{std::cerr, true}; + Manager mgr{1}; + Rooted sys{new SystemTypesystem(mgr)}; + // start with an easy example: Our book ontology should be valid. + { + Rooted ontology = constructBookOntology(mgr, sys, logger); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + } + { + // Even easier: An empty ontology should be valid. + Rooted ontology{new Ontology(mgr, sys, "ontology")}; + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // if we add a StructureClass it should be valid still. + Rooted base{ + new StructuredClass(mgr, "myClass", ontology)}; + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // if we tamper with the name, however, it shouldn't be valid anymore. + base->setName(""); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_FALSE(ontology->validate(logger)); + base->setName("my class"); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_FALSE(ontology->validate(logger)); + base->setName("myClass"); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // Let's add a primitive field (without a primitive type at first) + Rooted base_field = + base->createPrimitiveFieldDescriptor(nullptr, logger).first; + // this should not be valid. + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_FALSE(ontology->validate(logger)); + // but it should be if we set the type. + base_field->setPrimitiveType(sys->getStringType()); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // add a subclass for our base class. + Rooted sub{new StructuredClass(mgr, "sub", ontology)}; + // this should be valid in itself. + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // and still if we add a superclass. + sub->setSuperclass(base, logger); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // and still if we remove the subclass from the base class. + base->removeSubclass(sub, logger); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + ASSERT_EQ(nullptr, sub->getSuperclass()); + // and still if we re-add it. + base->addSubclass(sub, logger); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + ASSERT_EQ(base, sub->getSuperclass()); + // add a non-primitive field to the child class. + Rooted sub_field = + sub->createFieldDescriptor(logger).first; + // this should not be valid because we allow no children. + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_FALSE(ontology->validate(logger)); + // we should also be able to add a child and make it valid. + sub_field->addChild(base); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // it should be invalid if we add it twice. + sub_field->addChild(base); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_FALSE(ontology->validate(logger)); + // and valid again if we remove it once. + sub_field->removeChild(base); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // if we set a primitive type it should be invalid + sub_field->setPrimitiveType(sys->getStringType()); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_FALSE(ontology->validate(logger)); + // and valid again if we unset it. + sub_field->setPrimitiveType(nullptr); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + // It should be invalid if we set another TREE field. + Rooted sub_field2 = + sub->createFieldDescriptor(logger, FieldDescriptor::FieldType::TREE, + "test", false).first; + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_FALSE(ontology->validate(logger)); + // but valid again if we remove it + sub->removeFieldDescriptor(sub_field2); + ASSERT_EQ(ValidationState::UNKNOWN, ontology->getValidationState()); + ASSERT_TRUE(ontology->validate(logger)); + } +} +} \ No newline at end of file diff --git a/test/core/model/TestAdvanced.hpp b/test/core/model/TestAdvanced.hpp index 71379d2..c92effa 100644 --- a/test/core/model/TestAdvanced.hpp +++ b/test/core/model/TestAdvanced.hpp @@ -20,19 +20,19 @@ #define _MODEL_TEST_ADVANCED_HPP_ #include -#include +#include #include #include "TestDocumentBuilder.hpp" namespace ousia { -static Rooted resolveDescriptor(Handle domain, +static Rooted resolveDescriptor(Handle ontology, const std::string &className) { // use the actual resolve method. std::vector resolved = - domain->resolve(&RttiTypes::StructuredClass, className); + ontology->resolve(&RttiTypes::StructuredClass, className); // take the first valid result. for (auto &r : resolved) { return r.node.cast(); @@ -42,53 +42,53 @@ static Rooted resolveDescriptor(Handle domain, } /** - * This constructs the "heading" domain given the book domain. + * This constructs the "heading" ontology given the book ontology. */ -static Rooted constructHeadingDomain(Manager &mgr, +static Rooted constructHeadingOntology(Manager &mgr, Handle sys, - Handle bookDomain, + Handle bookOntology, Logger &logger) { - // set up domain node. - Rooted domain{new Domain(mgr, sys, "headings")}; + // set up ontology node. + Rooted ontology{new Ontology(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 heading{ - new StructuredClass(mgr, "heading", domain, card, {nullptr}, true)}; + new StructuredClass(mgr, "heading", ontology, card, {nullptr}, true)}; // as field want to reference the field of paragraph. - Rooted p = resolveDescriptor(bookDomain, "paragraph"); + Rooted p = resolveDescriptor(bookOntology, "paragraph"); heading->addFieldDescriptor(p->getFieldDescriptor(), logger); // create a new field for headings in each section type. std::vector secclasses{"book", "section", "subsection", "paragraph"}; for (auto &s : secclasses) { - Rooted desc = resolveDescriptor(bookDomain, s); + Rooted desc = resolveDescriptor(bookOntology, s); Rooted heading_field = desc->createFieldDescriptor(logger, FieldDescriptor::FieldType::SUBTREE, "heading", true).first; heading_field->addChild(heading); } - return domain; + return ontology; } /** - * This constructs the "list" domain given the book domain. + * This constructs the "list" ontology given the book ontology. */ -static Rooted constructListDomain(Manager &mgr, +static Rooted constructListOntology(Manager &mgr, Handle sys, - Handle bookDomain, + Handle bookOntology, Logger &logger) { - // set up domain node. - Rooted domain{new Domain(mgr, sys, "list")}; + // set up ontology node. + Rooted ontology{new Ontology(mgr, sys, "list")}; // get book.paragraph - Rooted p = resolveDescriptor(bookDomain, "paragraph"); + Rooted p = resolveDescriptor(bookOntology, "paragraph"); // set up item StructuredClass; Rooted item{new StructuredClass( - mgr, "item", domain, Cardinality::any(), {nullptr}, false)}; + mgr, "item", ontology, Cardinality::any(), {nullptr}, false)}; // as field we want to reference the field of paragraph. item->addFieldDescriptor(p->getFieldDescriptor(), logger); @@ -96,28 +96,28 @@ static Rooted constructListDomain(Manager &mgr, std::vector listTypes{"ol", "ul"}; for (auto &listType : listTypes) { Rooted list{new StructuredClass( - mgr, listType, domain, Cardinality::any(), p, false)}; + mgr, listType, ontology, Cardinality::any(), p, false)}; Rooted list_field{new FieldDescriptor(mgr, list)}; list_field->addChild(item); } - return domain; + return ontology; } /** - * This constructs the "emphasis" domain. + * This constructs the "emphasis" ontology. */ -static Rooted constructEmphasisDomain(Manager &mgr, +static Rooted constructEmphasisOntology(Manager &mgr, Handle sys, Logger &logger) { - // set up domain node. - Rooted domain{new Domain(mgr, sys, "emphasis")}; + // set up ontology node. + Rooted ontology{new Ontology(mgr, sys, "emphasis")}; // create AnnotationClasses - Rooted em{new AnnotationClass(mgr, "emphasized", domain)}; + Rooted em{new AnnotationClass(mgr, "emphasized", ontology)}; - Rooted strong{new AnnotationClass(mgr, "strong", domain)}; + Rooted strong{new AnnotationClass(mgr, "strong", ontology)}; - return domain; + return ontology; } static bool addText(Logger &logger, Handle doc, @@ -173,17 +173,17 @@ static bool addAnnotation(Logger &logger, Handle doc, /** * This constructs a more advanced book document using not only the book - * domain but also headings, emphasis and lists. + * ontology but also headings, emphasis and lists. */ static Rooted constructAdvancedDocument(Manager &mgr, Logger &logger, - Handle bookDom, - Handle headingDom, - Handle listDom, - Handle emphasisDom) + Handle bookDom, + Handle headingDom, + Handle listDom, + Handle emphasisDom) { // Start with the (empty) document. Rooted doc{new Document(mgr, "kant_was_ist_aufklaerung.oxd")}; - doc->referenceDomains({bookDom, headingDom, listDom, emphasisDom}); + doc->referenceOntologys({bookDom, headingDom, listDom, emphasisDom}); // Add the root. Rooted book = diff --git a/test/core/model/TestDocument.hpp b/test/core/model/TestDocument.hpp index 7675dab..a4a8f7c 100644 --- a/test/core/model/TestDocument.hpp +++ b/test/core/model/TestDocument.hpp @@ -20,7 +20,7 @@ #define _MODEL_TEST_DOCUMENT_HPP_ #include -#include +#include #include #include "TestDocumentBuilder.hpp" @@ -28,15 +28,15 @@ namespace ousia { /** - * This constructs a fairly simple test document for the "book" domain. The + * This constructs a fairly simple test document for the "book" ontology. The * structure of the document can be seen in the Code below. */ static Rooted constructBookDocument(Manager &mgr, Logger &logger, - Rooted bookDomain) + Rooted bookOntology) { // Start with the (empty) document. Rooted doc{new Document(mgr, "myDoc.oxd")}; - doc->referenceDomain(bookDomain); + doc->referenceOntology(bookOntology); // Add the root. Rooted root = diff --git a/test/core/model/TestDocumentBuilder.hpp b/test/core/model/TestDocumentBuilder.hpp index fc76b07..745f1ab 100644 --- a/test/core/model/TestDocumentBuilder.hpp +++ b/test/core/model/TestDocumentBuilder.hpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include namespace ousia { @@ -81,7 +81,7 @@ static Rooted resolveDescriptor(Handle doc, * @param name is the name of this StructuredEntity (empty per * default). * @return the newly created StructuredEntity or a nullptr if some - * input handle was empty or the given domains did not + * input handle was empty or the given ontologies did not * contain a StructuredClass with the given name. */ Rooted buildRootStructuredEntity( @@ -112,7 +112,7 @@ Rooted buildRootStructuredEntity( * This builds a StructuredEntity as child of the given DocumentEntity. It * automatically appends the newly build entity to its parent. * - * @param document is the document this entity shall be build for. The domains + * @param document is the document this entity shall be build for. The ontologies * referenced here are the basis to resolve the given path. * @param logger is the current logger. * @param parent is the parent DocumentEntity. The newly constructed @@ -126,7 +126,7 @@ Rooted buildRootStructuredEntity( * @param name is the name of this StructuredEntity (empty per * default). * @return the newly created StructuredEntity or a nullptr if some - * input handle was empty or the given domains did not + * input handle was empty or the given ontologies did not * contain a StructuredClass with the given name. */ Rooted buildStructuredEntity( @@ -167,7 +167,7 @@ Rooted buildStructuredEntity( * This builds an AnnotationEntity as child of the given Document. It * automatically appends the newly build entity to its parent. * - * @param document is the document this entity shall be build for. The domains + * @param document is the document this entity shall be build for. The ontologies * referenced here are the basis to resolve the given path. * @param logger is the current logger. * @param path is the name of the AnnotationClass or a path specifying it @@ -179,7 +179,7 @@ Rooted buildStructuredEntity( * @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 + * input handle was empty or the given ontologies did not * contain a AnnotationClass with the given name. */ Rooted buildAnnotationEntity( diff --git a/test/core/model/TestDomain.hpp b/test/core/model/TestDomain.hpp deleted file mode 100644 index 779ef03..0000000 --- a/test/core/model/TestDomain.hpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - 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 . -*/ - -#ifndef _MODEL_TEST_DOMAIN_HPP_ -#define _MODEL_TEST_DOMAIN_HPP_ - -#include -#include - -namespace ousia { -/** - * This constructs the "book" domain for test purposes. The structure of the - * domain is fairly simple and can be seen from the construction itself. - */ -static Rooted constructBookDomain(Manager &mgr, - Handle sys, - Logger &logger) -{ - // Start with the Domain itself. - Rooted domain{new Domain(mgr, sys, "book")}; - // Set up the cardinalities we'll need. - Cardinality single; - single.merge({1}); - - // Set up the "book" node. - Rooted book{new StructuredClass( - mgr, "book", domain, single, {nullptr}, false, true)}; - - // The structure field of it. - Rooted book_field = - book->createFieldDescriptor(logger).first; - - // From there on the "section". - Rooted section{ - new StructuredClass(mgr, "section", domain, Cardinality::any())}; - book_field->addChild(section); - - // And the field of it. - Rooted section_field = - section->createFieldDescriptor(logger).first; - - // We also add the "paragraph", which is transparent. - Rooted paragraph{new StructuredClass( - mgr, "paragraph", domain, Cardinality::any(), {nullptr}, true)}; - section_field->addChild(paragraph); - book_field->addChild(paragraph); - - // And the field of it. - Rooted paragraph_field = - paragraph->createFieldDescriptor(logger).first; - - // We append "subsection" to section. - Rooted subsection{ - new StructuredClass(mgr, "subsection", domain, Cardinality::any())}; - section_field->addChild(subsection); - - // And the field of it. - Rooted subsection_field = - subsection->createFieldDescriptor(logger).first; - - // and we add the paragraph to subsections fields - subsection_field->addChild(paragraph); - - // Finally we add the "text" node, which is transparent as well. - Rooted text{new StructuredClass( - mgr, "text", domain, Cardinality::any(), {nullptr}, true)}; - paragraph_field->addChild(text); - - // ... and has a primitive field. - Rooted text_field = - text->createPrimitiveFieldDescriptor(sys->getStringType(), logger) - .first; - - return domain; -} -} - -#endif /* _TEST_DOMAIN_HPP_ */ \ No newline at end of file diff --git a/test/core/model/TestOntology.hpp b/test/core/model/TestOntology.hpp new file mode 100644 index 0000000..c65d6f4 --- /dev/null +++ b/test/core/model/TestOntology.hpp @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +#ifndef _MODEL_TEST_DOMAIN_HPP_ +#define _MODEL_TEST_DOMAIN_HPP_ + +#include +#include + +namespace ousia { +/** + * This constructs the "book" ontology for test purposes. The structure of the + * ontology is fairly simple and can be seen from the construction itself. + */ +static Rooted constructBookOntology(Manager &mgr, + Handle sys, + Logger &logger) +{ + // Start with the Ontology itself. + Rooted ontology{new Ontology(mgr, sys, "book")}; + // Set up the cardinalities we'll need. + Cardinality single; + single.merge({1}); + + // Set up the "book" node. + Rooted book{new StructuredClass( + mgr, "book", ontology, single, {nullptr}, false, true)}; + + // The structure field of it. + Rooted book_field = + book->createFieldDescriptor(logger).first; + + // From there on the "section". + Rooted section{ + new StructuredClass(mgr, "section", ontology, Cardinality::any())}; + book_field->addChild(section); + + // And the field of it. + Rooted section_field = + section->createFieldDescriptor(logger).first; + + // We also add the "paragraph", which is transparent. + Rooted paragraph{new StructuredClass( + mgr, "paragraph", ontology, Cardinality::any(), {nullptr}, true)}; + section_field->addChild(paragraph); + book_field->addChild(paragraph); + + // And the field of it. + Rooted paragraph_field = + paragraph->createFieldDescriptor(logger).first; + + // We append "subsection" to section. + Rooted subsection{ + new StructuredClass(mgr, "subsection", ontology, Cardinality::any())}; + section_field->addChild(subsection); + + // And the field of it. + Rooted subsection_field = + subsection->createFieldDescriptor(logger).first; + + // and we add the paragraph to subsections fields + subsection_field->addChild(paragraph); + + // Finally we add the "text" node, which is transparent as well. + Rooted text{new StructuredClass( + mgr, "text", ontology, Cardinality::any(), {nullptr}, true)}; + paragraph_field->addChild(text); + + // ... and has a primitive field. + Rooted text_field = + text->createPrimitiveFieldDescriptor(sys->getStringType(), logger) + .first; + + return ontology; +} +} + +#endif /* _TEST_DOMAIN_HPP_ */ \ No newline at end of file diff --git a/test/core/resource/ResourceRequestTest.cpp b/test/core/resource/ResourceRequestTest.cpp index dac7a28..3112cc4 100644 --- a/test/core/resource/ResourceRequestTest.cpp +++ b/test/core/resource/ResourceRequestTest.cpp @@ -31,7 +31,7 @@ namespace ousia { namespace RttiTypes { extern const Rtti Document; -extern const Rtti Domain; +extern const Rtti Ontology; extern const Rtti Typesystem; } @@ -62,12 +62,12 @@ struct TestSetup { TestSetup() { - registry.registerExtension("domain", "application/domain"); + registry.registerExtension("ontology", "application/ontology"); registry.registerExtension("typesystem", "application/typesystem"); registry.registerExtension("document", "application/document"); registry.registerParser( - {"application/domain", "application/typesystem"}, - {&RttiTypes::Domain, &RttiTypes::Typesystem}, &pModule); + {"application/ontology", "application/typesystem"}, + {&RttiTypes::Ontology, &RttiTypes::Typesystem}, &pModule); registry.registerParser({"application/document"}, {&RttiTypes::Document}, &pDocument); } @@ -78,16 +78,16 @@ TEST(ResourceRequest, deduction) { TestSetup t; - ResourceRequest req("test.domain", "", "", {&RttiTypes::Domain}); + ResourceRequest req("test.ontology", "", "", {&RttiTypes::Ontology}); ASSERT_TRUE(req.deduce(t.registry, logger)); - ASSERT_EQ("test.domain", req.getPath()); - ASSERT_EQ("application/domain", req.getMimetype()); - ASSERT_EQ(RttiSet({&RttiTypes::Domain}), req.getSupportedTypes()); - ASSERT_EQ(ResourceType::DOMAIN_DESC, req.getResourceType()); + ASSERT_EQ("test.ontology", req.getPath()); + ASSERT_EQ("application/ontology", req.getMimetype()); + ASSERT_EQ(RttiSet({&RttiTypes::Ontology}), req.getSupportedTypes()); + ASSERT_EQ(ResourceType::ONTOLOGY, req.getResourceType()); ASSERT_EQ(&t.pModule, req.getParser()); - ASSERT_EQ(RttiSet({&RttiTypes::Domain, &RttiTypes::Typesystem}), + ASSERT_EQ(RttiSet({&RttiTypes::Ontology, &RttiTypes::Typesystem}), req.getParserTypes()); } @@ -95,17 +95,17 @@ TEST(ResourceRequest, deductionWithMimetype) { TestSetup t; - ResourceRequest req("test.domain", "application/typesystem", "", + ResourceRequest req("test.ontology", "application/typesystem", "", {&RttiTypes::Typesystem}); ASSERT_TRUE(req.deduce(t.registry, logger)); - ASSERT_EQ("test.domain", req.getPath()); + ASSERT_EQ("test.ontology", req.getPath()); ASSERT_EQ("application/typesystem", req.getMimetype()); ASSERT_EQ(RttiSet({&RttiTypes::Typesystem}), req.getSupportedTypes()); ASSERT_EQ(ResourceType::TYPESYSTEM, req.getResourceType()); ASSERT_EQ(&t.pModule, req.getParser()); - ASSERT_EQ(RttiSet({&RttiTypes::Domain, &RttiTypes::Typesystem}), + ASSERT_EQ(RttiSet({&RttiTypes::Ontology, &RttiTypes::Typesystem}), req.getParserTypes()); } @@ -113,18 +113,18 @@ TEST(ResourceRequest, deductionWithUnknownResourceType) { TestSetup t; - ResourceRequest req("test.domain", "", "", - {&RttiTypes::Domain, &RttiTypes::Typesystem}); + ResourceRequest req("test.ontology", "", "", + {&RttiTypes::Ontology, &RttiTypes::Typesystem}); ASSERT_TRUE(req.deduce(t.registry, logger)); - ASSERT_EQ("test.domain", req.getPath()); - ASSERT_EQ("application/domain", req.getMimetype()); - ASSERT_EQ(RttiSet({&RttiTypes::Domain, &RttiTypes::Typesystem}), + ASSERT_EQ("test.ontology", req.getPath()); + ASSERT_EQ("application/ontology", req.getMimetype()); + ASSERT_EQ(RttiSet({&RttiTypes::Ontology, &RttiTypes::Typesystem}), req.getSupportedTypes()); ASSERT_EQ(ResourceType::UNKNOWN, req.getResourceType()); ASSERT_EQ(&t.pModule, req.getParser()); - ASSERT_EQ(RttiSet({&RttiTypes::Domain, &RttiTypes::Typesystem}), + ASSERT_EQ(RttiSet({&RttiTypes::Ontology, &RttiTypes::Typesystem}), req.getParserTypes()); } @@ -132,17 +132,17 @@ TEST(ResourceRequest, deductionWithRel) { TestSetup t; - ResourceRequest req("test.domain", "", "domain", - {&RttiTypes::Domain, &RttiTypes::Typesystem}); + ResourceRequest req("test.ontology", "", "ontology", + {&RttiTypes::Ontology, &RttiTypes::Typesystem}); ASSERT_TRUE(req.deduce(t.registry, logger)); - ASSERT_EQ("test.domain", req.getPath()); - ASSERT_EQ("application/domain", req.getMimetype()); - ASSERT_EQ(RttiSet({&RttiTypes::Domain}), req.getSupportedTypes()); - ASSERT_EQ(ResourceType::DOMAIN_DESC, req.getResourceType()); + ASSERT_EQ("test.ontology", req.getPath()); + ASSERT_EQ("application/ontology", req.getMimetype()); + ASSERT_EQ(RttiSet({&RttiTypes::Ontology}), req.getSupportedTypes()); + ASSERT_EQ(ResourceType::ONTOLOGY, req.getResourceType()); ASSERT_EQ(&t.pModule, req.getParser()); - ASSERT_EQ(RttiSet({&RttiTypes::Domain, &RttiTypes::Typesystem}), + ASSERT_EQ(RttiSet({&RttiTypes::Ontology, &RttiTypes::Typesystem}), req.getParserTypes()); } } diff --git a/test/formats/osml/OsmlParserTest.cpp b/test/formats/osml/OsmlParserTest.cpp index 5127b32..c88232b 100644 --- a/test/formats/osml/OsmlParserTest.cpp +++ b/test/formats/osml/OsmlParserTest.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -37,7 +37,7 @@ namespace ousia { namespace RttiTypes { extern const Rtti Document; -extern const Rtti Domain; +extern const Rtti Ontology; extern const Rtti Typesystem; } @@ -70,15 +70,15 @@ TEST(OsmlParser, emptyDocument) ASSERT_TRUE(node->isa(&RttiTypes::Document)); } -TEST(OsmlParser, emptyDomain) +TEST(OsmlParser, emptyOntology) { OsmlStandaloneEnvironment env(logger); Rooted node = - env.parse("empty_domain.osml", "", "", RttiSet{&RttiTypes::Node}); + env.parse("empty_ontology.osml", "", "", RttiSet{&RttiTypes::Node}); ASSERT_TRUE(node != nullptr); - ASSERT_TRUE(node->isa(&RttiTypes::Domain)); - ASSERT_EQ("testDomain", node->getName()); + ASSERT_TRUE(node->isa(&RttiTypes::Ontology)); + ASSERT_EQ("testOntology", node->getName()); } TEST(OsmlParser, emptyTypesystem) @@ -106,13 +106,13 @@ TEST(OsmlParser, rollbackOnInvalidElement) ASSERT_TRUE(node->isa(&RttiTypes::Document)); } -TEST(OsmlParser, inlineDomain) +TEST(OsmlParser, inlineOntology) { OsmlStandaloneEnvironment env(logger); logger.reset(); Rooted node = - env.parse("inline_domain.osml", "", "", RttiSet{&RttiTypes::Node}); + env.parse("inline_ontology.osml", "", "", RttiSet{&RttiTypes::Node}); ASSERT_FALSE(logger.hasError()); ASSERT_TRUE(node != nullptr); @@ -156,7 +156,7 @@ TEST(OsmlParser, structureInheritance) ASSERT_FALSE(logger.hasError()); ASSERT_TRUE(node != nullptr); - ASSERT_TRUE(node->isa(&RttiTypes::Domain)); + ASSERT_TRUE(node->isa(&RttiTypes::Ontology)); } TEST(OsmlParser, structWithNoField) diff --git a/test/formats/osxml/OsxmlParserTest.cpp b/test/formats/osxml/OsxmlParserTest.cpp index 3bf4a47..79dd58b 100644 --- a/test/formats/osxml/OsxmlParserTest.cpp +++ b/test/formats/osxml/OsxmlParserTest.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,7 +36,7 @@ namespace ousia { namespace RttiTypes { extern const Rtti Document; -extern const Rtti Domain; +extern const Rtti Ontology; extern const Rtti Typesystem; } @@ -88,7 +88,7 @@ static void checkAttributes(Handle expected, } static void checkStructuredClass( - Handle n, const std::string &name, Handle domain, + Handle n, const std::string &name, Handle ontology, Variant cardinality = Cardinality::any(), Handle attributesDescriptor = nullptr, Handle superclass = nullptr, bool transparent = false, @@ -98,7 +98,7 @@ static void checkStructuredClass( Handle sc = n.cast(); ASSERT_FALSE(sc == nullptr); ASSERT_EQ(name, sc->getName()); - ASSERT_EQ(domain, sc->getParent()); + ASSERT_EQ(ontology, sc->getParent()); ASSERT_EQ(cardinality, sc->getCardinality()); ASSERT_EQ(transparent, sc->isTransparent()); ASSERT_EQ(root, sc->hasRootPermission()); @@ -106,44 +106,44 @@ static void checkStructuredClass( } static Rooted checkStructuredClass( - const std::string &resolve, const std::string &name, Handle domain, + 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 = domain->resolve(&RttiTypes::StructuredClass, resolve); + auto res = ontology->resolve(&RttiTypes::StructuredClass, resolve); if (res.size() != 1) { throw OusiaException("resolution error!"); } Handle sc = res[0].node.cast(); - checkStructuredClass(sc, name, domain, cardinality, attributesDescriptor, + checkStructuredClass(sc, name, ontology, cardinality, attributesDescriptor, superclass, transparent, root); return sc; } static void checkAnnotationClass( - Handle n, const std::string &name, Handle domain, + 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(domain, ac->getParent()); + ASSERT_EQ(ontology, ac->getParent()); checkAttributes(attributesDescriptor, ac); } static Rooted checkAnnotationClass( - const std::string &resolve, const std::string &name, Handle domain, + const std::string &resolve, const std::string &name, Handle ontology, Handle attributesDescriptor = nullptr) { - auto res = domain->resolve(&RttiTypes::AnnotationClass, resolve); + auto res = ontology->resolve(&RttiTypes::AnnotationClass, resolve); if (res.size() != 1) { throw OusiaException("resolution error!"); } Handle ac = res[0].node.cast(); - checkAnnotationClass(ac, name, domain, attributesDescriptor); + checkAnnotationClass(ac, name, ontology, attributesDescriptor); return ac; } @@ -192,33 +192,33 @@ static void checkFieldDescriptor( optional); } -TEST(OsxmlParser, domainParsing) +TEST(OsxmlParser, ontologyParsing) { XmlStandaloneEnvironment env(logger); - Rooted book_domain_node = - env.parse("book_domain.osxml", "", "", RttiSet{&RttiTypes::Domain}); - ASSERT_FALSE(book_domain_node == nullptr); + Rooted book_ontology_node = + env.parse("book_ontology.osxml", "", "", RttiSet{&RttiTypes::Ontology}); + ASSERT_FALSE(book_ontology_node == nullptr); ASSERT_FALSE(logger.hasError()); - // check the domain node. - Rooted book_domain = book_domain_node.cast(); - ASSERT_EQ("book", book_domain->getName()); + // 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_domain, single, nullptr, nullptr, false, true); + "book", "book", book_ontology, single, nullptr, nullptr, false, true); // get the chapter struct node. Rooted chapter = - checkStructuredClass("chapter", "chapter", book_domain); + checkStructuredClass("chapter", "chapter", book_ontology); Rooted section = - checkStructuredClass("section", "section", book_domain); + checkStructuredClass("section", "section", book_ontology); Rooted subsection = - checkStructuredClass("subsection", "subsection", book_domain); + checkStructuredClass("subsection", "subsection", book_ontology); Rooted paragraph = - checkStructuredClass("paragraph", "paragraph", book_domain, + checkStructuredClass("paragraph", "paragraph", book_ontology, Cardinality::any(), nullptr, nullptr, true, false); Rooted text = - checkStructuredClass("text", "text", book_domain, Cardinality::any(), + checkStructuredClass("text", "text", book_ontology, Cardinality::any(), nullptr, nullptr, true, false); // check the FieldDescriptors. @@ -231,19 +231,19 @@ TEST(OsxmlParser, domainParsing) text, {}, "", FieldDescriptor::FieldType::TREE, env.project->getSystemTypesystem()->getStringType(), false); - // check parent handling using the headings domain. - Rooted headings_domain_node = - env.parse("headings_domain.osxml", "", "", RttiSet{&RttiTypes::Domain}); - ASSERT_FALSE(headings_domain_node == nullptr); + // 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_domain = headings_domain_node.cast(); + Rooted headings_ontology = headings_ontology_node.cast(); // now there should be a heading struct. Rooted heading = - checkStructuredClass("heading", "heading", headings_domain, single, + 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 domain (except for text) should have a + // 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); @@ -256,21 +256,21 @@ TEST(OsxmlParser, domainParsing) checkFieldDescriptor(paragraph, {heading}, "heading", FieldDescriptor::FieldType::SUBTREE, nullptr, true); - // check annotation handling using the comments domain. - Rooted comments_domain_node = - env.parse("comments_domain.osxml", "", "", RttiSet{&RttiTypes::Domain}); - ASSERT_FALSE(comments_domain_node == nullptr); + // 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_domain = comments_domain_node.cast(); + Rooted comments_ontology = comments_ontology_node.cast(); // now we should be able to find a comment annotation. Rooted comment_anno = - checkAnnotationClass("comment", "comment", comments_domain); + checkAnnotationClass("comment", "comment", comments_ontology); // as well as a comment struct Rooted comment = - checkStructuredClass("comment", "comment", comments_domain); + checkStructuredClass("comment", "comment", comments_ontology); // and a reply struct Rooted reply = - checkStructuredClass("reply", "reply", comments_domain); + checkStructuredClass("reply", "reply", comments_ontology); // check the fields for each of them. { std::vector> descs{comment_anno, comment, reply}; diff --git a/test/plugins/filesystem/FileLocatorTest.cpp b/test/plugins/filesystem/FileLocatorTest.cpp index 22db926..e2aacb1 100644 --- a/test/plugins/filesystem/FileLocatorTest.cpp +++ b/test/plugins/filesystem/FileLocatorTest.cpp @@ -38,12 +38,12 @@ TEST(FileLocator, addSearchPath) // Add one path for three types. instance.addSearchPath(".", - {ResourceType::DOMAIN_DESC, ResourceType::SCRIPT, + {ResourceType::ONTOLOGY, ResourceType::SCRIPT, ResourceType::TYPESYSTEM}); ASSERT_EQ(3U, instance.getSearchPaths().size()); - auto it = instance.getSearchPaths().find(ResourceType::DOMAIN_DESC); + auto it = instance.getSearchPaths().find(ResourceType::ONTOLOGY); ASSERT_EQ(1U, it->second.size()); ASSERT_EQ(canonicalPath, it->second[0]); @@ -64,12 +64,12 @@ TEST(FileLocator, addSearchPath) // Adding the path another time should not increase the number of found // paths, except for new resource types instance.addSearchPath( - canonicalPath, {ResourceType::DOMAIN_DESC, ResourceType::SCRIPT, + canonicalPath, {ResourceType::ONTOLOGY, ResourceType::SCRIPT, ResourceType::TYPESYSTEM, ResourceType::ATTRIBUTES}); ASSERT_EQ(4U, instance.getSearchPaths().size()); - it = instance.getSearchPaths().find(ResourceType::DOMAIN_DESC); + it = instance.getSearchPaths().find(ResourceType::ONTOLOGY); ASSERT_EQ(1U, it->second.size()); ASSERT_EQ(canonicalPath, it->second[0]); @@ -93,11 +93,11 @@ TEST(FileLocator, addSearchPath) std::string canonicalPath2 = fs::canonical("..").generic_string(); - instance.addSearchPath("..", {ResourceType::DOMAIN_DESC}); + instance.addSearchPath("..", {ResourceType::ONTOLOGY}); ASSERT_EQ(4U, instance.getSearchPaths().size()); - it = instance.getSearchPaths().find(ResourceType::DOMAIN_DESC); + it = instance.getSearchPaths().find(ResourceType::ONTOLOGY); ASSERT_EQ(2U, it->second.size()); ASSERT_EQ(canonicalPath, it->second[0]); @@ -106,7 +106,7 @@ TEST(FileLocator, addSearchPath) void assert_located(const FileLocator &instance, const std::string &path, const std::string &relativeTo, - ResourceType type = ResourceType::DOMAIN_DESC) + ResourceType type = ResourceType::ONTOLOGY) { Resource res; ASSERT_TRUE(instance.locate(res, path, type, relativeTo)); @@ -117,7 +117,7 @@ void assert_located(const FileLocator &instance, const std::string &path, void assert_not_located(const FileLocator &instance, const std::string &path, const std::string &relativeTo, - ResourceType type = ResourceType::DOMAIN_DESC) + ResourceType type = ResourceType::ONTOLOGY) { Resource res; ASSERT_FALSE(instance.locate(res, path, type, relativeTo)); @@ -134,7 +134,7 @@ TEST(FileLocator, locate) assert_not_located(locator, "c.txt", ""); // Add the respective search path - locator.addUnittestSearchPath("filesystem/b", ResourceType::DOMAIN_DESC); + locator.addUnittestSearchPath("filesystem/b", ResourceType::ONTOLOGY); // Now we should be able to find both. assert_located(locator, "a.txt", ""); @@ -217,8 +217,8 @@ TEST(FileLocator, testDefaultSearchPaths) locator.addDefaultSearchPaths(); assert_not_located(locator, "book.osxml", "", ResourceType::UNKNOWN); - assert_located(locator, "domain/book.osxml", "", ResourceType::UNKNOWN); - assert_located(locator, "book.osxml", "", ResourceType::DOMAIN_DESC); + assert_located(locator, "ontology/book.osxml", "", ResourceType::UNKNOWN); + assert_located(locator, "book.osxml", "", ResourceType::ONTOLOGY); assert_not_located(locator, "color.osxml", "", ResourceType::UNKNOWN); assert_located(locator, "typesystem/color.osxml", "", ResourceType::UNKNOWN); diff --git a/test/plugins/html/DemoOutputTest.cpp b/test/plugins/html/DemoOutputTest.cpp index 2f56e40..debb667 100644 --- a/test/plugins/html/DemoOutputTest.cpp +++ b/test/plugins/html/DemoOutputTest.cpp @@ -26,10 +26,10 @@ #include #include #include -#include +#include #include -#include +#include namespace ousia { namespace html { @@ -40,12 +40,12 @@ TEST(DemoHTMLTransformer, writeHTML) TerminalLogger logger{std::cerr, true}; Manager mgr{1}; Rooted sys{new SystemTypesystem(mgr)}; - // Get the domains. - Rooted bookDom = constructBookDomain(mgr, sys, logger); - Rooted headingDom = - constructHeadingDomain(mgr, sys, bookDom, logger); - Rooted listDom = constructListDomain(mgr, sys, bookDom, logger); - Rooted emDom = constructEmphasisDomain(mgr, sys, logger); + // Get the ontologies. + Rooted bookDom = constructBookOntology(mgr, sys, logger); + Rooted headingDom = + constructHeadingOntology(mgr, sys, bookDom, logger); + Rooted listDom = constructListOntology(mgr, sys, bookDom, logger); + Rooted emDom = constructEmphasisOntology(mgr, sys, logger); // Construct the document. Rooted doc = constructAdvancedDocument( mgr, logger, bookDom, headingDom, listDom, emDom); @@ -75,13 +75,13 @@ TEST(DemoHTMLTransformer, AnnotationProcessing) TerminalLogger logger{std::cerr, true}; Manager mgr{1}; Rooted sys{new SystemTypesystem(mgr)}; - // Get the domains. - Rooted bookDom = constructBookDomain(mgr, sys, logger); - Rooted emDom = constructEmphasisDomain(mgr, sys, logger); + // Get the ontologies. + Rooted bookDom = constructBookOntology(mgr, sys, logger); + Rooted emDom = constructEmphasisOntology(mgr, sys, logger); // Construct a document only containing overlapping annotations. // it has the form: blablubbla Rooted doc{new Document(mgr, "annotations.oxd")}; - doc->referenceDomains({bookDom, emDom}); + doc->referenceOntologys({bookDom, emDom}); Rooted book = buildRootStructuredEntity(doc, logger, {"book"}); ASSERT_TRUE(book != nullptr); diff --git a/test/plugins/xml/XmlOutputTest.cpp b/test/plugins/xml/XmlOutputTest.cpp index fcf72d2..ac80a57 100644 --- a/test/plugins/xml/XmlOutputTest.cpp +++ b/test/plugins/xml/XmlOutputTest.cpp @@ -26,10 +26,10 @@ #include #include #include -#include +#include #include -#include +#include namespace ousia { namespace xml { @@ -40,12 +40,12 @@ TEST(DemoHTMLTransformer, writeHTML) TerminalLogger logger{std::cerr, true}; Manager mgr{1}; Rooted sys{new SystemTypesystem(mgr)}; - // Get the domains. - Rooted bookDom = constructBookDomain(mgr, sys, logger); - Rooted headingDom = - constructHeadingDomain(mgr, sys, bookDom, logger); - Rooted listDom = constructListDomain(mgr, sys, bookDom, logger); - Rooted emDom = constructEmphasisDomain(mgr, sys, logger); + // Get the ontologies. + Rooted bookDom = constructBookOntology(mgr, sys, logger); + Rooted headingDom = + constructHeadingOntology(mgr, sys, bookDom, logger); + Rooted listDom = constructListOntology(mgr, sys, bookDom, logger); + Rooted emDom = constructEmphasisOntology(mgr, sys, logger); // Construct the document. Rooted doc = constructAdvancedDocument( mgr, logger, bookDom, headingDom, listDom, emDom); @@ -71,13 +71,13 @@ TEST(DemoHTMLTransformer, AnnotationProcessing) TerminalLogger logger{std::cerr, true}; Manager mgr{1}; Rooted sys{new SystemTypesystem(mgr)}; - // Get the domains. - Rooted bookDom = constructBookDomain(mgr, sys, logger); - Rooted emDom = constructEmphasisDomain(mgr, sys, logger); + // Get the ontologies. + Rooted bookDom = constructBookOntology(mgr, sys, logger); + Rooted emDom = constructEmphasisOntology(mgr, sys, logger); // Construct a document only containing overlapping annotations. // it has the form: blablubbla Rooted doc{new Document(mgr, "annotations.oxd")}; - doc->referenceDomains({bookDom, emDom}); + doc->referenceOntologys({bookDom, emDom}); Rooted book = buildRootStructuredEntity(doc, logger, {"book"}); ASSERT_TRUE(book != nullptr); @@ -114,11 +114,11 @@ TEST(DemoHTMLTransformer, PrimitiveSubtreeFields) TerminalLogger logger{std::cerr, true}; Manager mgr{1}; Rooted sys{new SystemTypesystem(mgr)}; - // Construct a simple domain. - Rooted domain{new Domain(mgr, sys, "myDomain")}; + // Construct a simple ontology. + Rooted ontology{new Ontology(mgr, sys, "myOntology")}; Rooted A{new StructuredClass( - mgr, "A", domain, Cardinality::any(), nullptr, false, true)}; + mgr, "A", ontology, Cardinality::any(), nullptr, false, true)}; Rooted A_a = A->createPrimitiveFieldDescriptor(sys->getStringType(), logger, FieldDescriptor::FieldType::SUBTREE, @@ -129,7 +129,7 @@ TEST(DemoHTMLTransformer, PrimitiveSubtreeFields) "b").first; Rooted A_main = A->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; - ASSERT_TRUE(domain->validate(logger)); + ASSERT_TRUE(ontology->validate(logger)); // Construct a document for it. Rooted doc{new Document(mgr, "myDoc")}; Rooted A_impl = doc->createRootStructuredEntity(A); @@ -144,7 +144,7 @@ TEST(DemoHTMLTransformer, PrimitiveSubtreeFields) transformer.writeXml(doc, out, logger, dummy, false); const std::string res = out.str(); ASSERT_TRUE( - res.find("test_atest_btest") != + res.find("test_atest_btest") != std::string::npos); } } diff --git a/testdata/osmlparser/empty_domain.osml b/testdata/osmlparser/empty_domain.osml deleted file mode 100644 index e8d6452..0000000 --- a/testdata/osmlparser/empty_domain.osml +++ /dev/null @@ -1,2 +0,0 @@ -\begin{domain#testDomain} -\end{domain} diff --git a/testdata/osmlparser/empty_ontology.osml b/testdata/osmlparser/empty_ontology.osml new file mode 100644 index 0000000..ffc9d80 --- /dev/null +++ b/testdata/osmlparser/empty_ontology.osml @@ -0,0 +1,2 @@ +\begin{ontology#testOntology} +\end{ontology} diff --git a/testdata/osmlparser/explicit_fields.osml b/testdata/osmlparser/explicit_fields.osml index a9ba1a3..ea13c02 100644 --- a/testdata/osmlparser/explicit_fields.osml +++ b/testdata/osmlparser/explicit_fields.osml @@ -1,6 +1,6 @@ \document -\domain#test +\ontology#test \struct#a[isRoot=true] \primitive#b[type=string,isSubtree=true] \primitive#c[type=string,isSubtree=true] diff --git a/testdata/osmlparser/include_recursive_root.osml b/testdata/osmlparser/include_recursive_root.osml index b3e433f..b29f1f4 100644 --- a/testdata/osmlparser/include_recursive_root.osml +++ b/testdata/osmlparser/include_recursive_root.osml @@ -1,6 +1,6 @@ \begin{document} -\domain#testDomain +\ontology#testOntology \struct#test[isRoot=true] \begin{test} diff --git a/testdata/osmlparser/include_root.osml b/testdata/osmlparser/include_root.osml index 1d1d660..3c65ca1 100644 --- a/testdata/osmlparser/include_root.osml +++ b/testdata/osmlparser/include_root.osml @@ -1,6 +1,6 @@ \begin{document} -\domain#testDomain +\ontology#testOntology \struct#test[isRoot=true] \field \childRef[ref=a] diff --git a/testdata/osmlparser/inline_domain.osml b/testdata/osmlparser/inline_domain.osml deleted file mode 100644 index 1d71df4..0000000 --- a/testdata/osmlparser/inline_domain.osml +++ /dev/null @@ -1,14 +0,0 @@ -\begin{document} - -\domain#testDomain - \struct#test[isRoot=true] - \field - \childRef[ref=a] - \struct#a - -\begin{test} -\a -\end{test} - -\end{document} - diff --git a/testdata/osmlparser/inline_ontology.osml b/testdata/osmlparser/inline_ontology.osml new file mode 100644 index 0000000..1bba1f6 --- /dev/null +++ b/testdata/osmlparser/inline_ontology.osml @@ -0,0 +1,14 @@ +\begin{document} + +\ontology#testOntology + \struct#test[isRoot=true] + \field + \childRef[ref=a] + \struct#a + +\begin{test} +\a +\end{test} + +\end{document} + diff --git a/testdata/osmlparser/invalid_explicit_fields.osml b/testdata/osmlparser/invalid_explicit_fields.osml index 9986204..eeecc0a 100644 --- a/testdata/osmlparser/invalid_explicit_fields.osml +++ b/testdata/osmlparser/invalid_explicit_fields.osml @@ -1,6 +1,6 @@ \document -\domain#test +\ontology#test \struct#a[isRoot=true] \primitive#b[type=string,isSubtree=true] \primitive#c[type=string,isSubtree=true] diff --git a/testdata/osmlparser/rollback_on_invalid_element.osml b/testdata/osmlparser/rollback_on_invalid_element.osml index 671c76d..282e741 100644 --- a/testdata/osmlparser/rollback_on_invalid_element.osml +++ b/testdata/osmlparser/rollback_on_invalid_element.osml @@ -1,6 +1,6 @@ \begin{document} -\domain#testDomain +\ontology#testOntology \struct#test[isRoot=true] \field \childRef[ref=a] diff --git a/testdata/osmlparser/struct_with_no_field.osml b/testdata/osmlparser/struct_with_no_field.osml index 8cf2d02..4f27998 100644 --- a/testdata/osmlparser/struct_with_no_field.osml +++ b/testdata/osmlparser/struct_with_no_field.osml @@ -1,6 +1,6 @@ \document -\domain#test +\ontology#test \struct#a[isRoot=true] \field \childRef[ref=b] diff --git a/testdata/osmlparser/structure_inheritance.osml b/testdata/osmlparser/structure_inheritance.osml index 6b88f2f..008778a 100644 --- a/testdata/osmlparser/structure_inheritance.osml +++ b/testdata/osmlparser/structure_inheritance.osml @@ -1,4 +1,4 @@ -\domain#test +\ontology#test \struct#a \primitive[type=string] \struct#b[isa=a] diff --git a/testdata/osxmlparser/book_domain.osxml b/testdata/osxmlparser/book_domain.osxml deleted file mode 100644 index 5a8cc67..0000000 --- a/testdata/osxmlparser/book_domain.osxml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/testdata/osxmlparser/book_ontology.osxml b/testdata/osxmlparser/book_ontology.osxml new file mode 100644 index 0000000..0b393e6 --- /dev/null +++ b/testdata/osxmlparser/book_ontology.osxml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testdata/osxmlparser/comments_domain.osxml b/testdata/osxmlparser/comments_domain.osxml deleted file mode 100644 index dc7742e..0000000 --- a/testdata/osxmlparser/comments_domain.osxml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/testdata/osxmlparser/comments_ontology.osxml b/testdata/osxmlparser/comments_ontology.osxml new file mode 100644 index 0000000..f2423be --- /dev/null +++ b/testdata/osxmlparser/comments_ontology.osxml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testdata/osxmlparser/complex_book.osxml b/testdata/osxmlparser/complex_book.osxml index 8fff93d..222b146 100644 --- a/testdata/osxmlparser/complex_book.osxml +++ b/testdata/osxmlparser/complex_book.osxml @@ -1,12 +1,12 @@ - - - - - diff --git a/testdata/osxmlparser/headings_domain.osxml b/testdata/osxmlparser/headings_domain.osxml deleted file mode 100644 index 07d3db2..0000000 --- a/testdata/osxmlparser/headings_domain.osxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/testdata/osxmlparser/headings_ontology.osxml b/testdata/osxmlparser/headings_ontology.osxml new file mode 100644 index 0000000..413eaa9 --- /dev/null +++ b/testdata/osxmlparser/headings_ontology.osxml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testdata/osxmlparser/simple_book.osxml b/testdata/osxmlparser/simple_book.osxml index 310a413..ec1e45a 100644 --- a/testdata/osxmlparser/simple_book.osxml +++ b/testdata/osxmlparser/simple_book.osxml @@ -1,9 +1,9 @@ - + This might be some introductory text or a dedication. - Here we might have an introduction to the chapter. -- cgit v1.2.3