diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-20 00:53:59 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-20 00:53:59 +0100 |
commit | a5e125cd7f72907f95c83f5ec1611996f7a28a3a (patch) | |
tree | bb2a189c36c5d7b64d7a2f4d5fbfb946ff43cb4f /src/core/model | |
parent | 47311cc8b211a7fef033d744d9eba9f308726ea8 (diff) | |
parent | 7c64a0770a4800d80c5a53eea2243c46301f7749 (diff) |
Merge branch 'master' of somweyr.de:ousia
Diffstat (limited to 'src/core/model')
-rw-r--r-- | src/core/model/Document.cpp | 68 | ||||
-rw-r--r-- | src/core/model/Document.hpp | 208 | ||||
-rw-r--r-- | src/core/model/Domain.cpp | 37 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 132 | ||||
-rw-r--r-- | src/core/model/Node.cpp | 5 | ||||
-rw-r--r-- | src/core/model/Node.hpp | 4 |
6 files changed, 294 insertions, 160 deletions
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index a6bcb15..66b36b0 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -92,25 +92,19 @@ int DocumentEntity::getFieldDescriptorIndex( } } -void DocumentEntity::addStructureNode(Handle<StructureNode> s, - const std::string &fieldName) +void DocumentEntity::invalidateSubInstance() { if (subInst->isa(RttiTypes::StructuredEntity)) { - const StructuredEntity *s = - static_cast<const StructuredEntity *>(subInst); - s->invalidate(); + subInst.cast<StructuredEntity>()->invalidate(); } else { - const AnnotationEntity *a = - static_cast<const AnnotationEntity *>(subInst); - a->invalidate(); + subInst.cast<AnnotationEntity>()->invalidate(); } - fields[getFieldDescriptorIndex(fieldName, true)].push_back(s); } DocumentEntity::DocumentEntity(Handle<Node> subInst, Handle<Descriptor> descriptor, Variant attributes) - : subInst(subInst.get()), + : subInst(subInst), descriptor(subInst->acquire(descriptor)), attributes(std::move(attributes)) { @@ -162,7 +156,7 @@ bool DocumentEntity::doValidate(Logger &logger) const FieldDescriptor::FieldType::PRIMITIVE) { switch (fields[f].size()) { case 0: - if (!fieldDescs[f]->optional) { + if (!fieldDescs[f]->isOptional()) { logger.error(std::string("Primitive Field \"") + fieldDescs[f]->getName() + "\" had no content!"); @@ -186,7 +180,7 @@ bool DocumentEntity::doValidate(Logger &logger) const // we can do a faster check if this field is empty. if (fields[f].size() == 0) { // if this field is optional, an empty field is valid anyways. - if (fieldDescs[f]->optional) { + if (fieldDescs[f]->isOptional()) { continue; } /* @@ -218,6 +212,13 @@ bool DocumentEntity::doValidate(Logger &logger) const // iterate over every actual child of this DocumentEntity for (auto &rc : fields[f]) { + // check if the parent reference is correct. + if (rc->getParent() != subInst) { + logger.error(std::string("A child of field \"") + + fieldDescs[f]->getName() + + "\" has the wrong parent reference!"); + valid = false; + } if (rc->isa(RttiTypes::Anchor)) { // Anchors are uninteresting and can be ignored. continue; @@ -296,6 +297,45 @@ bool DocumentEntity::doValidate(Logger &logger) const return valid; } +void DocumentEntity::setAttributes(const Variant &a) +{ + invalidateSubInstance(); + attributes = a; +} + +void DocumentEntity::addStructureNode(Handle<StructureNode> s, + const std::string &fieldName) +{ + invalidateSubInstance(); + fields[getFieldDescriptorIndex(fieldName, true)].push_back(s); +} + +void DocumentEntity::addStructureNodes( + const std::vector<Handle<StructureNode>> &ss, const std::string &fieldName) +{ + invalidateSubInstance(); + NodeVector<StructureNode> &field = + fields[getFieldDescriptorIndex(fieldName, true)]; + field.insert(field.end(), ss.begin(), ss.end()); +} + +void DocumentEntity::addStructureNode(Handle<StructureNode> s, + Handle<FieldDescriptor> fieldDescriptor) +{ + invalidateSubInstance(); + fields[getFieldDescriptorIndex(fieldDescriptor, true)].push_back(s); +} + +void DocumentEntity::addStructureNodes( + const std::vector<Handle<StructureNode>> &ss, + Handle<FieldDescriptor> fieldDescriptor) +{ + invalidateSubInstance(); + NodeVector<StructureNode> &field = + fields[getFieldDescriptorIndex(fieldDescriptor, true)]; + field.insert(field.end(), ss.begin(), ss.end()); +} + /* Class StructureNode */ StructureNode::StructureNode(Manager &mgr, std::string name, @@ -420,7 +460,9 @@ bool Document::doValidate(Logger &logger) const valid = false; } else { // check if the root is allowed to be a root. - if (!root->getDescriptor().cast<StructuredClass>()->root) { + if (!root->getDescriptor() + .cast<StructuredClass>() + ->hasRootPermission()) { logger.error(std::string("A node of type \"") + root->getDescriptor()->getName() + "\" is not allowed to be the Document root!"); diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index 62109fc..d89ade8 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -144,12 +144,14 @@ class DocumentEntity { private: /* * this is a rather dirty method that should not be used in other cases: - * We store a pointer to the Node instance that inherits from - * DocumentEntity. + * We store a handle to the Node instance that inherits from + * DocumentEntity. This Handle is not registered and would lead to Segfaults + * if we could not garantuee that it lives exactly as long as this + * DocumentEntity because the handle is for the subclass instance. */ - const Node *subInst; + Handle<Node> subInst; Owned<Descriptor> descriptor; - const Variant attributes; + Variant attributes; std::vector<NodeVector<StructureNode>> fields; int getFieldDescriptorIndex(const std::string &fieldName, @@ -158,10 +160,9 @@ private: int getFieldDescriptorIndex(Handle<FieldDescriptor> fieldDescriptor, bool enforce) const; -protected: - void addStructureNode(Handle<StructureNode> s, - const std::string &fieldName = ""); + void invalidateSubInstance(); +protected: bool doValidate(Logger &logger) const; public: @@ -198,6 +199,15 @@ public: Variant getAttributes() const { return attributes; } /** + * Sets the attributes for this DocumentEntity. Attributes are set as a Map + * variant. + * + * @param a is a Map variant containing the attributes for this + * DocumentEntity. + */ + void setAttributes(const Variant &a); + + /** * This returns true if there is a FieldDescriptor in the Descriptor for * this DocumentEntity which has the given name. If an empty name is * given it is assumed that the 'default' FieldDescriptor is referenced, @@ -207,7 +217,7 @@ public: * * @param fieldName is the name of a field as specified in the * FieldDescriptor in the Domain description. - * @return true if this FieldDescriptor exists. + * @return true if this FieldDescriptor exists. */ bool hasField(const std::string &fieldName = "") const { @@ -225,7 +235,7 @@ public: * * @param fieldName is the name of a field as specified in the * FieldDescriptor in the Domain description. - * @return a NodeVector of all StructuredEntities in that field. + * @return a NodeVector of all StructuredEntities in that field. */ const NodeVector<StructureNode> &getField( const std::string &fieldName = "") const @@ -242,7 +252,8 @@ public: * * @param fieldDescriptor is a FieldDescriptor defined in the Descriptor for * this DocumentEntity. - * @return a NodeVector of all StructuredEntities in that field. + * @return a NodeVector of all StructuredEntities in that + * field. */ const NodeVector<StructureNode> &getField( Handle<FieldDescriptor> fieldDescriptor) const @@ -250,84 +261,63 @@ public: return fields[getFieldDescriptorIndex(fieldDescriptor, true)]; } - // TODO: Change this to move methods. - // /** - // * This adds a StructureNode to the field with the given name. If an - // * empty name is given it is assumed that the 'default' FieldDescriptor - // is - // * referenced, where 'default' means either: - // * 1.) The only TREE typed FieldDescriptor (if present) or - // * 2.) the only FieldDescriptor (if only one is specified). - // * - // * If the name is unknown an exception is thrown. - // * - // * @param s is the StructureNode that shall be added. - // * @param fieldName is the name of a field as specified in the - // * FieldDescriptor in the Domain description. - // */ - // void addStructureNode(Handle<StructureNode> s, - // const std::string &fieldName = "") - // { - // fields[getFieldDescriptorIndex(fieldName, true)].push_back(s); - // } - // /** - // * This adds multiple StructureNodes to the field with the given name. - // * If an empty name is given it is assumed that the 'default' - // * FieldDescriptor is referenced, where 'default' means either: - // * 1.) The only TREE typed FieldDescriptor (if present) or - // * 2.) the only FieldDescriptor (if only one is specified). - // * - // * If the name is unknown an exception is thrown. - // * - // * @param ss are the StructureNodes that shall be added. - // * @param fieldName is the name of a field as specified in the - // * FieldDescriptor in the Domain description. - // */ - // void addStructureNodes(const std::vector<Handle<StructureNode>> &ss, - // const std::string &fieldName = "") - // { - // NodeVector<StructureNode> &field = - // fields[getFieldDescriptorIndex(fieldName, true)]; - // field.insert(field.end(), ss.begin(), ss.end()); - // } - - // /** - // * This adds a StructureNode to the field with the given - // FieldDescriptor. - // * - // * If the FieldDescriptor does not belong to the Descriptor of this node - // * an exception is thrown. - // * - // * @param s is the StructureNode that shall be added. - // * @param fieldDescriptor is a FieldDescriptor defined in the Descriptor - // for - // * this DocumentEntity. - // */ - // void addStructureNode(Handle<StructureNode> s, - // Handle<FieldDescriptor> fieldDescriptor) - // { - // fields[getFieldDescriptorIndex(fieldDescriptor, true)].push_back(s); - // } - - // /** - // * This adds multiple StructureNodes to the field with the given - // * FieldDescriptor. - // * - // * If the FieldDescriptor does not belong to the Descriptor of this node - // * an exception is thrown. - // * - // * @param ss are the StructureNodes that shall be added. - // * @param fieldDescriptor is a FieldDescriptor defined in the Descriptor - // for - // * this DocumentEntity. - // */ - // void addStructureNodes(const std::vector<Handle<StructureNode>> &ss, - // Handle<FieldDescriptor> fieldDescriptor) - // { - // NodeVector<StructureNode> &field = - // fields[getFieldDescriptorIndex(fieldDescriptor, true)]; - // field.insert(field.end(), ss.begin(), ss.end()); - // } + /** + * This adds a StructureNode to the field with the given name. If an + * empty name is given it is assumed that the 'default' FieldDescriptor is + * referenced, where 'default' means either: + * 1.) The only TREE typed FieldDescriptor (if present) or + * 2.) the only FieldDescriptor (if only one is specified). + * + * If the name is unknown an exception is thrown. + * + * @param s is the StructureNode that shall be added. + * @param fieldName is the name of a field as specified in the + * FieldDescriptor in the Domain description. + */ + void addStructureNode(Handle<StructureNode> s, + const std::string &fieldName = ""); + /** + * This adds multiple StructureNodes to the field with the given name. + * If an empty name is given it is assumed that the 'default' + * FieldDescriptor is referenced, where 'default' means either: + * 1.) The only TREE typed FieldDescriptor (if present) or + * 2.) the only FieldDescriptor (if only one is specified). + * + * If the name is unknown an exception is thrown. + * + * @param ss are the StructureNodes that shall be added. + * @param fieldName is the name of a field as specified in the + * FieldDescriptor in the Domain description. + */ + void addStructureNodes(const std::vector<Handle<StructureNode>> &ss, + const std::string &fieldName = ""); + + /** + * This adds a StructureNode to the field with the given FieldDescriptor. + * + * If the FieldDescriptor does not belong to the Descriptor of this node + * an exception is thrown. + * + * @param s is the StructureNode that shall be added. + * @param fieldDescriptor is a FieldDescriptor defined in the Descriptor for + * this DocumentEntity. + */ + void addStructureNode(Handle<StructureNode> s, + Handle<FieldDescriptor> fieldDescriptor); + + /** + * This adds multiple StructureNodes to the field with the given + * FieldDescriptor. + * + * If the FieldDescriptor does not belong to the Descriptor of this node + * an exception is thrown. + * + * @param ss are the StructureNodes that shall be added. + * @param fieldDescriptor is a FieldDescriptor defined in the Descriptor for + * this DocumentEntity. + */ + void addStructureNodes(const std::vector<Handle<StructureNode>> &ss, + Handle<FieldDescriptor> fieldDescriptor); }; /** @@ -432,7 +422,7 @@ public: * where this DocumentPrimitive shall be added. It is empty * per default, referring to the default field. */ - DocumentPrimitive(Manager &mgr, Handle<Node> parent, Variant content, + DocumentPrimitive(Manager &mgr, Handle<Node> parent, Variant content = {}, const std::string &fieldName = "") : StructureNode(mgr, "", parent, fieldName), content(content) { @@ -444,6 +434,17 @@ public: * @return the content of this DocumentPrimitive. */ Variant getContent() const { return content; } + + /** + * Sets the content of this DocumentPrimitive to the given Variant. + * + * @param c is the new content of this DocumentPrimitive. + */ + void setContent(const Variant &c) + { + invalidate(); + content = c; + } }; /** @@ -527,8 +528,9 @@ public: * used for references later on. It is empty per default. */ AnnotationEntity(Manager &mgr, Handle<Document> parent, - Handle<AnnotationClass> descriptor, Handle<Anchor> start, - Handle<Anchor> end, Variant attributes = {}, + Handle<AnnotationClass> descriptor, + Handle<Anchor> start = nullptr, + Handle<Anchor> end = nullptr, Variant attributes = {}, std::string name = ""); /** @@ -544,6 +546,27 @@ public: * @return the end Anchor of this AnnotationEntity. */ Rooted<Anchor> getEnd() const { return end; } + + /** + * Sets the start Anchor of this AnnotationEntity. + * + * @param s is the new start Anchor for this AnnotationEntity. + */ + void setStart(Handle<Anchor> s) + { + invalidate(); + start = acquire(s); + } + + /** + * Sets the end Anchor of this AnnotationEntity. + * + * @param e is the new end Anchor for this AnnotationEntity. + */ + void setEnd(Handle<Anchor> e) + { + end = acquire(e); + } }; /** @@ -567,8 +590,7 @@ protected: public: Document(Manager &mgr, std::string name) - : Node(mgr, std::move(name), nullptr), - annotations(this) + : Node(mgr, std::move(name), nullptr), annotations(this) { } diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index e4f087c..f76c988 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -26,23 +26,6 @@ namespace ousia { namespace model { -template <class T> -static void checkUniqueName(Handle<Node> parent, NodeVector<T> vec, - Handle<T> child, const std::string &parentClassName, - const std::string &childClassName) -{ - std::set<std::string> childNames; - for (auto &c : vec) { - childNames.insert(c->getName()); - } - if (childNames.find(child->getName()) != childNames.end()) { - // TODO: Do we really want to have an exception here? - throw OusiaException(std::string("The ") + parentClassName + " " + - parent->getName() + " already has a " + - childClassName + " with name " + child->getName()); - } -} - /* Class FieldDescriptor */ FieldDescriptor::FieldDescriptor(Manager &mgr, Handle<Descriptor> parent, @@ -81,13 +64,6 @@ void Descriptor::doResolve(ResolutionState &state) state); } -void Descriptor::addFieldDescriptor(Handle<FieldDescriptor> fd) -{ - checkUniqueName(this, fieldDescriptors, fd, "Descriptor", - "FieldDescriptor"); - fieldDescriptors.push_back(fd); -} - std::vector<Rooted<Node>> Descriptor::pathTo( Handle<StructuredClass> target) const { @@ -126,7 +102,7 @@ bool Descriptor::continuePath(Handle<StructuredClass> target, return true; } // look for transparent intermediate nodes. - if (c->transparent) { + if (c->isTransparent()) { // copy the path. std::vector<Rooted<Node>> cPath = currentPath; cPath.push_back(fd); @@ -166,17 +142,18 @@ bool Descriptor::continuePath(Handle<StructuredClass> target, void Descriptor::copyFieldDescriptor(Handle<FieldDescriptor> fd) { + invalidate(); if (fd->getFieldType() == FieldDescriptor::FieldType::PRIMITIVE) { /* - *To call the "new" operation is enough here, because the + * To call the "new" operation is enough here, because the * constructor will add the newly constructed FieldDescriptor to this * Descriptor automatically. */ new FieldDescriptor(getManager(), this, fd->getPrimitiveType(), - fd->getName(), fd->optional); + fd->getName(), fd->isOptional()); } else { new FieldDescriptor(getManager(), this, fd->getFieldType(), - fd->getName(), fd->optional); + fd->getName(), fd->isOptional()); } } @@ -266,13 +243,13 @@ void Domain::doResolve(ResolutionState &state) void Domain::addStructuredClass(Handle<StructuredClass> s) { - checkUniqueName(this, structuredClasses, s, "Domain", "StructuredClass"); + invalidate(); structuredClasses.push_back(s); } void Domain::addAnnotationClass(Handle<AnnotationClass> a) { - checkUniqueName(this, annotationClasses, a, "Domain", "AnnotationClass"); + invalidate(); annotationClasses.push_back(a); } } diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index dad726a..4d33a91 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -269,10 +269,9 @@ private: NodeVector<StructuredClass> children; FieldType fieldType; Owned<Type> primitiveType; + bool optional; public: - const bool optional; - // TODO: What about the name of default fields? /** * This is the constructor for primitive fields. The type is automatically @@ -330,7 +329,11 @@ public: * Adds a StructuredClass whose instances shall be allowed as children in * the StructureTree of instances of this field. */ - void addChild(Handle<StructuredClass> c) { children.push_back(c); } + void addChild(Handle<StructuredClass> c) + { + invalidate(); + children.push_back(c); + } /** * Adds multiple StructuredClasses whose instances shall be allowed as @@ -338,14 +341,73 @@ public: */ void addChildren(const std::vector<Handle<StructuredClass>> &cs) { + invalidate(); children.insert(children.end(), cs.begin(), cs.end()); } + /** + * Returns the type of this field (not to be confused with the primitive + *type of this field). + * + * @return the type of this field. + */ FieldType getFieldType() const { return fieldType; } + /** + * Sets the type of this field (not to be confused with the primitive type + *of this field). + * + * @param ft is the new type of this field. + */ + void setFieldType(const FieldType &ft) + { + invalidate(); + fieldType = ft; + } + /** + * Returns true if and only if the type of this field is PRIMITIVE. + * + * @return true if and only if the type of this field is PRIMITIVE. + */ bool isPrimitive() const { return fieldType == FieldType::PRIMITIVE; } + /** + * Returns the primitive type of this field, which is only allowed to be + * set if the type of this field is PRIMITIVE. + * + * @return the primitive type of this field. + */ Rooted<Type> getPrimitiveType() const { return primitiveType; } + + /** + * Sets the primitive type of this field, which is only allowed to be + * set if the type of this field is PRIMITIVE. + * + * @param t is the new primitive type of this field- + */ + void setPrimitiveType(Handle<Type> t) + { + invalidate(); + primitiveType = acquire(t); + } + + /** + * Returns true if and only if this field is optional. + * + * @return true if and only if this field is optional. + */ + bool isOptional() const { return optional; } + + /** + * Specifies whether this field shall be optional. + * + * @param o should be true if and only if this field should be optional. + */ + void setOptional(bool o) + { + invalidate(); + optional = std::move(o); + } }; /** @@ -390,11 +452,6 @@ private: protected: void doResolve(ResolutionState &state) override; - /** - * Adds a FieldDescriptor and checks for name uniqueness. - */ - void addFieldDescriptor(Handle<FieldDescriptor> fd); - public: Descriptor(Manager &mgr, std::string name, Handle<Domain> domain, // TODO: What would be a wise default value for attributes? @@ -429,9 +486,30 @@ public: } /** + * Adds the given FieldDescriptor to this Descriptor. + * + * @param fd is a FieldDescriptor. + */ + void addFieldDescriptor(Handle<FieldDescriptor> fd) + { + invalidate(); + fieldDescriptors.push_back(fd); + } + + /** + * Adds the given FieldDescriptors to this Descriptor. + * + * @param fds are FieldDescriptors. + */ + void addFieldDescriptors(std::vector<Handle<FieldDescriptor>> fds) + { + invalidate(); + fieldDescriptors.insert(fieldDescriptors.end(), fds.begin(), fds.end()); + } + + /** * Copies a FieldDescriptor that belongs to another Descriptor to this - * Descriptor. This will throw an exception if a FieldDescriptor with the - * given name already exists. + * Descriptor. */ void copyFieldDescriptor(Handle<FieldDescriptor> fd); @@ -550,6 +628,8 @@ private: const Cardinality cardinality; Owned<StructuredClass> superclass; NodeVector<StructuredClass> subclasses; + bool transparent; + bool root; /** * Helper method for getFieldDescriptors. @@ -559,12 +639,6 @@ private: std::set<std::string> &overriddenFields) const; public: - const bool transparent; - // TODO: Is it possible to have root=true and cardinality other than 1? - // This also refers to the question in Document.hpp: Is it possible to have - // more than 1 root? - const bool root; - /** * The constructor for a StructuredClass. * @@ -589,6 +663,8 @@ public: * transparent. For more information on * transparency please refer to the class * documentation above. The default is false. + * @param root specifies whether this StructuredClass is + * allowed to be at the root of a Document. */ StructuredClass(Manager &mgr, std::string name, Handle<Domain> domain, const Cardinality &cardinality, @@ -651,6 +727,22 @@ public: * this StructuredClass. */ NodeVector<FieldDescriptor> getEffectiveFieldDescriptors() const; + + bool isTransparent() const { return transparent; } + + void setTransparent(bool t) + { + invalidate(); + transparent = std::move(t); + } + + bool hasRootPermission() const { return root; } + + void setRootPermission(bool r) + { + invalidate(); + root = std::move(r); + } }; /** @@ -699,11 +791,7 @@ private: protected: void doResolve(ResolutionState &state) override; - void addStructuredClass(Handle<StructuredClass> s); - void addAnnotationClass(Handle<AnnotationClass> a); - public: - /** * The constructor for a new domain. Note that this is an empty Domain and * still has to be filled with StructuredClasses and AnnotationClasses. @@ -780,6 +868,10 @@ public: { typesystems.insert(typesystems.end(), ts.begin(), ts.end()); } + + + void addStructuredClass(Handle<StructuredClass> s); + void addAnnotationClass(Handle<AnnotationClass> a); }; } diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index c4892af..5867fa3 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -227,6 +227,7 @@ std::vector<std::string> ResolutionResult::path() const void Node::setName(std::string name) { + invalidate(); // Call the name change event and (afterwards!) set the new name NameChangeEvent ev{this->name, name}; triggerEvent(ev); @@ -380,7 +381,7 @@ bool Node::validateName(Logger &logger) const return true; } -void Node::invalidate() const +void Node::invalidate() { // Only perform the invalidation if necessary if (validationState != ValidationState::UNKNOWN) { @@ -391,7 +392,7 @@ void Node::invalidate() const } } -void Node::markInvalid() const +void Node::markInvalid() { // Do not override the validationState if we're currently in the validation // procedure, try to mark the parent node as invalid diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index 190e8de..3e778a6 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -319,12 +319,12 @@ protected: * changed such that a new validation run has to be made. Also informs the * parent node about the invalidation. */ - void invalidate() const; + void invalidate(); /** * This method should be called if a Node finds itself in an invalid state. */ - void markInvalid() const; + void markInvalid(); /** * The convention for this function is as follows: |