Mercurial > projects > dwt2
comparison org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/ConcurrentTableUpdator.d @ 12:bc29606a740c
Added dwt-addons in original directory structure of eclipse.org
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 14 Mar 2009 18:23:29 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
11:43904fec5dca | 12:bc29606a740c |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2004, 2006 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 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module org.eclipse.jface.viewers.deferred.ConcurrentTableUpdator; | |
14 | |
15 import org.eclipse.jface.viewers.deferred.IntHashMap; | |
16 import org.eclipse.jface.viewers.deferred.AbstractVirtualTable; | |
17 | |
18 import java.lang.all; | |
19 import java.util.Map; | |
20 import java.util.Set; | |
21 | |
22 | |
23 | |
24 /** | |
25 * Allows a table to be accessed from a background thread. Provides a table-like public | |
26 * interface that can accessed from a background thread. As updates arrive from the | |
27 * background thread, it batches and schedules updates to the real table in the UI thread. | |
28 * This class can be used with any widget that can be wrapped in the | |
29 * <code>AbstractVirtualTable</code> interface. | |
30 * | |
31 * @since 3.1 | |
32 */ | |
33 /* package */ final class ConcurrentTableUpdator { | |
34 /** | |
35 * Wrapper for the real table. May only be accessed in the UI thread. | |
36 */ | |
37 private AbstractVirtualTable table; | |
38 | |
39 /** | |
40 * The array of objects that have been sent to the UI. Elements are null | |
41 * if they either haven't been sent yet or have been scheduled for clear. | |
42 * Maps indices onto elements. | |
43 */ | |
44 private Object[] sentObjects; | |
45 | |
46 /** | |
47 * Map of elements to object indices (inverse of the knownObjects array) | |
48 */ | |
49 private IntHashMap knownIndices; | |
50 | |
51 /** | |
52 * Contains all known objects that have been sent here from the background | |
53 * thread. | |
54 */ | |
55 private Object[] knownObjects; | |
56 | |
57 // Minimum length for the pendingFlushes stack | |
58 private static const int MIN_FLUSHLENGTH = 64; | |
59 | |
60 /** | |
61 * Array of element indices. Contains elements scheduled to be | |
62 * cleared. Only the beginning of the array is used. The number | |
63 * of used elements is stored in lastClear | |
64 */ | |
65 private int[] pendingClears; | |
66 | |
67 /** | |
68 * Number of pending clears in the pendingClears array (this is normally | |
69 * used instead of pendingClears.length since the | |
70 * pendingClears array is usually larger than the actual number of pending | |
71 * clears) | |
72 */ | |
73 private int lastClear = 0; | |
74 | |
75 /** | |
76 * Last known visible range | |
77 */ | |
78 private /+volatile+/ Range lastRange; | |
79 | |
80 /** | |
81 * True iff a UI update has been scheduled | |
82 */ | |
83 private /+volatile+/ bool updateScheduled; | |
84 | |
85 /** | |
86 * True iff this object has been disposed | |
87 */ | |
88 private /+volatile+/ bool disposed = false; | |
89 | |
90 /** | |
91 * Object that holds a start index and length. Allows | |
92 * the visible range to be returned as an atomic operation. | |
93 */ | |
94 public final static class Range { | |
95 int start = 0; | |
96 int length = 0; | |
97 | |
98 /** | |
99 * @param s | |
100 * @param l | |
101 */ | |
102 public this(int s, int l) { | |
103 start = s; | |
104 length = l; | |
105 } | |
106 } | |
107 | |
108 /** | |
109 * Runnable that can be posted with an asyncExec to schedule | |
110 * an update to the real table. | |
111 */ | |
112 Runnable uiRunnable; | |
113 private void init_uiRunnable(){ | |
114 uiRunnable = new class Runnable { | |
115 public void run() { | |
116 updateScheduled = false; | |
117 if(!table.getControl().isDisposed()) { | |
118 updateTable(); | |
119 } | |
120 } | |
121 }; | |
122 } | |
123 | |
124 /** | |
125 * Creates a new table updator | |
126 * | |
127 * @param table real table to update | |
128 */ | |
129 public this(AbstractVirtualTable table) { | |
130 init_uiRunnable(); | |
131 knownIndices = new IntHashMap(); | |
132 pendingClears = new int[MIN_FLUSHLENGTH]; | |
133 lastRange = new Range(0,0); | |
134 this.table = table; | |
135 } | |
136 | |
137 /** | |
138 * Cleans up the updator object (but not the table itself). | |
139 */ | |
140 public void dispose() { | |
141 disposed = true; | |
142 } | |
143 | |
144 /** | |
145 * True iff this object has been disposed. | |
146 * | |
147 * @return true iff dispose() has been called | |
148 */ | |
149 public bool isDisposed() { | |
150 return disposed; | |
151 } | |
152 | |
153 /** | |
154 * Returns the currently visible range | |
155 * | |
156 * @return the currently visible range | |
157 */ | |
158 public Range getVisibleRange() { | |
159 return lastRange; | |
160 } | |
161 | |
162 /** | |
163 * Marks the given object as dirty. Will cause it to be cleared | |
164 * in the table. | |
165 * | |
166 * @param toFlush | |
167 */ | |
168 public void clear(Object toFlush) { | |
169 synchronized(this) { | |
170 int currentIdx = knownIndices.get(toFlush, -1); | |
171 | |
172 // If we've never heard of this object, bail out. | |
173 if (currentIdx is -1) { | |
174 return; | |
175 } | |
176 | |
177 pushClear(currentIdx); | |
178 } | |
179 | |
180 } | |
181 | |
182 /** | |
183 * Sets the size of the table. Called from a background thread. | |
184 * | |
185 * @param newTotal | |
186 */ | |
187 public void setTotalItems(int newTotal) { | |
188 synchronized (this) { | |
189 if (newTotal !is knownObjects.length) { | |
190 if (newTotal < knownObjects.length) { | |
191 // Flush any objects that are being removed as a result of the resize | |
192 for (int i = newTotal; i < knownObjects.length; i++) { | |
193 Object toFlush = knownObjects[i]; | |
194 | |
195 if (toFlush !is null) { | |
196 knownIndices.remove(toFlush); | |
197 } | |
198 } | |
199 } | |
200 | |
201 int minSize = Math.min(knownObjects.length, newTotal); | |
202 | |
203 Object[] newKnownObjects = new Object[newTotal]; | |
204 System.arraycopy(knownObjects, 0, newKnownObjects, 0, minSize); | |
205 knownObjects = newKnownObjects; | |
206 | |
207 scheduleUIUpdate(); | |
208 } | |
209 } | |
210 } | |
211 | |
212 /** | |
213 * Pushes an index onto the clear stack | |
214 * | |
215 * @param toClear row to clear | |
216 */ | |
217 private void pushClear(int toClear) { | |
218 | |
219 // If beyond the end of the table | |
220 if (toClear >= sentObjects.length) { | |
221 return; | |
222 } | |
223 | |
224 // If already flushed or never sent | |
225 if (sentObjects[toClear] is null) { | |
226 return; | |
227 } | |
228 | |
229 // Mark as flushed | |
230 sentObjects[toClear] = null; | |
231 | |
232 if (lastClear >= pendingClears.length) { | |
233 int newCapacity = Math.min(MIN_FLUSHLENGTH, lastClear * 2); | |
234 int[] newPendingClears = new int[newCapacity]; | |
235 System.arraycopy(pendingClears, 0, newPendingClears, 0, lastClear); | |
236 pendingClears = newPendingClears; | |
237 } | |
238 | |
239 pendingClears[lastClear++] = toClear; | |
240 } | |
241 | |
242 /** | |
243 * Sets the item on the given row to the given value. May be called from a background | |
244 * thread. Schedules a UI update if necessary | |
245 * | |
246 * @param idx row to change | |
247 * @param value new value for the given row | |
248 */ | |
249 public void replace(Object value, int idx) { | |
250 // Keep the synchronized block as small as possible, since the UI may | |
251 // be waiting on it. | |
252 synchronized(this) { | |
253 Object oldObject = knownObjects[idx]; | |
254 | |
255 if (oldObject !is value) { | |
256 if (oldObject !is null) { | |
257 knownIndices.remove(oldObject); | |
258 } | |
259 | |
260 knownObjects[idx] = value; | |
261 | |
262 if (value !is null) { | |
263 int oldIndex = knownIndices.get(value, -1); | |
264 if (oldIndex !is -1) { | |
265 knownObjects[oldIndex] = null; | |
266 pushClear(oldIndex); | |
267 } | |
268 | |
269 knownIndices.put(value, idx); | |
270 } | |
271 | |
272 pushClear(idx); | |
273 | |
274 scheduleUIUpdate(); | |
275 } | |
276 } | |
277 } | |
278 | |
279 /** | |
280 * Schedules a UI update. Has no effect if an update has already been | |
281 * scheduled. | |
282 */ | |
283 private void scheduleUIUpdate() { | |
284 synchronized(this) { | |
285 if (!updateScheduled) { | |
286 updateScheduled = true; | |
287 if(!table.getControl().isDisposed()) { | |
288 table.getControl().getDisplay().asyncExec(uiRunnable); | |
289 } | |
290 } | |
291 } | |
292 } | |
293 | |
294 | |
295 /** | |
296 * Called in the UI thread by a SetData callback. Refreshes the | |
297 * table if necessary. Returns true iff a refresh is needed. | |
298 * @param includeIndex the index that should be included in the visible range. | |
299 */ | |
300 public void checkVisibleRange(int includeIndex) { | |
301 int start = Math.min(table.getTopIndex() - 1, includeIndex); | |
302 int length = Math.max(table.getVisibleItemCount(), includeIndex - start); | |
303 Range r = lastRange; | |
304 | |
305 if (start !is r.start || length !is r.length) { | |
306 updateTable(); | |
307 } | |
308 } | |
309 | |
310 /** | |
311 * Updates the table. Sends any unsent items in the visible range to the table, | |
312 * and clears any previously-visible items that have not yet been sent to the table. | |
313 * Must be called from the UI thread. | |
314 */ | |
315 private void updateTable() { | |
316 | |
317 synchronized(this) { | |
318 | |
319 // Resize the table if necessary | |
320 if (sentObjects.length !is knownObjects.length) { | |
321 Object[] newSentObjects = new Object[knownObjects.length]; | |
322 System.arraycopy(newSentObjects, 0, sentObjects, 0, | |
323 Math.min(newSentObjects.length, sentObjects.length)); | |
324 sentObjects = newSentObjects; | |
325 table.setItemCount(newSentObjects.length); | |
326 } | |
327 | |
328 // Compute the currently visible range | |
329 int start = Math.min(table.getTopIndex(), knownObjects.length); | |
330 int length = Math.min(table.getVisibleItemCount(), knownObjects.length - start); | |
331 int itemCount = table.getItemCount(); | |
332 | |
333 int oldStart = lastRange.start; | |
334 int oldLen = lastRange.length; | |
335 | |
336 // Store the visible range. Do it BEFORE sending any table.clear calls, | |
337 // since clearing a visible row will result in a SetData callback which | |
338 // cause another table update if the visible range is different from | |
339 // the stored values -- this could cause infinite recursion. | |
340 lastRange = new Range(start, length); | |
341 | |
342 // Re-clear any items in the old range that were never filled in | |
343 for(int idx = 0; idx < oldLen; idx++) { | |
344 int row = idx + oldStart; | |
345 | |
346 // If this item is no longer visible | |
347 if (row < itemCount && (row < start || row >= start + length)) { | |
348 | |
349 // Note: if we wanted to be really aggressive about clearing | |
350 // items that are no longer visible, we could clear here unconditionally. | |
351 // The current way of doing things won't clear a row if its contents are | |
352 // up-to-date. | |
353 if (sentObjects[row] is null) { | |
354 table.clear(row); | |
355 } | |
356 } | |
357 } | |
358 | |
359 // Process any pending clears | |
360 if (lastClear > 0) { | |
361 for (int i = 0; i < lastClear; i++) { | |
362 int row = pendingClears[i]; | |
363 | |
364 if (row < sentObjects.length) { | |
365 table.clear(row); | |
366 } | |
367 } | |
368 | |
369 if (pendingClears.length > MIN_FLUSHLENGTH) { | |
370 pendingClears = new int[MIN_FLUSHLENGTH]; | |
371 } | |
372 lastClear = 0; | |
373 } | |
374 | |
375 // Send any unsent items in the visible range | |
376 for (int idx = 0; idx < length; idx++) { | |
377 int row = idx + start; | |
378 | |
379 Object obj = knownObjects[row]; | |
380 if (obj !is null && obj !is sentObjects[idx]) { | |
381 table.replace(obj, row); | |
382 sentObjects[idx] = obj; | |
383 } | |
384 } | |
385 | |
386 } | |
387 } | |
388 | |
389 /** | |
390 * Return the array of all known objects that have been sent here from the background | |
391 * thread. | |
392 * @return the array of all known objects | |
393 */ | |
394 public Object[] getKnownObjects() { | |
395 return knownObjects; | |
396 } | |
397 | |
398 } |