View Javadoc
1   package org.synchronoss.cpo.core;
2   
3   /*-
4    * [[
5    * core
6    * ==
7    * Copyright (C) 2003 - 2026 Exaxis LLC, Synchronoss Technologies Inc
8    * ==
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ]]
23   */
24  
25  import java.io.Serializable;
26  import java.util.ArrayList;
27  import java.util.Comparator;
28  import java.util.List;
29  import java.util.concurrent.atomic.AtomicLong;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  /**
34   * This is a general Node class to be used to build different types of trees. There are very few
35   * rules in this class as they should be implemented by users of this class. This Object is the
36   * basis for the CompositePattern. It can be both a composite or a component node. The isLeaf flag
37   * determines how it treats itself. It is important for the inheriting classes to call setLeaf() to
38   * tell the Node how to act.
39   *
40   * @author David E. Berry
41   * @version 1.0
42   */
43  public class Node implements Serializable, Cloneable, Comparable<Node> {
44  
45    /** Version Id for this class. */
46    private static final long serialVersionUID = 1L;
47  
48    private static final Logger logger = LoggerFactory.getLogger(Node.class);
49  
50    private static final int CHILD_NODE = 0;
51  
52    private static final AtomicLong SERIAL_SOURCE = new AtomicLong();
53  
54    /**
55     * Creation-order serial backing the default natural ordering; see {@link #compareTo(Node)}.
56     * Deserialized nodes keep the serial they were created with.
57     */
58    private final long nodeSerial = SERIAL_SOURCE.incrementAndGet();
59  
60    /** The parent node for this Node */
61    private Node parent = null;
62  
63    /** The first child in the linked list of children */
64    private Node firstChild = null;
65  
66    /** The previous sibling for this node */
67    private Node prevSibling = null;
68  
69    /** The next sibling for this node */
70    private Node nextSibling = null;
71  
72    /** Whether this node is allowed to have chidren. */
73    private boolean allowChildren = true;
74  
75    /**
76     * This is the default constructor for the Node class. By default, it creates a Composite Node,
77     * that is, a Node that can have children nodes.
78     */
79    protected Node() {}
80  
81    /**
82     * This constructor allows you to create a composite or component node based on the node_type.
83     *
84     * @param nodeType nodeType can be one of two values:
85     *     <p>Node.ParentNode Node.ChildNode
86     */
87    protected Node(int nodeType) {
88      if (nodeType == CHILD_NODE) {
89        allowChildren = false;
90      }
91    }
92  
93    /**
94     * This is the factory method for creating Node objects.
95     *
96     * @param nodeType nodeType can be one of two values:
97     *     <p>Node.ParentNode Node.ChildNode
98     * @return an Instance of an Node
99     */
100   public static Node createNode(int nodeType) {
101     return new Node(nodeType);
102   }
103 
104   /** Resets all the attributes to their default state. */
105   public void release() {
106 
107     parent = null;
108     firstChild = null;
109     prevSibling = null;
110     nextSibling = null;
111     allowChildren = true;
112   }
113 
114   protected void setAllowChildren(boolean ac) {
115     allowChildren = ac;
116   }
117 
118   /**
119    * Gets whether this node is allowed to have children.
120    *
121    * @return {@code true} if this node may have children (it is a composite node), {@code false} if
122    *     it is a leaf-only component node
123    */
124   public boolean getAllowChildren() {
125     return this.allowChildren;
126   }
127 
128   /**
129    * Sets the Parent Node for this Node.
130    *
131    * @param node The node that will become the parent.
132    * @see Node
133    * @see Node
134    * @see Node
135    */
136   public void setParent(Node node) {
137     this.parent = node;
138   }
139 
140   /**
141    * Sets the PrevSibling for this node. It also sets the NextSibling of the previous node to insure
142    * that the doubly-linked list is maintained.
143    *
144    * @param node The node that will become the previous Sibling
145    */
146   public void setPrevSibling(Node node) {
147     this.prevSibling = node;
148     node.nextSibling = this;
149   }
150 
151   /**
152    * Sets the NextSibling for this node. It also sets the PrevSibling of the next node to insure
153    * that the doubly-linked list is maintained.
154    *
155    * @param node The node that will become the next Sibling
156    */
157   public void setNextSibling(Node node) {
158     this.nextSibling = node;
159     node.prevSibling = this;
160   }
161 
162   /**
163    * Gets the parent node for this node
164    *
165    * @return an Node representing the parent of this node or null if no parent exists.
166    */
167   public Node getParentNode() {
168     return this.parent;
169   }
170 
171   /**
172    * Gets the previous sibling for this node in the linked list of Nodes.
173    *
174    * @return an Node that represents the previous sibling or null if no sibling exists.
175    */
176   public Node getPrevSibling() {
177     return this.prevSibling;
178   }
179 
180   /**
181    * Gets the next sibling for this node in the linked list of Nodes.
182    *
183    * @return an Node that represents the next sibling or null if no sibling exists.
184    */
185   public Node getNextSibling() {
186     return this.nextSibling;
187   }
188 
189   /**
190    * Checks to see if this node has a parent.
191    *
192    * @return boolean indicating true if this node has a parent, false if it has no parent.
193    */
194   public boolean hasParent() {
195     return this.parent != null;
196   }
197 
198   /**
199    * Checks to see if this node is a leaf node, that is, if it has no children.
200    *
201    * @return boolean indicating true if it is a leafNode, false if not
202    */
203   public boolean isLeaf() {
204     return getFirstChild() == null;
205   }
206 
207   /**
208    * This function adds a child to the linked-list of children for this node. It adds the child to
209    * the end of the list.
210    *
211    * @param node Node that is the node to be added as a child of this Node.
212    * @throws ChildNodeException throws an exception if this child is not allowed to have children
213    */
214   public void addChild(Node node) throws ChildNodeException {
215     if (node != null) {
216       if (!allowChildren) {
217         throw new ChildNodeException();
218       }
219 
220       if (getFirstChild() == null) {
221         setFirstChild(node);
222         getFirstChild().setPrevSibling(firstChild);
223         getFirstChild().setNextSibling(firstChild);
224       } else { // Add it to the end of the list
225         Node lastChild = getFirstChild().getPrevSibling();
226         if (lastChild != null) {
227           lastChild.setNextSibling(node);
228         }
229         node.setNextSibling(getFirstChild());
230       }
231       node.setParent(this);
232     }
233   }
234 
235   /**
236    * This function adds a child to the linked-list of children for this node. It adds the child to
237    * the end of the list.
238    *
239    * @param node Node that is the node to be added as a child of this Node.
240    * @throws ChildNodeException throws an exception if this node is not allowed to have children
241    */
242   public void addChildSort(Node node) throws ChildNodeException {
243     addChildSort(node, null);
244   }
245 
246   /**
247    * Adds a child to the linked-list of children for this node, inserting it in sorted order.
248    *
249    * @param node the node to be added as a child of this node
250    * @param c the comparator used to determine sort order, or {@code null} to use the nodes' natural
251    *     ordering ({@link #compareTo(Node)})
252    * @throws ChildNodeException throws an exception if this node is not allowed to have children
253    */
254   public void addChildSort(Node node, Comparator<Node> c) throws ChildNodeException {
255     if (node != null) {
256       if (!allowChildren) {
257         throw new ChildNodeException();
258       }
259 
260       if (isLeaf()) {
261         setFirstChild(node);
262         getFirstChild().setPrevSibling(node);
263         getFirstChild().setNextSibling(node);
264       } else { // Add it in sorted order
265         boolean added = false;
266         Node currNode = getFirstChild();
267         do {
268           if (doCompare(node, currNode, c) < 0) {
269             node.setPrevSibling(currNode.getPrevSibling());
270             node.setNextSibling(currNode);
271             if (currNode == getFirstChild()) {
272               setFirstChild(node);
273             }
274             added = true;
275             break;
276           }
277 
278           currNode = currNode.getNextSibling();
279         } while (currNode != getFirstChild());
280 
281         if (!added) { // add to the end of the list.
282           Node lastChild = getFirstChild().getPrevSibling();
283           if (lastChild != null) {
284             lastChild.setNextSibling(node);
285           }
286           node.setNextSibling(getFirstChild());
287         }
288       }
289       node.setParent(this);
290     }
291   }
292 
293   protected int doCompare(Node n1, Node n2, Comparator<Node> c) {
294     int rc;
295 
296     if (c != null) {
297       rc = c.compare(n1, n2);
298     } else if (n1 == null && n2 == null) {
299       rc = 0;
300     } else {
301       rc = n1.compareTo(n2);
302     }
303 
304     return rc;
305   }
306 
307   /**
308    * Inserts a Sibling into the linked list just prior to this Node
309    *
310    * @param node Node to be made the prevSibling
311    * @throws ChildNodeException an exception inserting the child node
312    */
313   public void insertSiblingBefore(Node node) throws ChildNodeException {
314     if (node != null) {
315       node.setPrevSibling(getPrevSibling());
316       node.setNextSibling(this);
317     }
318   }
319 
320   /**
321    * Adds a Sibling immediately following this Node.
322    *
323    * @param node Node to be made the next sibling
324    */
325   public void insertSiblingAfter(Node node) {
326     if (node != null) {
327       node.setNextSibling(getNextSibling());
328       node.setPrevSibling(this);
329     }
330   }
331 
332   /**
333    * Inserts a new Parent into the tree structure and adds this node as its child.
334    *
335    * @param node Node that will become this nodes new Parent.
336    * @throws ChildNodeException an exception adding the parent
337    */
338   public void insertParentBefore(Node node) throws ChildNodeException {
339     if (node != null) {
340       if (hasParent()) {
341         getParentNode().addChild(node);
342         getParentNode().removeChild(this);
343       }
344       node.addChild(this);
345     }
346   }
347 
348   /**
349    * Inserts a new Parent Node as a child of this node and moves all pre-existing children to be
350    * children of the new Parent Node.
351    *
352    * @param node Node to become a child of this node and parent to all pre-existing children of this
353    *     node.
354    * @throws ChildNodeException an exception adding the parent
355    */
356   public void insertParentAfter(Node node) throws ChildNodeException {
357     if (node != null) {
358       // give this node my children
359       node.setFirstChild(getFirstChild());
360 
361       setFirstChild(null); // clear our list
362       addChild(node); // make our child
363     }
364   }
365 
366   /**
367    * Searches for an immediate child node and if found removes it from the linked-list of children.
368    *
369    * @param node Node to be searched for and removed if found.
370    * @return true if removed
371    * @throws ChildNodeException throws an exception if this node is not allowed to have children
372    */
373   public boolean removeChild(Node node) throws ChildNodeException {
374     Node currNode = getFirstChild();
375     boolean rc = false;
376 
377     if (!allowChildren) {
378       throw new ChildNodeException();
379     }
380 
381     if (node != null && !isLeaf()) {
382       // Is this the first and only child
383       if (node == currNode && node == currNode.getNextSibling()) {
384         setFirstChild(null);
385         rc = true;
386       } else {
387         // Verify that this Node is a child here
388         do {
389           if (currNode == node) {
390             // Remove this child
391             if (node.getPrevSibling() != null) {
392               node.getPrevSibling().setNextSibling(node.getNextSibling());
393             }
394             if (node == getFirstChild()) {
395               setFirstChild(node.getNextSibling());
396             }
397 
398             rc = true;
399 
400             // do not release. Item.resolve expects the
401             // pointers to be intact after a remove
402             // node.release();
403             break;
404           }
405           currNode = currNode.getNextSibling();
406         } while (currNode != getFirstChild());
407       }
408     }
409 
410     return rc;
411   }
412 
413   /**
414    * Remove just this node from the tree. The children of this node get attached to the parent.
415    *
416    * @throws ChildNodeException error removing the child node
417    */
418   public void removeChildNode() throws ChildNodeException {
419     Node parentLast;
420     Node thisLast;
421 
422     // Add this nodes children to the end of the Parents children list
423     if (!isLeaf() && hasParent()) {
424       parentLast = getParentNode().getFirstChild().getPrevSibling();
425       thisLast = getFirstChild().getPrevSibling();
426 
427       // add the first child to the end of the parent list
428       parentLast.setNextSibling(getFirstChild());
429 
430       // point the last child to the start of the parent list
431       thisLast.setNextSibling(getParentNode().getFirstChild());
432 
433       // Now remove self
434       getParentNode().removeChild(this);
435     }
436   }
437 
438   /**
439    * Remove this node and all its children from the tree.
440    *
441    * @throws ChildNodeException error removing the child node
442    */
443   public void removeAll() throws ChildNodeException {
444     if (hasParent()) {
445       getParentNode().removeChild(this);
446     }
447   }
448 
449   /**
450    * Gets the first child node in the linked-list of children.
451    *
452    * @return Node reference to the first child node in the linked-list of children
453    */
454   public Node getFirstChild() {
455     return this.firstChild;
456   }
457 
458   /**
459    * Sets the first child node in the linked-list of children.
460    *
461    * @param node Node which will be made the first child node in the linked-list of children.
462    * @throws ChildNodeException throws if this node is not allowed to have children
463    */
464   public void setFirstChild(Node node) throws ChildNodeException {
465     if (!allowChildren) {
466       throw new ChildNodeException();
467     }
468     this.firstChild = node;
469   }
470 
471   /**
472    * Implements the visitor pattern. This is a Depth-based traversal that will call the INodeVisitor
473    * visitBegin(), visitMiddle(), and visitEnd() for parent nodes and will call visit() for leaf
474    * nodes.
475    *
476    * @param nv INodeVisitor to call upon reaching a node when traversing the tree.
477    * @see NodeVisitor
478    * @return false to cancel the visitor
479    */
480   public boolean acceptDFVisitor(NodeVisitor nv) {
481     Node currNode;
482     boolean continueVisit = true;
483 
484     if (nv != null) {
485       if (isLeaf()) {
486         continueVisit = nv.visit(this);
487       } else {
488         continueVisit = nv.visitBegin(this);
489         if (continueVisit) {
490           currNode = getFirstChild();
491           do {
492             continueVisit = currNode.acceptDFVisitor(nv);
493             if (continueVisit) {
494               currNode = currNode.getNextSibling();
495               if (currNode == getFirstChild()) {
496                 break;
497               }
498               continueVisit = nv.visitMiddle(this);
499             }
500           } while (continueVisit);
501           if (continueVisit) {
502             continueVisit = nv.visitEnd(this);
503           }
504         }
505       }
506     }
507     return continueVisit;
508   }
509 
510   /**
511    * Counts the immediate children of this node.
512    *
513    * @return the count of children
514    */
515   public int getChildCount() {
516     Node currNode;
517     int count = 0;
518 
519     // Do we have any children
520     if (!isLeaf()) {
521       currNode = getFirstChild();
522       do {
523         ++count;
524         currNode = currNode.getNextSibling();
525       } while (currNode != getFirstChild());
526     }
527 
528     return count;
529   }
530 
531   /**
532    * Collects the immediate children of this node into a list, in sibling order.
533    *
534    * @return the list of child nodes
535    */
536   public List<Node> getChildList() {
537     Node currNode;
538     ArrayList<Node> al = new ArrayList<>();
539 
540     // Do we have any children
541     if (!isLeaf()) {
542       currNode = getFirstChild();
543       do {
544         al.add(currNode);
545         currNode = currNode.getNextSibling();
546       } while (currNode != getFirstChild());
547     }
548 
549     return al;
550   }
551 
552   @Override
553   public Object clone() throws CloneNotSupportedException {
554     Node thisClone = (Node) super.clone();
555     Node currNode;
556 
557     thisClone.release(); // Clear all the attributes
558     thisClone.setAllowChildren(getAllowChildren());
559 
560     // Do we have any children
561     if (!isLeaf()) {
562       currNode = getFirstChild();
563       do {
564         try {
565           thisClone.addChild((Node) currNode.clone());
566         } catch (ChildNodeException e) {
567           // This should not happen since we are cloning a parent if we got here.
568           logger.error("Error adding cloned node ", e);
569         }
570         currNode = currNode.getNextSibling();
571       } while (currNode != getFirstChild());
572     }
573 
574     return thisClone;
575   }
576 
577   /**
578    * Default natural ordering: creation order. Ordering by creation serial (rather than identity
579    * hash code) keeps the Comparable contract — it never reports two distinct live nodes as equal,
580    * so sorted collections and {@link #addChildSort(Node)} behave deterministically. Subclasses with
581    * a meaningful value ordering should override this.
582    */
583   @Override
584   public int compareTo(Node o) {
585     return Long.compare(nodeSerial, o.nodeSerial);
586   }
587 
588   @Override
589   public boolean equals(Object o) {
590     return this == o;
591   }
592 
593   @Override
594   public int hashCode() {
595     return System.identityHashCode(this);
596   }
597 }