/* 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 "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; } struct CollectState { Node *n; size_t depth; CollectState(Node *n, size_t depth) : n(n), depth(depth) {} }; 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(CollectState(const_cast(start), 0)); // set of visited nodes. std::unordered_set visited; while (!q.empty()) { CollectState state = q.front(); q.pop(); // do not proceed if this node was already visited. if (!visited.insert(state.n).second) { continue; } if (state.n->isa(&RttiTypes::Descriptor)) { Rooted strct{static_cast(state.n)}; // look through all fields. NodeVector fields = strct->getFieldDescriptors(); for (auto fd : fields) { // note matches. if (match(fd, state.depth)) { res.push_back(fd); } // only continue in the TREE field. if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { q.push(CollectState(fd.get(), state.depth)); } } } else { // otherwise this is a FieldDescriptor. Rooted field{ static_cast(state.n)}; // and we proceed by visiting all permitted children. for (auto c : field->getChildrenWithSubclasses()) { // note matches. if (match(c, state.depth)) { res.push_back(c); } // We only continue our search via transparent children. if (c->isTransparent()) { q.push(CollectState(c.get(), state.depth + 1)); } } } } return res; } static std::vector collectPermittedTokens( const Node *start, Handle ontology) { // gather SyntaxDescriptors for structure children first. std::vector res; collect(start, [&res](Handle n, size_t depth) { SyntaxDescriptor stx; if (n->isa(&RttiTypes::FieldDescriptor)) { stx = n.cast()->getSyntaxDescriptor(depth); } else { stx = n.cast()->getSyntaxDescriptor(depth); } // do not add trivial SyntaxDescriptors. if (!stx.isEmpty()) { res.push_back(stx); } return false; }); // gather SyntaxDescriptors for AnnotationClasses. for (auto a : ontology->getAnnotationClasses()) { SyntaxDescriptor stx = a->getSyntaxDescriptor(); if (!stx.isEmpty()) { res.push_back(stx); } } return res; } /* Class FieldDescriptor */ FieldDescriptor::FieldDescriptor(Manager &mgr, Handle primitiveType, Handle parent, FieldType fieldType, std::string name, bool optional, WhitespaceMode whitespaceMode) : Node(mgr, std::move(name), parent), children(this), fieldType(fieldType), primitiveType(acquire(primitiveType)), optional(optional), primitive(true), whitespaceMode(whitespaceMode) { } FieldDescriptor::FieldDescriptor(Manager &mgr, Handle parent, FieldType fieldType, std::string name, bool optional, WhitespaceMode whitespaceMode) : Node(mgr, std::move(name), parent), children(this), fieldType(fieldType), optional(optional), primitive(false), whitespaceMode(whitespaceMode) { } bool FieldDescriptor::doValidate(Logger &logger) const { bool valid = true; // check parent type if (getParent() == nullptr) { logger.error(std::string("Field \"") + getNameOrDefaultName() + "\" has no parent!", *this); return false; } else if (!getParent()->isa(&RttiTypes::Descriptor)) { logger.error(std::string("The parent of Field \"") + getNameOrDefaultName() + "\" is not a descriptor!", *this); return false; } const std::string &parentName = getParent().cast()->getName(); // check name if (getName().empty()) { if (fieldType != FieldType::TREE) { logger.error(std::string("Field \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" is not the main field but has an empty name!", *this); valid = false; } } else { valid = valid & validateName(logger); } // check open and close token. if (!openToken.isValid()) { // TODO: Correct error message. logger.error(std::string("Field \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" has an invalid custom open token: " + openToken.token, *this); valid = false; } if (!closeToken.isValid()) { // TODO: Correct error message. logger.error(std::string("Field \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" has an invalid custom close token: " + closeToken.token, *this); valid = false; } // check consistency of FieldType with the rest of the FieldDescriptor. if (primitive) { if (children.size() > 0) { logger.error(std::string("Field \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" is supposed to be primitive but has " "registered child classes!", *this); valid = false; } if (primitiveType == nullptr) { logger.error(std::string("Field \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" is supposed to be primitive but has " "no primitive type!", *this); valid = false; } } else { if (primitiveType != nullptr) { logger.error(std::string("Field \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" 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 \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" 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 \"") + getNameOrDefaultName() + "\" of descriptor \"" + parentName + "\" 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, size_t depth) { 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; } std::vector FieldDescriptor::getPermittedTokens() const { if (getParent() == nullptr || getParent().cast()->getParent() == nullptr) { return std::vector(); } return collectPermittedTokens( this, getParent().cast()->getParent().cast()); } /* 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 start and end token. if (!openToken.isValid()) { logger.error(std::string("Descriptor \"") + getName() + "\" has an invalid custom start token: " + openToken.token, *this); valid = false; } if (!closeToken.isValid()) { logger.error(std::string("Descriptor \"") + getName() + "\" has an invalid custom end token: " + closeToken.token, *this); valid = false; } // 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, size_t depth) { 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, size_t depth) { 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); } std::vector Descriptor::getPermittedTokens() const { if (getParent() == nullptr) { return std::vector(); } return collectPermittedTokens(this, getParent().cast()); } /* 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 short token. if (!shortToken.isValid()) { logger.error(std::string("Descriptor \"") + getName() + "\" has an invalid custom short form token: " + shortToken.token, *this); valid = false; } // check the validity of this superclass. if (superclass != nullptr) { valid = valid & superclass->validate(logger); } // Make sure root classes are not transparent if (root && transparent) { logger.error( std::string("Descriptor \"") + getName() + std::string("\" cannot be transparent and root at the same time.")); valid = false; } // 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)}; } static void gatherTokenDescriptors( Handle desc, std::vector &res, std::unordered_set &visited) { // add the TokenDescriptors for the Descriptor itself. if (!desc->getOpenToken().isEmpty()) { res.push_back(desc->getOpenTokenPointer()); } if (!desc->getCloseToken().isEmpty()) { res.push_back(desc->getCloseTokenPointer()); } // add the TokenDescriptors for its FieldDescriptors. for (auto fd : desc->getFieldDescriptors()) { if (!visited.insert(fd.get()).second) { continue; } if (!fd->getOpenToken().isEmpty()) { res.push_back(fd->getOpenTokenPointer()); } if (!fd->getCloseToken().isEmpty()) { res.push_back(fd->getCloseTokenPointer()); } } } std::vector Ontology::getAllTokenDescriptors() const { std::vector res; // note all fields that are already visited because FieldReferences might // lead to doubled fields. std::unordered_set visited; // add the TokenDescriptors for the StructuredClasses (and their fields). for (auto s : structuredClasses) { if (!s->getShortToken().isEmpty()) { res.push_back(s->getShortTokenPointer()); } gatherTokenDescriptors(s, res, visited); } // add the TokenDescriptors for the AnnotationClasses (and their fields). for (auto a : annotationClasses) { gatherTokenDescriptors(a, res, visited); } return res; } /* 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}); } }