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}