KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > viewers > deferred > ConcurrentTableUpdator


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  *******************************************************************************/

11 package org.eclipse.jface.viewers.deferred;
12
13
14
15 /**
16  * Allows a table to be accessed from a background thread. Provides a table-like public
17  * interface that can accessed from a background thread. As updates arrive from the
18  * background thread, it batches and schedules updates to the real table in the UI thread.
19  * This class can be used with any widget that can be wrapped in the
20  * <code>AbstractVirtualTable</code> interface.
21  *
22  * @since 3.1
23  */

24 /* package */ final class ConcurrentTableUpdator {
25     /**
26      * Wrapper for the real table. May only be accessed in the UI thread.
27      */

28     private AbstractVirtualTable table;
29     
30     /**
31      * The array of objects that have been sent to the UI. Elements are null
32      * if they either haven't been sent yet or have been scheduled for clear.
33      * Maps indices onto elements.
34      */

35     private Object JavaDoc[] sentObjects = new Object JavaDoc[0];
36     
37     /**
38      * Map of elements to object indices (inverse of the knownObjects array)
39      */

40     private IntHashMap knownIndices = new IntHashMap();
41     
42     /**
43      * Contains all known objects that have been sent here from the background
44      * thread.
45      */

46     private Object JavaDoc[] knownObjects = new Object JavaDoc[0];
47     
48     // Minimum length for the pendingFlushes stack
49
private static final int MIN_FLUSHLENGTH = 64;
50     
51     /**
52      * Array of element indices. Contains elements scheduled to be
53      * cleared. Only the beginning of the array is used. The number
54      * of used elements is stored in lastClear
55      */

56     private int[] pendingClears = new int[MIN_FLUSHLENGTH];
57     
58     /**
59      * Number of pending clears in the pendingClears array (this is normally
60      * used instead of pendingClears.length since the
61      * pendingClears array is usually larger than the actual number of pending
62      * clears)
63      */

64     private int lastClear = 0;
65     
66     /**
67      * Last known visible range
68      */

69     private volatile Range lastRange = new Range(0,0);
70     
71     /**
72      * True iff a UI update has been scheduled
73      */

74     private volatile boolean updateScheduled;
75     
76     /**
77      * True iff this object has been disposed
78      */

79     private volatile boolean disposed = false;
80     
81     /**
82      * Object that holds a start index and length. Allows
83      * the visible range to be returned as an atomic operation.
84      */

85     public final static class Range {
86         int start = 0;
87         int length = 0;
88         
89         /**
90          * @param s
91          * @param l
92          */

93         public Range(int s, int l) {
94             start = s;
95             length = l;
96         }
97     }
98     
99     /**
100      * Runnable that can be posted with an asyncExec to schedule
101      * an update to the real table.
102      */

103     Runnable JavaDoc uiRunnable = new Runnable JavaDoc() {
104         public void run() {
105             updateScheduled = false;
106             if(!table.getControl().isDisposed()) {
107                 updateTable();
108             }
109         }
110     };
111     
112     /**
113      * Creates a new table updator
114      *
115      * @param table real table to update
116      */

117     public ConcurrentTableUpdator(AbstractVirtualTable table) {
118         this.table = table;
119     }
120     
121     /**
122      * Cleans up the updator object (but not the table itself).
123      */

124     public void dispose() {
125         disposed = true;
126     }
127     
128     /**
129      * True iff this object has been disposed.
130      *
131      * @return true iff dispose() has been called
132      */

133     public boolean isDisposed() {
134         return disposed;
135     }
136     
137     /**
138      * Returns the currently visible range
139      *
140      * @return the currently visible range
141      */

142     public Range getVisibleRange() {
143         return lastRange;
144     }
145     
146     /**
147      * Marks the given object as dirty. Will cause it to be cleared
148      * in the table.
149      *
150      * @param toFlush
151      */

152     public void clear(Object JavaDoc toFlush) {
153         synchronized(this) {
154             int currentIdx = knownIndices.get(toFlush, -1);
155
156             // If we've never heard of this object, bail out.
157
if (currentIdx == -1) {
158                 return;
159             }
160
161             pushClear(currentIdx);
162         }
163         
164     }
165     
166     /**
167      * Sets the size of the table. Called from a background thread.
168      *
169      * @param newTotal
170      */

171     public void setTotalItems(int newTotal) {
172         synchronized (this) {
173             if (newTotal != knownObjects.length) {
174                 if (newTotal < knownObjects.length) {
175                     // Flush any objects that are being removed as a result of the resize
176
for (int i = newTotal; i < knownObjects.length; i++) {
177                         Object JavaDoc toFlush = knownObjects[i];
178                         
179                         if (toFlush != null) {
180                             knownIndices.remove(toFlush);
181                         }
182                     }
183                 }
184                 
185                 int minSize = Math.min(knownObjects.length, newTotal);
186                 
187                 Object JavaDoc[] newKnownObjects = new Object JavaDoc[newTotal];
188                 System.arraycopy(knownObjects, 0, newKnownObjects, 0, minSize);
189                 knownObjects = newKnownObjects;
190                                 
191                 scheduleUIUpdate();
192             }
193         }
194     }
195     
196     /**
197      * Pushes an index onto the clear stack
198      *
199      * @param toClear row to clear
200      */

201     private void pushClear(int toClear) {
202         
203         // If beyond the end of the table
204
if (toClear >= sentObjects.length) {
205             return;
206         }
207         
208         // If already flushed or never sent
209
if (sentObjects[toClear] == null) {
210             return;
211         }
212
213         // Mark as flushed
214
sentObjects[toClear] = null;
215         
216         if (lastClear >= pendingClears.length) {
217             int newCapacity = Math.min(MIN_FLUSHLENGTH, lastClear * 2);
218             int[] newPendingClears = new int[newCapacity];
219             System.arraycopy(pendingClears, 0, newPendingClears, 0, lastClear);
220             pendingClears = newPendingClears;
221         }
222         
223         pendingClears[lastClear++] = toClear;
224     }
225     
226     /**
227      * Sets the item on the given row to the given value. May be called from a background
228      * thread. Schedules a UI update if necessary
229      *
230      * @param idx row to change
231      * @param value new value for the given row
232      */

233     public void replace(Object JavaDoc value, int idx) {
234         // Keep the synchronized block as small as possible, since the UI may
235
// be waiting on it.
236
synchronized(this) {
237             Object JavaDoc oldObject = knownObjects[idx];
238             
239             if (oldObject != value) {
240                 if (oldObject != null) {
241                     knownIndices.remove(oldObject);
242                 }
243                 
244                 knownObjects[idx] = value;
245                 
246                 if (value != null) {
247                     int oldIndex = knownIndices.get(value, -1);
248                     if (oldIndex != -1) {
249                         knownObjects[oldIndex] = null;
250                         pushClear(oldIndex);
251                     }
252                     
253                     knownIndices.put(value, idx);
254                 }
255                 
256                 pushClear(idx);
257                 
258                 scheduleUIUpdate();
259             }
260         }
261     }
262
263     /**
264      * Schedules a UI update. Has no effect if an update has already been
265      * scheduled.
266      */

267     private void scheduleUIUpdate() {
268         synchronized(this) {
269             if (!updateScheduled) {
270                 updateScheduled = true;
271                 if(!table.getControl().isDisposed()) {
272                     table.getControl().getDisplay().asyncExec(uiRunnable);
273                 }
274             }
275         }
276     }
277     
278     
279     /**
280      * Called in the UI thread by a SetData callback. Refreshes the
281      * table if necessary. Returns true iff a refresh is needed.
282      * @param includeIndex the index that should be included in the visible range.
283      */

284     public void checkVisibleRange(int includeIndex) {
285         int start = Math.min(table.getTopIndex() - 1, includeIndex);
286         int length = Math.max(table.getVisibleItemCount(), includeIndex - start);
287         Range r = lastRange;
288
289         if (start != r.start || length != r.length) {
290             updateTable();
291         }
292     }
293     
294     /**
295      * Updates the table. Sends any unsent items in the visible range to the table,
296      * and clears any previously-visible items that have not yet been sent to the table.
297      * Must be called from the UI thread.
298      */

299     private void updateTable() {
300         
301         synchronized(this) {
302
303             // Resize the table if necessary
304
if (sentObjects.length != knownObjects.length) {
305                 Object JavaDoc[] newSentObjects = new Object JavaDoc[knownObjects.length];
306                 System.arraycopy(newSentObjects, 0, sentObjects, 0,
307                         Math.min(newSentObjects.length, sentObjects.length));
308                 sentObjects = newSentObjects;
309                 table.setItemCount(newSentObjects.length);
310             }
311
312             // Compute the currently visible range
313
int start = Math.min(table.getTopIndex(), knownObjects.length);
314             int length = Math.min(table.getVisibleItemCount(), knownObjects.length - start);
315             int itemCount = table.getItemCount();
316             
317             int oldStart = lastRange.start;
318             int oldLen = lastRange.length;
319             
320             // Store the visible range. Do it BEFORE sending any table.clear calls,
321
// since clearing a visible row will result in a SetData callback which
322
// cause another table update if the visible range is different from
323
// the stored values -- this could cause infinite recursion.
324
lastRange = new Range(start, length);
325             
326             // Re-clear any items in the old range that were never filled in
327
for(int idx = 0; idx < oldLen; idx++) {
328                 int row = idx + oldStart;
329                 
330                 // If this item is no longer visible
331
if (row < itemCount && (row < start || row >= start + length)) {
332                     
333                     // Note: if we wanted to be really aggressive about clearing
334
// items that are no longer visible, we could clear here unconditionally.
335
// The current way of doing things won't clear a row if its contents are
336
// up-to-date.
337
if (sentObjects[row] == null) {
338                         table.clear(row);
339                     }
340                 }
341             }
342             
343             // Process any pending clears
344
if (lastClear > 0) {
345                 for (int i = 0; i < lastClear; i++) {
346                     int row = pendingClears[i];
347         
348                     if (row < sentObjects.length) {
349                         table.clear(row);
350                     }
351                 }
352     
353                 if (pendingClears.length > MIN_FLUSHLENGTH) {
354                     pendingClears = new int[MIN_FLUSHLENGTH];
355                 }
356                 lastClear = 0;
357             }
358             
359             // Send any unsent items in the visible range
360
for (int idx = 0; idx < length; idx++) {
361                 int row = idx + start;
362                 
363                 Object JavaDoc obj = knownObjects[row];
364                 if (obj != null && obj != sentObjects[idx]) {
365                     table.replace(obj, row);
366                     sentObjects[idx] = obj;
367                 }
368             }
369             
370         }
371     }
372
373     /**
374      * Return the array of all known objects that have been sent here from the background
375      * thread.
376      * @return the array of all known objects
377      */

378     public Object JavaDoc[] getKnownObjects() {
379         return knownObjects;
380     }
381     
382 }
383
Popular Tags