package org.archstudio.bna.utils;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import java.util.Map;
import org.archstudio.bna.IThing;
import org.archstudio.sysutils.FastMap;
import com.google.common.collect.Lists;
class ThingTree {
private static class Node {
final IThing t;
final List<Node> children = Lists.newArrayList();
Node parent;
public Node(IThing t) {
this.t = t;
}
@Override
public String toString() {
return t.toString();
}
}
private final Node root = new Node(null);
private final FastMap<Object, Node> nodesByID = new FastMap<>(false);
private List<IThing> allThings = null;
public ThingTree() {
}
private Node getNode(IThing t) {
if (t == null) {
return root;
}
return checkNotNull(nodesByID.get(t.getID()), "Thing not added: %s", t);
}
private Node createNode(IThing t) {
Map.Entry<Object, Node> entry = nodesByID.createEntry(t.getID());
if (entry.getValue() != null) {
throw new IllegalArgumentException("Thing already added: " + t);
}
Node node = new Node(t);
entry.setValue(node);
return node;
}
public IThing getThing(Object id) {
Node node = nodesByID.get(id);
return node != null ? node.t : null;
}
public void add(IThing t) {
add(t, null);
}
public void add(IThing t, IThing parentThing) {
Node parentNode = getNode(parentThing);
Node childNode = createNode(t);
parentNode.children.add(childNode);
childNode.parent = parentNode;
if (parentNode == root) {
if (allThings != null) {
allThings.add(t);
}
}
else {
allThings = null;
}
}
public void insert(IThing t, IThing beforeThing) {
Node beforeNode = getNode(beforeThing);
Node parentNode = beforeNode.parent;
Node childNode = createNode(t);
parentNode.children.add(parentNode.children.indexOf(beforeNode), childNode);
childNode.parent = parentNode;
allThings = null;
}
public void remove(IThing t) {
Node childNode = nodesByID.remove(t.getID());
if (childNode != null) {
Node parentNode = childNode.parent;
List<Node> siblings = parentNode.children;
int index = siblings.indexOf(childNode);
siblings.remove(index);
siblings.addAll(index, childNode.children);
for (Node grandchild : childNode.children) {
grandchild.parent = parentNode;
}
if (allThings != null && allThings.size() > 0) {
int lastIndex = allThings.size() - 1;
if (allThings.get(lastIndex).equals(t)) {
allThings.remove(lastIndex);
}
else {
allThings = null;
}
}
}
}
private void move(Node childNode, int toIndex) {
List<Node> siblings = childNode.parent.children;
if (toIndex < 0) {
toIndex = siblings.size() + toIndex + 1;
}
int index = siblings.indexOf(childNode);
if (index != toIndex) {
siblings.remove(index);
if (index < toIndex) {
toIndex--;
}
siblings.add(toIndex, childNode);
allThings = null;
}
}
public void bringToFront(IThing t) {
Node childNode = getNode(t);
move(childNode, -1);
}
public void sendToBack(IThing t) {
Node childNode = getNode(t);
move(childNode, 0);
}
public void moveAfter(IThing t, IThing afterThing) {
Node childNode = getNode(t);
Node afterNode = getNode(afterThing);
if (childNode.parent != afterNode.parent) {
reparent(afterNode.parent.t, t);
}
int afterIndex = afterNode.parent.children.indexOf(afterNode);
move(childNode, afterIndex + 1);
}
public void moveBefore(IThing t, IThing beforeThing) {
Node childNode = getNode(t);
Node beforeNode = getNode(beforeThing);
if (childNode.parent != beforeNode.parent) {
reparent(beforeNode.parent.t, t);
}
int beforeIndex = beforeNode.parent.children.indexOf(beforeNode);
move(childNode, beforeIndex);
}
public void reparent(IThing parentThing, IThing t) {
Node parentNode = getNode(parentThing);
Node childNode = getNode(t);
childNode.parent.children.remove(childNode);
childNode.parent = parentNode;
parentNode.children.add(childNode);
allThings = null;
}
private List<IThing> appendAllDescendants(Node parent, List<IThing> toList) {
for (Node child : parent.children) {
toList.add(child.t);
appendAllDescendants(child, toList);
}
return toList;
}
public List<IThing> getAllDescendantThings(IThing t) {
Node node = getNode(t);
return appendAllDescendants(node, Lists.<IThing> newArrayList(t));
}
public List<IThing> getAllThings() {
if (allThings == null) {
allThings = Lists.newArrayListWithCapacity(size());
appendAllDescendants(root, allThings);
}
return Lists.newArrayList(allThings);
}
public List<IThing> getAncestorThings(IThing t) {
Node node = getNode(t);
List<IThing> ancestors = Lists.newArrayList();
do {
ancestors.add(node.t);
node = node.parent;
} while (node != null && node != root);
return ancestors;
}
public List<IThing> getChildThings(IThing t) {
Node parentNode = getNode(t);
List<IThing> children = Lists.newArrayListWithCapacity(parentNode.children.size());
for (Node child : parentNode.children) {
children.add(child.t);
}
return children;
}
public IThing getParent(IThing t) {
Node childNode = getNode(t);
return childNode.parent.t;
}
public int size() {
return nodesByID.size();
}
}