DYT/Tool/3rdParty_x64/include/dcmtk/dcmsr/dsrtree.h
2024-11-22 23:19:31 +08:00

965 lines
29 KiB
C++

/*
*
* 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 <typename T> class DSRTree;
/*---------------------*
* class declaration *
*---------------------*/
/** Class for tree nodes
*/
class DCMTK_DCMSR_EXPORT DSRTreeNode
: protected DSRTypes
{
// allow direct access to member variables
friend class DSRTree<DSRTreeNode>;
friend class DSRTree<DSRDocumentTreeNode>;
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<typename T = DSRTreeNode> class DSRTree
: public DSRTreeNodeCursor<T>,
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<T> &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<T> 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<T> *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<T> *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<T> &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<T> &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<typename T>
DSRTree<T>::DSRTree()
: DSRTreeNodeCursor<T>(),
RootNode(NULL)
{
}
template<typename T>
DSRTree<T>::DSRTree(const DSRTree<T> &tree)
: DSRTreeNodeCursor<T>(),
RootNode(NULL)
{
if (!tree.isEmpty())
{
E_AddMode addMode = AM_afterCurrent;
T *newNode = NULL;
T *nodeCursor = tree.getRoot();
OFStack<T *> 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<typename T>
DSRTree<T>::DSRTree(T *rootNode)
: DSRTreeNodeCursor<T>(),
RootNode(rootNode)
{
/* initialize the cursor */
gotoRoot();
}
template<typename T>
DSRTree<T>::DSRTree(const DSRTreeNodeCursor<T> &startCursor,
size_t stopAfterNodeID)
: DSRTreeNodeCursor<T>(),
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<T *> 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<typename T>
DSRTree<T>::~DSRTree()
{
clear();
}
template<typename T>
DSRTree<T> &DSRTree<T>::operator=(DSRTree<T> tree)
{
/* by-value parameter serves as a temporary */
swap(tree);
return *this;
}
template<typename T>
void DSRTree<T>::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<typename T>
void DSRTree<T>::clearAnnotations()
{
DSRTreeNodeCursor<T> cursor(RootNode);
if (cursor.isValid())
{
/* iterate over all nodes */
do {
cursor.getNode()->clearAnnotation();
} while (cursor.iterate());
}
}
template<typename T>
OFBool DSRTree<T>::isEmpty() const
{
return (RootNode == NULL);
}
template<typename T>
size_t DSRTree<T>::countNodes() const
{
size_t count = 0;
DSRTreeNodeCursor<T> cursor(RootNode);
if (cursor.isValid())
{
/* iterate over all nodes */
do {
++count;
} while (cursor.iterate());
}
return count;
}
template<typename T>
size_t DSRTree<T>::getNextNodeID() const
{
/* current value of the counter used to create unique identifiers */
return DSRTreeNode::getIdentCounter();
}
template<typename T>
size_t DSRTree<T>::gotoRoot()
{
return this->setCursor(RootNode);
}
template<typename T>
size_t DSRTree<T>::gotoNode(const size_t searchID,
const OFBool startFromRoot)
{
size_t nodeID = 0;
if (searchID > 0)
{
if (startFromRoot)
gotoRoot();
/* call the real function */
nodeID = DSRTreeNodeCursor<T>::gotoNode(searchID);
}
return nodeID;
}
template<typename T>
size_t DSRTree<T>::gotoNode(const OFString &reference,
const OFBool startFromRoot)
{
size_t nodeID = 0;
if (!reference.empty())
{
if (startFromRoot)
gotoRoot();
/* call the real function */
nodeID = DSRTreeNodeCursor<T>::gotoNode(reference);
}
return nodeID;
}
template<typename T>
size_t DSRTree<T>::gotoNode(const DSRTreeNodeAnnotation &annotation,
const OFBool startFromRoot)
{
size_t nodeID = 0;
if (!annotation.isEmpty())
{
if (startFromRoot)
gotoRoot();
/* call the real function */
nodeID = DSRTreeNodeCursor<T>::gotoNode(annotation);
}
return nodeID;
}
template<typename T>
size_t DSRTree<T>::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<typename T>
size_t DSRTree<T>::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<typename T>
T *DSRTree<T>::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<typename T>
T *DSRTree<T>::getAndRemoveRootNode()
{
T *root = RootNode;
/* "forget" reference to root node */
RootNode = NULL;
return root;
}
template<typename T>
size_t DSRTree<T>::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<T *> 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<typename T>
DSRTree<T> *DSRTree<T>::extractSubTree()
{
DSRTree<T> *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<T>(node);
return tree;
}
template<typename T>
DSRTree<T> *DSRTree<T>::cloneSubTree(const size_t stopAfterNodeID) const
{
/* create a copy of the specified subtree */
return new DSRTree<T>(this->NodeCursor, stopAfterNodeID);
}
// protected methods
template<typename T>
void DSRTree<T>::swap(DSRTree<T> &tree)
{
/* swap pointer to the root tree node */
OFswap(RootNode, tree.RootNode);
}
template<typename T>
T *DSRTree<T>::getRoot() const
{
return RootNode;
}
template<typename T>
void DSRTree<T>::deleteTreeFromRootNode(T *rootNode)
{
/* create a temporary tree object from the given node, */
/* the content will be deleted during destruction */
DSRTree<T> tree(rootNode);
}
#endif