diff options
-rw-r--r-- | src/plugins/xml/XmlParser.cpp | 74 | ||||
-rw-r--r-- | test/plugins/xml/XmlParserTest.cpp | 62 | ||||
-rw-r--r-- | testdata/xmlparser/comments_domain.oxm | 40 |
3 files changed, 161 insertions, 15 deletions
diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index 4440e75..c288f40 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -343,6 +343,31 @@ public: } }; +class DomainAnnotationHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + scope().setFlag(ParserFlag::POST_HEAD, true); + + Rooted<Domain> domain = scope().selectOrThrow<Domain>(); + + Rooted<AnnotationClass> annotationClass = + domain->createAnnotationClass(args["name"].asString(), nullptr); + annotationClass->setLocation(location()); + + scope().push(annotationClass); + } + + void end() override { scope().pop(); } + + static Handler *create(const HandlerData &handlerData) + { + return new DomainAnnotationHandler{handlerData}; + } +}; + class DomainFieldHandler : public Handler { public: using Handler::Handler; @@ -356,7 +381,6 @@ public: type = FieldDescriptor::FieldType::TREE; } - // TODO: Is inheritance possible here? Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>(); Rooted<FieldDescriptor> field = parent->createFieldDescriptor( @@ -380,7 +404,6 @@ public: void start(Variant::mapType &args) override { - // TODO: Is inheritance possible here? Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>(); const std::string &name = args["name"].asString(); @@ -408,7 +431,6 @@ public: void start(Variant::mapType &args) override { - // TODO: Is inheritance possible here? Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>(); Rooted<FieldDescriptor> field = parent->createPrimitiveFieldDescriptor( @@ -565,11 +587,13 @@ public: Handle<Node> strct, Logger &logger) { if (parent != nullptr) { - auto res = parent->resolve(RttiTypes::FieldDescriptor, name); + auto res = parent.cast<Descriptor>()->resolve( + RttiTypes::FieldDescriptor, name); if (res.size() != 1) { logger.error( std::string("Could not find referenced field ") + name, loc); + return; } Rooted<FieldDescriptor> field = res[0].node.cast<FieldDescriptor>(); @@ -586,8 +610,6 @@ public: } }; -// TODO: Add annotation handler - /* * Import and Include Handler */ @@ -703,6 +725,7 @@ static const ParserState Domain = ParserStateBuilder() .createdNodeType(&RttiTypes::Domain) .elementHandler(DomainHandler::create) .arguments({Argument::String("name")}); + static const ParserState DomainStruct = ParserStateBuilder() .parent(&Domain) @@ -713,39 +736,54 @@ static const ParserState DomainStruct = Argument::Bool("isRoot", false), Argument::Bool("transparent", false), Argument::String("isa", "")}); -static const ParserState DomainStructField = +// TODO: What about attributes? + +static const ParserState DomainAnnotation = ParserStateBuilder() - .parent(&DomainStruct) + .parent(&Domain) + .createdNodeType(&RttiTypes::AnnotationClass) + .elementHandler(DomainAnnotationHandler::create) + .arguments({Argument::String("name")}); +// TODO: What about attributes? + +static const ParserState DomainField = + ParserStateBuilder() + .parents({&DomainStruct, &DomainAnnotation}) .createdNodeType(&RttiTypes::FieldDescriptor) .elementHandler(DomainFieldHandler::create) .arguments({Argument::String("name", DEFAULT_FIELD_NAME), Argument::Bool("isSubtree", false), Argument::Bool("optional", false)}); -static const ParserState DomainStructFieldRef = + +static const ParserState DomainFieldRef = ParserStateBuilder() - .parent(&DomainStruct) + .parents({&DomainStruct, &DomainAnnotation}) .createdNodeType(&RttiTypes::FieldDescriptor) .elementHandler(DomainFieldRefHandler::create) .arguments({Argument::String("name", DEFAULT_FIELD_NAME)}); + static const ParserState DomainStructPrimitive = ParserStateBuilder() - .parent(&DomainStruct) + .parents({&DomainStruct, &DomainAnnotation}) .createdNodeType(&RttiTypes::FieldDescriptor) .elementHandler(DomainPrimitiveHandler::create) .arguments({Argument::String("name", DEFAULT_FIELD_NAME), Argument::Bool("optional", false), Argument::String("type")}); + static const ParserState DomainStructChild = ParserStateBuilder() - .parent(&DomainStructField) + .parent(&DomainField) .elementHandler(DomainChildHandler::create) .arguments({Argument::String("ref")}); + static const ParserState DomainStructParent = ParserStateBuilder() .parent(&DomainStruct) .createdNodeType(&RttiTypes::DummyParentNode) .elementHandler(DomainParentHandler::create) .arguments({Argument::String("name")}); + static const ParserState DomainStructParentField = ParserStateBuilder() .parent(&DomainStructParent) @@ -754,6 +792,7 @@ static const ParserState DomainStructParentField = .arguments({Argument::String("name", DEFAULT_FIELD_NAME), Argument::Bool("isSubtree", false), Argument::Bool("optional", false)}); + static const ParserState DomainStructParentFieldRef = ParserStateBuilder() .parent(&DomainStructParent) @@ -768,29 +807,34 @@ static const ParserState Typesystem = .createdNodeType(&RttiTypes::Typesystem) .elementHandler(TypesystemHandler::create) .arguments({Argument::String("name", "")}); + static const ParserState TypesystemEnum = ParserStateBuilder() .parent(&Typesystem) .createdNodeType(&RttiTypes::EnumType) .elementHandler(TypesystemEnumHandler::create) .arguments({Argument::String("name")}); + static const ParserState TypesystemEnumEntry = ParserStateBuilder() .parent(&TypesystemEnum) .elementHandler(TypesystemEnumEntryHandler::create) .arguments({}); + static const ParserState TypesystemStruct = ParserStateBuilder() .parent(&Typesystem) .createdNodeType(&RttiTypes::StructType) .elementHandler(TypesystemStructHandler::create) .arguments({Argument::String("name"), Argument::String("parent", "")}); + static const ParserState TypesystemStructField = ParserStateBuilder() .parent(&TypesystemStruct) .elementHandler(TypesystemStructFieldHandler::create) .arguments({Argument::String("name"), Argument::String("type"), Argument::Any("default", Variant::fromObject(nullptr))}); + static const ParserState TypesystemConstant = ParserStateBuilder() .parent(&Typesystem) @@ -806,6 +850,7 @@ static const ParserState Import = .elementHandler(ImportHandler::create) .arguments({Argument::String("rel", ""), Argument::String("type", ""), Argument::String("src", "")}); + static const ParserState Include = ParserStateBuilder() .parent(&All) @@ -817,8 +862,9 @@ static const std::multimap<std::string, const ParserState *> XmlStates{ {"document", &Document}, {"domain", &Domain}, {"struct", &DomainStruct}, - {"field", &DomainStructField}, - {"fieldRef", &DomainStructFieldRef}, + {"annotation", &DomainAnnotation}, + {"field", &DomainField}, + {"fieldRef", &DomainFieldRef}, {"primitive", &DomainStructPrimitive}, {"child", &DomainStructChild}, {"parent", &DomainStructParent}, diff --git a/test/plugins/xml/XmlParserTest.cpp b/test/plugins/xml/XmlParserTest.cpp index 2d0cb6f..6619199 100644 --- a/test/plugins/xml/XmlParserTest.cpp +++ b/test/plugins/xml/XmlParserTest.cpp @@ -89,6 +89,7 @@ static void checkStructuredClass( ASSERT_EQ(cardinality, sc->getCardinality()); ASSERT_EQ(transparent, sc->isTransparent()); ASSERT_EQ(root, sc->hasRootPermission()); + ASSERT_EQ(attributesDescriptor, sc->getAttributesDescriptor()); } static Rooted<StructuredClass> checkStructuredClass( @@ -108,6 +109,31 @@ static Rooted<StructuredClass> checkStructuredClass( return sc; } +static void checkAnnotationClass( + Handle<Node> n, const std::string &name, Handle<Domain> domain, + Handle<StructType> attributesDescriptor = nullptr) +{ + ASSERT_FALSE(n == nullptr); + Handle<AnnotationClass> ac = n.cast<AnnotationClass>(); + ASSERT_FALSE(ac == nullptr); + ASSERT_EQ(name, ac->getName()); + ASSERT_EQ(domain, ac->getParent()); + ASSERT_EQ(attributesDescriptor, ac->getAttributesDescriptor()); +} + +static Rooted<AnnotationClass> checkAnnotationClass( + const std::string &resolve, const std::string &name, Handle<Domain> domain, + Handle<StructType> attributesDescriptor = nullptr) +{ + auto res = domain->resolve(RttiTypes::AnnotationClass, resolve); + if (res.size() != 1) { + throw OusiaException("resolution error!"); + } + Handle<AnnotationClass> ac = res[0].node.cast<AnnotationClass>(); + checkAnnotationClass(ac, name, domain, attributesDescriptor); + return ac; +} + static void checkFieldDescriptor( Handle<Node> n, const std::string &name, Handle<Descriptor> parent, NodeVector<StructuredClass> children, @@ -180,7 +206,7 @@ TEST(XmlParser, domainParsing) text, {}, "content", FieldDescriptor::FieldType::PRIMITIVE, env.project->getSystemTypesystem()->getStringType(), false); - // check parent handling. + // check parent handling using the headings domain. Rooted<Node> headings_domain_node = env.parse("headings_domain.oxm", "", "", RttiSet{&RttiTypes::Domain}); ASSERT_FALSE(headings_domain_node == nullptr); @@ -204,6 +230,40 @@ TEST(XmlParser, domainParsing) FieldDescriptor::FieldType::SUBTREE, nullptr, true); checkFieldDescriptor(paragraph, {heading}, "heading", FieldDescriptor::FieldType::SUBTREE, nullptr, true); + + // check annotation handling using the comments domain. + Rooted<Node> comments_domain_node = + env.parse("comments_domain.oxm", "", "", RttiSet{&RttiTypes::Domain}); + ASSERT_FALSE(comments_domain_node == nullptr); + ASSERT_FALSE(logger.hasError()); + Rooted<Domain> comments_domain = comments_domain_node.cast<Domain>(); + // now we should be able to find a comment annotation. + Rooted<AnnotationClass> comment_anno = + checkAnnotationClass("comment", "comment", comments_domain); + // as well as a comment struct + Rooted<StructuredClass> comment = + checkStructuredClass("comment", "comment", comments_domain); + // and a reply struct + Rooted<StructuredClass> reply = + checkStructuredClass("reply", "reply", comments_domain); + // check the fields for each of them. + { + std::vector<Rooted<Descriptor>> descs{comment_anno, comment, reply}; + for (auto &d : descs) { + checkFieldDescriptor(d, {paragraph}, "content", + FieldDescriptor::FieldType::SUBTREE, nullptr, + false); + checkFieldDescriptor(d, {reply}, "replies", + FieldDescriptor::FieldType::SUBTREE, nullptr, + false); + } + } + // paragraph should have comment as child now as well. + checkFieldDescriptor(paragraph, {text, comment}); + // as should heading, because it references the paragraph default field. + // TODO: This does not work as of now, because in fact fields get copied, + // not referenced. Should we reference fields, though? + //checkFieldDescriptor(heading, {text, comment}); } } diff --git a/testdata/xmlparser/comments_domain.oxm b/testdata/xmlparser/comments_domain.oxm new file mode 100644 index 0000000..1336b47 --- /dev/null +++ b/testdata/xmlparser/comments_domain.oxm @@ -0,0 +1,40 @@ +<?xml version="1.0" standalone="yes"?> +<domain name="comments"> + <import rel="domain" src="./book_domain.oxm"/> + + <!-- an annotation comment --> + <annotation name="comment"> + <field name="content" isSubtree="true"> + <child ref="book.paragraph"/> + </field> + <field name="replies" isSubtree="true"> + <child ref="reply"/> + </field> + </annotation> + + <!-- an point-like structure comment. --> + <struct name="comment"> + <!-- Is there a chance to prevent users from having to redefine these + two fields in comment and reply? Could we use a fieldRef here? + Or would that be circular? --> + <field name="content" isSubtree="true"> + <child ref="book.paragraph"/> + </field> + <field name="replies" isSubtree="true"> + <child ref="reply"/> + </field> + <parent name="book.paragraph"> + <fieldRef name="$default"/> + </parent> + </struct> + <!-- note that replies are organized in a tree fashion: One can also reply + to a reply --> + <struct name="reply"> + <field name="content" isSubtree="true"> + <child ref="book.paragraph"/> + </field> + <field name="replies" isSubtree="true"> + <child ref="reply"/> + </field> + </struct> +</domain> |