summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/model/Document.cpp62
-rw-r--r--src/core/model/Domain.cpp481
-rw-r--r--src/core/model/Domain.hpp68
-rw-r--r--src/core/model/Typesystem.cpp21
-rw-r--r--src/core/model/Typesystem.hpp69
-rw-r--r--src/core/parser/stack/DocumentHandler.cpp213
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);
}
}