summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/plugins/xml/XmlParser.cpp74
-rw-r--r--test/plugins/xml/XmlParserTest.cpp62
-rw-r--r--testdata/xmlparser/comments_domain.oxm40
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>