78
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2006, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 *******************************************************************************/
|
|
11 module org.eclipse.jface.internal.databinding.viewers.LeafNodesSet;
|
|
12
|
|
13 import java.lang.all;
|
|
14
|
|
15 import java.util.Collections;
|
|
16 import java.util.HashMap;
|
|
17 import java.util.HashSet;
|
|
18 import java.util.Iterator;
|
|
19 import java.util.Set;
|
|
20
|
|
21 import org.eclipse.core.databinding.observable.Diffs;
|
|
22 import org.eclipse.core.databinding.observable.IStaleListener;
|
|
23 import org.eclipse.core.databinding.observable.StaleEvent;
|
|
24 import org.eclipse.core.databinding.observable.set.AbstractObservableSet;
|
|
25 import org.eclipse.core.databinding.observable.set.IObservableSet;
|
|
26 import org.eclipse.core.databinding.observable.set.ISetChangeListener;
|
|
27 import org.eclipse.core.databinding.observable.set.SetChangeEvent;
|
|
28 import org.eclipse.core.databinding.observable.set.SetDiff;
|
|
29 import org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider;
|
|
30 import org.eclipse.core.internal.databinding.observable.tree.TreePath;
|
|
31
|
|
32 /**
|
|
33 * This set consists of all leaf nodes from the given tree (that is, all nodes
|
|
34 * for which ITreeProvider.createChildSet returns null).
|
|
35 */
|
|
36 public class LeafNodesSet : AbstractObservableSet {
|
|
37
|
85
|
38 private HashSet leafNodes;
|
78
|
39
|
85
|
40 private HashMap mapElementsOntoNodeInfo;
|
78
|
41
|
|
42 private IUnorderedTreeProvider tree;
|
|
43
|
|
44 private Object input;
|
|
45
|
|
46 private int staleCount = 0;
|
|
47
|
|
48 private class NodeInfo : IStaleListener, ISetChangeListener {
|
|
49 // Number of times the element occurs in the tree
|
|
50 private int count;
|
|
51
|
|
52 // Element
|
|
53 private TreePath treePath;
|
|
54
|
|
55 // Children set (or null if this is a leaf node)
|
|
56 IObservableSet children;
|
|
57
|
|
58 private bool wasStale = false;
|
|
59
|
|
60 /**
|
|
61 * @param treePath
|
|
62 */
|
|
63 public this(TreePath treePath) {
|
|
64 this.treePath = treePath;
|
|
65 children = tree.createChildSet(this.treePath);
|
|
66 if (children !is null) {
|
|
67 children.addStaleListener(this);
|
|
68 children.addSetChangeListener(this);
|
|
69 }
|
|
70 count = 1;
|
|
71 }
|
|
72
|
|
73 public void handleSetChange(SetChangeEvent event) {
|
|
74 processDiff(treePath, event.diff);
|
|
75 }
|
|
76
|
|
77 public void handleStale(StaleEvent event) {
|
|
78 if (wasStale !is children.isStale()) {
|
|
79 if (wasStale) {
|
|
80 staleCount--;
|
|
81 } else {
|
|
82 staleCount++;
|
|
83 }
|
|
84 wasStale = !wasStale;
|
|
85 }
|
|
86 setStale(staleCount > 0);
|
|
87 }
|
|
88
|
|
89 /**
|
|
90 *
|
|
91 */
|
|
92 public void dispose() {
|
|
93 if (children !is null) {
|
|
94 children.dispose();
|
|
95 children = null;
|
|
96 if (wasStale) {
|
|
97 staleCount--;
|
|
98 }
|
|
99 }
|
|
100 }
|
|
101 }
|
|
102
|
|
103 /**
|
|
104 * Creates a set that will contain the leaf nodes from the given tree
|
|
105 *
|
|
106 * @param tree
|
|
107 * tree whose leaf nodes will be computed
|
|
108 */
|
|
109 public this(IUnorderedTreeProvider tree) {
|
|
110 this(null, tree);
|
|
111 }
|
|
112
|
|
113 /**
|
|
114 * Creates a set that will contain the leaf nodes from the given tree, and
|
|
115 * sets the root of the tree to the given element.
|
|
116 *
|
|
117 * @param initialInput
|
|
118 * root of the tree
|
|
119 * @param tree
|
|
120 * tree whose leaf nodes will be computed
|
|
121 */
|
|
122 public this(Object initialInput, IUnorderedTreeProvider tree) {
|
85
|
123 leafNodes = new HashSet();
|
|
124 mapElementsOntoNodeInfo = new HashMap();
|
78
|
125 super(tree.getRealm());
|
|
126 this.tree = tree;
|
|
127 if (initialInput !is null) {
|
|
128 setInput(initialInput);
|
|
129 }
|
|
130 }
|
|
131
|
|
132 private void processDiff(TreePath treePath, SetDiff diff) {
|
|
133 Set removals = new HashSet();
|
|
134 HashSet additions = new HashSet();
|
|
135
|
|
136 for (Iterator iter = diff.getRemovals().iterator(); iter.hasNext();) {
|
|
137 Object next = iter.next();
|
|
138
|
|
139 elementRemoved(treePath.createChildPath(next), removals);
|
|
140 }
|
|
141
|
|
142 for (Iterator iter = diff.getAdditions().iterator(); iter.hasNext();) {
|
|
143 Object next = iter.next();
|
|
144
|
|
145 elementDiscovered(treePath.createChildPath(next), additions);
|
|
146 }
|
|
147
|
|
148 HashSet newRemovals = new HashSet();
|
|
149 newRemovals.addAll(removals);
|
|
150 newRemovals.removeAll(additions);
|
|
151
|
|
152 HashSet newAdditions = new HashSet();
|
|
153 newAdditions.addAll(additions);
|
|
154 newAdditions.removeAll(removals);
|
|
155
|
|
156 leafNodes.addAll(newAdditions);
|
|
157 leafNodes.removeAll(newRemovals);
|
|
158
|
|
159 if (!newAdditions.isEmpty() || !newRemovals.isEmpty()) {
|
|
160 setStale(staleCount > 0);
|
|
161 fireSetChange(Diffs.createSetDiff(newAdditions, newRemovals));
|
|
162 }
|
|
163 }
|
|
164
|
|
165 /**
|
|
166 * Sets the root of the tree to the given element.
|
|
167 *
|
|
168 * @param input
|
|
169 * new root of the tree
|
|
170 */
|
|
171 public void setInput(Object input) {
|
|
172 Set removals = Collections.EMPTY_SET;
|
|
173 Set additions = Collections.EMPTY_SET;
|
|
174 if (this.input !is null) {
|
|
175 removals = Collections.singleton(this.input);
|
|
176 } else if (input !is null) {
|
|
177 additions = Collections.singleton(input);
|
|
178 }
|
|
179 this.input = input;
|
|
180 processDiff(TreePath.EMPTY, Diffs.createSetDiff(additions, removals));
|
|
181 }
|
|
182
|
|
183 /**
|
|
184 * Called when an element is removed from the tree. The given HashSet will
|
|
185 * be filled in with all removed leaf nodes.
|
|
186 *
|
|
187 * @param treePath
|
|
188 * @param removals
|
|
189 */
|
|
190 private void elementRemoved(TreePath treePath, Set removals) {
|
|
191 NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath);
|
|
192
|
|
193 if (newNode !is null) {
|
|
194 newNode = new NodeInfo(treePath);
|
|
195 newNode.count--;
|
|
196 if (newNode.count is 0) {
|
|
197 mapElementsOntoNodeInfo.remove(treePath);
|
|
198 if (newNode.children !is null) {
|
|
199 for (Iterator iter = newNode.children.iterator(); iter
|
|
200 .hasNext();) {
|
|
201 Object next = iter.next();
|
|
202
|
|
203 elementRemoved(treePath.createChildPath(next), removals);
|
|
204 }
|
|
205 newNode.children.dispose();
|
|
206 } else {
|
|
207 removals.add(treePath);
|
|
208 }
|
|
209 }
|
|
210 }
|
|
211 }
|
|
212
|
|
213 /**
|
|
214 * Called when a new element is discovered in the tree. The given HashSet
|
|
215 * will be filled in with all newly discovered leaf nodes.
|
|
216 *
|
|
217 * @param treePath
|
|
218 * @param additions
|
|
219 */
|
|
220 private void elementDiscovered(TreePath treePath, HashSet additions) {
|
|
221 NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath);
|
|
222
|
|
223 if (newNode is null) {
|
|
224 newNode = new NodeInfo(treePath);
|
|
225 mapElementsOntoNodeInfo.put(treePath, newNode);
|
|
226 if (newNode.children !is null) {
|
|
227 for (Iterator iter = newNode.children.iterator(); iter
|
|
228 .hasNext();) {
|
|
229 Object next = iter.next();
|
|
230
|
|
231 elementDiscovered(treePath.createChildPath(next), additions);
|
|
232 }
|
|
233 } else {
|
|
234 additions.add(treePath);
|
|
235 }
|
|
236 } else {
|
|
237 // If this node was already known, increment the reference count.
|
|
238 newNode.count++;
|
|
239 }
|
|
240 }
|
|
241
|
|
242 protected Set getWrappedSet() {
|
|
243 return leafNodes;
|
|
244 }
|
|
245
|
|
246 public Object getElementType() {
|
|
247 return Object.classinfo;
|
|
248 }
|
|
249
|
|
250 public void dispose() {
|
|
251 for (Iterator iter = mapElementsOntoNodeInfo.values().iterator(); iter
|
|
252 .hasNext();) {
|
|
253 NodeInfo next = cast(NodeInfo) iter.next();
|
|
254
|
|
255 if (next.children !is null) {
|
|
256 next.dispose();
|
|
257 }
|
|
258 }
|
|
259
|
|
260 mapElementsOntoNodeInfo.clear();
|
|
261 leafNodes.clear();
|
|
262 super.dispose();
|
|
263 }
|
|
264 }
|