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 }