/* * * Copyright (C) 2000-2016, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmsr * * Author: Joerg Riesmeier * * Purpose: * classes: DSRTreeNode, DSRTree * */ #ifndef DSRTREE_H #define DSRTREE_H #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/dcmsr/dsrtypes.h" #include "dcmtk/dcmsr/dsrtncsr.h" #include "dcmtk/ofstd/ofutil.h" /*-----------------------* * forward declaration * *-----------------------*/ template class DSRTree; /*---------------------* * class declaration * *---------------------*/ /** Class for tree nodes */ class DCMTK_DCMSR_EXPORT DSRTreeNode : protected DSRTypes { // allow direct access to member variables friend class DSRTree; friend class DSRTree; public: /** (default) constructor ** @param annotation optional annotation that should be set as the initial value */ DSRTreeNode(const DSRTreeNodeAnnotation &annotation = DSRTreeNodeAnnotation()) : Prev(NULL), Next(NULL), Down(NULL), Annotation(annotation), Ident(IdentCounter++) // tbc: is this MT-safe? { } /** destructor */ virtual ~DSRTreeNode() { } /** clone this tree node. * Actually, a new tree node is created since the pointer members are not needed. * Only the optional annotation is copied. ** @return copy of this tree node */ virtual DSRTreeNode *clone() const { return new DSRTreeNode(Annotation); } /** get pointer to previous tree node ** @return pointer to previous tree node (might be NULL) */ inline DSRTreeNode *getPrev() const { return Prev; } /** get pointer to next tree node ** @return pointer to next tree node (might be NULL) */ inline DSRTreeNode *getNext() const { return Next; } /** get pointer to first child node ** @return pointer to first child node (might be NULL) */ inline DSRTreeNode *getDown() const { return Down; } /** get unique identifier of this node ** @return unique identifier of this node */ inline size_t getIdent() const { return Ident; } /** check whether this node has a (non-empty) annotation ** @return OFTrue if this node has a (non-empty) annotation, OFFalse otherwise */ inline OFBool hasAnnotation() const { return !Annotation.isEmpty(); } /** clear annotation of this node */ inline void clearAnnotation() { Annotation.clear(); } /** get annotation of this node (optional) ** @return annotation of this node (might be empty) */ inline const DSRTreeNodeAnnotation &getAnnotation() const { return Annotation; } /** set annotation of this node (optional) ** @param annotation annotation to be set */ inline void setAnnotation(const DSRTreeNodeAnnotation &annotation) { Annotation = annotation; } protected: /** get current value of global node counter ** @return current value of global node counter */ inline static size_t getIdentCounter() { return IdentCounter; } /// pointer to previous tree node (if any) DSRTreeNode *Prev; /// pointer to next tree node (if any) DSRTreeNode *Next; /// pointer to first child node (if any) DSRTreeNode *Down; /// annotation of the tree node (optional) DSRTreeNodeAnnotation Annotation; private: /// unique identifier (created automatically) const size_t Ident; /// global counter used to create the unique identifiers static size_t IdentCounter; // --- declaration of copy constructor and assignment operator DSRTreeNode(const DSRTreeNode &); DSRTreeNode &operator=(const DSRTreeNode &); }; /** Class managing a tree of nodes */ template class DSRTree : public DSRTreeNodeCursor, protected DSRTypes { public: /** default constructor */ DSRTree(); /** copy constructor. * Please note that the internal cursor is not copied but reset, i.e. set to the * root node. ** @param tree tree to be copied */ DSRTree(const DSRTree &tree); /** destructor */ virtual ~DSRTree(); /** assignment operator. * Please note that internally the copy constructor is used, so the same comments * apply. ** @param tree tree to be copied ** @return reference to this tree after copying */ DSRTree &operator=(DSRTree tree); /** clear all member variables, i.e.\ the tree with all nodes */ virtual void clear(); /** clear annotations of all tree nodes */ void clearAnnotations(); /** check whether tree has any nodes ** @return OFTrue if tree is empty, OFFalse otherwise */ inline OFBool isEmpty() const; /** count number of nodes in the tree. * This method iterates over all nodes that are stored in the tree. ** @return number of nodes, 0 if tree is empty */ size_t countNodes() const; /** get ID of the next node to be created. * The node ID uniquely identifies a content item in the document tree. ** @return ID of the next node to be created (should never be 0) */ inline size_t getNextNodeID() const; /** set internal cursor to root node ** @return ID of root node if successful, 0 otherwise */ inline size_t gotoRoot(); /** set internal cursor to specified node ** @param searchID ID of the node to set the cursor to * @param startFromRoot flag indicating whether to start from the root node * or the current one ** @return ID of the new current node if successful, 0 otherwise */ size_t gotoNode(const size_t searchID, const OFBool startFromRoot = OFTrue); /** set internal cursor to specified node ** @param reference position string of the node to set the cursor to. * (the format is e.g. "1.2.3" for the third child of the * second child of the first node - see DSRTreeNodeCursor). * @param startFromRoot flag indicating whether to start from the root node * or the current one ** @return ID of the new current node if successful, 0 otherwise */ size_t gotoNode(const OFString &reference, const OFBool startFromRoot = OFTrue); /** set cursor to specified node. Starts from current position! ** @param annotation annotation of the node to set the cursor to * @param startFromRoot flag indicating whether to start from the root node * or the current one ** @return ID of the new current node if successful, 0 otherwise */ size_t gotoNode(const DSRTreeNodeAnnotation &annotation, const OFBool startFromRoot = OFTrue); /** add new node to the current one. * Please note that no copy of the given node is created. Therefore, the node * should be created with new() - do not use a reference to a local variable. * If the node could be added successfully, the cursor is set to it automatically. ** @param node pointer to the new node to be added * @param addMode flag specifying at which position to add the new node ** @return ID of the new node if successful, 0 otherwise */ virtual size_t addNode(T *node, const E_AddMode addMode = AM_afterCurrent); /** replace current node by the given one. * Please note that no copy of the given node is created. Therefore, the node * should be created with new() - do not use a reference to a local variable. If * the node could be replaced successfully, the "old" node (and all of its child * nodes) are deleted, and the cursor is set to the new one. ** @param node pointer to the new node to replace the current one ** @return ID of the new node if successful, 0 otherwise */ virtual size_t replaceNode(T *node); /** extract current node from tree. * Please note that not only the specified node but also all of its child nodes are * extracted from the tree. The cursor is set automatically to a new valid position. ** @return pointer to extracted node, NULL in case of error (or the tree was empty) */ virtual T *extractNode(); /** get pointer to root node and "forget" the internal reference to this node. * In other words: after calling this method, the stored tree will be empty. * This also means that the caller is responsible for deleting the allocated memory. ** @return pointer to root node, might be NULL (empty tree) */ virtual T *getAndRemoveRootNode(); /** remove current node from tree. * Please note that not only the specified node but also all of its child nodes are * removed from the tree and then deleted. The cursor is set automatically to a new * valid position. ** @return ID of the node which became the current one after deletion, 0 if an error * occurred or the tree is now empty. */ virtual size_t removeNode(); /** extract a subtree i.e.\ a fragment from this tree. * The subtree is specified by the current node, which becomes the root of the subtree. ** @return pointer to the extracted subtree, NULL in case of error */ virtual DSRTree *extractSubTree(); /** clone a subtree i.e.\ a fragment of this tree. * The cloning starts with the current node and ends with the given node. ** @param stopAfterNodeID ID of the node after which the cloning should stop. * By default (0), the process ends after cloning the * current node with all of its child nodes (if any). ** @return pointer to a copy of the specified subtree, NULL in case of error */ virtual DSRTree *cloneSubTree(const size_t stopAfterNodeID = 0) const; protected: /** special constructor that receives a pointer to the root node. * Please note that the 'rootNode' and the associated tree is not copied! ** @param rootNode pointer to the root node of the "new" tree */ DSRTree(T *rootNode); /** special copy constructor that clones a particular subtree only ** @param startCursor first node of the subtree to be copied * @param stopAfterNodeID ID of the node after which the cloning should stop */ DSRTree(const DSRTreeNodeCursor &startCursor, size_t stopAfterNodeID); /** fast, non-throwing swap function. * The time complexity of this function is constant. ** @param tree tree to swap with */ void swap(DSRTree &tree); /** get pointer to root node ** @return pointer to root node, might be NULL (empty tree) */ virtual T *getRoot() const; /** delete a tree given by its root node. * Please note that the given 'rootNode' pointer becomes invalid afterwards. ** @param rootNode pointer to the root node of the tree to be deleted */ virtual void deleteTreeFromRootNode(T *rootNode); private: /// pointer to the root tree node T *RootNode; }; /*------------------* * implementation * *------------------*/ template DSRTree::DSRTree() : DSRTreeNodeCursor(), RootNode(NULL) { } template DSRTree::DSRTree(const DSRTree &tree) : DSRTreeNodeCursor(), RootNode(NULL) { if (!tree.isEmpty()) { E_AddMode addMode = AM_afterCurrent; T *newNode = NULL; T *nodeCursor = tree.getRoot(); OFStack nodeCursorStack; /* perform a "deep search", just like DSRTreeNodeCursor<>::iterate() */ while (nodeCursor != NULL) { /* clone current node and add it to the tree */ if (addNode(newNode = nodeCursor->clone(), addMode) == 0) { /* failed to add node, so delete it and exit the loop */ delete newNode; break; } /* then goto to the next node to be copied */ if (nodeCursor->Down != NULL) { /* go one level down to the first child node */ nodeCursorStack.push(nodeCursor); nodeCursor = nodeCursor->getDown(); addMode = AM_belowCurrent; } else if (nodeCursor->Next != NULL) { /* proceed with the following sibling */ nodeCursor = nodeCursor->getNext(); addMode = AM_afterCurrent; } else { /* check whether there are any siblings on higher levels */ do { if (!nodeCursorStack.empty()) { nodeCursor = nodeCursorStack.top(); nodeCursorStack.pop(); this->goUp(); } else nodeCursor = NULL; } while ((nodeCursor != NULL) && (nodeCursor->Next == NULL)); if (nodeCursor != NULL) { nodeCursor = nodeCursor->getNext(); addMode = AM_afterCurrent; } } } /* initialize the cursor */ gotoRoot(); } } template DSRTree::DSRTree(T *rootNode) : DSRTreeNodeCursor(), RootNode(rootNode) { /* initialize the cursor */ gotoRoot(); } template DSRTree::DSRTree(const DSRTreeNodeCursor &startCursor, size_t stopAfterNodeID) : DSRTreeNodeCursor(), RootNode(NULL) { T *nodeCursor = startCursor.getNode(); /* since we start from a particular node, we need to check it first */ if (nodeCursor != NULL) { E_AddMode addMode = AM_afterCurrent; T *newNode = NULL; OFStack nodeCursorStack; /* use current node if none was specified */ if (stopAfterNodeID == 0) stopAfterNodeID = nodeCursor->getIdent(); /* perform a "deep search", just like DSRTreeNodeCursor<>::iterate() */ while (nodeCursor != NULL) { /* clone current node and add it to the tree */ if (addNode(newNode = nodeCursor->clone(), addMode) == 0) { /* failed to add node, so delete it and exit the loop */ delete newNode; break; } /* then goto to the next node to be copied */ if (nodeCursor->Down != NULL) { /* go one level down to the first child node */ nodeCursorStack.push(nodeCursor); nodeCursor = nodeCursor->getDown(); addMode = AM_belowCurrent; } else if (nodeCursor->Next != NULL) { /* check whether the last node has been processed */ if (nodeCursor->getIdent() == stopAfterNodeID) { /* exit the loop */ nodeCursor = NULL; } else { /* proceed with the following sibling */ nodeCursor = nodeCursor->getNext(); addMode = AM_afterCurrent; } } else { /* check whether there are any siblings on higher levels */ do { if (!nodeCursorStack.empty()) { nodeCursor = nodeCursorStack.top(); nodeCursorStack.pop(); this->goUp(); /* check whether the last node has been processed */ if ((nodeCursor != NULL) && (nodeCursor->getIdent() == stopAfterNodeID)) { /* exit the loop */ nodeCursor = NULL; } } else nodeCursor = NULL; } while ((nodeCursor != NULL) && (nodeCursor->Next == NULL)); if (nodeCursor != NULL) { nodeCursor = nodeCursor->getNext(); addMode = AM_afterCurrent; } } } /* initialize the cursor */ gotoRoot(); } } template DSRTree::~DSRTree() { clear(); } template DSRTree &DSRTree::operator=(DSRTree tree) { /* by-value parameter serves as a temporary */ swap(tree); return *this; } template void DSRTree::clear() { if (gotoRoot()) { size_t nodeID = 0; /* there might be more than one node at top-level */ do { /* so delete them all */ nodeID = removeNode(); } while (nodeID > 0); } } template void DSRTree::clearAnnotations() { DSRTreeNodeCursor cursor(RootNode); if (cursor.isValid()) { /* iterate over all nodes */ do { cursor.getNode()->clearAnnotation(); } while (cursor.iterate()); } } template OFBool DSRTree::isEmpty() const { return (RootNode == NULL); } template size_t DSRTree::countNodes() const { size_t count = 0; DSRTreeNodeCursor cursor(RootNode); if (cursor.isValid()) { /* iterate over all nodes */ do { ++count; } while (cursor.iterate()); } return count; } template size_t DSRTree::getNextNodeID() const { /* current value of the counter used to create unique identifiers */ return DSRTreeNode::getIdentCounter(); } template size_t DSRTree::gotoRoot() { return this->setCursor(RootNode); } template size_t DSRTree::gotoNode(const size_t searchID, const OFBool startFromRoot) { size_t nodeID = 0; if (searchID > 0) { if (startFromRoot) gotoRoot(); /* call the real function */ nodeID = DSRTreeNodeCursor::gotoNode(searchID); } return nodeID; } template size_t DSRTree::gotoNode(const OFString &reference, const OFBool startFromRoot) { size_t nodeID = 0; if (!reference.empty()) { if (startFromRoot) gotoRoot(); /* call the real function */ nodeID = DSRTreeNodeCursor::gotoNode(reference); } return nodeID; } template size_t DSRTree::gotoNode(const DSRTreeNodeAnnotation &annotation, const OFBool startFromRoot) { size_t nodeID = 0; if (!annotation.isEmpty()) { if (startFromRoot) gotoRoot(); /* call the real function */ nodeID = DSRTreeNodeCursor::gotoNode(annotation); } return nodeID; } template size_t DSRTree::addNode(T *node, const E_AddMode addMode) { size_t nodeID = 0; /* make sure that 'node' points to a single node or to the "root" of a subtree */ if ((node != NULL) && (node->Prev == NULL)) { if (this->NodeCursor != NULL) { DSRTreeNode *lastNode = node; /* update references based on 'addMode' */ switch (addMode) { case AM_afterCurrent: node->Prev = this->NodeCursor; /* goto last node (sibling), if any */ while (lastNode->Next != NULL) lastNode = lastNode->Next; lastNode->Next = this->NodeCursor->Next; /* connect to current node */ if (this->NodeCursor->Next != NULL) (this->NodeCursor->Next)->Prev = lastNode; this->NodeCursor->Next = node; ++this->Position; break; case AM_beforeCurrent: node->Prev = this->NodeCursor->Prev; /* goto last node (sibling), if any */ while (lastNode->Next != NULL) lastNode = lastNode->Next; lastNode->Next = this->NodeCursor; /* connect to current node */ if ((this->NodeCursor->Prev != NULL) && (this->Position > 1)) (this->NodeCursor->Prev)->Next = node; else if (!this->NodeCursorStack.empty() && (this->Position == 1)) this->NodeCursorStack.top()->Down = node; this->NodeCursor->Prev = lastNode; /* check whether root node has been replaced */ if (this->NodeCursor == this->RootNode) this->RootNode = node; break; case AM_belowCurrent: /* store old position */ this->Position.goDown(); this->NodeCursorStack.push(this->NodeCursor); /* parent node has already child nodes */ if (this->NodeCursor->Down != NULL) { DSRTreeNode *tempNode = this->NodeCursor->Down; /* goto last node (sibling) */ while (tempNode->Next != NULL) { tempNode = tempNode->Next; ++this->Position; } /* connect to last child */ tempNode->Next = node; node->Prev = tempNode; ++this->Position; } else this->NodeCursor->Down = node; break; case AM_belowCurrentBeforeFirstChild: /* store old position */ this->Position.goDown(); this->NodeCursorStack.push(this->NodeCursor); /* parent node has already child nodes */ if (this->NodeCursor->Down != NULL) { /* goto last node (sibling), if any */ while (lastNode->Next != NULL) lastNode = lastNode->Next; /* connect to (current) first child */ lastNode->Next = this->NodeCursor->Down; (this->NodeCursor->Down)->Prev = lastNode; } this->NodeCursor->Down = node; break; } this->NodeCursor = node; } else { /* originally, the tree was empty */ this->RootNode = this->NodeCursor = node; this->Position.initialize(); } nodeID = this->NodeCursor->getIdent(); } return nodeID; } template size_t DSRTree::replaceNode(T *node) { size_t nodeID = 0; /* make sure that 'node' points to a single node or to the "root" of a subtree */ if ((node != NULL) && (node->Prev == NULL)) { if (this->NodeCursor != NULL) { /* connect to previous node */ if (this->NodeCursor->Prev != NULL) { (this->NodeCursor->Prev)->Next = node; /* remove reference to former sibling */ this->NodeCursor->Prev = NULL; } else { /* is there any direct parent node? */ if (!this->NodeCursorStack.empty()) { DSRTreeNode *parent = this->NodeCursorStack.top(); if (parent != NULL) parent->Down = node; } } /* connect to next node */ if (this->NodeCursor->Next != NULL) { DSRTreeNode *lastNode = node; /* goto last node (sibling), if any */ while (lastNode->Next != NULL) lastNode = lastNode->Next; (this->NodeCursor->Next)->Prev = lastNode; lastNode->Next = this->NodeCursor->Next; /* remove reference to former sibling */ this->NodeCursor->Next = NULL; } /* check whether root node has been replaced */ if (this->NodeCursor == this->RootNode) this->RootNode = node; /* free memory of old (now replaced) node */ deleteTreeFromRootNode(this->NodeCursor); /* set cursor to new node */ this->NodeCursor = node; nodeID = node->getIdent(); } } return nodeID; } template T *DSRTree::extractNode() { T *cursor = this->NodeCursor; /* extract current node (incl. subtree) from tree */ if (cursor != NULL) { /* are there any siblings? */ if ((cursor->Prev != NULL) || (cursor->Next != NULL)) { /* connect to previous node */ if (cursor->Prev != NULL) (cursor->Prev)->Next = cursor->Next; else { /* is there any direct parent node? */ if (!this->NodeCursorStack.empty()) { DSRTreeNode *parent = this->NodeCursorStack.top(); if (parent != NULL) parent->Down = cursor->Next; } } /* connect to next node */ if (cursor->Next != NULL) { (cursor->Next)->Prev = cursor->Prev; if (this->NodeCursor == this->RootNode) this->RootNode = cursor->getNext(); // old root node deleted this->NodeCursor = cursor->getNext(); } else { /* set cursor to previous node since there is no next node */ this->NodeCursor = cursor->getPrev(); --this->Position; } } else { /* no siblings: check for child nodes */ if (!this->NodeCursorStack.empty()) { this->NodeCursor = this->NodeCursorStack.top(); this->NodeCursorStack.pop(); this->Position.goUp(); /* should never be NULL, but ... */ if (this->NodeCursor != NULL) this->NodeCursor->Down = NULL; else { this->RootNode = NULL; // tree is now empty this->Position.clear(); } } else { this->RootNode = this->NodeCursor = NULL; // tree is now empty this->Position.clear(); } } /* remove references to former siblings */ cursor->Prev = NULL; cursor->Next = NULL; } return cursor; } template T *DSRTree::getAndRemoveRootNode() { T *root = RootNode; /* "forget" reference to root node */ RootNode = NULL; return root; } template size_t DSRTree::removeNode() { size_t nodeID = 0; /* extract current node (incl. subtree) from tree */ T *cursor = extractNode(); if (cursor != NULL) { /* delete all nodes from extracted subtree */ /* (could also use the "new" DSRTreeNodeCursor class) */ T *delNode = NULL; OFStack cursorStack; while (cursor != NULL) { delNode = cursor; if (cursor->Down != NULL) { if (cursor->Next != NULL) cursorStack.push(cursor->getNext()); cursor = cursor->getDown(); } else if (cursor->Next != NULL) cursor = cursor->getNext(); else if (!cursorStack.empty()) { cursor = cursorStack.top(); cursorStack.pop(); } else cursor = NULL; delete delNode; } /* return identifier of (new) current node */ if (this->NodeCursor != NULL) nodeID = this->NodeCursor->getIdent(); } return nodeID; } template DSRTree *DSRTree::extractSubTree() { DSRTree *tree = NULL; /* extract current node from tree and create a new tree object (with this root) */ T *node = extractNode(); if (node != NULL) tree = new DSRTree(node); return tree; } template DSRTree *DSRTree::cloneSubTree(const size_t stopAfterNodeID) const { /* create a copy of the specified subtree */ return new DSRTree(this->NodeCursor, stopAfterNodeID); } // protected methods template void DSRTree::swap(DSRTree &tree) { /* swap pointer to the root tree node */ OFswap(RootNode, tree.RootNode); } template T *DSRTree::getRoot() const { return RootNode; } template void DSRTree::deleteTreeFromRootNode(T *rootNode) { /* create a temporary tree object from the given node, */ /* the content will be deleted during destruction */ DSRTree tree(rootNode); } #endif