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> | 
