diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/model/Document.cpp | 62 | ||||
-rw-r--r-- | src/core/model/Domain.cpp | 481 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 68 | ||||
-rw-r--r-- | src/core/model/Typesystem.cpp | 21 | ||||
-rw-r--r-- | src/core/model/Typesystem.hpp | 69 | ||||
-rw-r--r-- | src/core/parser/stack/DocumentHandler.cpp | 213 |
6 files changed, 559 insertions, 355 deletions
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index a2ba5cf..4e101fc 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -135,6 +135,15 @@ bool DocumentEntity::doValidate(Logger &logger) const continue; } + std::unordered_set<StructuredClass *> childClasses; + { + NodeVector<StructuredClass> tmp = + fieldDescs[f]->getChildrenWithSubclasses(); + for (auto s : tmp) { + childClasses.insert(s.get()); + } + } + // we can do a faster check if this field is empty. if (fields[f].size() == 0) { // if this field is optional, an empty field is valid anyways. @@ -145,7 +154,7 @@ bool DocumentEntity::doValidate(Logger &logger) const * if it is not optional we have to check if zero is a valid * cardinality. */ - for (auto childClass : fieldDescs[f]->getChildren()) { + for (auto childClass : childClasses) { const size_t min = childClass->getCardinality().asCardinality().min(); if (min > 0) { @@ -163,15 +172,10 @@ bool DocumentEntity::doValidate(Logger &logger) const continue; } - // create a set of allowed classes identified by their unique id. - std::set<ManagedUid> childClasses; - for (auto &childClass : fieldDescs[f]->getChildren()) { - childClasses.insert(childClass->getUid()); - } // store the actual numbers of children for each child class in a map - std::map<ManagedUid, unsigned int> nums; + std::unordered_map<StructuredClass *, unsigned int> nums; - // iterate over every actual child of this DocumentEntity + // iterate over every actual child of this field for (auto child : fields[f]) { // check if the parent reference is correct. if (child->getParent() != subInst) { @@ -195,25 +199,11 @@ bool DocumentEntity::doValidate(Logger &logger) const } // otherwise this is a StructuredEntity Handle<StructuredEntity> c = child.cast<StructuredEntity>(); + StructuredClass *classPtr = + c->getDescriptor().cast<StructuredClass>().get(); - ManagedUid id = c->getDescriptor()->getUid(); // check if its class is allowed. - bool allowed = childClasses.find(id) != childClasses.end(); - /* - * if it is not allowed directly, we have to check if the class is a - * child of a permitted class. - */ - if (!allowed) { - for (auto childClass : fieldDescs[f]->getChildren()) { - if (c->getDescriptor() - .cast<StructuredClass>() - ->isSubclassOf(childClass)) { - allowed = true; - id = childClass->getUid(); - } - } - } - if (!allowed) { + if (childClasses.find(classPtr) == childClasses.end()) { logger.error( std::string("An instance of \"") + c->getDescriptor()->getName() + @@ -224,18 +214,24 @@ bool DocumentEntity::doValidate(Logger &logger) const valid = false; continue; } - // note the number of occurences. - const auto &n = nums.find(id); - if (n != nums.end()) { - n->second++; - } else { - nums.emplace(id, 1); + // note the number of occurences for this class and all + // superclasses, because a subclass instance should count for + // superclasses as well. + while (classPtr != nullptr && + childClasses.find(classPtr) != childClasses.end()) { + const auto &n = nums.find(classPtr); + if (n != nums.end()) { + n->second++; + } else { + nums.emplace(classPtr, 1); + } + classPtr = classPtr->getSuperclass().get(); } } // now check if the cardinalities are right. - for (auto childClass : fieldDescs[f]->getChildren()) { - const auto &n = nums.find(childClass->getUid()); + for (auto childClass : childClasses) { + const auto &n = nums.find(childClass); unsigned int num = 0; if (n != nums.end()) { num = n->second; diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index 8288099..ae20068 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -27,6 +27,201 @@ namespace ousia { +/* Helper Functions */ + +struct PathState { + std::shared_ptr<PathState> pred; + Node *node; + size_t length; + + PathState(std::shared_ptr<PathState> pred, Node *node) + : pred(pred), node(node) + { + if (pred == nullptr) { + length = 1; + } else { + length = pred->length + 1; + } + } +}; + +static void constructPath(std::shared_ptr<PathState> state, + NodeVector<Node> &vec) +{ + if (state->pred != nullptr) { + constructPath(state->pred, vec); + } + vec.push_back(state->node); +} + +static NodeVector<Node> pathTo(const Node *start, Logger &logger, + Handle<Node> target, bool &success) +{ + success = false; + // shortest path. + NodeVector<Node> shortest; + // state queue for breadth-first search. + std::queue<std::shared_ptr<PathState>> states; + if (start->isa(&RttiTypes::Descriptor)) { + const Descriptor *desc = static_cast<const Descriptor *>(start); + // initially put every field descriptor on the queue. + NodeVector<FieldDescriptor> 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<PathState>(nullptr, fd.get())); + } + } + } else { + const FieldDescriptor *field = + static_cast<const FieldDescriptor *>(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<PathState>(nullptr, c.get())); + } + } + } + // set of visited nodes. + std::unordered_set<const Node *> visited; + while (!states.empty()) { + std::shared_ptr<PathState> 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<const StructuredClass *>(current->node); + + // look through all fields. + NodeVector<FieldDescriptor> 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<PathState>(current, fd.get())); + } + } + } else { + // otherwise this is a FieldDescriptor. + const FieldDescriptor *field = + static_cast<const FieldDescriptor *>(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<PathState>(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<Node> 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<Node> 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 <typename F> +static NodeVector<Node> collect(const Node *start, F match) +{ + // result + NodeVector<Node> res; + // queue for breadth-first search of graph. + std::queue<Rooted<Node>> q; + // put the initial node on the stack. + q.push(const_cast<Node *>(start)); + // set of visited nodes. + std::unordered_set<const Node *> visited; + while (!q.empty()) { + Rooted<Node> 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<StructuredClass> strct = n.cast<StructuredClass>(); + + // look through all fields. + NodeVector<FieldDescriptor> 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<FieldDescriptor> field = n.cast<FieldDescriptor>(); + // 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<Type> primitiveType, @@ -116,7 +311,7 @@ bool FieldDescriptor::doValidate(Logger &logger) const * there are duplicates. */ std::set<std::string> names; - for (Handle<StructuredClass> c : children) { + for (Handle<StructuredClass> c : getChildrenWithSubclasses()) { if (!names.insert(c->getName()).second) { logger.error(std::string("Field \"") + getName() + "\" had multiple children with the name \"" + @@ -129,6 +324,25 @@ bool FieldDescriptor::doValidate(Logger &logger) const return valid; } +static void gatherSubclasses(NodeVector<StructuredClass> &res, + Handle<StructuredClass> strct) +{ + for (auto sub : strct->getSubclasses()) { + res.push_back(sub); + gatherSubclasses(res, sub); + } +} + +NodeVector<StructuredClass> FieldDescriptor::getChildrenWithSubclasses() const +{ + NodeVector<StructuredClass> res; + for (auto c : children) { + res.push_back(c); + gatherSubclasses(res, c); + } + return res; +} + bool FieldDescriptor::removeChild(Handle<StructuredClass> c) { auto it = children.find(c); @@ -140,6 +354,38 @@ bool FieldDescriptor::removeChild(Handle<StructuredClass> c) return false; } +std::pair<NodeVector<Node>, bool> FieldDescriptor::pathTo( + Handle<StructuredClass> childDescriptor, Logger &logger) const +{ + bool success = false; + NodeVector<Node> path = + ousia::pathTo(this, logger, childDescriptor, success); + return std::make_pair(path, success); +} +NodeVector<Node> FieldDescriptor::pathTo(Handle<FieldDescriptor> field, + Logger &logger) const +{ + bool success = false; + return ousia::pathTo(this, logger, field, success); +} +NodeVector<FieldDescriptor> FieldDescriptor::getDefaultFields() const +{ + // TODO: In principle a cast would be nicer here, but for now we copy. + NodeVector<Node> nodes = collect(this, [](Handle<Node> n) { + if (!n->isa(&RttiTypes::FieldDescriptor)) { + return false; + } + Handle<FieldDescriptor> f = n.cast<FieldDescriptor>(); + return f->getFieldType() == FieldDescriptor::FieldType::TREE && + f->isPrimitive(); + }); + NodeVector<FieldDescriptor> res; + for (auto n : nodes) { + res.push_back(n.cast<FieldDescriptor>()); + } + return res; +} + /* Class Descriptor */ void Descriptor::doResolve(ResolutionState &state) @@ -209,152 +455,6 @@ bool Descriptor::doValidate(Logger &logger) const return valid & continueValidationCheckDuplicates(fds, logger); } -struct PathState { - std::shared_ptr<PathState> pred; - Node *node; - size_t length; - - PathState(std::shared_ptr<PathState> pred, Node *node) - : pred(pred), node(node) - { - if (pred == nullptr) { - length = 1; - } else { - length = pred->length + 1; - } - } -}; - -static void constructPath(std::shared_ptr<PathState> state, - NodeVector<Node> &vec) -{ - if (state->pred != nullptr) { - constructPath(state->pred, vec); - } - vec.push_back(state->node); -} - -static NodeVector<Node> pathTo(const Descriptor *start, Logger &logger, - Handle<Node> target, bool &success) -{ - success = false; - // shortest path. - NodeVector<Node> shortest; - // state queue for breadth-first search. - std::queue<std::shared_ptr<PathState>> states; - { - // initially put every field descriptor on the queue. - NodeVector<FieldDescriptor> fields = start->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<PathState>(nullptr, fd.get())); - } - } - } - // set of visited nodes. - std::unordered_set<const Node *> visited; - while (!states.empty()) { - std::shared_ptr<PathState> 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<const StructuredClass *>(current->node); - - // look through all fields. - NodeVector<FieldDescriptor> 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<PathState>(current, fd.get())); - } - } - - /* - * Furthermore we have to consider that all subclasses of this - * StructuredClass are allowed in place of this StructuredClass as - * well, so we continue the search for them as well. - */ - - NodeVector<StructuredClass> subs = strct->getSubclasses(); - for (auto sub : subs) { - // if we found our target, break off the search in this branch. - if (sub == target) { - fin = true; - current = current->pred; - continue; - } - // We only continue our path via transparent classes. - if (sub->isTransparent()) { - states.push( - std::make_shared<PathState>(current->pred, sub.get())); - } - } - } else { - // otherwise this is a FieldDescriptor. - const FieldDescriptor *field = - static_cast<const FieldDescriptor *>(current->node); - // and we proceed by visiting all permitted children. - for (auto c : field->getChildren()) { - // 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<PathState>(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<Node> 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<Node> 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; -} - NodeVector<Node> Descriptor::pathTo(Handle<StructuredClass> target, Logger &logger) const { @@ -370,89 +470,6 @@ std::pair<NodeVector<Node>, bool> Descriptor::pathTo( return std::make_pair(path, success); } -template <typename F> -static NodeVector<Node> collect(const Descriptor *start, F match) -{ - // result - NodeVector<Node> res; - // queue for breadth-first search of graph. - std::queue<Rooted<Node>> q; - { - // initially put every field descriptor on the queue. - NodeVector<FieldDescriptor> fields = start->getFieldDescriptors(); - - for (auto fd : fields) { - // note matches. - if (match(fd)) { - res.push_back(fd); - } - if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { - q.push(fd); - } - } - } - // set of visited nodes. - std::unordered_set<const Node *> visited; - while (!q.empty()) { - Rooted<Node> 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<StructuredClass> strct = n.cast<StructuredClass>(); - - // look through all fields. - NodeVector<FieldDescriptor> 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); - } - } - - /* - * Furthermore we have to consider that all subclasses of this - * StructuredClass are allowed in place of this StructuredClass as - * well, so we continue the search for them as well. - */ - - NodeVector<StructuredClass> subs = strct->getSubclasses(); - for (auto sub : subs) { - // note matches. - if (match(sub)) { - res.push_back(sub); - } - // We only continue our search via transparent classes. - if (sub->isTransparent()) { - q.push(sub); - } - } - } else { - // otherwise this is a FieldDescriptor. - Rooted<FieldDescriptor> field = n.cast<FieldDescriptor>(); - // and we proceed by visiting all permitted children. - for (auto c : field->getChildren()) { - // 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; -} - NodeVector<FieldDescriptor> Descriptor::getDefaultFields() const { // TODO: In principle a cast would be nicer here, but for now we copy. @@ -605,7 +622,7 @@ void Descriptor::copyFieldDescriptor(Handle<FieldDescriptor> fd, Logger &logger) copy = Rooted<FieldDescriptor>{ new FieldDescriptor(getManager(), this, fd->getFieldType(), fd->getName(), fd->isOptional())}; - for (auto &c : fd->getChildren()) { + for (auto c : fd->getChildren()) { copy->addChild(c); } } @@ -940,4 +957,4 @@ const Rtti Domain = RttiBuilder<ousia::Domain>("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 index d921a9c..081435a 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -275,6 +275,17 @@ public: const NodeVector<StructuredClass> &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<StructuredClass> getChildrenWithSubclasses() const; + + /** * Adds a StructuredClass whose instances shall be allowed as children in * the StructureTree of instances of this field. */ @@ -368,6 +379,60 @@ public: 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<NodeVector<Node>, bool> pathTo( + Handle<StructuredClass> 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<Node> pathTo(Handle<FieldDescriptor> 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<FieldDescriptor> getDefaultFields() const; }; /** @@ -1111,5 +1176,4 @@ extern const Rtti Domain; } } -#endif /* _OUSIA_MODEL_DOMAIN_HPP_ */ - +#endif /* _OUSIA_MODEL_DOMAIN_HPP_ */
\ No newline at end of file diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp index fb99f87..df2b9fb 100644 --- a/src/core/model/Typesystem.cpp +++ b/src/core/model/Typesystem.cpp @@ -109,6 +109,14 @@ bool StringType::doBuild(Variant &data, Logger &logger, return VariantConverter::toString(data, logger); } +/* Class CardinalityType */ + +bool CardinalityType::doBuild(Variant &data, Logger &logger, + const MagicCallback &magicCallback) const +{ + return VariantConverter::toCardinality(data, logger); +} + /* Class EnumType */ EnumType::EnumType(Manager &mgr, std::string name, Handle<Typesystem> system) @@ -769,12 +777,14 @@ SystemTypesystem::SystemTypesystem(Manager &mgr) stringType(new StringType(mgr, this)), intType(new IntType(mgr, this)), doubleType(new DoubleType(mgr, this)), - boolType(new BoolType(mgr, this)) + boolType(new BoolType(mgr, this)), + cardinalityType(new CardinalityType(mgr, this)) { addType(stringType); addType(intType); addType(doubleType); addType(boolType); + addType(cardinalityType); } /* RTTI type registrations */ @@ -787,6 +797,8 @@ const Rtti IntType = RttiBuilder<ousia::IntType>("IntType").parent(&Type); const Rtti DoubleType = RttiBuilder<ousia::DoubleType>("DoubleType").parent(&Type); const Rtti BoolType = RttiBuilder<ousia::BoolType>("BoolType").parent(&Type); +const Rtti CardinalityType = + RttiBuilder<ousia::CardinalityType>("CardinalityType").parent(&Type); const Rtti EnumType = RttiBuilder<ousia::EnumType>("EnumType").parent(&Type); const Rtti StructType = RttiBuilder<ousia::StructType>("StructType") .parent(&Type) @@ -798,10 +810,9 @@ const Rtti Constant = RttiBuilder<ousia::Constant>("Constant").parent(&Node); const Rtti Attribute = RttiBuilder<ousia::Attribute>("Attribute").parent(&Node); const Rtti Typesystem = RttiBuilder<ousia::Typesystem>("Typesystem").parent(&RootNode).composedOf( - {&StringType, &IntType, &DoubleType, &BoolType, &EnumType, &StructType, - &Constant}); + {&StringType, &IntType, &DoubleType, &BoolType, &CardinalityType, + &EnumType, &StructType, &Constant}); const Rtti SystemTypesystem = RttiBuilder<ousia::SystemTypesystem>( "SystemTypesystem").parent(&Typesystem); } -} - +}
\ No newline at end of file diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp index 53fb0df..39f777f 100644 --- a/src/core/model/Typesystem.hpp +++ b/src/core/model/Typesystem.hpp @@ -386,6 +386,55 @@ public: }; /** + * The CardinalityType class represents the cardinality type. There should be + * exactly one instance of this class available in a preloaded type system. + */ +class CardinalityType : public Type { +protected: + /** + * Expects the given variant to be a cardinality or a single int. + * + * @param data is a variant containing the data that should be checked. + * @param logger is the Logger instance into which errors should be written. + * @return true if the conversion was successful, false otherwise. + */ + bool doBuild(Variant &data, Logger &logger, + const MagicCallback &magicCallback) const override; + +public: + /** + * Constructor of the CardinalityType class. Only one instance of + *CardinalityType should + * exist per project graph. + * + * @param mgr is the Manager instance to be used for the Node. + * @param name is the name of the type. + * @param system is a reference to the parent Typesystem instance. + */ + CardinalityType(Manager &mgr, Handle<Typesystem> system) + : Type(mgr, "cardinality", system, true) + { + } + + /** + * Creates a variant with the cardinality value "any". + * + * @return a Variant with the cardinality value "any". + */ + Variant create() const override { return Variant{Cardinality::any()}; } + + /** + * Returns the cardinality VariantType. + * + * @return the cardinality VariantType. + */ + std::vector<VariantType> getVariantTypes() const override + { + return {VariantType::CARDINALITY}; + } +}; + +/** * The EnumType class represents a user defined enumeration type. */ class EnumType : public Type { @@ -1298,6 +1347,11 @@ private: */ Handle<BoolType> boolType; + /** + * Reference to the cardinality type. + */ + Handle<CardinalityType> cardinalityType; + public: /** * Creates the SystemTypesystem containing all basic types (string, int, @@ -1335,6 +1389,13 @@ public: * @return a reference to the primitive BoolType instance. */ Rooted<BoolType> getBoolType() { return boolType; } + + /** + * Returns the cardinality type. + * + * @return a reference to the CardinalityType instance. + */ + Rooted<CardinalityType> getCardinalityType() { return cardinalityType; } }; /* RTTI type registrations */ @@ -1366,6 +1427,11 @@ extern const Rtti DoubleType; extern const Rtti BoolType; /** + * Type information for the CardinalityType class. + */ +extern const Rtti CardinalityType; + +/** * Type information for the EnumType class. */ extern const Rtti EnumType; @@ -1407,5 +1473,4 @@ extern const Rtti SystemTypesystem; } } -#endif /* _OUSIA_MODEL_TYPESYSTEM_HPP_ */ - +#endif /* _OUSIA_MODEL_TYPESYSTEM_HPP_ */
\ No newline at end of file diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index 9fedabb..d514701 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -78,13 +78,13 @@ void DocumentChildHandler::preamble(Handle<Node> parentNode, } } -void DocumentChildHandler::createPath(const NodeVector<Node> &path, - DocumentEntity *&parent) +static void createPath(const NodeVector<Node> &path, DocumentEntity *&parent, + size_t p0 = 1) { // TODO (@benjamin): These should be pushed onto the scope and poped once // the scope is left. Otherwise stuff may not be correclty resolved. size_t S = path.size(); - for (size_t p = 1; p < S; p = p + 2) { + for (size_t p = p0; p < S; p = p + 2) { parent = static_cast<DocumentEntity *>( parent->createChildStructuredEntity( path[p].cast<StructuredClass>(), Variant::mapType{}, @@ -92,6 +92,18 @@ void DocumentChildHandler::createPath(const NodeVector<Node> &path, } } +static void createPath(const std::string &firstFieldName, + const NodeVector<Node> &path, DocumentEntity *&parent) +{ + // Add the first element + parent = static_cast<DocumentEntity *>( + parent->createChildStructuredEntity(path[0].cast<StructuredClass>(), + Variant::mapType{}, firstFieldName, + "").get()); + + createPath(path, parent, 2); +} + bool DocumentChildHandler::start(Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -111,7 +123,7 @@ bool DocumentChildHandler::start(Variant::mapType &args) if (!inField && parent != nullptr && parent->getDescriptor()->hasField(name())) { Rooted<DocumentField> field{ - new DocumentField(parentNode->getManager(), fieldName, parentNode)}; + new DocumentField(parentNode->getManager(), name(), parentNode)}; field->setLocation(location()); scope().push(field); return true; @@ -141,19 +153,42 @@ bool DocumentChildHandler::start(Variant::mapType &args) strct, args, name); } else { // calculate a path if transparent entities are needed in between. - auto path = parent->getDescriptor()->pathTo(strct, logger()); - if (path.empty()) { - throw LoggableException( - std::string("An instance of \"") + strct->getName() + - "\" is not allowed as child of an instance of \"" + - parent->getDescriptor()->getName() + "\"", - location()); - } + std::string lastFieldName = fieldName; + if (inField) { + Rooted<FieldDescriptor> field = + parent->getDescriptor()->getFieldDescriptor(fieldName); + auto pathRes = + field.cast<FieldDescriptor>()->pathTo(strct, logger()); + if (!pathRes.second) { + throw LoggableException( + std::string("An instance of \"") + strct->getName() + + "\" is not allowed as child of field \"" + fieldName + + "\"", + location()); + } + if (!pathRes.first.empty()) { + createPath(fieldName, pathRes.first, parent); + lastFieldName = DEFAULT_FIELD_NAME; + } + } else { + auto path = parent->getDescriptor()->pathTo(strct, logger()); + if (path.empty()) { + throw LoggableException( + std::string("An instance of \"") + strct->getName() + + "\" is not allowed as child of an instance of \"" + + parent->getDescriptor()->getName() + "\"", + location()); + } - // create all transparent entities until the last field. - createPath(path, parent); - entity = - parent->createChildStructuredEntity(strct, args, fieldName, name); + // create all transparent entities until the last field. + createPath(path, parent); + if (path.size() > 1) { + lastFieldName = DEFAULT_FIELD_NAME; + } + } + // create the entity for the new element at last. + entity = parent->createChildStructuredEntity(strct, args, lastFieldName, + name); } entity->setLocation(location()); scope().push(entity); @@ -196,15 +231,17 @@ bool DocumentChildHandler::data(Variant &data) &RttiTypes::DocumentField}); std::string fieldName; - DocumentEntity *parent; + DocumentEntity *strctParent; bool inField; - preamble(parentNode, fieldName, parent, inField); + preamble(parentNode, fieldName, strctParent, inField); - Rooted<Descriptor> desc = parent->getDescriptor(); + Rooted<Descriptor> desc = strctParent->getDescriptor(); + // The parent from which we need to connect to the primitive content. + Rooted<Node> parentClass; // We distinguish two cases here: One for fields that are given. - if (fieldName != DEFAULT_FIELD_NAME) { + if (inField) { // Retrieve the actual FieldDescriptor Rooted<FieldDescriptor> field = desc->getFieldDescriptor(fieldName); if (field == nullptr) { @@ -215,81 +252,95 @@ bool DocumentChildHandler::data(Variant &data) location()); return false; } - // If it is not primitive at all, we can't parse the content. - if (!field->isPrimitive()) { - logger().error(std::string("Can't handle data because field \"") + - fieldName + "\" of descriptor \"" + - desc->getName() + "\" is not primitive!", - location()); - return false; - } + // If it is a primitive field directly, try to parse the content. + if (field->isPrimitive()) { + // Add it as primitive content. + if (!convertData(field, data, logger())) { + return false; + } - // Try to convert the data variable to the correct format, abort if this - // does not work - if (!convertData(field, data, logger())) { - return false; + strctParent->createChildDocumentPrimitive(data, fieldName); + return true; } + // If it is not primitive we need to connect via transparent elements + // and default fields. + parentClass = field; + } else { + // In case of default fields we need to construct via default fields + // and maybe transparent elements. + parentClass = desc; + } - // Add it as primitive content - parent->createChildDocumentPrimitive(data, fieldName); - return true; + // Search through all permitted default fields of the parent class that + // allow primitive content at this point and could be constructed via + // transparent intermediate entities. + + // Retrieve all default fields at this point, either from the field + // descriptor or the structured class + NodeVector<FieldDescriptor> defaultFields; + if (inField) { + defaultFields = parentClass.cast<FieldDescriptor>()->getDefaultFields(); } else { - // The second case is for primitive fields. Here we search through - // all FieldDescriptors that allow primitive content at this point - // and could be constructed via transparent intermediate entities. - // We then try to parse the data using the type specified by the - // respective field. If that does not work we proceed to the next - // possible field. - NodeVector<FieldDescriptor> fields = desc->getDefaultFields(); - std::vector<LoggerFork> forks; - for (auto field : fields) { - // Then try to parse the content using the type specification - forks.emplace_back(logger().fork()); - - // Try to convert the data variable to the correct format, abort if - // this does not work - if (!convertData(field, data, forks.back())) { - return false; - } + defaultFields = parentClass.cast<StructuredClass>()->getDefaultFields(); + } + + // Try to parse the data using the type specified by the respective field. + // If that does not work we proceed to the next possible field. + std::vector<LoggerFork> forks; + for (auto field : defaultFields) { + // Then try to parse the content using the type specification. + forks.emplace_back(logger().fork()); + if (!convertData(field, data, forks.back())) { + continue; + } - // Show possible warnings that were emitted by this type conversion - forks.back().commit(); + // The conversion worked, commit any possible warnings + forks.back().commit(); - // If that worked, construct the necessary path + // Construct the necessary path + if (inField) { + NodeVector<Node> path = + parentClass.cast<FieldDescriptor>()->pathTo(field, logger()); + createPath(fieldName, path, strctParent); + } else { auto pathRes = desc->pathTo(field, logger()); assert(pathRes.second); - NodeVector<Node> path = pathRes.first; - createPath(path, parent); - - // Then create the primitive element - parent->createChildDocumentPrimitive(data, fieldName); - return true; - } - logger().error("Could not read data with any of the possible fields:"); - for (size_t f = 0; f < fields.size(); f++) { - logger().note(Utils::join(fields[f]->path(), ".") + ":", - SourceLocation{}, MessageMode::NO_CONTEXT); - forks[f].commit(); + createPath(pathRes.first, strctParent); } - return false; + + // Then create the primitive element + strctParent->createChildDocumentPrimitive(data); + return true; } - return true; + + // No field was found that might take the data -- dump the error messages + // from the loggers + logger().error("Could not read data with any of the possible fields:", + SourceLocation{}, MessageMode::NO_CONTEXT); + size_t f = 0; + for (auto field : defaultFields) { + logger().note(std::string("Field ") + Utils::join(field->path(), ".") + + std::string(":"), + SourceLocation{}, MessageMode::NO_CONTEXT); + forks[f].commit(); + f++; + } + return false; } namespace States { const State Document = StateBuilder() - .parent(&None) - .createdNodeType(&RttiTypes::Document) - .elementHandler(DocumentHandler::create) - .arguments({Argument::String("name", "")}); - -const State DocumentChild = - StateBuilder() - .parents({&Document, &DocumentChild}) - .createdNodeTypes({&RttiTypes::StructureNode, - &RttiTypes::AnnotationEntity, - &RttiTypes::DocumentField}) - .elementHandler(DocumentChildHandler::create); + .parent(&None) + .createdNodeType(&RttiTypes::Document) + .elementHandler(DocumentHandler::create) + .arguments({Argument::String("name", "")}); + +const State DocumentChild = StateBuilder() + .parents({&Document, &DocumentChild}) + .createdNodeTypes({&RttiTypes::StructureNode, + &RttiTypes::AnnotationEntity, + &RttiTypes::DocumentField}) + .elementHandler(DocumentChildHandler::create); } } |