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
|
|
38 private HashSet leafNodes = new HashSet();
|
|
39
|
|
40 private HashMap mapElementsOntoNodeInfo = new HashMap();
|
|
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) {
|
|
123 super(tree.getRealm());
|
|
124 this.tree = tree;
|
|
125 if (initialInput !is null) {
|
|
126 setInput(initialInput);
|
|
127 }
|
|
128 }
|
|
129
|
|
130 private void processDiff(TreePath treePath, SetDiff diff) {
|
|
131 Set removals = new HashSet();
|
|
132 HashSet additions = new HashSet();
|
|
133
|
|
134 for (Iterator iter = diff.getRemovals().iterator(); iter.hasNext();) {
|
|
135 Object next = iter.next();
|
|
136
|
|
137 elementRemoved(treePath.createChildPath(next), removals);
|
|
138 }
|
|
139
|
|
140 for (Iterator iter = diff.getAdditions().iterator(); iter.hasNext();) {
|
|
141 Object next = iter.next();
|
|
142
|
|
143 elementDiscovered(treePath.createChildPath(next), additions);
|
|
144 }
|
|
145
|
|
146 HashSet newRemovals = new HashSet();
|
|
147 newRemovals.addAll(removals);
|
|
148 newRemovals.removeAll(additions);
|
|
149
|
|
150 HashSet newAdditions = new HashSet();
|
|
151 newAdditions.addAll(additions);
|
|
152 newAdditions.removeAll(removals);
|
|
153
|
|
154 leafNodes.addAll(newAdditions);
|
|
155 leafNodes.removeAll(newRemovals);
|
|
156
|
|
157 if (!newAdditions.isEmpty() || !newRemovals.isEmpty()) {
|
|
158 setStale(staleCount > 0);
|
|
159 fireSetChange(Diffs.createSetDiff(newAdditions, newRemovals));
|
|
160 }
|
|
161 }
|
|
162
|
|
163 /**
|
|
164 * Sets the root of the tree to the given element.
|
|
165 *
|
|
166 * @param input
|
|
167 * new root of the tree
|
|
168 */
|
|
169 public void setInput(Object input) {
|
|
170 Set removals = Collections.EMPTY_SET;
|
|
171 Set additions = Collections.EMPTY_SET;
|
|
172 if (this.input !is null) {
|
|
173 removals = Collections.singleton(this.input);
|
|
174 } else if (input !is null) {
|
|
175 additions = Collections.singleton(input);
|
|
176 }
|
|
177 this.input = input;
|
|
178 processDiff(TreePath.EMPTY, Diffs.createSetDiff(additions, removals));
|
|
179 }
|
|
180
|
|
181 /**
|
|
182 * Called when an element is removed from the tree. The given HashSet will
|
|
183 * be filled in with all removed leaf nodes.
|
|
184 *
|
|
185 * @param treePath
|
|
186 * @param removals
|
|
187 */
|
|
188 private void elementRemoved(TreePath treePath, Set removals) {
|
|
189 NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath);
|
|
190
|
|
191 if (newNode !is null) {
|
|
192 newNode = new NodeInfo(treePath);
|
|
193 newNode.count--;
|
|
194 if (newNode.count is 0) {
|
|
195 mapElementsOntoNodeInfo.remove(treePath);
|
|
196 if (newNode.children !is null) {
|
|
197 for (Iterator iter = newNode.children.iterator(); iter
|
|
198 .hasNext();) {
|
|
199 Object next = iter.next();
|
|
200
|
|
201 elementRemoved(treePath.createChildPath(next), removals);
|
|
202 }
|
|
203 newNode.children.dispose();
|
|
204 } else {
|
|
205 removals.add(treePath);
|
|
206 }
|
|
207 }
|
|
208 }
|
|
209 }
|
|
210
|
|
211 /**
|
|
212 * Called when a new element is discovered in the tree. The given HashSet
|
|
213 * will be filled in with all newly discovered leaf nodes.
|
|
214 *
|
|
215 * @param treePath
|
|
216 * @param additions
|
|
217 */
|
|
218 private void elementDiscovered(TreePath treePath, HashSet additions) {
|
|
219 NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath);
|
|
220
|
|
221 if (newNode is null) {
|
|
222 newNode = new NodeInfo(treePath);
|
|
223 mapElementsOntoNodeInfo.put(treePath, newNode);
|
|
224 if (newNode.children !is null) {
|
|
225 for (Iterator iter = newNode.children.iterator(); iter
|
|
226 .hasNext();) {
|
|
227 Object next = iter.next();
|
|
228
|
|
229 elementDiscovered(treePath.createChildPath(next), additions);
|
|
230 }
|
|
231 } else {
|
|
232 additions.add(treePath);
|
|
233 }
|
|
234 } else {
|
|
235 // If this node was already known, increment the reference count.
|
|
236 newNode.count++;
|
|
237 }
|
|
238 }
|
|
239
|
|
240 protected Set getWrappedSet() {
|
|
241 return leafNodes;
|
|
242 }
|
|
243
|
|
244 public Object getElementType() {
|
|
245 return Object.classinfo;
|
|
246 }
|
|
247
|
|
248 public void dispose() {
|
|
249 for (Iterator iter = mapElementsOntoNodeInfo.values().iterator(); iter
|
|
250 .hasNext();) {
|
|
251 NodeInfo next = cast(NodeInfo) iter.next();
|
|
252
|
|
253 if (next.children !is null) {
|
|
254 next.dispose();
|
|
255 }
|
|
256 }
|
|
257
|
|
258 mapElementsOntoNodeInfo.clear();
|
|
259 leafNodes.clear();
|
|
260 super.dispose();
|
|
261 }
|
|
262 }
|