From 3cc0c9fffa35f83d45209030407465f3756df3fd Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 16 Feb 2015 11:39:18 +0100 Subject: Allowing numeric argument keys for static handlers --- src/core/parser/stack/Stack.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/core/parser') diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index 905edb4..b98cddb 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -361,9 +361,10 @@ void Stack::command(const Variant &name, const Variant::mapType &args) info.valid = false; if (validStack) { // Canonicalize the arguments (if this has not already been done), - // allow additional arguments + // allow additional arguments and numeric indices Variant::mapType canonicalArgs = args; - targetState->arguments.validateMap(canonicalArgs, loggerFork, true); + targetState->arguments.validateMap(canonicalArgs, loggerFork, true, + true); handler->setLogger(loggerFork); try { -- cgit v1.2.3 From 3cca090a650d2e8268977b57aa0dfdb0fb2cae85 Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Mon, 16 Feb 2015 11:57:08 +0100 Subject: added return value in addFieldDescriptor related methods to indicate whether the order of fields had to be changed. --- src/core/model/Domain.cpp | 49 +++++++++++-------------- src/core/model/Domain.hpp | 31 +++++++++++----- src/core/parser/stack/DocumentHandler.cpp | 2 +- src/core/parser/stack/DomainHandler.cpp | 60 +++++++++++++++++++++---------- test/core/model/DocumentTest.cpp | 8 ++--- test/core/model/DomainTest.cpp | 30 ++++++++-------- test/core/model/TestAdvanced.hpp | 2 +- test/core/model/TestDomain.hpp | 14 ++++---- 8 files changed, 115 insertions(+), 81 deletions(-) (limited to 'src/core/parser') diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index ac0699e..f6c3956 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -558,7 +558,7 @@ Rooted Descriptor::getFieldDescriptor( } } -void Descriptor::addAndSortFieldDescriptor(Handle fd, +bool Descriptor::addAndSortFieldDescriptor(Handle fd, Logger &logger) { // only add it if we need to. @@ -571,37 +571,25 @@ void Descriptor::addAndSortFieldDescriptor(Handle fd, fd->getFieldType() != FieldDescriptor::FieldType::TREE) { // if so we add the new field before the TREE field. fieldDescriptors.insert(fieldDescriptors.end() - 1, fd); - - // if the new field was from the same domain we warn the user - // because that is bad coding style. - if (fd->getParent() != nullptr && - fd->getParent().cast()->getParent() == - getParent()) { - logger.warning( - std::string("Field \"") + fd->getName() + - "\" was declared after main field \"" + - fds.back()->getName() + - "\". The order of fields was changed to make the " - "main field the last field.", - *fd); - } + return true; } else { fieldDescriptors.push_back(fd); } } + return false; } -void Descriptor::addFieldDescriptor(Handle fd, Logger &logger) +bool Descriptor::addFieldDescriptor(Handle fd, Logger &logger) { - addAndSortFieldDescriptor(fd, logger); if (fd->getParent() == nullptr) { fd->setParent(this); } + return addAndSortFieldDescriptor(fd, logger); } -void Descriptor::moveFieldDescriptor(Handle fd, Logger &logger) +bool Descriptor::moveFieldDescriptor(Handle fd, Logger &logger) { - addAndSortFieldDescriptor(fd, logger); + bool sorted = addAndSortFieldDescriptor(fd, logger); Handle par = fd->getParent(); if (par != this) { if (par != nullptr) { @@ -610,9 +598,10 @@ void Descriptor::moveFieldDescriptor(Handle fd, Logger &logger) } fd->setParent(this); } + return sorted; } -void Descriptor::copyFieldDescriptor(Handle fd, Logger &logger) +bool Descriptor::copyFieldDescriptor(Handle fd, Logger &logger) { Rooted copy; if (fd->isPrimitive()) { @@ -631,7 +620,7 @@ void Descriptor::copyFieldDescriptor(Handle fd, Logger &logger) copy->addChild(c); } } - addFieldDescriptor(copy, logger); + return addFieldDescriptor(copy, logger); } bool Descriptor::removeFieldDescriptor(Handle fd) @@ -646,25 +635,27 @@ bool Descriptor::removeFieldDescriptor(Handle fd) return false; } -Rooted Descriptor::createPrimitiveFieldDescriptor( - Handle primitiveType, Logger &logger, - FieldDescriptor::FieldType fieldType, std::string name, bool optional) +std::pair, bool> +Descriptor::createPrimitiveFieldDescriptor(Handle primitiveType, + Logger &logger, + FieldDescriptor::FieldType fieldType, + std::string name, bool optional) { Rooted fd{new FieldDescriptor(getManager(), primitiveType, this, fieldType, std::move(name), optional)}; - addFieldDescriptor(fd, logger); - return fd; + bool sorted = addFieldDescriptor(fd, logger); + return std::make_pair(fd, sorted); } -Rooted Descriptor::createFieldDescriptor( +std::pair, bool> Descriptor::createFieldDescriptor( Logger &logger, FieldDescriptor::FieldType fieldType, std::string name, bool optional) { Rooted fd{new FieldDescriptor( getManager(), this, fieldType, std::move(name), optional)}; - addFieldDescriptor(fd, logger); - return fd; + bool sorted = addFieldDescriptor(fd, logger); + return std::make_pair(fd, sorted); } /* Class StructuredClass */ diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index 350c7ba..476a38c 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -469,7 +469,7 @@ private: Owned attributesDescriptor; NodeVector fieldDescriptors; - void addAndSortFieldDescriptor(Handle fd, Logger &logger); + bool addAndSortFieldDescriptor(Handle fd, Logger &logger); protected: void doResolve(ResolutionState &state) override; @@ -557,8 +557,11 @@ public: * parent of the given FieldDescriptor if it is not set yet. * * @param fd is a FieldDescriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. */ - void addFieldDescriptor(Handle fd, Logger &logger); + bool addFieldDescriptor(Handle fd, Logger &logger); /** * Adds the given FieldDescriptor to this Descriptor. This also sets the @@ -566,16 +569,22 @@ public: * already and removes it from the old parent Descriptor. * * @param fd is a FieldDescriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. */ - void moveFieldDescriptor(Handle fd, Logger &logger); + bool moveFieldDescriptor(Handle fd, Logger &logger); /** * Copies a FieldDescriptor that belongs to another Descriptor to this * Descriptor. * * @param fd some FieldDescriptor belonging to another Descriptor. + * @return returns true if the given FieldDescriptor was not added at the + * end one place before because a TREE field already existed and + * the TREE field has to be at the end. */ - void copyFieldDescriptor(Handle fd, Logger &logger); + bool copyFieldDescriptor(Handle fd, Logger &logger); /** * Removes the given FieldDescriptor from this Descriptor. This also sets @@ -598,9 +607,12 @@ public: * filled in order for an instance of the parent * Descriptor to be valid. * - * @return the newly created FieldDescriptor. + * @return the newly created FieldDescriptor and a bool + * indicating whether the order of FieldDescriptors had + * to be changed for the TREE field to be in the last + * spot. */ - Rooted createPrimitiveFieldDescriptor( + std::pair, bool> createPrimitiveFieldDescriptor( Handle primitiveType, Logger &logger, FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, std::string name = "", bool optional = false); @@ -617,9 +629,12 @@ public: * filled in order for an instance of the parent * Descriptor to be valid. * - * @return the newly created FieldDescriptor. + * @return the newly created FieldDescriptor and a bool + * indicating whether the order of FieldDescriptors had + * to be changed for the TREE field to be in the last + * spot. */ - Rooted createFieldDescriptor( + std::pair, bool> createFieldDescriptor( Logger &logger, FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, std::string name = "", bool optional = false); diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index d514701..41ca8ec 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -348,4 +348,4 @@ namespace RttiTypes { const Rtti DocumentField = RttiBuilder( "DocumentField").parent(&Node); } -} +} \ No newline at end of file diff --git a/src/core/parser/stack/DomainHandler.cpp b/src/core/parser/stack/DomainHandler.cpp index a2c8eec..ddec1ee 100644 --- a/src/core/parser/stack/DomainHandler.cpp +++ b/src/core/parser/stack/DomainHandler.cpp @@ -133,11 +133,18 @@ bool DomainFieldHandler::start(Variant::mapType &args) Rooted parent = scope().selectOrThrow(); - Rooted field = parent->createFieldDescriptor( + auto res = parent->createFieldDescriptor( logger(), type, args["name"].asString(), args["optional"].asBool()); - field->setLocation(location()); + res.first->setLocation(location()); + if (res.second) { + logger().warning( + std::string("Field \"") + res.first->getName() + + "\" was declared after main field. The order of fields " + "was changed to make the main field the last field.", + *res.first); + } - scope().push(field); + scope().push(res.first); return true; } @@ -150,14 +157,24 @@ bool DomainFieldRefHandler::start(Variant::mapType &args) Rooted parent = scope().selectOrThrow(); const std::string &name = args["ref"].asString(); - scope().resolveFieldDescriptor( - name, parent, logger(), - [](Handle field, Handle parent, Logger &logger) { - if (field != nullptr) { - parent.cast()->addFieldDescriptor( - field.cast(), logger); - } - }); + + auto loc = location(); + + scope().resolveFieldDescriptor(name, parent, logger(), + [loc](Handle field, + Handle parent, Logger &logger) { + if (field != nullptr) { + if (parent.cast()->addFieldDescriptor( + field.cast(), logger)) { + logger.warning( + std::string("Field \"") + field->getName() + + "\" was referenced after main field was declared. The " + "order of fields was changed to make the main field " + "the last field.", + loc); + } + } + }); return true; } @@ -176,13 +193,20 @@ bool DomainPrimitiveHandler::start(Variant::mapType &args) fieldType = FieldDescriptor::FieldType::TREE; } - Rooted field = parent->createPrimitiveFieldDescriptor( + auto res = parent->createPrimitiveFieldDescriptor( new UnknownType(manager()), logger(), fieldType, args["name"].asString(), args["optional"].asBool()); - field->setLocation(location()); + res.first->setLocation(location()); + if (res.second) { + logger().warning( + std::string("Field \"") + res.first->getName() + + "\" was declared after main field. The order of fields " + "was changed to make the main field the last field.", + *res.first); + } const std::string &type = args["type"].asString(); - scope().resolve(type, field, logger(), + scope().resolve(type, res.first, logger(), [](Handle type, Handle field, Logger &logger) { if (type != nullptr) { @@ -190,7 +214,7 @@ bool DomainPrimitiveHandler::start(Variant::mapType &args) } }); - scope().push(field); + scope().push(res.first); return true; } @@ -254,8 +278,8 @@ bool DomainParentFieldHandler::start(Variant::mapType &args) Logger &logger) { if (parent != nullptr) { Rooted field = - parent.cast()->createFieldDescriptor( - logger, type, name, optional); + (parent.cast()->createFieldDescriptor( + logger, type, name, optional)).first; field->addChild(strct.cast()); } }); @@ -390,4 +414,4 @@ namespace RttiTypes { const Rtti DomainParent = RttiBuilder( "DomainParent").parent(&Node); } -} +} \ No newline at end of file diff --git a/test/core/model/DocumentTest.cpp b/test/core/model/DocumentTest.cpp index 0c6eea6..1bb2356 100644 --- a/test/core/model/DocumentTest.cpp +++ b/test/core/model/DocumentTest.cpp @@ -147,7 +147,7 @@ TEST(Document, validate) // now let's extend the rootClass with a default field. Rooted rootField = - rootClass->createFieldDescriptor(logger); + rootClass->createFieldDescriptor(logger).first; // and add a child class for it. Rooted childClass{ new StructuredClass(mgr, "child", domain, single)}; @@ -194,7 +194,7 @@ TEST(Document, validate) * instances now. */ Rooted childField = - childClass->createFieldDescriptor(logger); + childClass->createFieldDescriptor(logger).first; childField->addChild(childClass); { /* @@ -214,7 +214,7 @@ TEST(Document, validate) */ Rooted childSubField = childSubClass->createFieldDescriptor( - logger, FieldDescriptor::FieldType::TREE, "dummy", true); + logger, FieldDescriptor::FieldType::TREE, "dummy", true).first; // add a child pro forma to make it valid. childSubField->addChild(childSubClass); { @@ -234,7 +234,7 @@ TEST(Document, validate) Rooted primitive_field = childSubClass->createPrimitiveFieldDescriptor( sys->getIntType(), logger, FieldDescriptor::FieldType::SUBTREE, - "int", false); + "int", false).first; { /* * Now a document with one instance of the Child subclass should be diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp index 4cb4331..d68648e 100644 --- a/test/core/model/DomainTest.cpp +++ b/test/core/model/DomainTest.cpp @@ -186,21 +186,21 @@ TEST(Descriptor, pathToAdvanced) new StructuredClass(mgr, "target", domain, Cardinality::any())}; // We create a field for A - Rooted A_field = A->createFieldDescriptor(logger); + Rooted A_field = A->createFieldDescriptor(logger).first; A_field->addChild(B); A_field->addChild(D); // We create no field for B // One for C - Rooted C_field = C->createFieldDescriptor(logger); + Rooted C_field = C->createFieldDescriptor(logger).first; C_field->addChild(target); // One for D - Rooted D_field = D->createFieldDescriptor(logger); + Rooted D_field = D->createFieldDescriptor(logger).first; D_field->addChild(E); // One for E - Rooted E_field = E->createFieldDescriptor(logger); + Rooted E_field = E->createFieldDescriptor(logger).first; E_field->addChild(target); ASSERT_TRUE(domain->validate(logger)); @@ -239,7 +239,7 @@ TEST(Descriptor, getDefaultFields) // create a field. Rooted A_prim_field = - A->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + A->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; // now we should find that. auto fields = A->getDefaultFields(); ASSERT_EQ(1U, fields.size()); @@ -262,7 +262,7 @@ TEST(Descriptor, getDefaultFields) ASSERT_EQ(A_prim_field, fields[0]); // and we should not be able to find it if we override the field. - Rooted A_field = A->createFieldDescriptor(logger); + Rooted A_field = A->createFieldDescriptor(logger).first; ASSERT_TRUE(A->getDefaultFields().empty()); // add a transparent child class. @@ -273,7 +273,7 @@ TEST(Descriptor, getDefaultFields) // add a primitive field for it. Rooted C_field = - C->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + C->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; // now we should find that. fields = A->getDefaultFields(); @@ -285,14 +285,14 @@ TEST(Descriptor, getDefaultFields) Rooted D{new StructuredClass( mgr, "D", domain, Cardinality::any(), nullptr, true, false)}; A_field->addChild(D); - Rooted D_field = D->createFieldDescriptor(logger); + Rooted D_field = D->createFieldDescriptor(logger).first; Rooted E{new StructuredClass( mgr, "E", domain, Cardinality::any(), nullptr, true, false)}; D_field->addChild(E); Rooted F{new StructuredClass( mgr, "E", domain, Cardinality::any(), E, true, false)}; Rooted F_field = - F->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + F->createPrimitiveFieldDescriptor(sys->getStringType(), logger).first; // now we should find both primitive fields, but the C field first. fields = A->getDefaultFields(); @@ -436,7 +436,7 @@ TEST(Domain, validate) ASSERT_TRUE(domain->validate(logger)); // Let's add a primitive field (without a primitive type at first) Rooted base_field = - base->createPrimitiveFieldDescriptor(nullptr, logger); + base->createPrimitiveFieldDescriptor(nullptr, logger).first; // this should not be valid. ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_FALSE(domain->validate(logger)); @@ -464,7 +464,8 @@ TEST(Domain, validate) ASSERT_TRUE(domain->validate(logger)); ASSERT_EQ(base, sub->getSuperclass()); // add a non-primitive field to the child class. - Rooted sub_field = sub->createFieldDescriptor(logger); + Rooted sub_field = + sub->createFieldDescriptor(logger).first; // this should not be valid because we allow no children. ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_FALSE(domain->validate(logger)); @@ -489,8 +490,9 @@ TEST(Domain, validate) ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); // It should be invalid if we set another TREE field. - Rooted sub_field2 = sub->createFieldDescriptor( - logger, FieldDescriptor::FieldType::TREE, "test", false); + Rooted sub_field2 = + sub->createFieldDescriptor(logger, FieldDescriptor::FieldType::TREE, + "test", false).first; ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_FALSE(domain->validate(logger)); // but valid again if we remove it @@ -499,4 +501,4 @@ TEST(Domain, validate) ASSERT_TRUE(domain->validate(logger)); } } -} +} \ No newline at end of file diff --git a/test/core/model/TestAdvanced.hpp b/test/core/model/TestAdvanced.hpp index 27f33cc..8e81554 100644 --- a/test/core/model/TestAdvanced.hpp +++ b/test/core/model/TestAdvanced.hpp @@ -66,7 +66,7 @@ static Rooted constructHeadingDomain(Manager &mgr, for (auto &s : secclasses) { Rooted desc = resolveDescriptor(bookDomain, s); Rooted heading_field = desc->createFieldDescriptor( - logger, FieldDescriptor::FieldType::SUBTREE, "heading", true); + logger, FieldDescriptor::FieldType::SUBTREE, "heading", true).first; heading_field->addChild(heading); } return domain; diff --git a/test/core/model/TestDomain.hpp b/test/core/model/TestDomain.hpp index c107a0d..779ef03 100644 --- a/test/core/model/TestDomain.hpp +++ b/test/core/model/TestDomain.hpp @@ -42,7 +42,8 @@ static Rooted constructBookDomain(Manager &mgr, mgr, "book", domain, single, {nullptr}, false, true)}; // The structure field of it. - Rooted book_field = book->createFieldDescriptor(logger); + Rooted book_field = + book->createFieldDescriptor(logger).first; // From there on the "section". Rooted section{ @@ -51,7 +52,7 @@ static Rooted constructBookDomain(Manager &mgr, // And the field of it. Rooted section_field = - section->createFieldDescriptor(logger); + section->createFieldDescriptor(logger).first; // We also add the "paragraph", which is transparent. Rooted paragraph{new StructuredClass( @@ -61,7 +62,7 @@ static Rooted constructBookDomain(Manager &mgr, // And the field of it. Rooted paragraph_field = - paragraph->createFieldDescriptor(logger); + paragraph->createFieldDescriptor(logger).first; // We append "subsection" to section. Rooted subsection{ @@ -70,7 +71,7 @@ static Rooted constructBookDomain(Manager &mgr, // And the field of it. Rooted subsection_field = - subsection->createFieldDescriptor(logger); + subsection->createFieldDescriptor(logger).first; // and we add the paragraph to subsections fields subsection_field->addChild(paragraph); @@ -82,10 +83,11 @@ static Rooted constructBookDomain(Manager &mgr, // ... and has a primitive field. Rooted text_field = - text->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + text->createPrimitiveFieldDescriptor(sys->getStringType(), logger) + .first; return domain; } } -#endif /* _TEST_DOMAIN_HPP_ */ +#endif /* _TEST_DOMAIN_HPP_ */ \ No newline at end of file -- cgit v1.2.3 From f8c457674aaf1c6b1b3855ca9621ef2887b5100b Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Mon, 16 Feb 2015 18:48:58 +0100 Subject: completely reworked document handler to be consistent with OSML and provide more coherent behaviour in general. --- src/core/parser/stack/DocumentHandler.cpp | 381 ++++++++++++++++++------------ src/core/parser/stack/DocumentHandler.hpp | 78 +++++- 2 files changed, 290 insertions(+), 169 deletions(-) (limited to 'src/core/parser') diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index 41ca8ec..49bf26b 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -51,20 +51,15 @@ void DocumentHandler::end() { scope().pop(); } /* DocumentChildHandler */ -void DocumentChildHandler::preamble(Handle parentNode, - std::string &fieldName, - DocumentEntity *&parent, bool &inField) +void DocumentChildHandler::preamble(Rooted &parentNode, size_t &fieldIdx, + DocumentEntity *&parent) { // Check if the parent in the structure tree was an explicit field // reference. - inField = parentNode->isa(&RttiTypes::DocumentField); - if (inField) { - fieldName = parentNode->getName(); + if (parentNode->isa(&RttiTypes::DocumentField)) { + fieldIdx = parentNode.cast()->fieldIdx; parentNode = scope().selectOrThrow( {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity}); - } else { - // If it wasn't an explicit reference, we use the default field. - fieldName = DEFAULT_FIELD_NAME; } // Reference the parent entity explicitly. @@ -78,124 +73,235 @@ void DocumentChildHandler::preamble(Handle parentNode, } } -static void createPath(const NodeVector &path, DocumentEntity *&parent, - size_t p0 = 1) +void DocumentChildHandler::createPath(const NodeVector &path, + DocumentEntity *&parent, size_t p0) { - // 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 = p0; p < S; p = p + 2) { - parent = static_cast( - parent->createChildStructuredEntity( - path[p].cast(), Variant::mapType{}, - path[p - 1]->getName(), "").get()); + // add the field. + Rooted field{new DocumentField( + manager(), scope().getLeaf(), + parent->getDescriptor()->getFieldDescriptorIndex(), true)}; + scope().push(field); + // add the transparent/implicit structure element. + Rooted transparent = + parent->createChildStructuredEntity(path[p].cast(), + Variant::mapType{}, + path[p - 1]->getName(), ""); + transparent->setLocation(location()); + transparent->setTransparent(true); + scope().push(transparent); + parent = static_cast(transparent.get()); } + // add the last field. + Rooted field{new DocumentField( + manager(), scope().getLeaf(), + parent->getDescriptor()->getFieldDescriptorIndex(), true)}; + scope().push(field); } -static void createPath(const std::string &firstFieldName, - const NodeVector &path, DocumentEntity *&parent) +void DocumentChildHandler::createPath(const size_t &firstFieldIdx, + const NodeVector &path, + DocumentEntity *&parent) { // Add the first element - parent = static_cast( - parent->createChildStructuredEntity(path[0].cast(), - Variant::mapType{}, firstFieldName, - "").get()); + Rooted transparent = parent->createChildStructuredEntity( + path[0].cast(), firstFieldIdx); + transparent->setLocation(location()); + transparent->setTransparent(true); + scope().push(transparent); + parent = static_cast(transparent.get()); createPath(path, parent, 2); } bool DocumentChildHandler::start(Variant::mapType &args) { + // extract the special "name" attribute from the input arguments. + // the remaining attributes will be forwarded to the newly constructed + // element. + std::string nameAttr; + { + auto it = args.find("name"); + if (it != args.end()) { + nameAttr = it->second.asString(); + args.erase(it); + } + } + scope().setFlag(ParserFlag::POST_HEAD, true); - Rooted parentNode = scope().selectOrThrow( - {&RttiTypes::Document, &RttiTypes::StructuredEntity, - &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField}); + while (true) { + Rooted parentNode = scope().getLeaf(); + + Rooted entity; + // handle the root note specifically. + if (parentNode->isa(&RttiTypes::Document)) { + Rooted strct = scope().resolve( + Utils::split(name(), ':'), logger()); + if (strct == nullptr) { + // if we could not resolve the name, throw an exception. + throw LoggableException( + std::string("\"") + name() + "\" could not be resolved.", + location()); + } + entity = parentNode.cast()->createRootStructuredEntity( + strct, args, nameAttr); + } else { + assert(parentNode->isa(&RttiTypes::DocumentField)); - std::string fieldName; - DocumentEntity *parent; - bool inField; - - preamble(parentNode, fieldName, parent, inField); - - // Try to find a FieldDescriptor for the given tag if we are not in a - // field already. This does _not_ try to construct transparent paths - // in between. - if (!inField && parent != nullptr && - parent->getDescriptor()->hasField(name())) { - Rooted field{ - new DocumentField(parentNode->getManager(), name(), parentNode)}; - field->setLocation(location()); - scope().push(field); - return true; - } + size_t fieldIdx; + DocumentEntity *parent; - // Otherwise create a new StructuredEntity - // TODO: Consider Anchors and AnnotationEntities - Rooted strct = - scope().resolve(Utils::split(name(), ':'), logger()); - if (strct == nullptr) { - // if we could not resolve the name, throw an exception. - throw LoggableException( - std::string("\"") + name() + "\" could not be resolved.", - location()); - } + preamble(parentNode, fieldIdx, parent); - std::string name; - auto it = args.find("name"); - if (it != args.end()) { - name = it->second.asString(); - args.erase(it); - } + // TODO: REMOVE + std::string thisName = name(); + std::string parentClassName; + if (parent != nullptr) { + parentClassName = parent->getDescriptor()->getName(); + } - Rooted entity; - if (parentNode->isa(&RttiTypes::Document)) { - entity = parentNode.cast()->createRootStructuredEntity( - strct, args, name); - } else { - // calculate a path if transparent entities are needed in between. - std::string lastFieldName = fieldName; - if (inField) { - Rooted field = - parent->getDescriptor()->getFieldDescriptor(fieldName); - auto pathRes = - field.cast()->pathTo(strct, logger()); - if (!pathRes.second) { + /* + * Try to find a FieldDescriptor for the given tag if we are not in + * a field already. This does _not_ try to construct transparent + * paths in between. + */ + { + ssize_t newFieldIdx = + parent->getDescriptor()->getFieldDescriptorIndex(name()); + if (newFieldIdx != -1) { + Rooted field{new DocumentField( + manager(), parentNode, newFieldIdx, false)}; + field->setLocation(location()); + scope().push(field); + isExplicitField = true; + return true; + } + } + + // Otherwise create a new StructuredEntity + // TODO: Consider Anchors and AnnotationEntities + Rooted strct = scope().resolve( + Utils::split(name(), ':'), logger()); + if (strct == nullptr) { + // if we could not resolve the name, throw an exception. throw LoggableException( - std::string("An instance of \"") + strct->getName() + - "\" is not allowed as child of field \"" + fieldName + - "\"", + std::string("\"") + name() + "\" could not be resolved.", 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()) { + + // calculate a path if transparent entities are needed in between. + Rooted field = + parent->getDescriptor()->getFieldDescriptors()[fieldIdx]; + size_t lastFieldIdx = fieldIdx; + auto pathRes = field->pathTo(strct, logger()); + if (!pathRes.second) { + if (scope().getLeaf().cast()->transparent) { + // if we have transparent elements above us in the structure + // tree we try to unwind them before we give up. + // pop the implicit field. + scope().pop(); + // pop the implicit element. + scope().pop(); + continue; + } throw LoggableException( std::string("An instance of \"") + strct->getName() + - "\" is not allowed as child of an instance of \"" + + "\" is not allowed as child of field \"" + + field->getName() + "\" of descriptor \"" + parent->getDescriptor()->getName() + "\"", location()); } - - // create all transparent entities until the last field. - createPath(path, parent); - if (path.size() > 1) { - lastFieldName = DEFAULT_FIELD_NAME; + if (!pathRes.first.empty()) { + createPath(lastFieldIdx, pathRes.first, parent); + lastFieldIdx = + parent->getDescriptor()->getFieldDescriptorIndex(); } + // create the entity for the new element at last. + entity = parent->createChildStructuredEntity(strct, lastFieldIdx, + args, nameAttr); } - // create the entity for the new element at last. - entity = parent->createChildStructuredEntity(strct, args, lastFieldName, - name); + entity->setLocation(location()); + scope().push(entity); + return true; } - entity->setLocation(location()); - scope().push(entity); +} + +void DocumentChildHandler::end() +{ + // in case of explicit fields we do not want to pop something from the + // stack. + if (isExplicitField) { + return; + } + // pop the "main" element. + scope().pop(); +} + +bool DocumentChildHandler::fieldStart(bool &isDefault, size_t fieldIdx) +{ + if (isExplicitField) { + // In case of explicit fields we do not want to create another field. + isDefault = true; + return fieldIdx == 0; + } + Rooted parentNode = scope().getLeaf(); + assert(parentNode->isa(&RttiTypes::StructuredEntity) || + parentNode->isa(&RttiTypes::AnnotationEntity)); + size_t dummy; + DocumentEntity *parent; + + preamble(parentNode, dummy, parent); + + NodeVector fields = + parent->getDescriptor()->getFieldDescriptors(); + + if (isDefault) { + fieldIdx = fields.size() - 1; + } else { + if (fieldIdx >= fields.size()) { + return false; + } + isDefault = fieldIdx == fields.size() - 1; + } + // push the field on the stack. + Rooted field{ + new DocumentField(manager(), parentNode, fieldIdx, false)}; + field->setLocation(location()); + scope().push(field); return true; } -void DocumentChildHandler::end() { scope().pop(); } +void DocumentChildHandler::fieldEnd() +{ + assert(scope().getLeaf()->isa(&RttiTypes::DocumentField)); + + // pop the field from the stack. + scope().pop(); + + // pop all remaining transparent elements. + while (scope().getLeaf()->isa(&RttiTypes::StructuredEntity) && + scope().getLeaf().cast()->isTransparent()) { + // pop the transparent element. + scope().pop(); + // pop the transparent field. + scope().pop(); + } +} + +bool DocumentChildHandler::annotationStart(const Variant &className, + Variant::mapType &args) +{ + // TODO: Implement + return false; +} + +bool DocumentChildHandler::annotationEnd(const Variant &className, + const Variant &elementName) +{ + // TODO: Implement + return false; +} bool DocumentChildHandler::convertData(Handle field, Variant &data, Logger &logger) @@ -226,71 +332,40 @@ bool DocumentChildHandler::convertData(Handle field, bool DocumentChildHandler::data(Variant &data) { - Rooted parentNode = scope().selectOrThrow( - {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity, - &RttiTypes::DocumentField}); - - std::string fieldName; - DocumentEntity *strctParent; - bool inField; - - preamble(parentNode, fieldName, strctParent, inField); - - Rooted desc = strctParent->getDescriptor(); - // The parent from which we need to connect to the primitive content. - Rooted parentClass; - - // We distinguish two cases here: One for fields that are given. - if (inField) { - // Retrieve the actual FieldDescriptor - Rooted field = desc->getFieldDescriptor(fieldName); - if (field == nullptr) { - logger().error( - std::string("Can't handle data because no field with name \"") + - fieldName + "\" exists in descriptor\"" + desc->getName() + - "\".", - location()); + Rooted parentField = scope().getLeaf(); + assert(parentField->isa(&RttiTypes::DocumentField)); + + size_t fieldIdx; + DocumentEntity *parent; + + preamble(parentField, fieldIdx, parent); + + Rooted desc = parent->getDescriptor(); + + // Retrieve the actual FieldDescriptor + Rooted field = desc->getFieldDescriptors()[fieldIdx]; + // 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; } - // 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; - } - 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; + parent->createChildDocumentPrimitive(data, fieldIdx); + 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 defaultFields; - if (inField) { - defaultFields = parentClass.cast()->getDefaultFields(); - } else { - defaultFields = parentClass.cast()->getDefaultFields(); - } - + NodeVector defaultFields = field->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 forks; - for (auto field : defaultFields) { + for (auto primitiveField : defaultFields) { // Then try to parse the content using the type specification. forks.emplace_back(logger().fork()); - if (!convertData(field, data, forks.back())) { + if (!convertData(primitiveField, data, forks.back())) { continue; } @@ -298,18 +373,12 @@ bool DocumentChildHandler::data(Variant &data) forks.back().commit(); // Construct the necessary path - if (inField) { - NodeVector path = - parentClass.cast()->pathTo(field, logger()); - createPath(fieldName, path, strctParent); - } else { - auto pathRes = desc->pathTo(field, logger()); - assert(pathRes.second); - createPath(pathRes.first, strctParent); - } + NodeVector path = field->pathTo(primitiveField, logger()); + // TODO: Create methods with indices instead of names. + createPath(fieldIdx, path, parent); // Then create the primitive element - strctParent->createChildDocumentPrimitive(data); + parent->createChildDocumentPrimitive(data); return true; } diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp index 2c474f9..5953e3a 100644 --- a/src/core/parser/stack/DocumentHandler.hpp +++ b/src/core/parser/stack/DocumentHandler.hpp @@ -76,29 +76,74 @@ public: */ class DocumentField : public Node { public: - using Node::Node; + const size_t fieldIdx; + const bool transparent; + + DocumentField(Manager &mgr, Handle parent, size_t fieldIdx, + bool transparent) + : Node(mgr, parent), fieldIdx(fieldIdx), transparent(transparent) + { + } }; /** * The DocumentChildHandler class performs the actual parsing of the user * defined elements in an Ousía document. */ -class DocumentChildHandler : public StaticHandler { +class DocumentChildHandler : public Handler { private: + bool isExplicitField = false; + /** - * Code shared by both the start() and the end() method. Checks whether the - * parser currently is in a field and returns the name of this field. + * Code shared by both the start(), fieldStart() and the data() method. + * Checks whether the parser currently is in a field and returns the name + * of this field. * * @param parentNode is the next possible parent node (a document, * a structured entity, an annotation entity or a field). - * @param fieldName is an output parameter to which the name of the current + * @param fieldIdx is an output parameter to which the index of the current * field is written (or unchanged if we're not in a field). * @param parent is an output parameter to which the parent document entity * will be written. - * @param inField is set to true if we actually are in a field. */ - void preamble(Handle parentNode, std::string &fieldName, - DocumentEntity *&parent, bool &inField); + void preamble(Rooted &parentNode, size_t &fieldIdx, + DocumentEntity *&parent); + + /** + * Creates transparent elements that are stored in the given path. + * + * @param path a NodeVector of alternating FieldDescriptors and + * StructuredClasses. For each of the StructuredClasses at + * index p an instance is created and added to the field at + * index p-1 of the previously created instance of the + * StructuredClass at index p-2. + * @param parent is the parent DocumentEntity for the first transparent + * element. This will be reset for each created transparent + * element. + * @param p0 is the index of the path vector of the first + * StructuredClass for which an instance shall be created. + * This is 1 per default. + */ + void createPath(const NodeVector &path, DocumentEntity *&parent, + size_t p0 = 1); + + /** + * Creates transparent elements that are stored in the given path. + * + * @param fieldIdx is the index of the field for which the first instance + * shall be added. + * @param path a NodeVector of alternating FieldDescriptors and + * StructuredClasses. For each of the StructuredClasses at + * index p an instance is created and added to the field at + * index p-1 of the previously created instance of the + * StructuredClass at index p-2. The first element has + * to be a StructuredClass. + * @param parent is the parent DocumentEntity for the first transparent + * element. This will be reset for each created transparent + * element. + */ + void createPath(const size_t &fieldIdx, const NodeVector &path, + DocumentEntity *&parent); /** * Tries to convert the given data to the type that is specified in the @@ -116,12 +161,22 @@ private: Logger &logger); public: - using StaticHandler::StaticHandler; + using Handler::Handler; bool start(Variant::mapType &args) override; void end() override; bool data(Variant &data) override; + bool fieldStart(bool &isDefault, size_t fieldIdx) override; + + void fieldEnd() override; + + bool annotationStart(const Variant &className, + Variant::mapType &args) override; + + bool annotationEnd(const Variant &className, + const Variant &elementName) override; + /** * Creates a new instance of the DocumentChildHandler. * @@ -146,7 +201,6 @@ extern const State Document; */ extern const State DocumentChild; } - } namespace RttiTypes { @@ -155,8 +209,6 @@ namespace RttiTypes { */ extern const Rtti DocumentField; } - } -#endif /* _OUSIA_PARSER_STACK_DOCUMENT_HANDLER_HPP_ */ - +#endif /* _OUSIA_PARSER_STACK_DOCUMENT_HANDLER_HPP_ */ \ No newline at end of file -- cgit v1.2.3