lose embedded source jars from upstream branch
[debian/openrocket] / android / src / pl / polidea / treeview / InMemoryTreeStateManager.java
1 package pl.polidea.treeview;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import android.database.DataSetObserver;
13
14 /**
15  * In-memory manager of tree state.
16  * 
17  * @param <T>
18  *            type of identifier
19  */
20 public class InMemoryTreeStateManager<T> implements TreeStateManager<T> {
21     private static final long serialVersionUID = 1L;
22     private final Map<T, InMemoryTreeNode<T>> allNodes = new HashMap<T, InMemoryTreeNode<T>>();
23     private final InMemoryTreeNode<T> topSentinel = new InMemoryTreeNode<T>(
24             null, null, -1, true);
25     private transient List<T> visibleListCache = null; // lasy initialised
26     private transient List<T> unmodifiableVisibleList = null;
27     private boolean visibleByDefault = true;
28     private final transient Set<DataSetObserver> observers = new HashSet<DataSetObserver>();
29
30     private synchronized void internalDataSetChanged() {
31         visibleListCache = null;
32         unmodifiableVisibleList = null;
33         for (final DataSetObserver observer : observers) {
34             observer.onChanged();
35         }
36     }
37
38     /**
39      * If true new nodes are visible by default.
40      * 
41      * @param visibleByDefault
42      *            if true, then newly added nodes are expanded by default
43      */
44     public void setVisibleByDefault(final boolean visibleByDefault) {
45         this.visibleByDefault = visibleByDefault;
46     }
47
48     private InMemoryTreeNode<T> getNodeFromTreeOrThrow(final T id) {
49         if (id == null) {
50             throw new NodeNotInTreeException("(null)");
51         }
52         final InMemoryTreeNode<T> node = allNodes.get(id);
53         if (node == null) {
54             throw new NodeNotInTreeException(id.toString());
55         }
56         return node;
57     }
58
59     private InMemoryTreeNode<T> getNodeFromTreeOrThrowAllowRoot(final T id) {
60         if (id == null) {
61             return topSentinel;
62         }
63         return getNodeFromTreeOrThrow(id);
64     }
65
66     private void expectNodeNotInTreeYet(final T id) {
67         final InMemoryTreeNode<T> node = allNodes.get(id);
68         if (node != null) {
69             throw new NodeAlreadyInTreeException(id.toString(), node.toString());
70         }
71     }
72
73     @Override
74     public synchronized TreeNodeInfo<T> getNodeInfo(final T id) {
75         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrow(id);
76         final List<InMemoryTreeNode<T>> children = node.getChildren();
77         boolean expanded = false;
78         if (!children.isEmpty() && children.get(0).isVisible()) {
79             expanded = true;
80         }
81         return new TreeNodeInfo<T>(id, node.getLevel(), !children.isEmpty(),
82                 node.isVisible(), expanded);
83     }
84
85     @Override
86     public synchronized List<T> getChildren(final T id) {
87         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
88         return node.getChildIdList();
89     }
90
91     @Override
92     public synchronized T getParent(final T id) {
93         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
94         return node.getParent();
95     }
96
97     private boolean getChildrenVisibility(final InMemoryTreeNode<T> node) {
98         boolean visibility;
99         final List<InMemoryTreeNode<T>> children = node.getChildren();
100         if (children.isEmpty()) {
101             visibility = visibleByDefault;
102         } else {
103             visibility = children.get(0).isVisible();
104         }
105         return visibility;
106     }
107
108     @Override
109     public synchronized void addBeforeChild(final T parent, final T newChild,
110             final T beforeChild) {
111         expectNodeNotInTreeYet(newChild);
112         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);
113         final boolean visibility = getChildrenVisibility(node);
114         // top nodes are always expanded.
115         if (beforeChild == null) {
116             final InMemoryTreeNode<T> added = node.add(0, newChild, visibility);
117             allNodes.put(newChild, added);
118         } else {
119             final int index = node.indexOf(beforeChild);
120             final InMemoryTreeNode<T> added = node.add(index == -1 ? 0 : index,
121                     newChild, visibility);
122             allNodes.put(newChild, added);
123         }
124         if (visibility) {
125             internalDataSetChanged();
126         }
127     }
128
129     @Override
130     public synchronized void addAfterChild(final T parent, final T newChild,
131             final T afterChild) {
132         expectNodeNotInTreeYet(newChild);
133         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);
134         final boolean visibility = getChildrenVisibility(node);
135         if (afterChild == null) {
136             final InMemoryTreeNode<T> added = node.add(
137                     node.getChildrenListSize(), newChild, visibility);
138             allNodes.put(newChild, added);
139         } else {
140             final int index = node.indexOf(afterChild);
141             final InMemoryTreeNode<T> added = node.add(
142                     index == -1 ? node.getChildrenListSize() : index, newChild,
143                     visibility);
144             allNodes.put(newChild, added);
145         }
146         if (visibility) {
147             internalDataSetChanged();
148         }
149     }
150
151     @Override
152     public synchronized void removeNodeRecursively(final T id) {
153         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
154         final boolean visibleNodeChanged = removeNodeRecursively(node);
155         final T parent = node.getParent();
156         final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
157         parentNode.removeChild(id);
158         if (visibleNodeChanged) {
159             internalDataSetChanged();
160         }
161     }
162
163     private boolean removeNodeRecursively(final InMemoryTreeNode<T> node) {
164         boolean visibleNodeChanged = false;
165         for (final InMemoryTreeNode<T> child : node.getChildren()) {
166             if (removeNodeRecursively(child)) {
167                 visibleNodeChanged = true;
168             }
169         }
170         node.clearChildren();
171         if (node.getId() != null) {
172             allNodes.remove(node.getId());
173             if (node.isVisible()) {
174                 visibleNodeChanged = true;
175             }
176         }
177         return visibleNodeChanged;
178     }
179
180     private void setChildrenVisibility(final InMemoryTreeNode<T> node,
181             final boolean visible, final boolean recursive) {
182         for (final InMemoryTreeNode<T> child : node.getChildren()) {
183             child.setVisible(visible);
184             if (recursive) {
185                 setChildrenVisibility(child, visible, true);
186             }
187         }
188     }
189
190     @Override
191     public synchronized void expandDirectChildren(final T id) {
192         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
193         setChildrenVisibility(node, true, false);
194         internalDataSetChanged();
195     }
196
197     @Override
198     public synchronized void expandEverythingBelow(final T id) {
199         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
200         setChildrenVisibility(node, true, true);
201         internalDataSetChanged();
202     }
203
204     @Override
205     public synchronized void collapseChildren(final T id) {
206         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
207         if (node == topSentinel) {
208             for (final InMemoryTreeNode<T> n : topSentinel.getChildren()) {
209                 setChildrenVisibility(n, false, true);
210             }
211         } else {
212             setChildrenVisibility(node, false, true);
213         }
214         internalDataSetChanged();
215     }
216
217     @Override
218     public synchronized T getNextSibling(final T id) {
219         final T parent = getParent(id);
220         final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
221         boolean returnNext = false;
222         for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {
223             if (returnNext) {
224                 return child.getId();
225             }
226             if (child.getId().equals(id)) {
227                 returnNext = true;
228             }
229         }
230         return null;
231     }
232
233     @Override
234     public synchronized T getPreviousSibling(final T id) {
235         final T parent = getParent(id);
236         final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
237         final T previousSibling = null;
238         for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {
239             if (child.getId().equals(id)) {
240                 return previousSibling;
241             }
242         }
243         return null;
244     }
245
246     @Override
247     public synchronized boolean isInTree(final T id) {
248         return allNodes.containsKey(id);
249     }
250
251     @Override
252     public synchronized int getVisibleCount() {
253         return getVisibleList().size();
254     }
255
256     @Override
257     public synchronized List<T> getVisibleList() {
258         T currentId = null;
259         if (visibleListCache == null) {
260             visibleListCache = new ArrayList<T>(allNodes.size());
261             do {
262                 currentId = getNextVisible(currentId);
263                 if (currentId == null) {
264                     break;
265                 } else {
266                     visibleListCache.add(currentId);
267                 }
268             } while (true);
269         }
270         if (unmodifiableVisibleList == null) {
271             unmodifiableVisibleList = Collections
272                     .unmodifiableList(visibleListCache);
273         }
274         return unmodifiableVisibleList;
275     }
276
277     public synchronized T getNextVisible(final T id) {
278         final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
279         if (!node.isVisible()) {
280             return null;
281         }
282         final List<InMemoryTreeNode<T>> children = node.getChildren();
283         if (!children.isEmpty()) {
284             final InMemoryTreeNode<T> firstChild = children.get(0);
285             if (firstChild.isVisible()) {
286                 return firstChild.getId();
287             }
288         }
289         final T sibl = getNextSibling(id);
290         if (sibl != null) {
291             return sibl;
292         }
293         T parent = node.getParent();
294         do {
295             if (parent == null) {
296                 return null;
297             }
298             final T parentSibling = getNextSibling(parent);
299             if (parentSibling != null) {
300                 return parentSibling;
301             }
302             parent = getNodeFromTreeOrThrow(parent).getParent();
303         } while (true);
304     }
305
306     @Override
307     public synchronized void registerDataSetObserver(
308             final DataSetObserver observer) {
309         observers.add(observer);
310     }
311
312     @Override
313     public synchronized void unregisterDataSetObserver(
314             final DataSetObserver observer) {
315         observers.remove(observer);
316     }
317
318     @Override
319     public int getLevel(final T id) {
320         return getNodeFromTreeOrThrow(id).getLevel();
321     }
322
323     @Override
324     public Integer[] getHierarchyDescription(final T id) {
325         final int level = getLevel(id);
326         final Integer[] hierarchy = new Integer[level + 1];
327         int currentLevel = level;
328         T currentId = id;
329         T parent = getParent(currentId);
330         while (currentLevel >= 0) {
331             hierarchy[currentLevel--] = getChildren(parent).indexOf(currentId);
332             currentId = parent;
333             parent = getParent(parent);
334         }
335         return hierarchy;
336     }
337
338     private void appendToSb(final StringBuilder sb, final T id) {
339         if (id != null) {
340             final TreeNodeInfo<T> node = getNodeInfo(id);
341             final int indent = node.getLevel() * 4;
342             final char[] indentString = new char[indent];
343             Arrays.fill(indentString, ' ');
344             sb.append(indentString);
345             sb.append(node.toString());
346             sb.append(Arrays.asList(getHierarchyDescription(id)).toString());
347             sb.append("\n");
348         }
349         final List<T> children = getChildren(id);
350         for (final T child : children) {
351             appendToSb(sb, child);
352         }
353     }
354
355     @Override
356     public synchronized String toString() {
357         final StringBuilder sb = new StringBuilder();
358         appendToSb(sb, null);
359         return sb.toString();
360     }
361
362     @Override
363     public synchronized void clear() {
364         allNodes.clear();
365         topSentinel.clearChildren();
366         internalDataSetChanged();
367     }
368
369     @Override
370     public void refresh() {
371         internalDataSetChanged();
372     }
373
374 }