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 }