/*
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});
}
}