Newer
Older
dom-persist / source / nodecode.d
  1. module nodecode;
  2.  
  3. import std.traits;
  4. import std.format;
  5.  
  6. import dom_persist;
  7.  
  8.  
  9. enum TreeNodeType {
  10. nulltype=-2, // indicates a type read from the database which is not one of the recognised types
  11. tree=-1, docType, element, text, comment
  12. }
  13.  
  14. TreeNodeType getTreeNodeType( int tip ){
  15. auto tnts = [EnumMembers!TreeNodeType];
  16. foreach( tnt; tnts ){
  17. if( tnt==tip ) return tnt;
  18. }
  19. return TreeNodeType.nulltype;
  20. }
  21.  
  22.  
  23. struct NodeData {
  24. long ID;
  25. string e_data;
  26. long pid;
  27. TreeNodeType type;
  28. bool dirty = false;
  29. long orig_pid=0;
  30. this( long ID, string e_data, long pid, TreeNodeType type){
  31. this.ID = ID;
  32. this.e_data = e_data;
  33. this.pid = pid;
  34. this.type = type;
  35. }
  36. }
  37.  
  38. /**
  39. * TreeNode is a class because we need the references to remain valid when we modify containers such as
  40. * the hashmap which indexes on ID.
  41. *
  42. * If we use a struct, then we need to hold the TreeNode data somewhere in order to use a pointer to the data. If
  43. * we move the data, such as updating a map, then all the pointers need to change, or we need pointers to pointers.
  44. * Either is not ideal. If we use references, as provided by a class, then the GC will track the references and ensure
  45. * that they remain valid. We can move the references knowing that the data remains in place on the heap
  46. *
  47. */
  48. class TreeNode {
  49. private:
  50. Tree_Db owner_tree;
  51. // end private
  52. public:
  53. NodeData node_data;
  54. TreeNode[] child_nodes;
  55. bool dirty; // true indicates a change in child_nodes ordering
  56.  
  57. this( Tree_Db owner_tree, NodeData node_data){
  58. this.owner_tree = owner_tree;
  59. this.node_data = node_data;
  60. }
  61. /**
  62. * Returns the parent node of this node or null if no parent exists (i.e. tree-root)
  63. */
  64. TreeNode parentNode(){
  65. if(node_data.pid==0) return null;
  66. return owner_tree.getTreeNodeById( node_data.pid );
  67. }
  68. /**
  69. * Returns the next sibling of this node or null if none exists
  70. */
  71. TreeNode nextSibling(){
  72. if(node_data.pid==0) return null;
  73. //bDebug_out = true;
  74. TreeNode p_node = owner_tree.getTreeNodeById( node_data.pid );
  75. if(p_node is null){
  76. debug_out("(ID,e_data) = ", node_data.ID, node_data.e_data );
  77. throw new Exception("oops");
  78. }
  79. foreach( i, c_node; p_node.child_nodes){
  80. if(c_node==this){
  81. if( i == p_node.child_nodes.length-1) return null;
  82. return p_node.child_nodes[i+1];
  83. }
  84. }
  85. throw new Exception("Damaged tree, possibly incorrect parent id for a child.");
  86. }
  87.  
  88. bool hasChildNodes(){
  89. return child_nodes.length>0;
  90. }
  91.  
  92. TreeNode firstChild(){
  93. if( child_nodes.length==0 ) return null;
  94. return child_nodes[0];
  95. }
  96.  
  97. TreeNode getChildAt( int pos ){
  98. if( child_nodes.length<=pos ) return null;
  99. return child_nodes[pos];
  100. }
  101.  
  102. /**
  103. * Set the data for this node.
  104. *
  105. * The data is interpreted using the type of node. For example, the text content of a text node is
  106. * the data whereas for element types, the data is used to hold the element name.
  107. */
  108. void setData( string nData ){
  109. node_data.e_data = nData;
  110. node_data.dirty = true;
  111. }
  112. /**
  113. * Set the node ID of this treenode. Also sets the parent IDs of all child nodes.
  114. */
  115. long setNodeId( long nnid ){
  116. long oldid = node_data.ID;
  117. node_data.ID = nnid;
  118. foreach(child; child_nodes ){
  119. child.node_data.pid = nnid;
  120. }
  121. return nnid;
  122. }
  123.  
  124. /**
  125. * Insert a new child node at the position indicated.
  126. */
  127. TreeNode insertChild( TreeNodeType n_type, string e_data, int pos ){
  128. return owner_tree.insertChild( this, n_type, e_data, pos );
  129. }
  130.  
  131. /**
  132. * Append a new child node.
  133. */
  134. TreeNode appendChild( TreeNodeType n_type, string e_data ){
  135. return owner_tree.insertChild( this, n_type, e_data, cast(int)(child_nodes.length) );
  136. }
  137. void moveNode( TreeNode new_p_node, int pos ){
  138. owner_tree.moveNode( this, new_p_node, pos );
  139. }
  140. /**
  141. * Delete this node from the the tree.
  142. */
  143. void deleteNode( ){
  144. owner_tree.deleteNode( this );
  145. }
  146.  
  147. /**
  148. * Internal use only
  149. */
  150. void cutChild( TreeNode c_node ){
  151. foreach( i, child; child_nodes ){
  152. if(child == c_node ){
  153. removeAt!TreeNode( child_nodes, cast(int)(i) );
  154. c_node.node_data.pid = 0; //it has no parent now
  155. dirty = true;
  156. return;
  157. }
  158. }
  159. throw new Exception( format("Node %d is not a child of node %d ", c_node.node_data.ID, node_data.ID) );
  160. }
  161. // end public
  162. }