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