diff options
| author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-12-19 15:36:41 +0100 | 
|---|---|---|
| committer | Andreas Stöckel <andreas@somweyr.de> | 2014-12-19 15:36:41 +0100 | 
| commit | 2eda4f5ba1e3ed0577e3804110debf102abc9d43 (patch) | |
| tree | f7f823e10d32ebc96aba8d9014a148bd6a6d3a9b | |
| parent | 6fa81636f088654f154534fd939421a5b25d9c5c (diff) | |
| parent | 3654efa59547d23b3c27715483999570daedc0b3 (diff) | |
Merge branch 'master' of somweyr.de:ousia
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/Node.hpp | 5 | ||||
| -rw-r--r-- | src/core/managed/ManagedContainer.hpp | 11 | ||||
| -rw-r--r-- | src/core/model/Document.cpp | 104 | ||||
| -rw-r--r-- | src/core/model/Document.hpp | 108 | ||||
| -rw-r--r-- | src/core/model/Domain.cpp | 54 | ||||
| -rw-r--r-- | src/core/model/Domain.hpp | 112 | ||||
| -rw-r--r-- | src/core/model/Typesystem.hpp | 80 | ||||
| -rw-r--r-- | test/core/managed/ManagedContainerTest.cpp | 41 | ||||
| -rw-r--r-- | test/core/model/DocumentTest.cpp | 44 | ||||
| -rw-r--r-- | test/core/model/DomainTest.cpp | 100 | ||||
| -rw-r--r-- | test/core/model/TestDocument.hpp | 84 | ||||
| -rw-r--r-- | test/core/model/TestDomain.hpp | 94 | 
13 files changed, 767 insertions, 72 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 285abca..077ac47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,8 @@ IF(TEST)  		test/core/managed/ManagedContainerTest  		test/core/managed/ManagedTest  		test/core/managed/ManagerTest +		test/core/model/DomainTest +		test/core/model/DocumentTest  		test/core/parser/ParserStackTest  #		test/core/script/FunctionTest  #		test/core/script/ObjectTest diff --git a/src/core/Node.hpp b/src/core/Node.hpp index 4bc95be..516da03 100644 --- a/src/core/Node.hpp +++ b/src/core/Node.hpp @@ -350,6 +350,11 @@ public:  	 * Returns the name of the node.  	 */  	std::string getName() const { return name; } +	 +	/** +	 * Returns a reference to the name of the node. +	 */ +	const std::string& getNameRef() const { return name; }  	/**  	 * Specifies whether the node has a name, e.g. whether the current name is diff --git a/src/core/managed/ManagedContainer.hpp b/src/core/managed/ManagedContainer.hpp index 19bff3f..6bf1915 100644 --- a/src/core/managed/ManagedContainer.hpp +++ b/src/core/managed/ManagedContainer.hpp @@ -32,6 +32,7 @@  #include <unordered_set>  #include <vector>  #include <map> +#include <stdexcept>  #include <type_traits>  #include "Manager.hpp" @@ -496,6 +497,16 @@ public:  		}  		Base::c.pop_back();  	} +	 +	Rooted<T> operator[](int i) const { +		for (const_iterator it = Base::cbegin(); it != Base::cend(); it++) { +			if (i == 0) { +				return Rooted<T>(*it); +			} +			i--; +		} +		throw std::out_of_range(std::to_string(i) + " is out of range!"); +	}  };  /** diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index 31b22e3..709981b 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -58,7 +58,7 @@ int DocumentEntity::getFieldDescriptorIndex(const std::string &fieldName)  }  void DocumentEntity::getField(ManagedVector<StructuredEntity> &res, -              const std::string &fieldName) +                              const std::string &fieldName)  {  	int f = getFieldDescriptorIndex(fieldName);  	if (f < 0) { @@ -85,6 +85,108 @@ ManagedVector<StructuredEntity> &DocumentEntity::getField(  	    "The given FieldDescriptor is not specified in the Descriptor of this "  	    "node.");  } + +static Rooted<StructuredClass> resolveDescriptor( +    std::vector<Handle<Domain>> domains, const std::string &className) +{ +	// iterate over all domains. +	for (auto &d : domains) { +		// use the actual resolve method. +		std::vector<Rooted<Managed>> resolved = d->resolve(className); +		// if we don't find anything, continue. +		if (resolved.size() == 0) { +			continue; +		} +		// Otherwise take the first valid result. +		for (auto &r : resolved) { +			Managed *m = &(*r); +			StructuredClass *c = dynamic_cast<StructuredClass *>(m); +			if (c != nullptr) { +				return Rooted<StructuredClass>(c); +			} +		} +	} +	return {nullptr}; +} + +Rooted<StructuredEntity> StructuredEntity::buildRootEntity( +    Handle<Document> document, std::vector<Handle<Domain>> domains, +    const std::string &className, Variant attributes, std::string name) +{ +	// If the parent is not set, we can not build the entity. +	if (document == nullptr) { +		return {nullptr}; +	} +	// If we can not find the correct descriptor, we can not build the entity +	// either. +	Rooted<StructuredClass> descriptor = resolveDescriptor(domains, className); +	if (descriptor == nullptr) { +		return {nullptr}; +	} +	// Then construct the StructuredEntity itself. +	Rooted<StructuredEntity> root{ +	    new StructuredEntity(document->getManager(), document, descriptor, +	                         attributes, std::move(name))}; +	// append it to the document. +	document->setRoot(root); +	// and return it. +	return root; +} + +Rooted<StructuredEntity> StructuredEntity::buildEntity( +    Handle<DocumentEntity> parent, std::vector<Handle<Domain>> domains, +    const std::string &className, const std::string &fieldName, +    Variant attributes, std::string name) +{ +	// If the parent is not set, we can not build the entity. +	if (parent == nullptr) { +		return {nullptr}; +	} +	// If we can not find the correct descriptor, we can not build the entity +	// either. +	Rooted<StructuredClass> descriptor = resolveDescriptor(domains, className); +	if (descriptor == nullptr) { +		return {nullptr}; +	} +	// Then construct the StructuredEntity itself. +	Rooted<StructuredEntity> entity{new StructuredEntity( +	    parent->getManager(), parent, descriptor, attributes, std::move(name))}; +	// if the field does not exist, return null handle as well. +	if (!parent->hasField(fieldName)) { +		return {nullptr}; +	} +	// append the new entity to the right field. +	ManagedVector<StructuredEntity> field{parent}; +	parent->getField(field, fieldName); +	field.push_back(entity); + +	// and return it. +	return entity; +} + +Rooted<DocumentPrimitive> DocumentPrimitive::buildEntity( +    Handle<DocumentEntity> parent, Variant content, +    const std::string &fieldName) +{ +	// If the parent is not set, we can not build the entity. +	if (parent == nullptr) { +		return {nullptr}; +	} +	// Then construct the StructuredEntity itself. +	Rooted<DocumentPrimitive> entity{ +	    new DocumentPrimitive(parent->getManager(), parent, content)}; +	// if the field does not exist, return null handle as well. +	if (!parent->hasField(fieldName)) { +		return {nullptr}; +	} +	// append the new entity to the right field. +	ManagedVector<StructuredEntity> field{parent}; +	parent->getField(field, fieldName); +	field.push_back(entity); + +	// and return it. +	return entity; +}  }  } diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index 3114480..15a4599 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -26,9 +26,11 @@   *   * A Document, from top to bottom, consists of "Document" instance,   * which "owns" the structural root node of the in-document graph. This might - * for example be a "book" node, if the respective document implements the - * "book" domain. That root node in turn has structure nodes as children as well - * as annotations that refer to the content of that structure node. + * for example be a "book" node of the "book" domain. That root node in turn has + * structure nodes as children, which in turn may have children. This + * constitutes a Structure Tree. Additionally annotations may be attached to + * Structure Nodes, effectively resulting in a Document Graph instead of a + * Document Tree (other references may introduce cycles as well).   *   * Consider this simplified XML representation of a document (TODO: Use   * non-simplified XML as soon as possible): @@ -78,6 +80,7 @@ namespace model {  class StructuredEntity;  class AnnotationEntity; +class Document;  /**   * A DocumentEntity is the common superclass for StructuredEntities and @@ -108,8 +111,11 @@ public:  	{  		// TODO: Validation at construction time?  		// insert empty vectors for each field. -		for (size_t f = 0; f < descriptor->getFieldDescriptors().size(); f++) { -			fields.push_back(ManagedVector<StructuredEntity>(this)); +		if (!descriptor.isNull()) { +			for (size_t f = 0; f < descriptor->getFieldDescriptors().size(); +			     f++) { +				fields.push_back(ManagedVector<StructuredEntity>(this)); +			}  		}  	} @@ -200,6 +206,57 @@ public:  	}  	ManagedVector<AnnotationEntity> &getAnnotations() { return annotations; } + +	/** +	 * This builds the root StructuredEntity for the given document. It +	 * automatically appends the newly build entity to the given document. +	 * +	 * @param document   is the document this entity shall be build for. The +	 *                   resulting entity will automatically be appended to that +	 *                   document. Also the manager of that document will be +	 *                   used to register the new node. +	 * @param domains    are the domains that are used to find the +	 *                   StructuredClass for the new node. The domains will be +	 *                   searched in the given order. +	 * @param className  is the name of the StructuredClass. +	 * @param attributes are the attributes of the new node in terms of a Struct +	 *                   variant (empty per default). +	 * @param name       is the name of this StructuredEntity (empty per +	 *                   default). +	 * @return           the newly created StructuredEntity or a nullptr if some +	 *                   input handle was empty or the given domains did not +	 *                   contain a StructuredClass with the given name. +	 */ +	static Rooted<StructuredEntity> buildRootEntity( +	    Handle<Document> document, std::vector<Handle<Domain>> domains, +	    const std::string &className, Variant attributes = Variant(), +	    std::string name = ""); + +	/** +	 * This builds a StructuredEntity as child of the given DocumentEntity. It +	 * automatically appends the newly build entity to its parent. +	 * +	 * @param parent     is the parent DocumentEntity. The newly constructed +	 *                   StructuredEntity will automatically be appended to it. +	 * @param domains    are the domains that are used to find the +	 *                   StructuredClass for the new node. The domains will be +	 *                   searched in the given order. +	 * @param className  is the name of the StructuredClass. +	 * @param fieldName  is the name of the field where the newly constructed +	 *                   StructuredEntity shall be appended. +	 * @param attributes are the attributes of the new node in terms of a Struct +	 *                   variant (empty per default). +	 * @param name       is the name of this StructuredEntity (empty per +	 *                   default). +	 * +	 * @return           the newly created StructuredEntity or a nullptr if some +	 *                   input handle was empty or the given domains did not +	 *                   contain a StructuredClass with the given name. +	 */ +	static Rooted<StructuredEntity> buildEntity( +	    Handle<DocumentEntity> parent, std::vector<Handle<Domain>> domains, +	    const std::string &className, const std::string &fieldName = "", +	    Variant attributes = Variant(), std::string name = "");  };  /** @@ -209,7 +266,7 @@ public:   */  class DocumentPrimitive : public StructuredEntity {  public: -	DocumentPrimitive(Manager &mgr, Handle<StructuredEntity> parent, +	DocumentPrimitive(Manager &mgr, Handle<DocumentEntity> parent,  	                  Variant content)  	    : StructuredEntity(mgr, parent, nullptr, std::move(content))  	{ @@ -218,6 +275,25 @@ public:  	Variant getContent() const { return getAttributes(); }  	// TODO: Override such methods like "getField" to disable them? + +	/** +	 * This builds a DocumentPrimitive as child of the given DocumentEntity. It +	 * automatically appends the newly build entity to its parent. +	 * +	 * @param parent     is the parent DocumentEntity. The newly constructed +	 *                   DocumentPrimitive will automatically be appended to it. +	 * @param content    is the primitive content of the new node in terms of a +	 *                   Struct variant. +	 * @param fieldName  is the name of the field where the newly constructed +	 *                   StructuredEntity shall be appended. +	 * +	 * @return           the newly created StructuredEntity or a nullptr if some +	 *                   input handle was empty or the given domains did not +	 *                   contain a StructuredClass with the given name. +	 */ +	static Rooted<DocumentPrimitive> buildEntity( +	    Handle<DocumentEntity> parent,  +	    Variant content, const std::string &fieldName = "");  };  /** @@ -282,6 +358,26 @@ public:  	Rooted<Anchor> getEnd() { return end; }  }; + +/** + * A Document is mainly a wrapper for the Root structure node of the Document + * Graph. + */ +class Document : public Node { +private: +	Owned<StructuredEntity> root; + +public: +	Document(Manager &mgr, std::string name) +	    // TODO: Can a document have a parent? +	    : Node(mgr, std::move(name), nullptr) +	{ +	} + +	void setRoot(Handle<StructuredEntity> root) { root = acquire(root); }; + +	Rooted<StructuredEntity> getRoot() const { return root; } +};  }  } diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index bafd205..8eee86a 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -21,6 +21,60 @@  namespace ousia {  namespace model { +void FieldDescriptor::doResolve(std::vector<Rooted<Managed>> &res, +                                const std::vector<std::string> &path, +                                Filter filter, void *filterData, unsigned idx, +                                VisitorSet &visited) +{ +	// We call resolve for the children, but give them the field name as +	// alias. +	for (auto &c : children) { +		c->resolve(res, path, filter, filterData, idx, visited, &getNameRef()); +	} +} + +// TODO: better alias? +static std::string DESCRIPTOR_ATTRIBUTES_ALIAS {"attributes"}; + +void Descriptor::doResolve(std::vector<Rooted<Managed>> &res, +                           const std::vector<std::string> &path, Filter filter, +                           void *filterData, unsigned idx, VisitorSet &visited) +{ +	// TODO: This could be a problem, because the name of the field might be +	// needed in the path. +	for (auto &fd : fieldDescriptors) { +		fd->resolve(res, path, filter, filterData, idx, visited, nullptr); +	} +	// TODO: This throws a SEGFAULT for some reason. +//	attributesDescriptor->resolve(res, path, filter, filterData, idx, visited, +//	                              &DESCRIPTOR_ATTRIBUTES_ALIAS); +} + +void StructuredClass::doResolve(std::vector<Rooted<Managed>> &res, +                                const std::vector<std::string> &path, +                                Filter filter, void *filterData, unsigned idx, +                                VisitorSet &visited) +{ +	Descriptor::doResolve(res, path, filter, filterData, idx, visited); +	if(!isa.isNull()){ +		isa->doResolve(res, path, filter, filterData, idx, visited); +	} +} + +void Domain::doResolve(std::vector<Rooted<Managed>> &res, +                       const std::vector<std::string> &path, Filter filter, +                       void *filterData, unsigned idx, VisitorSet &visited) +{ +	for (auto &s : rootStructures) { +		s->resolve(res, path, filter, filterData, idx, visited, nullptr); +	} +	for (auto &a : annotationClasses) { +		a->resolve(res, path, filter, filterData, idx, visited, nullptr); +	} +	for (auto &t : typesystems) { +		t->resolve(res, path, filter, filterData, idx, visited, nullptr); +	} +}  }  } diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index 50c0bb1..112f2fa 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -84,14 +84,16 @@  #include <core/managed/ManagedContainer.hpp>  #include <core/Node.hpp> +#include <core/RangeSet.hpp>  #include "Typesystem.hpp"  namespace ousia {  namespace model { -class StructuredClass;  class Descriptor; +class StructuredClass; +class Domain;  /**   * As mentioned in the description above a FieldDescriptor specifies the @@ -137,6 +139,12 @@ private:  	FieldType fieldType;  	Owned<Type> primitiveType; +protected: +	void doResolve(std::vector<Rooted<Managed>> &res, +	               const std::vector<std::string> &path, Filter filter, +	               void *filterData, unsigned idx, +	               VisitorSet &visited) override; +  public:  	const bool optional; @@ -146,17 +154,18 @@ public:  	 * set to "PRIMITIVE".  	 *  	 * @param mgr           is the global Manager instance. -	 * @param name          is the name of this field.  	 * @param parent        is a handle of the Descriptor node that has this  	 *                      FieldDescriptor.  	 * @param primitiveType is a handle to some Type in some Typesystem of which  	 *                      one instance is allowed to fill this field. +	 * @param name          is the name of this field.  	 * @param optional      should be set to 'false' is this field needs to be  	 *                      filled in order for an instance of the parent  	 *                      Descriptor to be valid.  	 */ -	FieldDescriptor(Manager &mgr, std::string name, Handle<Descriptor> parent, -	                Handle<Type> primitiveType, bool optional) +	FieldDescriptor(Manager &mgr, Handle<Descriptor> parent, +	                Handle<Type> primitiveType, std::string name = "", +	                bool optional = false)  	    : Node(mgr, std::move(name), parent),  	      children(this),  	      fieldType(FieldType::PRIMITIVE), @@ -170,21 +179,21 @@ public:  	 * children here.  	 *  	 * @param mgr           is the global Manager instance. -	 * @param name          is the name of this field.  	 * @param parent        is a handle of the Descriptor node that has this  	 *                      FieldDescriptor.  	 * @param fieldType     is the FieldType of this FieldDescriptor, either  	 *                      TREE for the main or default structure or SUBTREE  	 *                      for supporting structures. +	 * @param name          is the name of this field.  	 * @param optional      should be set to 'false' is this field needs to be  	 *                      filled in order for an instance of the parent  	 *                      Descriptor to be valid.  	 */ -	FieldDescriptor(Manager &mgr, std::string name, Handle<Descriptor> parent, -	                FieldType fieldType, -	                ManagedVector<StructuredClass> children, bool optional) +	FieldDescriptor(Manager &mgr, Handle<Descriptor> parent, +	                FieldType fieldType = FieldType::TREE, +	                std::string name = "", bool optional = false)  	    : Node(mgr, std::move(name), parent), -	      children(children), +	      children(this),  	      fieldType(fieldType),  	      // TODO: What would be a wise initialization of the primitiveType?  	      optional(optional) @@ -194,6 +203,11 @@ public:  	// TODO: Is returning a ManagedVector alright?  	ManagedVector<StructuredClass> &getChildren() { return children; } +	const ManagedVector<StructuredClass> &getChildren() const +	{ +		return children; +	} +  	FieldType getFieldType() const { return fieldType; }  	bool isPrimitive() const { return fieldType == FieldType::PRIMITIVE; } @@ -212,7 +226,7 @@ public:   * the attribute specification of a descriptor is done by referencing an   * appropriate StructType that contains all permitted keys and value types.   * - * TODO: What aout optional attributes? + * TODO: What about optional attributes?   *   * In XML terms the difference between primitive fields and attributes can be   * explained as the difference between node attributes and node children. @@ -233,14 +247,19 @@ private:  	Owned<StructType> attributesDescriptor;  	ManagedVector<FieldDescriptor> fieldDescriptors; +protected: +	void doResolve(std::vector<Rooted<Managed>> &res, +	               const std::vector<std::string> &path, Filter filter, +	               void *filterData, unsigned idx, +	               VisitorSet &visited) override; +  public: -	Descriptor(Manager &mgr, std::string name, Handle<Node> parent, +	Descriptor(Manager &mgr, std::string name, Handle<Domain> domain,  	           // TODO: What would be a wise default value for attributes? -	           Handle<StructType> attributesDescriptor, -	           ManagedVector<FieldDescriptor> fieldDescriptors) -	    : Node(mgr, std::move(name), parent), +	           Handle<StructType> attributesDescriptor) +	    : Node(mgr, std::move(name), domain),  	      attributesDescriptor(acquire(attributesDescriptor)), -	      fieldDescriptors(fieldDescriptors) +	      fieldDescriptors(this)  	{  	} @@ -261,9 +280,7 @@ public:  	}  }; -// TODO: Implement -class Cardinality { -}; +typedef RangeSet<size_t> Cardinality;  /**   * A StructuredClass specifies nodes in the StructureTree of a document that @@ -346,21 +363,25 @@ private:  	Owned<StructuredClass> isa;  	ManagedVector<FieldDescriptor> parents; +protected: +	void doResolve(std::vector<Rooted<Managed>> &res, +	               const std::vector<std::string> &path, Filter filter, +	               void *filterData, unsigned idx, +	               VisitorSet &visited) override; +  public:  	const bool transparent; -	StructuredClass(Manager &mgr, std::string name, Handle<Node> parent, -	                Handle<StructType> attributesDescriptor, -	                ManagedVector<FieldDescriptor> fieldDescriptors, +	StructuredClass(Manager &mgr, std::string name, Handle<Domain> domain,  	                const Cardinality &cardinality, +	                Handle<StructType> attributesDescriptor = {nullptr},  	                // TODO: What would be a wise default value for isa? -	                Handle<StructuredClass> isa, -	                ManagedVector<FieldDescriptor> parents, bool transparent) -	    : Descriptor(mgr, std::move(name), parent, attributesDescriptor, -	                 fieldDescriptors), +	                Handle<StructuredClass> isa = {nullptr}, +	                bool transparent = false) +	    : Descriptor(mgr, std::move(name), domain, attributesDescriptor),  	      cardinality(cardinality),  	      isa(acquire(isa)), -	      parents(parents), +	      parents(this),  	      transparent(transparent)  	{  	} @@ -370,7 +391,7 @@ public:  	Rooted<StructuredClass> getIsA() const { return isa; }  	// TODO: Is returning a ManagedVector alright? -	ManagedVector<FieldDescriptor>& getParents() { return parents; } +	ManagedVector<FieldDescriptor> &getParents() { return parents; }  	const ManagedVector<FieldDescriptor> &getParents() const { return parents; }  }; @@ -394,28 +415,51 @@ class Domain : public Node {  private:  	ManagedVector<StructuredClass> rootStructures;  	ManagedVector<AnnotationClass> annotationClasses; +	ManagedVector<Typesystem> typesystems; + +protected: +	void doResolve(std::vector<Rooted<Managed>> &res, +	               const std::vector<std::string> &path, Filter filter, +	               void *filterData, unsigned idx, +	               VisitorSet &visited) override;  public: -	Domain(Manager &mgr, std::string name, -	       ManagedVector<StructuredClass> rootStructures, -	       ManagedVector<AnnotationClass> annotationClasses) +	Domain(Manager &mgr, std::string name)  	    // TODO: Can a domain have a parent?  	    : Node(mgr, std::move(name), nullptr), -	      rootStructures(rootStructures), -	      annotationClasses(annotationClasses) +	      rootStructures(this), +	      annotationClasses(this), +	      typesystems(this)  	{  	}  	// TODO: Is returning a ManagedVector alright? -	ManagedVector<StructuredClass> getRootStructures() +	ManagedVector<StructuredClass> &getRootStructures()  	{  		return rootStructures;  	} -	ManagedVector<AnnotationClass> getAnnotationClasses() +	const ManagedVector<StructuredClass> &getRootStructures() const +	{ +		return rootStructures; +	} + +	ManagedVector<AnnotationClass> &getAnnotationClasses() +	{ +		return annotationClasses; +	} + +	const ManagedVector<AnnotationClass> &getAnnotationClasses() const  	{  		return annotationClasses;  	} + +	ManagedVector<Typesystem> &getTypesystems() { return typesystems; } + +	const ManagedVector<Typesystem> &getTypesystems() const +	{ +		return typesystems; +	}  };  }  } diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp index 347adb8..20c6e8b 100644 --- a/src/core/model/Typesystem.hpp +++ b/src/core/model/Typesystem.hpp @@ -50,7 +50,7 @@ protected:  	{  	} -	virtual bool doPrepare(Variant &var, Logger &log) = 0; +	virtual bool doPrepare(Variant &var, Logger &log) const = 0;  public:  	/** @@ -65,12 +65,12 @@ public:  	/**  	 * TODO: DOC  	 */ -	virtual Variant create() = 0; +	virtual Variant create() const = 0;  	/**  	 * TODO: DOC  	 */ -	bool prepare(Variant &var, Logger &log) +	bool prepare(Variant &var, Logger &log) const  	{  		try {  			return doPrepare(var, log); @@ -88,7 +88,7 @@ protected:  	/**  	 * TODO: DOC  	 */ -	bool doPrepare(Variant &var, Logger &log) override +	bool doPrepare(Variant &var, Logger &log) const override  	{  		if (!var.isPrimitive()) {  			throw LoggableException{"Expected a string or primitive input."}; @@ -114,7 +114,7 @@ public:  	/**  	 * TODO: DOC  	 */ -	Variant create() override { return Variant{""}; } +	Variant create() const override { return Variant{""}; }  };  class IntType : public Type { @@ -122,7 +122,7 @@ protected:  	/**  	 * TODO: DOC  	 */ -	bool doPrepare(Variant &var, Logger &log) override +	bool doPrepare(Variant &var, Logger &log) const override  	{  		if (!var.isInt()) {  			throw LoggableException{"Expected an integer value."}; @@ -142,7 +142,7 @@ public:  	/**  	 * TODO: DOC  	 */ -	Variant create() override { return Variant{0}; } +	Variant create() const override { return Variant{0}; }  };  class DoubleType : public Type { @@ -150,7 +150,7 @@ protected:  	/**  	 * TODO: DOC  	 */ -	bool doPrepare(Variant &var, Logger &log) override +	bool doPrepare(Variant &var, Logger &log) const override  	{  		if (!var.isInt() && !var.isDouble()) {  			throw LoggableException{"Expected a double value."}; @@ -171,7 +171,7 @@ public:  	/**  	 * TODO: DOC  	 */ -	Variant create() override { return Variant{0.}; } +	Variant create() const override { return Variant{0.}; }  };  class UnknownType : public Type { @@ -179,7 +179,7 @@ protected:  	/**  	 * TODO: DOC  	 */ -	bool doPrepare(Variant &var, Logger &log) override { return true; } +	bool doPrepare(Variant &var, Logger &log) const override { return true; }  public:  	/** @@ -193,7 +193,7 @@ public:  	/**  	 * TODO: DOC  	 */ -	Variant create() override { return Variant{nullptr}; } +	Variant create() const override { return Variant{nullptr}; }  };  class BoolType : public Type { @@ -201,7 +201,7 @@ protected:  	/**  	 * TODO: DOC  	 */ -	bool doPrepare(Variant &var, Logger &log) override +	bool doPrepare(Variant &var, Logger &log) const override  	{  		if (!var.isBool()) {  			throw LoggableException("Expected boolean value!"); @@ -221,7 +221,7 @@ public:  	/**  	 * TODO: DOC  	 */ -	Variant create() override { return Variant{false}; } +	Variant create() const override { return Variant{false}; }  };  class EnumerationType : public Type { @@ -232,7 +232,7 @@ protected:  	/**  	 * TODO: DOC  	 */ -	bool doPrepare(Variant &var, Logger &log) override +	bool doPrepare(Variant &var, Logger &log) const override  	{  		if (var.isInt()) {  			int i = var.asInt(); @@ -275,7 +275,7 @@ public:  	/**  	 * TODO: DOC  	 */ -	Variant create() override { return Variant{0}; } +	Variant create() const override { return Variant{0}; }  };  class StructType : public Type { @@ -296,7 +296,45 @@ public:  		}  	}; -private: +protected: +	/** +	 * TODO: DOC +	 */ +	bool doPrepare(Variant &var, Logger &log) const override +	{ +		// If we already have an array, we just check that. +		if(var.isArray()){ +			auto arr = var.asArray(); +			for(size_t a = 0; a < attrs.size(); a++){ +				if(!attrs[a].type->prepare(arr[a], log)){ +					return false; +				} +			} +			return true; +		} +		// Otherwise we expect a map. +		if (!var.isMap()) { +			throw LoggableException("Expected map!"); +		} +		auto &map = var.asMap(); +		// We transform the map into an array with the correct values at the +		// correct places. +		std::vector<Variant> vec; +		for (auto &a : attrs) { +			auto it = map.find(a.name); +			// we use the default if nothing is set. +			if (it == map.end() || !a.type->prepare(it->second, log)) { +				log.note(std::string("Using default value for ") + a.name); +				vec.push_back(a.defaultValue); +			} else{ +				vec.push_back(it->second); +			} +		} +		var = Variant(vec); +		return true; +	} + +public:  	std::vector<AttributeDescriptor> attrs;  	StructType(Manager &mgr, std::string name, Handle<Typesystem> system, @@ -305,15 +343,13 @@ private:  	      attrs(std::move(attrs))  	{  	} - -public:  	// TODO  	//	static StructType createValidated(  	//	    Manager &mgr, std::string name, Handle<Typesystem> system,  	//	    Handle<StructType> parent,  	//	    const std::vector<AttributeDescriptor> &attrs, Logger &logger); -	Variant create() override { return Variant{Variant::arrayType{}}; } +	Variant create() const override { return Variant{Variant::arrayType{}}; }  };  class ArrayType : public Type { @@ -324,7 +360,7 @@ protected:  	/**  	 * TODO: DOC  	 */ -	bool doPrepare(Variant &var, Logger &log) override +	bool doPrepare(Variant &var, Logger &log) const override  	{  		if (!var.isArray()) {  			throw LoggableException("Expected array!"); @@ -353,7 +389,7 @@ public:  	/**  	 * TODO: DOC  	 */ -	Variant create() override { return Variant{Variant::arrayType{}}; } +	Variant create() const override { return Variant{Variant::arrayType{}}; }  	Rooted<Type> getType() { return innerType; }  }; @@ -372,6 +408,8 @@ public:  	 * TODO: DOC  	 */  	void addType(Handle<Type> type) { types.push_back(type); } + +	const NodeVector<Type> &getTypes() const { return types; }  };  }  } diff --git a/test/core/managed/ManagedContainerTest.cpp b/test/core/managed/ManagedContainerTest.cpp index c34541a..7ff819f 100644 --- a/test/core/managed/ManagedContainerTest.cpp +++ b/test/core/managed/ManagedContainerTest.cpp @@ -38,7 +38,7 @@ TEST(ManagedVector, managedVector)  	{  		Rooted<Managed> root{new Managed{mgr}}; -		std::vector<TestManaged*> elems; +		std::vector<TestManaged *> elems;  		for (int i = 0; i < nElem; i++) {  			elems.push_back(new TestManaged{mgr, a[i]});  		} @@ -49,7 +49,8 @@ TEST(ManagedVector, managedVector)  		ManagedVector<TestManaged> v(root, elems.begin(), elems.end()); -		// Remove the last element from the list. It should be garbage collected. +		// Remove the last element from the list. It should be garbage +		// collected.  		v.pop_back();  		ASSERT_FALSE(a[nElem - 1]); @@ -85,7 +86,6 @@ TEST(ManagedVector, managedVector)  	}  } -  TEST(ManagedVector, moveAssignment)  {  	constexpr int nElem = 16; @@ -220,16 +220,39 @@ TEST(ManagedVector, moveWithNewOwner)  	}  } -class TestManagedWithContainer : public Managed { +TEST(ManagedVector, accessOperator) +{ +	Manager mgr{1}; +	Rooted<Managed> root{new Managed{mgr}}; +	ManagedVector<Managed> instance{root}; +	Rooted<Managed> elem{new Managed{mgr}}; +	instance.push_back(elem); + +	ASSERT_EQ(elem, instance[0]); + +	// Test out of bounds. +	bool caught = false; +	try { +		instance[1]; +	} +	catch (std::out_of_range ex) { +		caught = true; +	} +	ASSERT_TRUE(caught); + +	instance.push_back(elem); +	ASSERT_EQ(elem, instance[1]); +} +class TestManagedWithContainer : public Managed {  public:  	ManagedVector<TestManaged> elems; -	TestManagedWithContainer(Manager &mgr) : Managed(mgr), elems(this) {}; - +	TestManagedWithContainer(Manager &mgr) : Managed(mgr), elems(this){};  }; -TEST(ManagedVector, embedded) { +TEST(ManagedVector, embedded) +{  	// Note: This test depends on the correct deletion order -- otherwise  	// valgrind shows an error  	bool a; @@ -248,7 +271,6 @@ TEST(ManagedVector, embedded) {  	ASSERT_FALSE(a);  } -  TEST(ManagedMap, managedMap)  {  	// TODO: This test is highly incomplete @@ -260,7 +282,7 @@ TEST(ManagedMap, managedMap)  	{  		Rooted<Managed> root{new Managed{mgr}}; -		std::map<int, TestManaged*> elems; +		std::map<int, TestManaged *> elems;  		for (int i = 0; i < nElem; i++) {  			elems.insert(std::make_pair(i, new TestManaged{mgr, a[i]}));  		} @@ -298,6 +320,5 @@ TEST(ManagedMap, managedMap)  		ASSERT_FALSE(v);  	}  } -  } diff --git a/test/core/model/DocumentTest.cpp b/test/core/model/DocumentTest.cpp new file mode 100644 index 0000000..26553dd --- /dev/null +++ b/test/core/model/DocumentTest.cpp @@ -0,0 +1,44 @@ +/* +    Ousía +    Copyright (C) 2014, 2015  Benjamin Paaßen, Andreas Stöckel + +    This program is free software: you can redistribute it and/or modify +    it under the terms of the GNU General Public License as published by +    the Free Software Foundation, either version 3 of the License, or +    (at your option) any later version. + +    This program is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +    GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License +    along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gtest/gtest.h> + +#include <core/model/Document.hpp> +#include <core/model/Domain.hpp> + +#include "TestDocument.hpp" +#include "TestDomain.hpp" + +namespace ousia { +namespace model { + + +TEST(Document, testDocumentConstruction) +{ +	// Construct Manager +	Manager mgr{1}; +	// Get the domain. +	Rooted<Domain> domain = constructBookDomain(mgr); +	// Construct the document. +	Rooted<Document> doc = constructBookDocument(mgr, domain); +	 +	// If that works we are happy already. +	ASSERT_FALSE(doc.isNull()); +} +} +} diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp new file mode 100644 index 0000000..f6dff3c --- /dev/null +++ b/test/core/model/DomainTest.cpp @@ -0,0 +1,100 @@ +/* +    Ousía +    Copyright (C) 2014, 2015  Benjamin Paaßen, Andreas Stöckel + +    This program is free software: you can redistribute it and/or modify +    it under the terms of the GNU General Public License as published by +    the Free Software Foundation, either version 3 of the License, or +    (at your option) any later version. + +    This program is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +    GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License +    along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gtest/gtest.h> + +#include <iostream> + +#include <core/model/Domain.hpp> + +#include "TestDomain.hpp" + +namespace ousia { +namespace model { + +void assert_path(std::vector<Rooted<Managed>> &result, size_t idx, +                 const std::type_info &expected_type, +                 std::vector<std::string> expected_path) +{ +	ASSERT_TRUE(result.size() > idx); +	// check class/type +	ASSERT_EQ(expected_type, typeid(*result[idx])); +	// transform to node +	Managed *m = &(*result[idx]); +	Node *n = dynamic_cast<Node *>(m); +	ASSERT_TRUE(n); +	// extract actual path +	std::vector<std::string> actual_path = n->path(); +	// check path +	ASSERT_EQ(expected_path, actual_path); +} + +TEST(Document, testDomainResolving) +{ +	// Construct Manager +	Manager mgr{1}; +	// Get the domain. +	Rooted<Domain> domain = constructBookDomain(mgr); + +	/* +	 * Start with the "book" search keyword. This should resolve to the domain +	 * itself (because it is called "book"), as well as the structure "book" +	 * node. +	 */ +	std::vector<Rooted<Managed>> res = +	    domain->resolve(std::vector<std::string>{"book"}); +	// First we expect the book domain. +	assert_path(res, 0, typeid(Domain), {"book"}); +	// Then the book structure. +	assert_path(res, 1, typeid(StructuredClass), {"book", "book"}); +	ASSERT_EQ(2, res.size()); + +	/* +	 * If we explicitly ask for the "book, book" path, then only the +	 * StructuredClass should be returned. +	 */ +	res = domain->resolve(std::vector<std::string>{"book", "book"}); +	assert_path(res, 0, typeid(StructuredClass), {"book", "book"}); +	ASSERT_EQ(1, res.size()); + +	/* +	 * If we ask for "section" the result should be unique as well. +	 */ +	res = domain->resolve(std::vector<std::string>{"section"}); +	// TODO: Is that the path result we want? +	assert_path(res, 0, typeid(StructuredClass), {"book", "section"}); +	ASSERT_EQ(1, res.size()); + +	/* +	 * If we ask for the path "book", "book", "" we reference the +	 * FieldDescriptor of the StructuredClass "book". +	 */ +	res = domain->resolve(std::vector<std::string>{"book", "book", ""}); +	assert_path(res, 0, typeid(FieldDescriptor), {"book", "book", ""}); +	ASSERT_EQ(1, res.size()); + +	/* +	 * If we ask for "paragraph" it is references two times in the Domain graph, +	 * but should be returned only once. +	 */ +	res = domain->resolve("paragraph"); +	assert_path(res, 0, typeid(StructuredClass), {"book", "paragraph"}); +	ASSERT_EQ(1, res.size()); +} +} +} diff --git a/test/core/model/TestDocument.hpp b/test/core/model/TestDocument.hpp new file mode 100644 index 0000000..a1a3434 --- /dev/null +++ b/test/core/model/TestDocument.hpp @@ -0,0 +1,84 @@ +/* +    Ousía +    Copyright (C) 2014, 2015  Benjamin Paaßen, Andreas Stöckel + +    This program is free software: you can redistribute it and/or modify +    it under the terms of the GNU General Public License as published by +    the Free Software Foundation, either version 3 of the License, or +    (at your option) any later version. + +    This program is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +    GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License +    along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _MODEL_TEST_DOCUMENT_HPP_ +#define _MODEL_TEST_DOCUMENT_HPP_ + +#include <core/model/Document.hpp> +#include <core/model/Domain.hpp> +#include <core/model/Typesystem.hpp> + +namespace ousia { +namespace model { + +/** + * This constructs a fairly simple test document for the "book" domain. The + * structure of the document can be seen in the Code below. + */ +static Rooted<Document> constructBookDocument(Manager &mgr, +                                              Rooted<Domain> bookDomain) +{ +	// Start with the (empty) document. +	Rooted<Document> doc{new Document(mgr, "myDoc.oxd")}; + +	// Add the root. +	Rooted<StructuredEntity> root = +	    StructuredEntity::buildRootEntity(doc, {bookDomain}, "book"); +	if (root.isNull()) { +		return {nullptr}; +	} + +	// Add a paragraph. +	Rooted<StructuredEntity> foreword = +	    StructuredEntity::buildEntity(root, {bookDomain}, "paragraph"); +	if (foreword.isNull()) { +		return {nullptr}; +	} +	// Add its text. +	Variant text{std::map<std::string, Variant>{ +	    {"content", Variant("Some introductory text")}}}; +	Rooted<DocumentPrimitive> foreword_text = +	    DocumentPrimitive::buildEntity(foreword, text, "text"); +	if (foreword_text.isNull()) { +		return {nullptr}; +	} +	// Add a section. +	Rooted<StructuredEntity> section = +	    StructuredEntity::buildEntity(root, {bookDomain}, "section"); +	// Add a paragraph for it. +	Rooted<StructuredEntity> main = +	    StructuredEntity::buildEntity(section, {bookDomain}, "paragraph"); +	if (main.isNull()) { +		return {nullptr}; +	} +	// Add its text. +	text = Variant{std::map<std::string, Variant>{ +	    {"content", Variant("Some introductory text")}}}; +	Rooted<DocumentPrimitive> main_text = +	    DocumentPrimitive::buildEntity(foreword, text, "text"); +	if (main_text.isNull()) { +		return {nullptr}; +	} + +	return doc; +} +} +} + +#endif /* _TEST_DOCUMENT_HPP_ */ + diff --git a/test/core/model/TestDomain.hpp b/test/core/model/TestDomain.hpp new file mode 100644 index 0000000..41fcdef --- /dev/null +++ b/test/core/model/TestDomain.hpp @@ -0,0 +1,94 @@ +/* +    Ousía +    Copyright (C) 2014, 2015  Benjamin Paaßen, Andreas Stöckel + +    This program is free software: you can redistribute it and/or modify +    it under the terms of the GNU General Public License as published by +    the Free Software Foundation, either version 3 of the License, or +    (at your option) any later version. + +    This program is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +    GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License +    along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _MODEL_TEST_DOMAIN_HPP_ +#define _MODEL_TEST_DOMAIN_HPP_ + +#include <core/model/Domain.hpp> +#include <core/model/Typesystem.hpp> + +namespace ousia { +namespace model { + +/** + * This constructs a somewhat trivial system of standard types. + * + * Currently contained: string, text (struct wrapper for string) + */ +static Rooted<Typesystem> constructTypeSystem(Manager &mgr) +{ +	Rooted<Typesystem> sys{new Typesystem(mgr, "std")}; +	Rooted<StringType> string{new StringType(mgr, sys)}; +	sys->addType(string); +	Rooted<StructType> string_struct{new StructType( +	    mgr, "text", sys, {{"content", "", false, sys->acquire(string)}})}; +	sys->addType(string_struct); + +	return sys; +} + +/** + * This constructs the "book" domain for test purposes. The structure of the + * domain is fairly simple and can be seen from the construction itself. + */ +static Rooted<Domain> constructBookDomain(Manager &mgr) +{ +	// Start with the Domain itself. +	Rooted<Domain> domain{new Domain(mgr, "book")}; +	// The standard type system. +	domain->getTypesystems().push_back(constructTypeSystem(mgr)); +	// Set up the cardinalities we'll need. +	Cardinality single; +	single.merge({1}); +	Cardinality any; +	any.merge(Range<size_t>::typeRangeFrom(0)); + +	// Set up the "book" node. +	Rooted<StructuredClass> book{ +	    new StructuredClass(mgr, "book", domain, single)}; +	domain->getRootStructures().push_back(book); +	// The structure field of it. +	Rooted<FieldDescriptor> book_field{new FieldDescriptor(mgr, book)}; +	book->getFieldDescriptors().push_back(book_field); + +	// From there on the "section". +	Rooted<StructuredClass> section{ +	    new StructuredClass(mgr, "section", domain, any)}; +	book_field->getChildren().push_back(section); +	// And the field of it. +	Rooted<FieldDescriptor> section_field{new FieldDescriptor(mgr, section)}; +	section->getFieldDescriptors().push_back(section_field); + +	// We also add the "paragraph", which is transparent. +	Rooted<StructuredClass> paragraph{new StructuredClass( +	    mgr, "paragraph", domain, any, {nullptr}, {nullptr}, true)}; +	section_field->getChildren().push_back(paragraph); +	book_field->getChildren().push_back(paragraph); +	// ... and has a primitive field. +	Rooted<FieldDescriptor> paragraph_field{new FieldDescriptor( +	    mgr, paragraph, domain->getTypesystems()[0]->getTypes()[1], "text", +	    false)}; +	paragraph->getFieldDescriptors().push_back(paragraph_field); + +	return domain; +} +} +} + +#endif /* _TEST_DOMAIN_HPP_ */ + | 
