diff options
-rw-r--r-- | src/plugins/xml/XmlParser.cpp | 128 | ||||
-rw-r--r-- | test/plugins/xml/XmlParserTest.cpp | 29 | ||||
-rw-r--r-- | testdata/xmlparser/book_domain.oxm | 98 |
3 files changed, 242 insertions, 13 deletions
diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index f4e5caf..010b707 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -235,6 +235,10 @@ public: } }; +/* + * Domain Handlers + */ + class DomainHandler : public Handler { public: using Handler::Handler; @@ -264,8 +268,6 @@ public: { scope().setFlag(ParserFlag::POST_HEAD, true); - const std::string &isa = args["isa"].asString(); - Rooted<Domain> domain = scope().select<Domain>(); Rooted<StructuredClass> structuredClass = domain->createStructuredClass( args["name"].asString(), args["cardinality"].asCardinality(), @@ -273,6 +275,7 @@ public: args["isRoot"].asBool()); structuredClass->setLocation(location()); + const std::string &isa = args["isa"].asString(); if (!isa.empty()) { scope().resolve<StructuredClass>( isa, structuredClass, logger(), @@ -296,6 +299,105 @@ public: } }; +class DomainFieldHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + FieldDescriptor::FieldType type; + if (args["isSubtree"].asBool()) { + type = FieldDescriptor::FieldType::SUBTREE; + } else { + type = FieldDescriptor::FieldType::TREE; + } + + // TODO: Is inheritance possible here? + Rooted<Descriptor> parent = scope().select<Descriptor>(); + + Rooted<FieldDescriptor> field = parent->createFieldDescriptor( + type, args["name"].asString(), args["optional"].asBool()); + field->setLocation(location()); + + scope().push(field); + } + + void end() override { scope().pop(); } + + static Handler *create(const HandlerData &handlerData) + { + return new DomainFieldHandler{handlerData}; + } +}; + +class DomainPrimitiveHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + // TODO: Is inheritance possible here? + Rooted<Descriptor> parent = scope().select<Descriptor>(); + + Rooted<FieldDescriptor> field = parent->createPrimitiveFieldDescriptor( + nullptr, args["name"].asString(), args["optional"].asBool()); + field->setLocation(location()); + + const std::string &type = args["type"].asString(); + scope().resolve<Type>( + type, field, logger(), + [](Handle<Node> type, Handle<Node> field, Logger &logger) { + if (type != nullptr) { + field.cast<FieldDescriptor>()->setPrimitiveType( + type.cast<Type>()); + } + }); + + scope().push(field); + } + + void end() override { scope().pop(); } + + static Handler *create(const HandlerData &handlerData) + { + return new DomainPrimitiveHandler{handlerData}; + } +}; + +class DomainChildHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + Rooted<FieldDescriptor> field = scope().select<FieldDescriptor>(); + + const std::string &ref = args["ref"].asString(); + scope().resolve<StructuredClass>( + ref, field, logger(), + [](Handle<Node> child, Handle<Node> field, Logger &logger) { + if (child != nullptr) { + field.cast<FieldDescriptor>()->addChild( + child.cast<StructuredClass>()); + } + }); + } + + void end() override {} + + static Handler *create(const HandlerData &handlerData) + { + return new DomainChildHandler{handlerData}; + } +}; + +//TODO: Add parent handler +//TODO: Add annotation handler + +/* + * Import and Include Handler + */ + class ImportIncludeHandler : public Handler { public: using Handler::Handler; @@ -417,19 +519,27 @@ static const ParserState DomainStruct = Argument::Bool("isRoot", false), Argument::Bool("transparent", false), Argument::String("isa", "")}); -static const ParserState DomainStructFields = - ParserStateBuilder().parent(&DomainStruct).arguments({}); static const ParserState DomainStructField = ParserStateBuilder() - .parent(&DomainStructFields) + .parent(&DomainStruct) .createdNodeType(&RttiTypes::FieldDescriptor) + .elementHandler(DomainFieldHandler::create) .arguments({Argument::String("name", ""), Argument::Bool("isSubtree", false), Argument::Bool("optional", false)}); static const ParserState DomainStructPrimitive = - ParserStateBuilder().parent(&DomainStructFields).arguments( - {Argument::String("name", ""), Argument::Bool("optional", false), - Argument::String("type")}); + ParserStateBuilder() + .parent(&DomainStruct) + .createdNodeType(&RttiTypes::FieldDescriptor) + .elementHandler(DomainPrimitiveHandler::create) + .arguments({Argument::String("name", ""), + Argument::Bool("optional", false), + Argument::String("type")}); +static const ParserState DomainStructChild = + ParserStateBuilder() + .parent(&DomainStructField) + .elementHandler(DomainChildHandler::create) + .arguments({Argument::String("ref")}); /* Typesystem states */ static const ParserState Typesystem = @@ -481,9 +591,9 @@ static const std::multimap<std::string, const ParserState *> XmlStates{ {"document", &Document}, {"domain", &Domain}, {"struct", &DomainStruct}, - {"fields", &DomainStructFields}, {"field", &DomainStructField}, {"primitive", &DomainStructPrimitive}, + {"child", &DomainStructChild}, {"typesystem", &Typesystem}, {"enum", &TypesystemEnum}, {"struct", &TypesystemStruct}, diff --git a/test/plugins/xml/XmlParserTest.cpp b/test/plugins/xml/XmlParserTest.cpp index 0f37d0a..b90f39e 100644 --- a/test/plugins/xml/XmlParserTest.cpp +++ b/test/plugins/xml/XmlParserTest.cpp @@ -22,6 +22,7 @@ #include <core/common/CharReader.hpp> #include <core/common/SourceContextReader.hpp> +#include <core/model/Domain.hpp> #include <core/model/Project.hpp> #include <core/frontend/TerminalLogger.hpp> #include <core/StandaloneEnvironment.hpp> @@ -48,10 +49,8 @@ struct XmlStandaloneEnvironment : public StandaloneEnvironment { fileLocator.addUnittestSearchPath("xmlparser"); registry.registerDefaultExtensions(); - registry.registerParser( - {"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}, - {&RttiTypes::Node}, - &xmlParser); + registry.registerParser({"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}, + {&RttiTypes::Node}, &xmlParser); registry.registerResourceLocator(&fileLocator); } }; @@ -73,5 +72,27 @@ TEST(XmlParser, generic) env.manager.exportGraphviz("xmlDocument.dot"); #endif } + +TEST(XmlParser, domainParsing) +{ + XmlStandaloneEnvironment env(logger); + Rooted<Node> n = + env.parse("book_domain.oxm", "", "", RttiSet{&RttiTypes::Domain}); + ASSERT_FALSE(n.isNull()); + // check the domain node. + Rooted<Domain> domain = n.cast<Domain>(); + ASSERT_EQ("book", domain->getName()); + // get the book struct node. + auto res = domain->resolve(RttiTypes::StructuredClass, "book"); + ASSERT_EQ(1, res.size()); + Rooted<StructuredClass> bookStruct = res[0].node.cast<StructuredClass>(); + ASSERT_EQ("book", bookStruct->getName()); + Cardinality single; + single.merge({1}); + ASSERT_EQ(single, bookStruct->getCardinality()); + //TODO: Something is wrong here +// ASSERT_TRUE(bookStruct->isRoot()); + ASSERT_FALSE(bookStruct->isTransparent()); +} } diff --git a/testdata/xmlparser/book_domain.oxm b/testdata/xmlparser/book_domain.oxm new file mode 100644 index 0000000..625e3c0 --- /dev/null +++ b/testdata/xmlparser/book_domain.oxm @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<!-- The domain node is the root node of a single domain definition --> +<domain name="book"> + <!-- We start by declaring the structured classes. This implicitly defines + a context free grammar, which specifies the language of documents that + may be constructed using this domain. Note that this grammar may be + influenced by other domains depending on this one. --> + <!-- Note that we specify neither attributes, + nor parent, nor transparency, meaning that we refer to the default + values. Also note that we need to specify explicitly, which classes + are allowed as root nodes. --> + <struct name="book" cardinality="1" isRoot="true"> + <!-- implicitly: + <struct name="book" cardinality="1" isRoot="true" + transparent="false" isa="" attributesDescriptor=""> + --> + <!-- Note that we assume that, if not specified, a + field is assumed to have no name, be of type TREE + and not optional. --> + <field> + <!-- implicitly: + <field name="" isSubtree="false" optional="false"> + --> + <!-- Using such child references might be problematic if + multiple nodes are matched. This should probably + result in an exception. + Also note that we only reference the child classes. + We do _not_ declare them here. This might lead to + some difficulties in the parsing process as I + effectively use forward declarations here. So the + resolve() process may only be started _after_ all + delcarations are read. --> + <child ref="book.chapter"/> + <!-- The dot notation as path separator might be changed + but I think it to be intuitive. If we want a more + CSS like style we can use whitespaces here. --> + <child ref="book.paragraph"/> + </field> + </struct> + <struct name="chapter"> + <!-- implicitly: + <struct name="chapter" isRoot="false" cardinality="*" + transparent="false" isa="" attributesDescriptor=""> + --> + <field> + <!-- implicitly: + <field name="" isSubtree="false" optional="false"> + --> + <child ref="book.section"/> + <child ref="book.paragraph"/> + </field> + </struct> + <struct name="section"> + <!-- implicitly: + <struct name="section" isRoot="false" cardinality="*" + transparent="false" isa="" attributesDescriptor=""> + --> + <field> + <!-- implicitly: + <field name="" isSubtree="false" optional="false"> + --> + <child ref="book.subsection"/> + <child ref="book.paragraph"/> + </field> + </struct> + <struct name="subsection"> + <!-- implicitly: + <struct name="subsection" isRoot="false" cardinality="*" + transparent="false" isa="" attributesDescriptor=""> + --> + <field> + <!-- implicitly: + <field name="" isSubtree="false" optional="false"> + --> + <child ref="book.paragraph"/> + </field> + </struct> + <struct name="paragraph" transparent="true" role="paragraph"> + <!-- implicitly: + <struct name="subsection" isRoot="false" cardinality="*" + transparent="true" isa="" attributesDescriptor=""> + --> + <field> + <!-- implicitly: + <field name="" type="TREE" optional="false"> + --> + <child ref="book.text"/> + </field> + </struct> + <struct name="text" transparent="true" role="text"> + <!-- implicitly: + <struct name="text" isRoot="false" cardinality="*" + transparent="true" isa="" attributesDescriptor=""> + --> + <!-- we might want to specify std.string here --> + <primitive name="content" type="string"/> + </struct> +</domain> |