/* 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 "Domain.hpp" namespace ousia { /* Class FieldDescriptor */ FieldDescriptor::FieldDescriptor(Manager &mgr, Handle parent, Handle primitiveType, std::string name, bool optional) : Node(mgr, std::move(name), parent), children(this), fieldType(FieldType::PRIMITIVE), primitiveType(acquire(primitiveType)), optional(optional) { if (parent != nullptr) { parent->addFieldDescriptor(this); } } 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) { if (parent != nullptr) { parent->addFieldDescriptor(this); } } bool FieldDescriptor::doValidate(Logger &logger) const { bool valid = true; // check parent type if (getParent() == nullptr) { logger.error("This field has no parent!", *this); valid = false; } else if (!getParent()->isa(RttiTypes::Descriptor)) { logger.error("The parent of this field is not a descriptor!", *this); valid = false; } // check name if (!getName().empty()) { valid = valid & validateName(logger); } // check consistency of FieldType with the rest of the FieldDescriptor. if (fieldType == FieldType::PRIMITIVE) { if (children.size() > 0) { logger.error( "This field is supposed to be primitive but has " "registered child classes!", *this); valid = false; } if (primitiveType == nullptr) { logger.error( "This field is supposed to be primitive but has " "no primitive type!", *this); valid = false; } } else { if (primitiveType != nullptr) { logger.error( "This field is supposed to be non-primitive but has " "a primitive type!", *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 no duplicates. */ std::set names; for (Handle c : children) { 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; } bool FieldDescriptor::removeChild(Handle c) { auto it = children.find(c); if (it != children.end()) { invalidate(); children.erase(it); return true; } return false; } /* Class Descriptor */ void Descriptor::doResolve(ResolutionState &state) { if (attributesDescriptor != nullptr) { 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("This Descriptor has no parent!", *this); valid = false; } else if (!getParent()->isa(RttiTypes::Domain)) { logger.error("The parent of this Descriptor 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); } // check if all FieldDescriptors have this Descriptor as parent. for (Handle fd : fieldDescriptors) { if (fd->getParent() != this) { logger.error(std::string("Descriptor \"") + getName() + "\" has " "field \"" + fd->getName() + "\" as child but the field does not " "have the Descriptor as parent.", *this); valid = false; } } // check the FieldDescriptors themselves. return valid & continueValidationCheckDuplicates(fieldDescriptors, logger); } std::vector> Descriptor::pathTo( Handle target) const { std::vector> path; continuePath(target, path); return path; } bool Descriptor::continuePath(Handle target, std::vector> ¤tPath) const { // check if we are at the target already if (this == target) { return true; } // a variable to determine if we already found a solution bool found = false; // the currently optimal path. std::vector> optimum; // use recursive depth-first search from the top to reach the given child // get the list of effective FieldDescriptors. NodeVector fields; if (isa(RttiTypes::StructuredClass)) { const StructuredClass *tis = static_cast(this); fields = tis->getEffectiveFieldDescriptors(); } else { fields = getFieldDescriptors(); } for (auto &fd : fields) { for (auto &c : fd->getChildren()) { // check if a child is the target node. if (c == target) { // if we have made the connection, stop the search. currentPath.push_back(fd); return true; } // look for transparent intermediate nodes. if (c->isTransparent()) { // copy the path. std::vector> cPath = currentPath; cPath.push_back(fd); cPath.push_back(c); // recursion. if (c->continuePath(target, cPath) && (!found || optimum.size() > cPath.size())) { // look if this path is better than the current optimum. optimum = std::move(cPath); found = true; } } } } if (isa(RttiTypes::StructuredClass)) { const StructuredClass *tis = static_cast(this); // if this is a StructuredClass we also can call the subclasses. for (auto &c : tis->getSubclasses()) { // copy the path. std::vector> cPath = currentPath; if (c->continuePath(target, cPath) && (!found || optimum.size() > cPath.size())) { // look if this path is better than the current optimum. optimum = std::move(cPath); found = true; } } } // put the optimum in the given path reference. currentPath = std::move(optimum); // return if we found something. return found; } void Descriptor::addFieldDescriptor(Handle fd) { // only add it if we need to. if (fieldDescriptors.find(fd) == fieldDescriptors.end()) { invalidate(); fieldDescriptors.push_back(fd); } Handle par = fd->getParent(); if (par != this) { if (par != nullptr) { // remove the FieldDescriptor from the old parent. par.cast()->removeFieldDescriptor(fd); } fd->setParent(this); } } void Descriptor::copyFieldDescriptor(Handle fd) { if (fd->getFieldType() == FieldDescriptor::FieldType::PRIMITIVE) { /* * To call the "new" operation is enough here, because the * constructor will add the newly constructed FieldDescriptor to this * Descriptor automatically. */ new FieldDescriptor(getManager(), this, fd->getPrimitiveType(), fd->getName(), fd->isOptional()); } else { new FieldDescriptor(getManager(), this, fd->getFieldType(), fd->getName(), fd->isOptional()); } } 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; } Rooted Descriptor::createPrimitiveFieldDescriptor( Handle primitiveType, std::string name, bool optional) { return Rooted{new FieldDescriptor( getManager(), this, primitiveType, std::move(name), optional)}; } Rooted Descriptor::createFieldDescriptor( FieldDescriptor::FieldType fieldType, std::string name, bool optional) { return Rooted{new FieldDescriptor( getManager(), this, fieldType, std::move(name), optional)}; } /* Class StructuredClass */ StructuredClass::StructuredClass(Manager &mgr, std::string name, Handle domain, Variant cardinality, Handle attributesDescriptor, Handle superclass, bool transparent, bool root) : Descriptor(mgr, std::move(name), domain, attributesDescriptor), cardinality(std::move(cardinality)), superclass(acquire(superclass)), subclasses(this), transparent(transparent), root(root) { if (superclass != nullptr) { superclass->addSubclass(this); } 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) { if (superclass == sup) { return; } // remove this subclass from the old superclass. if (superclass != nullptr) { superclass->removeSubclass(this); } // set the new superclass superclass = acquire(sup); invalidate(); // add this class as new subclass of the new superclass. if (sup != nullptr) { sup->addSubclass(this); } } 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) { // check if we already have that class. if (subclasses.find(sc) == subclasses.end()) { invalidate(); subclasses.push_back(sc); } sc->setSuperclass(this); } void StructuredClass::removeSubclass(Handle sc) { // 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); } const void StructuredClass::gatherFieldDescriptors( NodeVector ¤t, std::set &overriddenFields) const { // append all FieldDescriptors that are not overridden. for (auto &f : Descriptor::getFieldDescriptors()) { if (overriddenFields.insert(f->getName()).second) { current.push_back(f); } } if (superclass != nullptr) { superclass->gatherFieldDescriptors(current, overriddenFields); } } NodeVector StructuredClass::getEffectiveFieldDescriptors() const { // in this case we return a NodeVector of Rooted entries without owner. NodeVector vec; std::set overriddenFields; gatherFieldDescriptors(vec, overriddenFields); return std::move(vec); } /* Class AnnotationClass */ AnnotationClass::AnnotationClass( Manager &mgr, std::string name, Handle domain, // TODO: What would be a wise default value for attributes? Handle attributesDescriptor) : Descriptor(mgr, std::move(name), domain, attributesDescriptor) { if (!domain.isNull()) { domain->addAnnotationClass(this); } } /* Class Domain */ void Domain::doResolve(ResolutionState &state) { if (!continueResolveComposita(structuredClasses, structuredClasses.getIndex(), state) | continueResolveComposita(annotationClasses, annotationClasses.getIndex(), state)) { continueResolveReferences(typesystems, 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::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 attributesDescriptor, Handle superclass, bool transparent, bool root) { return Rooted{new StructuredClass( getManager(), std::move(name), this, std::move(cardinality), attributesDescriptor, 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, Handle attributesDescriptor) { return Rooted{new AnnotationClass( getManager(), std::move(name), this, attributesDescriptor)}; } /* 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(&Node) .composedOf({&StructuredClass, &AnnotationClass}); } }