-package pl.polidea.treeview;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import android.database.DataSetObserver;
-
-/**
- * In-memory manager of tree state.
- *
- * @param <T>
- * type of identifier
- */
-public class InMemoryTreeStateManager<T> implements TreeStateManager<T> {
- private static final long serialVersionUID = 1L;
- private final Map<T, InMemoryTreeNode<T>> allNodes = new HashMap<T, InMemoryTreeNode<T>>();
- private final InMemoryTreeNode<T> topSentinel = new InMemoryTreeNode<T>(
- null, null, -1, true);
- private transient List<T> visibleListCache = null; // lasy initialised
- private transient List<T> unmodifiableVisibleList = null;
- private boolean visibleByDefault = true;
- private final transient Set<DataSetObserver> observers = new HashSet<DataSetObserver>();
-
- private synchronized void internalDataSetChanged() {
- visibleListCache = null;
- unmodifiableVisibleList = null;
- for (final DataSetObserver observer : observers) {
- observer.onChanged();
- }
- }
-
- /**
- * If true new nodes are visible by default.
- *
- * @param visibleByDefault
- * if true, then newly added nodes are expanded by default
- */
- public void setVisibleByDefault(final boolean visibleByDefault) {
- this.visibleByDefault = visibleByDefault;
- }
-
- private InMemoryTreeNode<T> getNodeFromTreeOrThrow(final T id) {
- if (id == null) {
- throw new NodeNotInTreeException("(null)");
- }
- final InMemoryTreeNode<T> node = allNodes.get(id);
- if (node == null) {
- throw new NodeNotInTreeException(id.toString());
- }
- return node;
- }
-
- private InMemoryTreeNode<T> getNodeFromTreeOrThrowAllowRoot(final T id) {
- if (id == null) {
- return topSentinel;
- }
- return getNodeFromTreeOrThrow(id);
- }
-
- private void expectNodeNotInTreeYet(final T id) {
- final InMemoryTreeNode<T> node = allNodes.get(id);
- if (node != null) {
- throw new NodeAlreadyInTreeException(id.toString(), node.toString());
- }
- }
-
- @Override
- public synchronized TreeNodeInfo<T> getNodeInfo(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrow(id);
- final List<InMemoryTreeNode<T>> children = node.getChildren();
- boolean expanded = false;
- if (!children.isEmpty() && children.get(0).isVisible()) {
- expanded = true;
- }
- return new TreeNodeInfo<T>(id, node.getLevel(), !children.isEmpty(),
- node.isVisible(), expanded);
- }
-
- @Override
- public synchronized List<T> getChildren(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
- return node.getChildIdList();
- }
-
- @Override
- public synchronized T getParent(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
- return node.getParent();
- }
-
- private boolean getChildrenVisibility(final InMemoryTreeNode<T> node) {
- boolean visibility;
- final List<InMemoryTreeNode<T>> children = node.getChildren();
- if (children.isEmpty()) {
- visibility = visibleByDefault;
- } else {
- visibility = children.get(0).isVisible();
- }
- return visibility;
- }
-
- @Override
- public synchronized void addBeforeChild(final T parent, final T newChild,
- final T beforeChild) {
- expectNodeNotInTreeYet(newChild);
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);
- final boolean visibility = getChildrenVisibility(node);
- // top nodes are always expanded.
- if (beforeChild == null) {
- final InMemoryTreeNode<T> added = node.add(0, newChild, visibility);
- allNodes.put(newChild, added);
- } else {
- final int index = node.indexOf(beforeChild);
- final InMemoryTreeNode<T> added = node.add(index == -1 ? 0 : index,
- newChild, visibility);
- allNodes.put(newChild, added);
- }
- if (visibility) {
- internalDataSetChanged();
- }
- }
-
- @Override
- public synchronized void addAfterChild(final T parent, final T newChild,
- final T afterChild) {
- expectNodeNotInTreeYet(newChild);
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);
- final boolean visibility = getChildrenVisibility(node);
- if (afterChild == null) {
- final InMemoryTreeNode<T> added = node.add(
- node.getChildrenListSize(), newChild, visibility);
- allNodes.put(newChild, added);
- } else {
- final int index = node.indexOf(afterChild);
- final InMemoryTreeNode<T> added = node.add(
- index == -1 ? node.getChildrenListSize() : index, newChild,
- visibility);
- allNodes.put(newChild, added);
- }
- if (visibility) {
- internalDataSetChanged();
- }
- }
-
- @Override
- public synchronized void removeNodeRecursively(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
- final boolean visibleNodeChanged = removeNodeRecursively(node);
- final T parent = node.getParent();
- final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
- parentNode.removeChild(id);
- if (visibleNodeChanged) {
- internalDataSetChanged();
- }
- }
-
- private boolean removeNodeRecursively(final InMemoryTreeNode<T> node) {
- boolean visibleNodeChanged = false;
- for (final InMemoryTreeNode<T> child : node.getChildren()) {
- if (removeNodeRecursively(child)) {
- visibleNodeChanged = true;
- }
- }
- node.clearChildren();
- if (node.getId() != null) {
- allNodes.remove(node.getId());
- if (node.isVisible()) {
- visibleNodeChanged = true;
- }
- }
- return visibleNodeChanged;
- }
-
- private void setChildrenVisibility(final InMemoryTreeNode<T> node,
- final boolean visible, final boolean recursive) {
- for (final InMemoryTreeNode<T> child : node.getChildren()) {
- child.setVisible(visible);
- if (recursive) {
- setChildrenVisibility(child, visible, true);
- }
- }
- }
-
- @Override
- public synchronized void expandDirectChildren(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
- setChildrenVisibility(node, true, false);
- internalDataSetChanged();
- }
-
- @Override
- public synchronized void expandEverythingBelow(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
- setChildrenVisibility(node, true, true);
- internalDataSetChanged();
- }
-
- @Override
- public synchronized void collapseChildren(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
- if (node == topSentinel) {
- for (final InMemoryTreeNode<T> n : topSentinel.getChildren()) {
- setChildrenVisibility(n, false, true);
- }
- } else {
- setChildrenVisibility(node, false, true);
- }
- internalDataSetChanged();
- }
-
- @Override
- public synchronized T getNextSibling(final T id) {
- final T parent = getParent(id);
- final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
- boolean returnNext = false;
- for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {
- if (returnNext) {
- return child.getId();
- }
- if (child.getId().equals(id)) {
- returnNext = true;
- }
- }
- return null;
- }
-
- @Override
- public synchronized T getPreviousSibling(final T id) {
- final T parent = getParent(id);
- final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
- final T previousSibling = null;
- for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {
- if (child.getId().equals(id)) {
- return previousSibling;
- }
- }
- return null;
- }
-
- @Override
- public synchronized boolean isInTree(final T id) {
- return allNodes.containsKey(id);
- }
-
- @Override
- public synchronized int getVisibleCount() {
- return getVisibleList().size();
- }
-
- @Override
- public synchronized List<T> getVisibleList() {
- T currentId = null;
- if (visibleListCache == null) {
- visibleListCache = new ArrayList<T>(allNodes.size());
- do {
- currentId = getNextVisible(currentId);
- if (currentId == null) {
- break;
- } else {
- visibleListCache.add(currentId);
- }
- } while (true);
- }
- if (unmodifiableVisibleList == null) {
- unmodifiableVisibleList = Collections
- .unmodifiableList(visibleListCache);
- }
- return unmodifiableVisibleList;
- }
-
- public synchronized T getNextVisible(final T id) {
- final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
- if (!node.isVisible()) {
- return null;
- }
- final List<InMemoryTreeNode<T>> children = node.getChildren();
- if (!children.isEmpty()) {
- final InMemoryTreeNode<T> firstChild = children.get(0);
- if (firstChild.isVisible()) {
- return firstChild.getId();
- }
- }
- final T sibl = getNextSibling(id);
- if (sibl != null) {
- return sibl;
- }
- T parent = node.getParent();
- do {
- if (parent == null) {
- return null;
- }
- final T parentSibling = getNextSibling(parent);
- if (parentSibling != null) {
- return parentSibling;
- }
- parent = getNodeFromTreeOrThrow(parent).getParent();
- } while (true);
- }
-
- @Override
- public synchronized void registerDataSetObserver(
- final DataSetObserver observer) {
- observers.add(observer);
- }
-
- @Override
- public synchronized void unregisterDataSetObserver(
- final DataSetObserver observer) {
- observers.remove(observer);
- }
-
- @Override
- public int getLevel(final T id) {
- return getNodeFromTreeOrThrow(id).getLevel();
- }
-
- @Override
- public Integer[] getHierarchyDescription(final T id) {
- final int level = getLevel(id);
- final Integer[] hierarchy = new Integer[level + 1];
- int currentLevel = level;
- T currentId = id;
- T parent = getParent(currentId);
- while (currentLevel >= 0) {
- hierarchy[currentLevel--] = getChildren(parent).indexOf(currentId);
- currentId = parent;
- parent = getParent(parent);
- }
- return hierarchy;
- }
-
- private void appendToSb(final StringBuilder sb, final T id) {
- if (id != null) {
- final TreeNodeInfo<T> node = getNodeInfo(id);
- final int indent = node.getLevel() * 4;
- final char[] indentString = new char[indent];
- Arrays.fill(indentString, ' ');
- sb.append(indentString);
- sb.append(node.toString());
- sb.append(Arrays.asList(getHierarchyDescription(id)).toString());
- sb.append("\n");
- }
- final List<T> children = getChildren(id);
- for (final T child : children) {
- appendToSb(sb, child);
- }
- }
-
- @Override
- public synchronized String toString() {
- final StringBuilder sb = new StringBuilder();
- appendToSb(sb, null);
- return sb.toString();
- }
-
- @Override
- public synchronized void clear() {
- allNodes.clear();
- topSentinel.clearChildren();
- internalDataSetChanged();
- }
-
- @Override
- public void refresh() {
- internalDataSetChanged();
- }
-
-}