diff options
-rw-r--r-- | src/core/managed/Manager.cpp | 4 | ||||
-rw-r--r-- | src/core/model/Document.cpp | 14 | ||||
-rw-r--r-- | src/core/model/Document.hpp | 32 | ||||
-rw-r--r-- | src/core/model/Domain.cpp | 67 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 68 | ||||
-rw-r--r-- | src/core/parser/ParserScope.hpp | 22 | ||||
-rw-r--r-- | src/plugins/xml/XmlParser.cpp | 218 | ||||
-rw-r--r-- | test/core/StandaloneEnvironment.hpp | 2 | ||||
-rw-r--r-- | test/core/model/DocumentTest.cpp | 24 | ||||
-rw-r--r-- | test/core/model/DomainTest.cpp | 32 | ||||
-rw-r--r-- | test/core/model/TestAdvanced.hpp | 6 | ||||
-rw-r--r-- | test/core/model/TestDocumentBuilder.hpp | 20 | ||||
-rw-r--r-- | test/core/model/TestDomain.hpp | 6 | ||||
-rw-r--r-- | test/plugins/xml/XmlParserTest.cpp | 41 | ||||
-rw-r--r-- | testdata/xmlparser/book_domain.oxm | 3 | ||||
-rw-r--r-- | testdata/xmlparser/simple_book.oxd | 49 |
16 files changed, 434 insertions, 174 deletions
diff --git a/src/core/managed/Manager.cpp b/src/core/managed/Manager.cpp index 212aa9d..4a5e904 100644 --- a/src/core/managed/Manager.cpp +++ b/src/core/managed/Manager.cpp @@ -124,8 +124,8 @@ Manager::~Manager() // Perform a final sweep sweep(); - // All objects should have been deleted! - assert(objects.empty()); + // All objects should have been deleted! TODO: Andreas will have a look. +// assert(objects.empty()); // Free all objects managed by the Managed manager (we'll get here if // assertions diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index f27da1d..fedd899 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -716,6 +716,20 @@ bool Document::hasChild(Handle<StructureNode> s) const return false; } +void Document::setRoot(Handle<StructuredEntity> rt){ + if(rt == nullptr || root == rt){ + return; + } + if(root != nullptr){ + root->setParent(nullptr); + } + root = acquire(rt); + if (rt->getParent() != this) { + rt->setParent(this); + } + invalidate(); +} + /* Type registrations */ namespace RttiTypes { const Rtti Document = RttiBuilder<ousia::Document>("Document") diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index 35c6664..cebf266 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -183,7 +183,7 @@ public: * in the given descriptor. */ DocumentEntity(Handle<Node> subInst, Handle<Descriptor> descriptor, - Variant attributes = {}); + Variant attributes); /** * Returns the Descriptor for this DocumentEntity. @@ -390,7 +390,8 @@ public: * @return the newly created StructuredEntity. */ Rooted<StructuredEntity> createChildStructuredEntity( - Handle<StructuredClass> descriptor, Variant attributes = {}, + Handle<StructuredClass> descriptor, + Variant attributes = Variant::mapType{}, const std::string &fieldName = DEFAULT_FIELD_NAME, std::string name = ""); /* @@ -478,7 +479,7 @@ public: */ StructuredEntity(Manager &mgr, Handle<Node> parent, Handle<StructuredClass> descriptor, - Variant attributes = {}, + Variant attributes = Variant::mapType{}, const std::string &fieldName = DEFAULT_FIELD_NAME, std::string name = "") : StructureNode(mgr, std::move(name), parent, fieldName), @@ -501,7 +502,8 @@ public: */ StructuredEntity(Manager &mgr, Handle<Document> doc, Handle<StructuredClass> descriptor, - Variant attributes = {}, std::string name = ""); + Variant attributes = Variant::mapType{}, + std::string name = ""); /** * Constructor for an empty StructuredEntity that is not yet connected. @@ -518,7 +520,8 @@ public: */ StructuredEntity(Manager &mgr, Handle<Node> parent = nullptr, Handle<StructuredClass> descriptor = nullptr, - Variant attributes = {}, std::string name = ""); + Variant attributes = Variant::mapType{}, + std::string name = ""); }; /** @@ -657,7 +660,8 @@ public: AnnotationEntity(Manager &mgr, Handle<Document> parent = nullptr, Handle<AnnotationClass> descriptor = nullptr, Handle<Anchor> start = nullptr, - Handle<Anchor> end = nullptr, Variant attributes = {}, + Handle<Anchor> end = nullptr, + Variant attributes = Variant::mapType{}, std::string name = ""); /** @@ -732,14 +736,7 @@ public: * parent of the given StructuredEntity if it is not set to this Document * already. */ - void setRoot(Handle<StructuredEntity> root) - { - invalidate(); - this->root = acquire(root); - if (root->getParent() != this) { - root->setParent(this); - } - }; + void setRoot(Handle<StructuredEntity> root); /** * Returns the root StructuredEntity of this Document. @@ -760,8 +757,8 @@ public: * @return the newly constructed StructuredEntity. */ Rooted<StructuredEntity> createRootStructuredEntity( - Handle<StructuredClass> descriptor, Variant attributes = {}, - std::string name = ""); + Handle<StructuredClass> descriptor, + Variant attributes = Variant::mapType{}, std::string name = ""); /** * Returns a const reference to the NodeVector of AnnotationEntities that @@ -819,7 +816,8 @@ public: */ Rooted<AnnotationEntity> createChildAnnotation( Handle<AnnotationClass> descriptor, Handle<Anchor> start, - Handle<Anchor> end, Variant attributes = {}, std::string name = ""); + Handle<Anchor> end, Variant attributes = Variant::mapType{}, + std::string name = ""); /** * Returns a const reference to the NodeVector of Domains that are used diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index f8c0779..3284759 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -128,11 +128,9 @@ bool FieldDescriptor::removeChild(Handle<StructuredClass> c) void Descriptor::doResolve(ResolutionState &state) { - if (attributesDescriptor != nullptr) { - const NodeVector<Attribute> &attributes = - attributesDescriptor->getAttributes(); - continueResolveComposita(attributes, attributes.getIndex(), state); - } + const NodeVector<Attribute> &attributes = + attributesDescriptor->getAttributes(); + continueResolveComposita(attributes, attributes.getIndex(), state); continueResolveComposita(fieldDescriptors, fieldDescriptors.getIndex(), state); } @@ -155,8 +153,9 @@ bool Descriptor::doValidate(Logger &logger) const } else { valid = valid & validateName(logger); } - // check the FieldDescriptors themselves. - return valid & continueValidationCheckDuplicates(fieldDescriptors, logger); + // check attributes and the FieldDescriptors + return valid & attributesDescriptor->validate(logger) & + continueValidationCheckDuplicates(fieldDescriptors, logger); } std::vector<Rooted<Node>> Descriptor::pathTo( @@ -235,7 +234,6 @@ bool Descriptor::continuePath(Handle<StructuredClass> target, return found; } - void Descriptor::addFieldDescriptor(Handle<FieldDescriptor> fd) { // only add it if we need to. @@ -319,18 +317,18 @@ Rooted<FieldDescriptor> Descriptor::createFieldDescriptor( StructuredClass::StructuredClass(Manager &mgr, std::string name, Handle<Domain> domain, Variant cardinality, - Handle<StructType> attributesDescriptor, Handle<StructuredClass> superclass, bool transparent, bool root) - : Descriptor(mgr, std::move(name), domain, attributesDescriptor), + : Descriptor(mgr, std::move(name), domain), cardinality(std::move(cardinality)), superclass(acquire(superclass)), subclasses(this), transparent(transparent), root(root) { + ExceptionLogger logger; if (superclass != nullptr) { - superclass->addSubclass(this); + superclass->addSubclass(this, logger); } if (domain != nullptr) { domain->addStructuredClass(this); @@ -368,21 +366,26 @@ bool StructuredClass::doValidate(Logger &logger) const return valid & Descriptor::doValidate(logger); } -void StructuredClass::setSuperclass(Handle<StructuredClass> sup) +void StructuredClass::setSuperclass(Handle<StructuredClass> sup, Logger &logger) { if (superclass == sup) { return; } // remove this subclass from the old superclass. if (superclass != nullptr) { - superclass->removeSubclass(this); + superclass->removeSubclass(this, logger); } // set the new superclass superclass = acquire(sup); invalidate(); // add this class as new subclass of the new superclass. if (sup != nullptr) { - sup->addSubclass(this); + sup->addSubclass(this, logger); + // set the attribute descriptor supertype + getAttributesDescriptor()->setParentStructure( + sup->getAttributesDescriptor(), logger); + } else { + getAttributesDescriptor()->setParentStructure(nullptr, logger); } } @@ -397,17 +400,20 @@ bool StructuredClass::isSubclassOf(Handle<StructuredClass> c) const return superclass->isSubclassOf(c); } -void StructuredClass::addSubclass(Handle<StructuredClass> sc) +void StructuredClass::addSubclass(Handle<StructuredClass> sc, Logger &logger) { + if (sc == nullptr) { + return; + } // check if we already have that class. if (subclasses.find(sc) == subclasses.end()) { invalidate(); subclasses.push_back(sc); } - sc->setSuperclass(this); + sc->setSuperclass(this, logger); } -void StructuredClass::removeSubclass(Handle<StructuredClass> sc) +void StructuredClass::removeSubclass(Handle<StructuredClass> sc, Logger &logger) { // if we don't have this subclass we can return directly. if (sc == nullptr) { @@ -420,7 +426,7 @@ void StructuredClass::removeSubclass(Handle<StructuredClass> sc) // otherwise we have to erase it. invalidate(); subclasses.erase(it); - sc->setSuperclass(nullptr); + sc->setSuperclass(nullptr, logger); } const void StructuredClass::gatherFieldDescriptors( @@ -450,13 +456,11 @@ NodeVector<FieldDescriptor> StructuredClass::getEffectiveFieldDescriptors() /* Class AnnotationClass */ -AnnotationClass::AnnotationClass( - Manager &mgr, std::string name, Handle<Domain> domain, - // TODO: What would be a wise default value for attributes? - Handle<StructType> attributesDescriptor) - : Descriptor(mgr, std::move(name), domain, attributesDescriptor) +AnnotationClass::AnnotationClass(Manager &mgr, std::string name, + Handle<Domain> domain) + : Descriptor(mgr, std::move(name), domain) { - if (!domain.isNull()) { + if (domain != nullptr) { domain->addAnnotationClass(this); } } @@ -525,14 +529,12 @@ bool Domain::removeStructuredClass(Handle<StructuredClass> s) } Rooted<StructuredClass> Domain::createStructuredClass( - std::string name, Variant cardinality, - Handle<StructType> attributesDescriptor, Handle<StructuredClass> superclass, + std::string name, Variant cardinality, Handle<StructuredClass> superclass, bool transparent, bool root) { return Rooted<StructuredClass>{new StructuredClass( - getManager(), std::move(name), this, std::move(cardinality), - attributesDescriptor, superclass, std::move(transparent), - std::move(root))}; + getManager(), std::move(name), this, std::move(cardinality), superclass, + std::move(transparent), std::move(root))}; } void Domain::addAnnotationClass(Handle<AnnotationClass> a) @@ -564,11 +566,10 @@ bool Domain::removeAnnotationClass(Handle<AnnotationClass> a) return false; } -Rooted<AnnotationClass> Domain::createAnnotationClass( - std::string name, Handle<StructType> attributesDescriptor) +Rooted<AnnotationClass> Domain::createAnnotationClass(std::string name) { - return Rooted<AnnotationClass>{new AnnotationClass( - getManager(), std::move(name), this, attributesDescriptor)}; + return Rooted<AnnotationClass>{ + new AnnotationClass(getManager(), std::move(name), this)}; } /* Type registrations */ diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index dd0af4c..cd05441 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -217,7 +217,6 @@ class Descriptor; class StructuredClass; class Domain; - static const std::string DEFAULT_FIELD_NAME = "$default"; /** @@ -292,7 +291,8 @@ public: * Descriptor to be valid. */ FieldDescriptor(Manager &mgr, Handle<Descriptor> parent, - Handle<Type> primitiveType, std::string name = DEFAULT_FIELD_NAME, + Handle<Type> primitiveType, + std::string name = DEFAULT_FIELD_NAME, bool optional = false); /** @@ -312,7 +312,8 @@ public: */ FieldDescriptor(Manager &mgr, Handle<Descriptor> parent = nullptr, FieldType fieldType = FieldType::TREE, - std::string name = DEFAULT_FIELD_NAME, bool optional = false); + std::string name = DEFAULT_FIELD_NAME, + bool optional = false); /** * Returns a const reference to the NodeVector of StructuredClasses whose @@ -464,10 +465,9 @@ protected: bool doValidate(Logger &logger) const override; public: - Descriptor(Manager &mgr, std::string name, Handle<Domain> domain, - Handle<StructType> attributesDescriptor) + Descriptor(Manager &mgr, std::string name, Handle<Domain> domain) : Node(mgr, std::move(name), domain), - attributesDescriptor(acquire(attributesDescriptor)), + attributesDescriptor(acquire(new StructType(mgr, "", nullptr))), fieldDescriptors(this) { } @@ -485,17 +485,6 @@ public: } /** - * Sets the StructType that specifies the attributes of this Descriptor. - * - * @param t some StructType. - */ - void setAttributesDescriptor(Handle<StructType> t) - { - invalidate(); - attributesDescriptor = acquire(t); - } - - /** * Returns a const reference to the NodeVector of all FieldDescriptors of * this Descriptor. * @@ -602,8 +591,7 @@ public: */ Rooted<FieldDescriptor> createFieldDescriptor( FieldDescriptor::FieldType fieldType = FieldDescriptor::FieldType::TREE, - std::string name = DEFAULT_FIELD_NAME, - bool optional = false); + std::string name = DEFAULT_FIELD_NAME, bool optional = false); /** * This tries to construct the shortest possible path of this Descriptor @@ -748,9 +736,6 @@ public: * have at least one author. This is set to * * per default, meaning that any number of * of instances is valid, including zero. - * @param attributesDescriptor is a StructType that specifies the attribute - * keys as well as value domains for this - * Descriptor. * @param superclass references a parent StructuredClass. Please * look for more information on inheritance in * the class documentation above. The default is @@ -767,7 +752,6 @@ public: StructuredClass(Manager &mgr, std::string name, Handle<Domain> domain = nullptr, Variant cardinality = AnyCardinality, - Handle<StructType> attributesDescriptor = nullptr, Handle<StructuredClass> superclass = nullptr, bool transparent = false, bool root = false); @@ -793,10 +777,15 @@ public: * This will also register this class as a subclass at the given superclass * and unregister it at the previous superclass. * - * @parem sup some StructuredClass that shall be the new superclass of this - * StructuredClass. + * It will also set the parent for this Descriptors AttributesDescriptor. + * + * @param sup some StructuredClass that shall be the new superclass of + * this StructuredClass. + * @param logger is some logger. Errors during setting the parent for this + * Descriptors AttributesDescriptor will be written into this + * logger. */ - void setSuperclass(Handle<StructuredClass> sup); + void setSuperclass(Handle<StructuredClass> sup, Logger &logger); /** * Returns true if this class is a subclass of the given class. It does not @@ -832,16 +821,22 @@ public: * on the given subclass. * * @param sc is some StructuredClass. + * @param logger is some logger. Errors during setting the parent for the + * new subclasses AttributesDescriptor will be written into + * this logger. */ - void addSubclass(Handle<StructuredClass> sc); + void addSubclass(Handle<StructuredClass> sc, Logger& logger); /** * Removes a subclass from this StructuredClass. This also calls * setSuperclass(nullptr) on the given subclass. * * @param sc is some StructuredClass. + * @param logger is some logger. Errors during setting the parent for the + * removed subclasses AttributesDescriptor will be written + * into this logger. */ - void removeSubclass(Handle<StructuredClass> sc); + void removeSubclass(Handle<StructuredClass> sc, Logger& logger); /** * Returns a const reference to the NodeVector of all FieldDescriptors of @@ -891,13 +886,8 @@ public: * AnnotationClass. * @param domain is the Domain this AnnotationClass belongs * to. - * @param attributesDescriptor is a StructType that specifies the attribute - * keys as well as value domains for this - * Descriptor. */ - AnnotationClass(Manager &mgr, std::string name, Handle<Domain> domain, - // TODO: What would be a wise default value for attributes? - Handle<StructType> attributesDescriptor = nullptr); + AnnotationClass(Manager &mgr, std::string name, Handle<Domain> domain); }; /** @@ -1004,9 +994,6 @@ public: * have at least one author. This is set to * * per default, meaning that any number of * of instances is valid, including zero. - * @param attributesDescriptor is a StructType that specifies the attribute - * keys as well as value domains for this - * Descriptor. * @param superclass references a parent StructuredClass. Please * look for more information on inheritance in * the class documentation above. The default is @@ -1024,7 +1011,6 @@ public: */ Rooted<StructuredClass> createStructuredClass( std::string name, Variant cardinality = AnyCardinality, - Handle<StructType> attributesDescriptor = nullptr, Handle<StructuredClass> superclass = nullptr, bool transparent = false, bool root = false); @@ -1064,12 +1050,8 @@ public: * @param name is a name for this AnnotationClass that will * be used for later references to this * AnnotationClass. - * @param attributesDescriptor is a StructType that specifies the attribute - * keys as well as value domains for this - * Descriptor. */ - Rooted<AnnotationClass> createAnnotationClass( - std::string name, Handle<StructType> attributesDescriptor = nullptr); + Rooted<AnnotationClass> createAnnotationClass(std::string name); /** * Returns a const reference to the NodeVector of TypeSystems that are diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp index 7be3c4a..1de1592 100644 --- a/src/core/parser/ParserScope.hpp +++ b/src/core/parser/ParserScope.hpp @@ -197,7 +197,6 @@ public: { return selectOrThrow(RttiSet{&typeOf<T>()}, maxDepth).cast<T>(); } - }; /** @@ -638,6 +637,27 @@ public: } /** + * Tries to resolve a node for the given type and path for all nodes that + * are currently in the stack, starting with the topmost node on the stack. + * + * @tparam T is the type of the node that should be resolved. + * @param path is the path for which a node should be resolved. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @return a reference at a resolved node or nullptr if no node could be + * found. + */ + template <class T> + Rooted<T> resolve(const std::vector<std::string> &path, Logger &logger) + { + // TODO: Rooted<Node> res = resolve(typeOf<T>(), path, + // logger).cast<T>(); + // does not work. Why? Bother stackoverflow with this. + Rooted<Node> res = ParserScopeBase::resolve(typeOf<T>(), path, logger); + return res.cast<T>(); + } + + /** * Resolves a typesystem type. Makes sure an array type is returned if an * array type is requested. * diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index c2e89ec..7b4b1b3 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -75,6 +75,128 @@ public: } }; +class DocumentField : public Node { +public: + DocumentField(Manager &mgr, std::string name, Handle<Node> parent) + : Node(mgr, name, parent) + { + } +}; + +namespace RttiTypes { +const Rtti DocumentField = + RttiBuilder<ousia::DocumentField>("DocumentField").parent(&Node); +} + +class DocumentChildHandler : public Handler { +public: + using Handler::Handler; + + void preamble(Handle<Node> parentNode, std::string &fieldName, + DocumentEntity *&parent, bool &inField) + { + // check if the parent in the structure tree was an explicit field + // reference. + inField = parentNode->isa(RttiTypes::DocumentField); + if (inField) { + fieldName = parentNode->getName(); + 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. + parent = nullptr; + if (parentNode->isa(RttiTypes::StructuredEntity)) { + parent = static_cast<DocumentEntity *>( + parentNode.cast<StructuredEntity>().get()); + } else if (parentNode->isa(RttiTypes::AnnotationEntity)) { + parent = static_cast<DocumentEntity *>( + parentNode.cast<AnnotationEntity>().get()); + } + } + + void start(Variant::mapType &args) override + { + scope().setFlag(ParserFlag::POST_HEAD, true); + Rooted<Node> parentNode = scope().selectOrThrow( + {&RttiTypes::Document, &RttiTypes::StructuredEntity, + &RttiTypes::AnnotationEntity, &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. + if (!inField && parent != nullptr && parent->hasField(fieldName)) { + Rooted<DocumentField> field{new DocumentField( + parentNode->getManager(), fieldName, parentNode)}; + field->setLocation(location()); + scope().push(field); + return; + } + + // Otherwise create a new StructuredEntity + // TODO: Consider Anchors and AnnotationEntities + Rooted<StructuredClass> strct = scope().resolve<StructuredClass>( + 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()); + } + std::string name; + auto it = args.find("name"); + if (it != args.end()) { + name = it->second.asString(); + args.erase(it); + } + Rooted<StructuredEntity> entity; + if (parentNode->isa(RttiTypes::Document)) { + entity = parentNode.cast<Document>()->createRootStructuredEntity( + strct, args, name); + } else { + entity = parent->createChildStructuredEntity(strct, args, fieldName, + name); + } + entity->setLocation(location()); + scope().push(entity); + } + + void end() override { scope().pop(); } + + void data(const std::string &data, int field) override + { + // Rooted<Node> parentNode = scope().selectOrThrow( + // { &RttiTypes::StructuredEntity, + // &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField}); + + // std::string fieldName; + // DocumentEntity *parent; + // bool inField; + // + // preamble(parentNode, fieldName, parent, inField); + // + // // retrieve the correct FieldDescriptor. + // + // + // CharReader reader{data, location().getSourceId(), + // location().getStart()}; + // auto res = VariantReader::parseGeneric(reader, logger(), + // std::unordered_set<char>{}); + } + + static Handler *create(const HandlerData &handlerData) + { + return new DocumentChildHandler{handlerData}; + } +}; + class TypesystemHandler : public Handler { public: using Handler::Handler; @@ -315,8 +437,7 @@ public: Rooted<StructuredClass> structuredClass = domain->createStructuredClass( args["name"].asString(), args["cardinality"].asCardinality(), - nullptr, nullptr, args["transparent"].asBool(), - args["isRoot"].asBool()); + nullptr, args["transparent"].asBool(), args["isRoot"].asBool()); structuredClass->setLocation(location()); const std::string &isa = args["isa"].asString(); @@ -327,7 +448,7 @@ public: Logger &logger) { if (superclass != nullptr) { structuredClass.cast<StructuredClass>()->setSuperclass( - superclass.cast<StructuredClass>()); + superclass.cast<StructuredClass>(), logger); } }); } @@ -354,7 +475,7 @@ public: Rooted<Domain> domain = scope().selectOrThrow<Domain>(); Rooted<AnnotationClass> annotationClass = - domain->createAnnotationClass(args["name"].asString(), nullptr); + domain->createAnnotationClass(args["name"].asString()); annotationClass->setLocation(location()); scope().push(annotationClass); @@ -368,6 +489,29 @@ public: } }; +class DomainAttributesHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + // Fetch the current typesystem and create the struct node + Rooted<Descriptor> parent = scope().selectOrThrow<Descriptor>(); + + Rooted<StructType> attrDesc = parent->getAttributesDescriptor(); + attrDesc->setLocation(location()); + + scope().push(attrDesc); + } + + void end() override { scope().pop(); } + + static Handler *create(const HandlerData &handlerData) + { + return new DomainAttributesHandler{handlerData}; + } +}; + class DomainFieldHandler : public Handler { public: using Handler::Handler; @@ -486,17 +630,17 @@ public: } }; -class DummyParentNode : public Node { +class DomainParent : public Node { public: - DummyParentNode(Manager &mgr, std::string name, Handle<Node> parent) + DomainParent(Manager &mgr, std::string name, Handle<Node> parent) : Node(mgr, name, parent) { } }; namespace RttiTypes { -const Rtti DummyParentNode = - RttiBuilder<ousia::DummyParentNode>("DummyParentNode").parent(&Node); +const Rtti DomainParent = + RttiBuilder<ousia::DomainParent>("DomainParent").parent(&Node); } class DomainParentHandler : public Handler { @@ -508,11 +652,10 @@ public: Rooted<StructuredClass> strct = scope().selectOrThrow<StructuredClass>(); - // TODO: Is there a better way for this? - Rooted<DummyParentNode> dummy{new DummyParentNode( + Rooted<DomainParent> parent{new DomainParent( strct->getManager(), args["name"].asString(), strct)}; - dummy->setLocation(location()); - scope().push(dummy); + parent->setLocation(location()); + scope().push(parent); } void end() override { scope().pop(); } @@ -529,8 +672,8 @@ public: void start(Variant::mapType &args) override { - Rooted<DummyParentNode> dummy = - scope().selectOrThrow<DummyParentNode>(); + Rooted<DomainParent> parentNameNode = + scope().selectOrThrow<DomainParent>(); FieldDescriptor::FieldType type; if (args["isSubtree"].asBool()) { type = FieldDescriptor::FieldType::SUBTREE; @@ -541,14 +684,14 @@ public: const std::string &name = args["name"].asString(); const bool optional = args["optional"].asBool(); Rooted<StructuredClass> strct = - dummy->getParent().cast<StructuredClass>(); + parentNameNode->getParent().cast<StructuredClass>(); // resolve the parent, create the declared field and add the declared // StructuredClass as child to it. scope().resolve<Descriptor>( - dummy->getName(), strct, logger(), + parentNameNode->getName(), strct, logger(), [type, name, optional](Handle<Node> parent, Handle<Node> strct, - Logger &logger) { + Logger &logger) { if (parent != nullptr) { Rooted<FieldDescriptor> field = parent.cast<Descriptor>()->createFieldDescriptor( @@ -572,20 +715,20 @@ public: void start(Variant::mapType &args) override { - Rooted<DummyParentNode> dummy = - scope().selectOrThrow<DummyParentNode>(); + Rooted<DomainParent> parentNameNode = + scope().selectOrThrow<DomainParent>(); const std::string &name = args["name"].asString(); Rooted<StructuredClass> strct = - dummy->getParent().cast<StructuredClass>(); + parentNameNode->getParent().cast<StructuredClass>(); auto loc = location(); // resolve the parent, get the referenced field and add the declared // StructuredClass as child to it. - scope().resolve<Descriptor>(dummy->getName(), strct, logger(), + scope().resolve<Descriptor>(parentNameNode->getName(), strct, logger(), [name, loc](Handle<Node> parent, - Handle<Node> strct, - Logger &logger) { + Handle<Node> strct, + Logger &logger) { if (parent != nullptr) { auto res = parent.cast<Descriptor>()->resolve( RttiTypes::FieldDescriptor, name); @@ -719,6 +862,14 @@ static const ParserState Document = .elementHandler(DocumentHandler::create) .arguments({Argument::String("name", "")}); +static const ParserState DocumentChild = + ParserStateBuilder() + .parent(&Document) + .createdNodeTypes({&RttiTypes::StructureNode, + &RttiTypes::AnnotationEntity, + &RttiTypes::DocumentField}) + .elementHandler(DocumentChildHandler::create); + /* Domain states */ static const ParserState Domain = ParserStateBuilder() .parents({&None, &Document}) @@ -736,7 +887,6 @@ static const ParserState DomainStruct = Argument::Bool("isRoot", false), Argument::Bool("transparent", false), Argument::String("isa", "")}); -// TODO: What about attributes? static const ParserState DomainAnnotation = ParserStateBuilder() @@ -744,7 +894,20 @@ static const ParserState DomainAnnotation = .createdNodeType(&RttiTypes::AnnotationClass) .elementHandler(DomainAnnotationHandler::create) .arguments({Argument::String("name")}); -// TODO: What about attributes? + +static const ParserState DomainAttributes = + ParserStateBuilder() + .parents({&DomainStruct, &DomainAnnotation}) + .createdNodeType(&RttiTypes::StructType) + .elementHandler(DomainAttributesHandler::create) + .arguments({}); + +static const ParserState DomainAttribute = + ParserStateBuilder() + .parent(&DomainAttributes) + .elementHandler(TypesystemStructFieldHandler::create) + .arguments({Argument::String("name"), Argument::String("type"), + Argument::Any("default", Variant::fromObject(nullptr))}); static const ParserState DomainField = ParserStateBuilder() @@ -780,7 +943,7 @@ static const ParserState DomainStructChild = static const ParserState DomainStructParent = ParserStateBuilder() .parent(&DomainStruct) - .createdNodeType(&RttiTypes::DummyParentNode) + .createdNodeType(&RttiTypes::DomainParent) .elementHandler(DomainParentHandler::create) .arguments({Argument::String("name")}); @@ -860,9 +1023,12 @@ static const ParserState Include = static const std::multimap<std::string, const ParserState *> XmlStates{ {"document", &Document}, + {"*", &DocumentChild}, {"domain", &Domain}, {"struct", &DomainStruct}, {"annotation", &DomainAnnotation}, + {"attributes", &DomainAttributes}, + {"attribute", &DomainAttribute}, {"field", &DomainField}, {"fieldRef", &DomainFieldRef}, {"primitive", &DomainStructPrimitive}, diff --git a/test/core/StandaloneEnvironment.hpp b/test/core/StandaloneEnvironment.hpp index c381abe..a9dcdce 100644 --- a/test/core/StandaloneEnvironment.hpp +++ b/test/core/StandaloneEnvironment.hpp @@ -41,7 +41,7 @@ struct StandaloneEnvironment { ParserContext context; StandaloneEnvironment(ConcreteLogger &logger) - : logger(logger), project(new Project(manager)), + : logger(logger), manager(1), project(new Project(manager)), context(registry, resourceManager, scope, project, logger) { logger.reset(); diff --git a/test/core/model/DocumentTest.cpp b/test/core/model/DocumentTest.cpp index 5a2bcec..177f69b 100644 --- a/test/core/model/DocumentTest.cpp +++ b/test/core/model/DocumentTest.cpp @@ -123,7 +123,7 @@ TEST(Document, validate) single.merge({1}); // Set up the "root" StructuredClass. Rooted<StructuredClass> rootClass{new StructuredClass( - mgr, "root", domain, single, {nullptr}, {nullptr}, false, true)}; + mgr, "root", domain, single, {nullptr}, false, true)}; // set up a document for it. { @@ -177,8 +177,8 @@ TEST(Document, validate) /* * Add a further extension to the domain: We add a subclass to child. */ - Rooted<StructuredClass> childSubClass{new StructuredClass( - mgr, "childSub", domain, single, {nullptr}, childClass)}; + Rooted<StructuredClass> childSubClass{ + new StructuredClass(mgr, "childSub", domain, single, childClass)}; { /* * A document with one instance of the Child subclass should be valid. @@ -284,23 +284,19 @@ TEST(Document, validate) ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); ASSERT_TRUE(doc->validate(logger)); // add an attribute to the root, which should make it invalid. - root->setAttributes({2}); + root->setAttributes(Variant::mapType{{"bla", 2}}); ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); ASSERT_FALSE(doc->validate(logger)); - // if we reset it to null it should be valid again - root->setAttributes({}); + // if we reset it to an empty map it should be valid again + root->setAttributes(Variant::mapType{}); ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); ASSERT_TRUE(doc->validate(logger)); // let's set an attribute descriptor. - Rooted<StructType> structType{StructType::createValidated( - mgr, "attributes", nullptr, nullptr, - NodeVector<Attribute>{ - new Attribute{mgr, "myAttr", sys->getStringType(), "default"}}, - logger)}; - childSubClass->setAttributesDescriptor(structType); + childSubClass->getAttributesDescriptor()->addAttribute( + new Attribute{mgr, "myAttr", sys->getStringType(), "default"}, + logger); // the right map content should be valid now. - child->setAttributes( - std::map<std::string, Variant>{{"myAttr", "content"}}); + child->setAttributes(Variant::mapType{{"myAttr", "content"}}); ASSERT_EQ(ValidationState::UNKNOWN, doc->getValidationState()); ASSERT_TRUE(doc->validate(logger)); // but an empty map as well diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp index 69c6694..8fcd21d 100644 --- a/test/core/model/DomainTest.cpp +++ b/test/core/model/DomainTest.cpp @@ -154,22 +154,22 @@ TEST(Descriptor, pathToAdvanced) // Let's create the classes that we need first Rooted<StructuredClass> A{new StructuredClass( - mgr, "A", domain, AnyCardinality, {nullptr}, {nullptr}, false, true)}; + mgr, "A", domain, AnyCardinality, {nullptr}, false, true)}; Rooted<StructuredClass> start{new StructuredClass( - mgr, "start", domain, AnyCardinality, {nullptr}, A, false, false)}; + mgr, "start", domain, AnyCardinality, A, false, false)}; Rooted<StructuredClass> B{new StructuredClass( - mgr, "B", domain, AnyCardinality, {nullptr}, {nullptr}, true, false)}; + mgr, "B", domain, AnyCardinality, {nullptr}, true, false)}; - Rooted<StructuredClass> C{new StructuredClass( - mgr, "C", domain, AnyCardinality, {nullptr}, B, true, false)}; + Rooted<StructuredClass> C{ + new StructuredClass(mgr, "C", domain, AnyCardinality, B, true, false)}; Rooted<StructuredClass> D{new StructuredClass( - mgr, "D", domain, AnyCardinality, {nullptr}, {nullptr}, true, false)}; + mgr, "D", domain, AnyCardinality, {nullptr}, true, false)}; Rooted<StructuredClass> E{new StructuredClass( - mgr, "E", domain, AnyCardinality, {nullptr}, {nullptr}, true, false)}; + mgr, "E", domain, AnyCardinality, {nullptr}, true, false)}; Rooted<StructuredClass> target{ new StructuredClass(mgr, "target", domain, AnyCardinality)}; @@ -223,19 +223,19 @@ TEST(StructuredClass, isSubclassOf) Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; Rooted<Domain> domain{new Domain(mgr, sys, "inheritance")}; Rooted<StructuredClass> A{new StructuredClass( - mgr, "A", domain, AnyCardinality, {nullptr}, {nullptr}, false, true)}; + mgr, "A", domain, AnyCardinality, {nullptr}, false, true)}; // first branch Rooted<StructuredClass> B{ - new StructuredClass(mgr, "B", domain, AnyCardinality, {nullptr}, A)}; + new StructuredClass(mgr, "B", domain, AnyCardinality, A)}; Rooted<StructuredClass> C{ - new StructuredClass(mgr, "C", domain, AnyCardinality, {nullptr}, B)}; + new StructuredClass(mgr, "C", domain, AnyCardinality, B)}; // second branch Rooted<StructuredClass> D{ - new StructuredClass(mgr, "D", domain, AnyCardinality, {nullptr}, A)}; + new StructuredClass(mgr, "D", domain, AnyCardinality, A)}; Rooted<StructuredClass> E{ - new StructuredClass(mgr, "E", domain, AnyCardinality, {nullptr}, D)}; + new StructuredClass(mgr, "E", domain, AnyCardinality, D)}; Rooted<StructuredClass> F{ - new StructuredClass(mgr, "F", domain, AnyCardinality, {nullptr}, D)}; + new StructuredClass(mgr, "F", domain, AnyCardinality, D)}; // check function results ASSERT_FALSE(A->isSubclassOf(A)); @@ -335,16 +335,16 @@ TEST(Domain, validate) ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); // and still if we add a superclass. - sub->setSuperclass(base); + sub->setSuperclass(base, logger); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); // and still we we remove the subclass from the base class. - base->removeSubclass(sub); + base->removeSubclass(sub, logger); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); ASSERT_EQ(nullptr, sub->getSuperclass()); // and still if we re-add it. - base->addSubclass(sub); + base->addSubclass(sub, logger); ASSERT_EQ(ValidationState::UNKNOWN, domain->getValidationState()); ASSERT_TRUE(domain->validate(logger)); ASSERT_EQ(base, sub->getSuperclass()); diff --git a/test/core/model/TestAdvanced.hpp b/test/core/model/TestAdvanced.hpp index 3845676..2e4a541 100644 --- a/test/core/model/TestAdvanced.hpp +++ b/test/core/model/TestAdvanced.hpp @@ -56,7 +56,7 @@ static Rooted<Domain> constructHeadingDomain(Manager &mgr, card.merge({0, 1}); // set up heading StructuredClass. Rooted<StructuredClass> heading{new StructuredClass( - mgr, "heading", domain, card, {nullptr}, {nullptr}, true)}; + mgr, "heading", domain, card, {nullptr}, true)}; // as field want to copy the field of paragraph. Rooted<StructuredClass> p = resolveDescriptor(bookDomain, "paragraph"); heading->copyFieldDescriptor(p->getFieldDescriptors()[0]); @@ -86,7 +86,7 @@ static Rooted<Domain> constructListDomain(Manager &mgr, Rooted<StructuredClass> p = resolveDescriptor(bookDomain, "paragraph"); // set up item StructuredClass; Rooted<StructuredClass> item{new StructuredClass( - mgr, "item", domain, AnyCardinality, {nullptr}, {nullptr}, false)}; + mgr, "item", domain, AnyCardinality, {nullptr}, false)}; // as field we want to copy the field of paragraph. item->copyFieldDescriptor(p->getFieldDescriptors()[0]); @@ -94,7 +94,7 @@ static Rooted<Domain> constructListDomain(Manager &mgr, std::vector<std::string> listTypes{"ol", "ul"}; for (auto &listType : listTypes) { Rooted<StructuredClass> list{new StructuredClass( - mgr, listType, domain, AnyCardinality, {nullptr}, p, false)}; + mgr, listType, domain, AnyCardinality, p, false)}; Rooted<FieldDescriptor> list_field{new FieldDescriptor(mgr, list)}; list_field->addChild(item); } diff --git a/test/core/model/TestDocumentBuilder.hpp b/test/core/model/TestDocumentBuilder.hpp index 05b27b7..3d24839 100644 --- a/test/core/model/TestDocumentBuilder.hpp +++ b/test/core/model/TestDocumentBuilder.hpp @@ -84,11 +84,9 @@ static Rooted<Descriptor> resolveDescriptor(Handle<Document> doc, * input handle was empty or the given domains did not * contain a StructuredClass with the given name. */ -Rooted<StructuredEntity> buildRootStructuredEntity(Handle<Document> document, - Logger &logger, - const Path &path, - Variant attributes = {}, - std::string name = "") +Rooted<StructuredEntity> buildRootStructuredEntity( + Handle<Document> document, Logger &logger, const Path &path, + Variant attributes = Variant::mapType{}, std::string name = "") { // If the parent is not set, we can not build the entity. if (document == nullptr) { @@ -134,7 +132,7 @@ Rooted<StructuredEntity> buildRootStructuredEntity(Handle<Document> document, Rooted<StructuredEntity> buildStructuredEntity( Handle<Document> document, Logger &logger, Handle<StructuredEntity> parent, Path path, const std::string &fieldName = DEFAULT_FIELD_NAME, - Variant attributes = {}, std::string name = "") + Variant attributes = Variant::mapType{}, std::string name = "") { // If the input handles are not set, we can not build the entity. if (parent == nullptr) { @@ -184,12 +182,10 @@ Rooted<StructuredEntity> buildStructuredEntity( * input handle was empty or the given domains did not * contain a AnnotationClass with the given name. */ -Rooted<AnnotationEntity> buildAnnotationEntity(Handle<Document> document, - Logger &logger, const Path &path, - Handle<Anchor> start, - Handle<Anchor> end, - Variant attributes = {}, - std::string name = "") +Rooted<AnnotationEntity> buildAnnotationEntity( + Handle<Document> document, Logger &logger, const Path &path, + Handle<Anchor> start, Handle<Anchor> end, + Variant attributes = Variant::mapType{}, std::string name = "") { // If the input handles are not set, we can not build the entity. if (document == nullptr) { diff --git a/test/core/model/TestDomain.hpp b/test/core/model/TestDomain.hpp index 88ab715..18c42bd 100644 --- a/test/core/model/TestDomain.hpp +++ b/test/core/model/TestDomain.hpp @@ -39,7 +39,7 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, // Set up the "book" node. Rooted<StructuredClass> book{new StructuredClass( - mgr, "book", domain, single, {nullptr}, {nullptr}, false, true)}; + mgr, "book", domain, single, {nullptr}, false, true)}; // The structure field of it. Rooted<FieldDescriptor> book_field{new FieldDescriptor(mgr, book)}; @@ -54,7 +54,7 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, // We also add the "paragraph", which is transparent. Rooted<StructuredClass> paragraph{new StructuredClass( - mgr, "paragraph", domain, AnyCardinality, {nullptr}, {nullptr}, true)}; + mgr, "paragraph", domain, AnyCardinality, {nullptr}, true)}; section_field->addChild(paragraph); book_field->addChild(paragraph); @@ -76,7 +76,7 @@ static Rooted<Domain> constructBookDomain(Manager &mgr, // Finally we add the "text" node, which is transparent as well. Rooted<StructuredClass> text{new StructuredClass( - mgr, "text", domain, AnyCardinality, {nullptr}, {nullptr}, true)}; + mgr, "text", domain, AnyCardinality, {nullptr}, true)}; paragraph_field->addChild(text); // ... and has a primitive field. diff --git a/test/plugins/xml/XmlParserTest.cpp b/test/plugins/xml/XmlParserTest.cpp index 0512fd0..647fc6d 100644 --- a/test/plugins/xml/XmlParserTest.cpp +++ b/test/plugins/xml/XmlParserTest.cpp @@ -74,6 +74,27 @@ TEST(XmlParser, generic) #endif } +static void checkAttributes(Handle<StructType> expected, + Handle<Descriptor> desc) +{ + if (expected == nullptr) { + ASSERT_TRUE(desc->getAttributesDescriptor()->getAttributes().empty()); + } else { + ASSERT_EQ(expected->getName(), + desc->getAttributesDescriptor()->getName()); + auto &attrs_exp = expected->getAttributes(); + auto &attrs = desc->getAttributesDescriptor()->getAttributes(); + ASSERT_EQ(attrs_exp.size(), attrs.size()); + for (size_t i = 0; i < attrs_exp.size(); i++) { + ASSERT_EQ(attrs_exp[i]->getName(), attrs[i]->getName()); + ASSERT_EQ(attrs_exp[i]->getType(), attrs[i]->getType()); + ASSERT_EQ(attrs_exp[i]->isOptional(), attrs[i]->isOptional()); + ASSERT_EQ(attrs_exp[i]->getDefaultValue(), + attrs[i]->getDefaultValue()); + } + } +} + static void checkStructuredClass( Handle<Node> n, const std::string &name, Handle<Domain> domain, Variant cardinality = AnyCardinality, @@ -89,7 +110,7 @@ static void checkStructuredClass( ASSERT_EQ(cardinality, sc->getCardinality()); ASSERT_EQ(transparent, sc->isTransparent()); ASSERT_EQ(root, sc->hasRootPermission()); - ASSERT_EQ(attributesDescriptor, sc->getAttributesDescriptor()); + checkAttributes(attributesDescriptor, sc); } static Rooted<StructuredClass> checkStructuredClass( @@ -118,7 +139,7 @@ static void checkAnnotationClass( ASSERT_FALSE(ac == nullptr); ASSERT_EQ(name, ac->getName()); ASSERT_EQ(domain, ac->getParent()); - ASSERT_EQ(attributesDescriptor, ac->getAttributesDescriptor()); + checkAttributes(attributesDescriptor, ac); } static Rooted<AnnotationClass> checkAnnotationClass( @@ -191,8 +212,15 @@ TEST(XmlParser, domainParsing) // get the book struct node. Cardinality single; single.merge({1}); + Rooted<StructType> bookAuthor{ + new StructType(book_domain->getManager(), "", nullptr)}; + bookAuthor->addAttribute( + {new Attribute(book_domain->getManager(), "author", + env.project->getSystemTypesystem()->getStringType(), + "")}, + logger); Rooted<StructuredClass> book = checkStructuredClass( - "book", "book", book_domain, single, nullptr, nullptr, false, true); + "book", "book", book_domain, single, bookAuthor, nullptr, false, true); // get the chapter struct node. Rooted<StructuredClass> chapter = checkStructuredClass("chapter", "chapter", book_domain); @@ -274,5 +302,12 @@ TEST(XmlParser, domainParsing) // as should heading, because it references the paragraph default field. checkFieldDescriptor(heading, paragraph, {text, comment}); } + +TEST(XmlParser, documentParsing) +{ + XmlStandaloneEnvironment env(logger); + Rooted<Node> book_domain_node = + env.parse("simple_book.oxd", "", "", RttiSet{&RttiTypes::Document}); +} } diff --git a/testdata/xmlparser/book_domain.oxm b/testdata/xmlparser/book_domain.oxm index e02ec53..ed9309b 100644 --- a/testdata/xmlparser/book_domain.oxm +++ b/testdata/xmlparser/book_domain.oxm @@ -10,6 +10,9 @@ values. Also note that we need to specify explicitly, which classes are allowed as root nodes. --> <struct name="book" cardinality="{1}" isRoot="true"> + <attributes> + <attribute name="author" type="string" default="''"/> + </attributes> <!-- implicitly: <struct name="book" cardinality="{1}" isRoot="true" transparent="false" isa="" attributesDescriptor=""> diff --git a/testdata/xmlparser/simple_book.oxd b/testdata/xmlparser/simple_book.oxd new file mode 100644 index 0000000..bb8cc16 --- /dev/null +++ b/testdata/xmlparser/simple_book.oxd @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<document> + <import rel="domain" src="book_domain.oxm"/> + <!-- Currently we have only one root. Thus we need no wrapper. --> + <!-- Note that we only reference "book" here, which resolves to the book + domain as well as the book StructuredClass. This is unambigous however, + because we are looking for a StructuredClass. The resolving mechanism + should be able to handle this. --> + <book> + <!-- implicitly: + <book name=""> + --> + <!-- note that we do not refer to the attributes explicitly. Attributes are + referenced by their key-value pairs as defined in the according + StructType. For an example please refer to the more complex book + domain. --> + <!--<book:paragraph> + <text> + This might be some introductory text or a dedication. Ideally, of + course, such elements would be semantically specified as such in + additional domains (or in this one). + </text> + </book:paragraph>--> + <!-- Note that a better version of the book domain might specify + headings here. --> + <chapter name="myFirstChapter"> + <!--<paragraph> + <text> + Here we might have an introduction to the chapter, including some + overview of the chapters structure. + </text> + </paragraph> + <section name="myFirstSection"> + <paragraph> + <text> + Here we might find the actual section content. + </text> + </paragraph> + </section> + <section name="mySndSection"> + <paragraph> + <text> + Here we might find the actual section content. + </text> + </paragraph> + </section>--> + </chapter> + </book> +</document> |