View Javadoc

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