001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util.tree; 017 018import static com.google.common.base.Optional.absent; 019import static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.base.Preconditions.checkNotNull; 021import static com.google.common.collect.Lists.newArrayList; 022import static org.kuali.common.util.base.Precondition.checkNotNull; 023 024import java.util.List; 025 026import com.google.common.base.Optional; 027import com.google.common.collect.ImmutableList; 028 029public class MutableNode<T> extends AbstractNode<T> { 030 031 protected Optional<MutableNode<T>> mutableParent = absent(); 032 protected List<MutableNode<T>> mutableChildren = newArrayList(); 033 protected T element; 034 035 public static <T> MutableNode<T> of(T element) { 036 return new MutableNode<T>(element); 037 } 038 039 public static <T> MutableNode<T> copyOf(Node<T> node) { 040 MutableNode<T> mutable = new MutableNode<T>(node.getElement()); 041 for (Node<T> child : node.getChildren()) { 042 mutable.add(copyOf(child)); 043 } 044 return mutable; 045 } 046 047 protected MutableNode() { 048 } 049 050 public MutableNode(T element) { 051 setElement(element); 052 } 053 054 public void setElement(T element) { 055 this.element = checkNotNull(element); 056 } 057 058 @Override 059 public T getElement() { 060 return element; 061 } 062 063 @Override 064 public Optional<Node<T>> getParent() { 065 return Optional.<Node<T>> fromNullable(getMutableParent().orNull()); 066 } 067 068 public Optional<MutableNode<T>> getMutableParent() { 069 return mutableParent; 070 } 071 072 protected void setMutableParent(Optional<MutableNode<T>> parent) { 073 this.mutableParent = parent; 074 } 075 076 protected void setParent(MutableNode<T> parent) { 077 setMutableParent(Optional.of(checkNotNull(parent, "parent"))); 078 } 079 080 /** 081 * Returns an immutable list of the nodes current children 082 */ 083 @Override 084 public List<Node<T>> getChildren() { 085 List<Node<T>> list = newArrayList(); 086 for (Node<T> child : mutableChildren) { 087 list.add(child); 088 } 089 return ImmutableList.copyOf(list); 090 } 091 092 public void remove(MutableNode<T> child) { 093 boolean parent = isParent(checkNotNull(child, "child")); 094 checkArgument(parent, "remove can only be invoked with a current child of this node"); 095 remove(mutableChildren.indexOf(child)); 096 } 097 098 public void remove(int index) { 099 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}