View Javadoc
1   /**
2    * Copyright 2010-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util.tree;
17  
18  import static com.google.common.base.Optional.absent;
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.collect.Lists.newArrayList;
21  import static org.kuali.common.util.base.Precondition.checkNotNull;
22  
23  import java.util.List;
24  
25  import com.google.common.base.Optional;
26  import com.google.common.collect.ImmutableList;
27  
28  public class MutableNode<T> extends AbstractNode<T> {
29  
30  	protected Optional<MutableNode<T>> mutableParent = absent();
31  	protected List<MutableNode<T>> mutableChildren = newArrayList();
32  	protected T element;
33  
34  	public static <T> MutableNode<T> of(T element) {
35  		return new MutableNode<T>(element);
36  	}
37  
38  	public static <T> MutableNode<T> copyOf(Node<T> node) {
39  		MutableNode<T> mutable = new MutableNode<T>(node.getElement());
40  		for (Node<T> child : node.getChildren()) {
41  			mutable.add(copyOf(child));
42  		}
43  		return mutable;
44  	}
45  
46  	protected MutableNode() {
47  	}
48  
49  	public MutableNode(T element) {
50  		setElement(element);
51  	}
52  
53  	public void setElement(T element) {
54  		checkNotNull(element, "element");
55  		this.element = element;
56  	}
57  
58  	@Override
59  	public T getElement() {
60  		return element;
61  	}
62  
63  	@Override
64  	public Optional<Node<T>> getParent() {
65  		return Optional.<Node<T>> fromNullable(getMutableParent().orNull());
66  	}
67  
68  	public Optional<MutableNode<T>> getMutableParent() {
69  		return mutableParent;
70  	}
71  
72  	protected void setMutableParent(Optional<MutableNode<T>> parent) {
73  		this.mutableParent = parent;
74  	}
75  
76  	protected void setParent(MutableNode<T> parent) {
77  		setMutableParent(Optional.of(checkNotNull(parent, "parent")));
78  	}
79  
80  	/**
81  	 * Returns an immutable list of the nodes current children
82  	 */
83  	@Override
84  	public List<Node<T>> getChildren() {
85  		List<Node<T>> list = newArrayList();
86  		for (Node<T> child : mutableChildren) {
87  			list.add(child);
88  		}
89  		return ImmutableList.copyOf(list);
90  	}
91  
92  	public void remove(MutableNode<T> child) {
93  		boolean parent = isParent(checkNotNull(child, "child"));
94  		checkArgument(parent, "remove can only be invoked with a current child of this node");
95  		remove(mutableChildren.indexOf(child));
96  	}
97  
98  	public void remove(int index) {
99  		MutableNode<T> child = mutableChildren.get(index);
100 		mutableChildren.remove(index);
101 		child.setMutableParent(Optional.<MutableNode<T>> absent());
102 	}
103 
104 	public void add(List<MutableNode<T>> children) {
105 		for (MutableNode<T> child : checkNotNull(children, "children")) {
106 			add(child);
107 		}
108 	}
109 
110 	public void add(MutableNode<T> child1, MutableNode<T> child2) {
111 		add(ImmutableList.of(child1, child2));
112 	}
113 
114 	public void add(MutableNode<T> child1, MutableNode<T> child2, MutableNode<T> child3) {
115 		add(ImmutableList.of(child1, child2, child3));
116 	}
117 
118 	public void add(MutableNode<T> child1, MutableNode<T> child2, MutableNode<T> child3, MutableNode<T> child4) {
119 		add(ImmutableList.of(child1, child2, child3, child4));
120 	}
121 
122 	public void add(MutableNode<T> child1, MutableNode<T> child2, MutableNode<T> child3, MutableNode<T> child4, MutableNode<T> child5) {
123 		add(ImmutableList.of(child1, child2, child3, child4, child5));
124 	}
125 
126 	public void add(MutableNode<T> child) {
127 		checkNotNull(child, "child");
128 		add(mutableChildren.size(), child);
129 	}
130 
131 	public void add(int index, MutableNode<T> child) {
132 		// Can't be null
133 		checkNotNull(child, "child");
134 
135 		// If it's already a child, it gets removed from it's current position and then added to the end
136 		// Thus, index needs to be children.size() - 1 if it's already a child
137 		int actualIndex = isChild(child) ? mutableChildren.size() - 1 : mutableChildren.size();
138 
139 		// Child can't be us, our parent, our grandparent, etc
140 		checkArgument(!isAncestor(child), "cannot be an ancestor of 'child'");
141 
142 		// Remove this child from it's current parent
143 		// If the child's parent is us, this decreases our child count by 1 (temporarily)
144 		if (child.getMutableParent().isPresent()) {
145 			child.getMutableParent().get().remove(child);
146 		}
147 
148 		// Make the child's parent this node
149 		child.setParent(this);
150 
151 		// Add the child
152 		mutableChildren.add(actualIndex, child);
153 	}
154 
155 	public void removeAllChildren() {
156 		for (int i = 0; i < mutableChildren.size(); i++) {
157 			remove(i);
158 		}
159 	}
160 
161 	/**
162 	 * Removes the subtree rooted at this node from the tree, giving this node an absent parent. Does nothing if this node is the root of its tree.
163 	 */
164 	public void removeFromParent() {
165 		Optional<MutableNode<T>> parent = getMutableParent();
166 		if (parent.isPresent()) {
167 			parent.get().remove(this);
168 		}
169 	}
170 }