Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 72358 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/jface/viewers/deferred/AbstractConcurrentContentProvider.java (+66 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import org.eclipse.jface.util.ListenerList;
14
15
/**
16
 * @since 3.1
17
 */
18
public abstract class AbstractConcurrentContentProvider implements
19
        IConcurrentContentProvider {
20
21
    private ListenerList listeners = new ListenerList(); 
22
    
23
    /* (non-Javadoc)
24
     * @see org.eclipse.jface.viewers.deferred.IConcurrentContentProvider#addListener(org.eclipse.jface.viewers.deferred.IConcurrentContentProviderListener)
25
     */
26
    public void addListener(IConcurrentContentProviderListener listener) {
27
        listeners.add(listener);
28
    }
29
    
30
    protected void fireAdd(Object[] added) {
31
        Object[] listenerArray = listeners.getListeners();
32
        
33
        for (int i = 0; i < listenerArray.length; i++) {
34
            IConcurrentContentProviderListener next = (IConcurrentContentProviderListener) listenerArray[i];
35
            
36
            next.add(added);
37
        }
38
    }
39
40
    protected void fireRemove(Object[] removed) {
41
        Object[] listenerArray = listeners.getListeners();
42
        
43
        for (int i = 0; i < listenerArray.length; i++) {
44
            IConcurrentContentProviderListener next = (IConcurrentContentProviderListener) listenerArray[i];
45
            
46
            next.remove(removed);
47
        }
48
    }
49
    
50
    protected void fireUpdate(Object[] updated) {
51
        Object[] listenerArray = listeners.getListeners();
52
        
53
        for (int i = 0; i < listenerArray.length; i++) {
54
            IConcurrentContentProviderListener next = (IConcurrentContentProviderListener) listenerArray[i];
55
            
56
            next.update(updated);
57
        }
58
    }
59
    
60
    /* (non-Javadoc)
61
     * @see org.eclipse.jface.viewers.deferred.IConcurrentContentProvider#removeListener(org.eclipse.jface.viewers.deferred.IConcurrentContentProviderListener)
62
     */
63
    public void removeListener(IConcurrentContentProviderListener listener) {
64
        listeners.remove(listener);
65
    }
66
}
(-)src/org/eclipse/jface/viewers/deferred/ConcurrentTableManager.java (+305 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import java.util.Comparator;
14
15
import org.eclipse.core.runtime.IProgressMonitor;
16
import org.eclipse.core.runtime.IStatus;
17
import org.eclipse.core.runtime.NullProgressMonitor;
18
import org.eclipse.core.runtime.Status;
19
import org.eclipse.core.runtime.jobs.Job;
20
import org.eclipse.swt.widgets.Display;
21
22
/**
23
 * @since 3.1
24
 */
25
public final class ConcurrentTableManager implements IConcurrentContentProviderListener {
26
    
27
    private int limit = -1;
28
    private IConcurrentContentProvider content;
29
    private IConcurrentTable table;
30
    private Comparator sortOrder;
31
    private boolean containsUnsorted = false;
32
    private boolean dirty = false;
33
    
34
    private LazySortedCollection sorter;
35
    private volatile FastProgressReporter searchReporter = null;
36
    
37
    // Any thread accessing the following objects should synchronize on uiMutex.
38
    // The UI thread will wait on this lock, so it should never be held for anything
39
    // but trivial operations, and threads should never wait for other locks while holding
40
    // uiMutex.
41
    private ConcurrentTableUpdator updator;
42
    private Object sortMutex = new Object();
43
    private int sortStart = -1;
44
    private int sortLength = -1;
45
    
46
    private Job sortJob = new Job("sorting") {
47
        protected IStatus run(IProgressMonitor monitor) {
48
            doSort(monitor);
49
            return Status.OK_STATUS;
50
        }
51
    };
52
    
53
    public ConcurrentTableManager(IConcurrentTable table, 
54
            IConcurrentContentProvider contentProvider, Comparator sortOrder, 
55
            Display display) {
56
        
57
        updator = new ConcurrentTableUpdator(table, display);
58
        content = contentProvider;
59
        this.table = table;
60
        this.sortOrder = sortOrder;
61
        sorter = new LazySortedCollection(sortOrder);
62
        sortJob.setSystem(true);
63
        sortJob.setPriority(Job.SHORT);
64
        contentProvider.addListener(this);
65
    }
66
    
67
    public void dispose() {
68
        content.removeListener(this);
69
    }
70
    
71
    public void refresh() {
72
        content.requestUpdate(this);
73
    }
74
75
    /**
76
     * Called from sortJob. Sorts the elements defined by sortStart and sortLength.
77
     * Schedules a UI update when finished 
78
     * 
79
     * @param mon
80
     * @since 3.1
81
     */
82
    private void doSort(IProgressMonitor mon) {        
83
        LazySortedCollection collection;
84
        
85
        // Workaround for some weirdness in the Jobs framework: if you cancel a monitor
86
        // for a job that has ended and reschedule that same job, it will start 
87
        // the job with a monitor that is already cancelled. We can workaround this by
88
        // removing all references to the progress monitor whenever the job terminates,
89
        // but this would require additional synchronize blocks (which are slow) and more
90
        // complexity. Instead, we just un-cancel the monitor at the start of each job. 
91
        mon.setCanceled(false);
92
        
93
        synchronized(sortMutex) {
94
            ConcurrentTableUpdator.Range currentRange = updator.getRange();
95
            sortStart = currentRange.start;
96
            sortLength = currentRange.length;
97
            collection = sorter;
98
        }
99
100
        mon.beginTask("sorting", 100);
101
        try {
102
            
103
	        // First, sort the objects that are currently visible in the table
104
	        Object[] objectsOfInterest; 
105
	        int totalElements = 0;
106
	        
107
	        synchronized(collection) {
108
		        FastProgressReporter subMon = new FastProgressReporter(mon, 50);
109
		        searchReporter = subMon;
110
		        totalElements = collection.size();
111
	            if (limit != -1) {
112
	                if (totalElements > limit) {
113
	                    totalElements = limit;
114
	                }
115
	            }
116
	            
117
	            // Send the total items to the updator ASAP -- the user may want
118
	            // to scroll to a different section of the table, which would
119
	            // cause our sort range to change and cause this job to get cancelled.
120
		        updator.setTotalItems(totalElements);
121
		        
122
		        if (limit != -1) {
123
		            collection.retainFirst(limit, subMon);
124
		        }
125
		        
126
		        sortLength = Math.min(sortLength, totalElements - sortStart);
127
		        sortLength = Math.max(sortLength, 0);
128
		        
129
		        objectsOfInterest = new Object[sortLength];
130
		     
131
		        collection.getRange(objectsOfInterest, sortStart, true, subMon);
132
		        searchReporter = null;
133
	        }
134
	        
135
	        updator.setElements(objectsOfInterest, sortStart, sortLength);
136
	        
137
	        if (mon.isCanceled()) {
138
	            return;
139
	        }
140
	
141
	        // Finally, sort all objects in the collection
142
	        synchronized(collection) {
143
		        objectsOfInterest = new Object[collection.size()];
144
		        
145
		        FastProgressReporter subMon = new FastProgressReporter(mon, 50);
146
		        searchReporter = subMon;
147
		        collection.getFirst(objectsOfInterest, true, subMon);
148
		        searchReporter = null;
149
	        }
150
	        
151
	        updator.setElements(objectsOfInterest, 0, totalElements);
152
	        containsUnsorted = false;
153
        } catch (InterruptedException e) {
154
            mon.setCanceled(true);
155
        } finally {
156
            searchReporter = null;
157
            mon.done();
158
        }
159
        
160
    }
161
    
162
    public void setSortOrder(Comparator sorter) {
163
        this.sortOrder = sorter;
164
        refresh();
165
    }
166
    
167
    public void setLimit(int limit) {
168
        this.limit = limit;
169
        refresh();
170
    }
171
    
172
    public int getLimit() {
173
        return limit;
174
    }
175
    
176
    public void setRangeOfInterest(int start, int length) {
177
        updator.setRangeOfInterest(start, length);
178
        triggerSort();
179
    }
180
    
181
    private void makeDirty() {
182
        synchronized(sortMutex) {
183
            dirty = true;
184
            containsUnsorted = true;
185
            triggerSort();
186
        }
187
    }
188
    
189
    private void triggerSort() {
190
        synchronized(sortMutex) {
191
            // If we've already sent everything to the updator, there is nothing to do.
192
            if (!containsUnsorted) {
193
                return;
194
            }
195
            
196
            ConcurrentTableUpdator.Range range = updator.getRange();
197
            
198
            if (dirty || sortStart != range.start || sortLength != range.length) {
199
                dirty = false;
200
                cancelSortJob();
201
		        sortJob.schedule();
202
            }
203
        }
204
    }
205
206
    private void cancelSortJob() {
207
        FastProgressReporter rep = searchReporter;
208
        if (rep != null) {
209
            rep.cancel();
210
        }
211
        sortJob.cancel();
212
    }
213
    
214
    /* (non-Javadoc)
215
     * @see org.eclipse.jface.viewers.deferred.TableManager#add(java.util.Collection, org.eclipse.core.runtime.IProgressMonitor)
216
     */
217
    public void add(Object[] toAdd) {
218
        cancelSortJob();
219
        LazySortedCollection collection = sorter;
220
        synchronized(collection) {
221
            collection.addAll(toAdd);
222
            makeDirty();
223
        }
224
    }
225
    
226
    public void setContents(Object[] contents) {
227
        LazySortedCollection collection = sorter;
228
        synchronized(collection) {
229
            // Flush all current items
230
            flush(collection.getItems(false), collection);
231
            
232
            //LazySortedCollection newCollection = new LazySortedCollection(sortOrder);
233
            //newCollection.addAll(contents);
234
        }
235
236
        synchronized(sortMutex) {
237
            LazySortedCollection newCollection = new LazySortedCollection(sortOrder);
238
            newCollection.addAll(contents);
239
            this.sorter = newCollection;
240
            makeDirty();
241
        }
242
    }
243
244
    /* (non-Javadoc)
245
     * @see org.eclipse.jface.viewers.deferred.TableManager#remove(java.util.Collection, org.eclipse.core.runtime.IProgressMonitor)
246
     */
247
    public void remove(Object[] toRemove) {
248
        cancelSortJob();
249
        LazySortedCollection collection = sorter;
250
        synchronized(collection) {
251
            
252
            try {
253
                collection.removeAll(toRemove, new NullProgressMonitor());
254
            } catch (InterruptedException e) {
255
            }
256
            
257
            flush(toRemove, collection);
258
            
259
            makeDirty();
260
        }
261
        refresh();
262
    }
263
264
    /**
265
     * @param toRemove
266
     * @param collection
267
     * @since 3.1
268
     */
269
    private void flush(Object[] toRemove, LazySortedCollection collection) {
270
        for (int i = 0; i < toRemove.length; i++) {
271
            Object item = toRemove[i];
272
            
273
            if (collection.contains(item)) {
274
                updator.flushCache(item);
275
            }
276
        }
277
    }
278
279
    /* (non-Javadoc)
280
     * @see org.eclipse.jface.viewers.deferred.TableManager#update(java.util.Collection, org.eclipse.core.runtime.IProgressMonitor)
281
     */
282
    public void update(Object[] items) {
283
        
284
        boolean dirty = false;
285
        LazySortedCollection collection = sorter;
286
        synchronized(collection) {
287
	        for (int i = 0; i < items.length; i++) {
288
	            Object item = items[i];
289
	            
290
	            if (collection.contains(item)) {
291
	                // TODO: write a collection.update(...) method
292
	                collection.remove(item);
293
	                collection.add(item);
294
	                updator.flushCache(item);
295
	                
296
	                dirty = true;
297
	            }
298
	        }
299
	        
300
	        if (dirty) {
301
	            makeDirty();
302
	        }
303
        }
304
    }
305
}
(-)src/org/eclipse/jface/viewers/deferred/ConcurrentTableUpdator.java (+326 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import org.eclipse.swt.widgets.Display;
14
15
16
/**
17
 * @since 3.1
18
 */
19
public class ConcurrentTableUpdator {
20
    private IConcurrentTable table;
21
    
22
    /**
23
     * Set of all non-flushed elements known to this updator. Maps elements
24
     * onto sort positions. 
25
     */
26
    private IntHashMap sentIndices = new IntHashMap();
27
28
    /**
29
     * The array of objects that has been sent to the UI. Maps indices onto elements.
30
     */
31
    private Object[] sentObjects = new Object[0];
32
    
33
    private IntHashMap knownIndices = new IntHashMap();
34
    
35
    private Object[] knownObjects = new Object[0];
36
    
37
    // Minimum length for the pendingFlushes stack
38
    private static final int MIN_FLUSHLENGTH = 64;
39
    
40
    // Stack of pending indices to flush
41
    private Object[] pendingFlushes = new Object[MIN_FLUSHLENGTH];
42
    private int lastFlush = 0;
43
    
44
    private int totalElements = 0;
45
    private Display display;
46
    private int rangeStart;
47
    private int rangeLength;
48
    private boolean updateScheduled;
49
    
50
    public final static class Range {
51
        int start = 0;
52
        int length = 0;
53
        
54
        public Range(int s, int l) {
55
            start = s;
56
            length = l;
57
        }
58
    }
59
    
60
    Runnable uiRunnable = new Runnable() {
61
        public void run() {
62
            synchronized(this) {
63
                updateScheduled = false;
64
            }
65
            updateTable();
66
        }
67
    };
68
    
69
    public ConcurrentTableUpdator(IConcurrentTable table, Display display) {
70
        this.table = table;
71
        this.display = display;
72
    }
73
    
74
    public Range getRange() {
75
        synchronized(this) {
76
            return new Range(rangeStart, rangeLength);
77
        }
78
    }
79
    
80
    /**
81
     * Updates all elements in the given range 
82
     * 
83
     * @param changedElements
84
     * @param rangeStart
85
     * @param rangeLength
86
     * @since 3.1
87
     */
88
    public void setElements(Object[] changedElements, int start, int length) {
89
        int len = Math.min(changedElements.length, length);
90
        boolean visibleDirty = false;
91
        
92
        synchronized(this) {
93
	        for (int i = 0; i < len; i++) {
94
	            int element = i + start;
95
	            Object object = changedElements[i];
96
97
                Object oldObject = knownObjects[element];
98
                
99
                if (oldObject != object && isVisible(element)) {
100
                    visibleDirty = true;
101
                }
102
                
103
                knownObjects[element] = object;
104
	        }
105
	        
106
	        if (visibleDirty) {
107
	            scheduleUIUpdate();
108
	        }
109
        }
110
        
111
    }
112
    
113
    /**
114
     * @param element
115
     * @return
116
     * @since 3.1
117
     */
118
    private boolean isVisible(int element) {
119
        return element >= rangeStart && element < rangeStart + rangeLength;
120
    }
121
122
    public void flushCache(Object toFlush) {
123
        synchronized(this) {
124
            int currentIdx = knownIndices.get(toFlush, -1);
125
            
126
            // If we've never heard of this object, bail out.
127
            if (currentIdx == -1) {
128
                return;
129
            }
130
            
131
            knownIndices.remove(toFlush);
132
            knownObjects[currentIdx] = null;            
133
            
134
            // If we've sent this object to the UI, schedule a flush
135
            int sentIdx = sentIndices.get(toFlush, -1);
136
            if (sentIdx != -1) {
137
                pushFlush(toFlush);
138
               
139
                sentIndices.remove(toFlush);
140
                sentObjects[sentIdx] = null;
141
                
142
                scheduleUIUpdate();
143
            }
144
        }
145
        
146
    }
147
    
148
    /**
149
     * Sets the size of the table. Called from a background thread.
150
     * 
151
     * @param newTotal
152
     * @since 3.1
153
     */
154
    public void setTotalItems(int newTotal) {
155
        synchronized (this) {
156
            if (newTotal != knownObjects.length) {
157
                if (newTotal < knownObjects.length) {
158
                    // Flush any objects that are being removed as a result of the resize
159
                    for (int i = newTotal; i < knownObjects.length; i++) {
160
                        Object toFlush = knownObjects[i];
161
                        
162
                        if (toFlush != null) {
163
                            flushCache(toFlush);
164
                        }
165
                    }
166
                }
167
                
168
                int minSize = Math.min(knownObjects.length, newTotal);
169
                
170
	            Object[] newKnownObjects = new Object[newTotal];
171
	            System.arraycopy(knownObjects, 0, newKnownObjects, 0, minSize);
172
	            knownObjects = newKnownObjects;
173
	            	            
174
	            scheduleUIUpdate();
175
            }
176
        }
177
    }
178
    
179
    /**
180
     * Pushes an object onto the flush stack
181
     * @param toFlush
182
     * @since 3.1
183
     */
184
    private void pushFlush(Object toFlush) {
185
        if (lastFlush >= pendingFlushes.length) {
186
            int newCapacity = Math.min(MIN_FLUSHLENGTH, lastFlush * 2);
187
            Object[] newPendingFlushes = new Object[newCapacity];
188
            System.arraycopy(pendingFlushes, 0, newPendingFlushes, 0, lastFlush);
189
            pendingFlushes = newPendingFlushes;
190
        }
191
        
192
        pendingFlushes[lastFlush++] = toFlush;
193
    }
194
    
195
    /**
196
     * 
197
     * @param idx
198
     * @param element
199
     * @since 3.1
200
     */
201
    public void update(int idx, Object value) {        
202
        // Keep the synchronized block as small as possible, since the UI may
203
        // be waiting on it.
204
        synchronized(this) {
205
            Object oldObject = knownObjects[idx];
206
            
207
            if (oldObject != value && isVisible(idx)) {
208
                knownObjects[idx] = value;
209
                
210
                scheduleUIUpdate();
211
            }
212
        } 
213
    }
214
    
215
    /**
216
     * Called whenever the set of visible items in the table changes.
217
     * Must be called from the UI thread. Will ensure that the 
218
     * set of visible items is up-to-date.
219
     * 
220
     * @param start
221
     * @param length
222
     * @since 3.1
223
     */
224
    public void setRangeOfInterest(int start, int length) {
225
        synchronized (this) {
226
            if (rangeStart == start && rangeLength == length) {
227
                return;
228
            }
229
            
230
            rangeStart = start;
231
            rangeLength = length;
232
        }
233
        
234
        updateTable();
235
    }
236
237
    /**
238
     * Schedules a UI update
239
     * 
240
     * @since 3.1
241
     */
242
    private void scheduleUIUpdate() {
243
        synchronized(this) {
244
	        if (!updateScheduled) {
245
	            updateScheduled = true;
246
	            display.asyncExec(uiRunnable);
247
	        }
248
        }
249
    }
250
        
251
    private boolean hasPendingFlushes() {
252
        return lastFlush != 0;
253
    }
254
    
255
    /**
256
     * Sends all known items in the given range to the table. Must be called from the
257
     * UI thread. Returns the number of items in the range that haven't been computed yet.
258
     * 
259
     * @param start
260
     * @param length
261
     * @since 3.1
262
     */
263
    private void updateTable() {
264
        int counter = 0;
265
        
266
        Object[] flushes = null;
267
        int localNumFlushes = 0;
268
        Object[] elementsToSend = null;
269
        int newLength = -1;
270
        int start = 0;
271
        int length = 0;
272
        
273
        // Copy everything to local variables -- don't make any calls to other
274
        // objects while we're inside our synchronized block.
275
        synchronized (this) {
276
            start = rangeStart;
277
            length = rangeLength;
278
            
279
            if (lastFlush > 0) {
280
                localNumFlushes = lastFlush;
281
                flushes = pendingFlushes;
282
                pendingFlushes = new Object[MIN_FLUSHLENGTH];
283
                lastFlush = 0;
284
            }
285
            
286
            // Check if we need to resize the table
287
            if (knownObjects.length != sentObjects.length) {
288
                newLength = knownObjects.length;
289
                
290
                Object[] newSentObjects = new Object[knownObjects.length];
291
                System.arraycopy(sentObjects, 0, newSentObjects, 0, 
292
                        Math.min(knownObjects.length, sentObjects.length));
293
                sentObjects = newSentObjects;
294
            }
295
            
296
            length = Math.min(length, sentObjects.length - start);
297
298
            if (length > 0) {
299
	            elementsToSend = new Object[length];
300
	            for (int i = 0; i < elementsToSend.length; i++) {
301
	                int element = i + start;
302
	                
303
	                Object object = knownObjects[element];
304
	                if (object != sentObjects[element]) {
305
	                    elementsToSend[i] = object;
306
	                }
307
	            }
308
            }
309
        }
310
        
311
        for (int idx = 0; idx < localNumFlushes; idx++) {
312
            table.flushCache(flushes[idx]);
313
        }
314
        
315
        if (newLength != -1) {
316
            table.setTotalItems(newLength);
317
        }
318
        
319
        for (int idx = 0; idx < length; idx++) {
320
            Object next = elementsToSend[idx];
321
            if (next != null) {
322
                table.update(idx + start, next);
323
            }
324
        }
325
    }
326
}
(-)src/org/eclipse/jface/viewers/deferred/FastProgressReporter.java (+134 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import org.eclipse.core.runtime.IProgressMonitor;
14
15
/**
16
 * @since 3.1
17
 */
18
public final class FastProgressReporter {
19
    private IProgressMonitor monitor;
20
    private volatile boolean canceled = false;
21
    private int cancelCheck = 0;
22
    private String taskName;
23
    
24
    private int taskDepth = 0;
25
    private int subTaskSize = 1;
26
    private int totalWork = 1;
27
    private int parentWork = 1;
28
    private int monitorUnitsRemaining;
29
    
30
    private static int CANCEL_CHECK_PERIOD = 40;
31
    
32
    /**
33
     * Constructs a null FastProgressReporter
34
     */
35
    public FastProgressReporter() {
36
    }
37
    
38
    /**
39
     * Constructs a FastProgressReporter that wraps the given progress monitor
40
     * 
41
     * @param taskName
42
     * @param monitor
43
     */
44
    public FastProgressReporter(IProgressMonitor monitor, int totalProgress) {
45
        this.monitor = monitor;
46
        monitorUnitsRemaining = totalProgress;
47
        canceled = monitor.isCanceled();
48
    }
49
    
50
//    /**
51
//     * Every call to beginTask must have a corresponding call to endTask, with the
52
//     * same argument.
53
//     * 
54
//     * @param totalWork
55
//     * @since 3.1
56
//     */
57
//    public void beginTask(int totalWork) {
58
//        
59
//        if (monitor == null) {
60
//            return;
61
//        }
62
//        
63
//        taskDepth++;
64
//
65
//        if (totalWork == 0) {
66
//            return;
67
//        }
68
//        
69
//        this.totalWork *= totalWork;
70
//    }
71
//    
72
//    public void beginSubTask(int subTaskWork) {
73
//        subTaskSize *= subTaskWork;
74
//    }
75
//    
76
//    public void endSubTask(int subTaskWork) {
77
//        subTaskSize /= subTaskWork;
78
//    }
79
//    
80
//    public void worked(int amount) {
81
//        amount *= subTaskSize;
82
//        
83
//        if (amount > totalWork) {
84
//            amount = totalWork;
85
//        }
86
//        
87
//        int consumed = monitorUnitsRemaining * amount / totalWork;
88
//        
89
//        if (consumed > 0) {
90
//            monitor.worked(consumed);
91
//            monitorUnitsRemaining -= consumed;
92
//        }
93
//        totalWork -= amount;
94
//    }
95
//    
96
//    public void endTask(int totalWork) {        
97
//        taskDepth--;
98
//        
99
//        if (taskDepth == 0) {
100
//            if (monitor != null && monitorUnitsRemaining > 0) {
101
//                monitor.worked(monitorUnitsRemaining);
102
//            }
103
//        }
104
//        
105
//        if (totalWork == 0) {
106
//            return;
107
//        }
108
//        
109
//        this.totalWork /= totalWork;
110
//
111
//    }
112
    
113
    public boolean isCanceled() {
114
        if (monitor == null) {
115
            return canceled;
116
        }
117
        
118
        cancelCheck++;
119
        if (cancelCheck > CANCEL_CHECK_PERIOD) {
120
            canceled = monitor.isCanceled();
121
            cancelCheck = 0;
122
        }
123
        return canceled;
124
    }
125
    
126
    public void cancel() {        
127
        canceled = true;
128
        
129
        if (monitor == null) {
130
            return;
131
        }
132
        monitor.setCanceled(true);
133
    }
134
}
(-)src/org/eclipse/jface/viewers/deferred/IConcurrentContentProvider.java (+33 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
14
/**
15
 * @since 3.1
16
 */
17
public interface IConcurrentContentProvider {
18
19
    /**
20
     * Called by a listener to request an update. The receiver should call
21
     * the given listener's setContents(...) method at its earliest convenience.
22
     * The receiver is allowed to compute the elements asynchronously. That is,
23
     * it can compute the result in a background thread and call setContents(...)
24
     * once the result is ready. 
25
     * 
26
     * @param listener
27
     * @since 3.1
28
     */
29
    public void requestUpdate(IConcurrentContentProviderListener listener);
30
    
31
    public void addListener(IConcurrentContentProviderListener listener);
32
    public void removeListener(IConcurrentContentProviderListener listener);
33
}
(-)src/org/eclipse/jface/viewers/deferred/IConcurrentContentProviderListener.java (+28 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
/**
14
 * @since 3.1
15
 */
16
public interface IConcurrentContentProviderListener {
17
    public void add(Object[] added);
18
    public void remove(Object[] removed);
19
    public void update(Object[] changed);
20
    
21
    /**
22
     * Sends the complete set of elements in the model.
23
     *  
24
     * @param newContents
25
     * @since 3.1
26
     */
27
    public void setContents(Object[] newContents);
28
}
(-)src/org/eclipse/jface/viewers/deferred/IConcurrentTable.java (+43 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
/**
14
 * @since 3.1
15
 */
16
public interface IConcurrentTable {
17
    /**
18
     * Tells the receiver that it should remove any cached information about the given
19
     * element. Either the element has changed or is about to be removed. The row
20
     * that was previously occupied by the element will be filled by a subsequent call
21
     * to update(...) 
22
     * 
23
     * @param element
24
     * @since 3.1
25
     */
26
    public void flushCache(Object element);
27
    
28
    /**
29
     * Updates the contents of the item on the itemIndex row. It should be filled
30
     * in using the given element. 
31
     * 
32
     * The receiver is allowed to cache properties of the given object. If a subsequent
33
     * call to update(...) is made moving the object to a new row, the receiver can
34
     * reuse cached values rather than computing them again. A call to flushCache(...)
35
     * will indicate that cached values may have been changed.
36
     * 
37
     * @param itemIndex
38
     * @param element
39
     * @since 3.1
40
     */
41
    public void update(int itemIndex, Object element);
42
    public void setTotalItems(int total);
43
}
(-)src/org/eclipse/jface/viewers/deferred/IntHashMap.java (+59 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import java.util.HashMap;
14
15
/**
16
 * Represents a map of objects onto ints. This is intended for future optimization:
17
 * using the concrete int class would allow for an implementation that doesn't require
18
 * additional object allocations for Integers. However, the current implementation
19
 * simply delegates to the Java HashMap class. 
20
 * 
21
 * @since 3.1
22
 */
23
public class IntHashMap {
24
    private HashMap map; 
25
    
26
    public IntHashMap(int size, float loadFactor) {
27
        map = new HashMap(size, loadFactor);
28
    }
29
    
30
    public IntHashMap() {
31
        map = new HashMap();
32
    }
33
    
34
    public void remove(Object key) {
35
        map.remove(key);
36
    }
37
    
38
    public void put(Object key, int value) {
39
        map.put(key, new Integer(value));
40
    }
41
    
42
    public int get(Object key) {
43
        return get(key, 0);
44
    }
45
    
46
    public int get(Object key, int defaultValue) {
47
        Integer result = (Integer)map.get(key);
48
        
49
        if (result != null) {
50
            return result.intValue();
51
        }
52
        
53
        return defaultValue;
54
    }
55
    
56
    public boolean containsKey(Object key) {
57
        return map.containsKey(key);
58
    }
59
}
(-)src/org/eclipse/jface/viewers/deferred/LazySortedCollection.java (+1482 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import java.util.Collection;
14
import java.util.Comparator;
15
import java.util.Iterator;
16
17
import org.eclipse.core.runtime.IProgressMonitor;
18
import org.eclipse.jface.util.Assert;
19
20
/**
21
 * This object maintains a collection of elements, sorted by a comparator
22
 * given in the constructor. The collection is lazily sorted, allowing 
23
 * more efficient runtimes for most methods. There are several methods on this
24
 * object that allow objects to be queried by their position in the sorted
25
 * collection.
26
 * 
27
 * <p>
28
 * This is a modified binary search tree. Each subtree has a value, a left and right subtree, 
29
 * a count of the number of children, and a set of unsorted children. 
30
 * Insertion happens lazily. When a new node N is inserted into a subtree T, it is initially 
31
 * added to the set of unsorted children for T without actually comparing it with the value for T. 
32
 * </p>
33
 * <p>
34
 * The unsorted children will remain in the unsorted set until some subsequent operation requires
35
 * us to know the exact set of elements in one of the subtrees. At that time, we partition
36
 * T by comparing all of its unsorted children with T's value and moving them into the left 
37
 * or right subtrees.
38
 * </p>
39
 * 
40
 * @since 3.1
41
 */
42
public class LazySortedCollection {
43
    private final int MIN_CAPACITY = 8;
44
    private Object[] contents = new Object[MIN_CAPACITY];
45
    private int[] leftSubTree = new int[MIN_CAPACITY];
46
    private int[] rightSubTree = new int[MIN_CAPACITY];
47
    private int[] nextUnsorted = new int[MIN_CAPACITY];
48
    private int[] treeSize = new int[MIN_CAPACITY];
49
    private int[] parentTree = new int[MIN_CAPACITY];
50
    private int root = -1;
51
    private int lastNode = 0;
52
    private int firstUnusedNode = -1;
53
        
54
    
55
  
56
    
57
    private static final float loadFactor = 0.75f;
58
    
59
    private IntHashMap objectIndices;
60
    private Comparator comparator;
61
    private static int counter = 0;
62
    public boolean enableRandomization = true;
63
    
64
    // Cancellation support
65
    private IProgressMonitor progressMonitor = null;
66
    // Normally, we return this cached result when determining if
67
    // we're cancelled. However, periodically (whenever cancelCheckCounter reaches 0)
68
    // we will update the cached value by calling progressMonitor.isCancelled().
69
    // The rest of these "cancellation
70
    private boolean cancelled = false;
71
    private int cancelCheckCounter = 0;
72
    // Minimum milliseconds between each call to isCancelled
73
    private static final int CANCEL_CHECK_PERIOD = 50;
74
    // Minimum number of elements to process before calling isCancelled
75
    private static final int MIN_CANCEL_CHECK_FREQUENCY = 10;
76
    // Number of bogus checks before a real 
77
    private int cancelCheckFrequency = MIN_CANCEL_CHECK_FREQUENCY;
78
    // Timestamp of the last call to isCancelled
79
    private long lastCancelCheckTimestamp = 0;
80
81
    
82
    // This object is inserted as the value into any node scheduled for lazy removal
83
    private Object lazyRemovalFlag = new Object() {
84
        public String toString() {
85
            return "Lazy removal flag"; 
86
        }
87
    };
88
    
89
    private final static int DIR_LEFT = 0;
90
    private final static int DIR_RIGHT = 1;
91
    private final static int DIR_UNSORTED = 2;
92
    
93
    // Direction constants indicating root nodes
94
    private final static int DIR_ROOT = 3;
95
    private final static int DIR_UNUSED = 4;
96
       
97
    private final class Edge {
98
        private int startNode;
99
        private int direction;
100
        
101
        private Edge() {
102
            startNode = -1;
103
            direction = -1;
104
        }
105
        
106
        private Edge(int node, int dir) {
107
            startNode = node;
108
            direction = dir;
109
        }
110
111
        private void copy(Edge toCopy) {
112
            startNode = toCopy.startNode;
113
            direction = toCopy.direction;
114
        }
115
        
116
        private int getStart() {
117
            return startNode;
118
        }
119
        
120
        private int getTarget() {
121
            if (startNode == -1) {
122
                if (direction == DIR_UNSORTED) {
123
                    return firstUnusedNode;
124
                } else if (direction == DIR_ROOT) {
125
                    return root;
126
                }
127
                return -1;
128
            }
129
            
130
            if (direction == DIR_LEFT) {
131
                return leftSubTree[startNode];
132
            }
133
            if (direction == DIR_RIGHT) {
134
                return rightSubTree[startNode];
135
            }
136
            return nextUnsorted[startNode];
137
        }
138
        
139
        private boolean isNull() {
140
            return getTarget() == -1;
141
        }
142
     
143
        /**
144
         * Redirects this edge to a new node
145
         * @param newNode
146
         * @since 3.1
147
         */
148
        private void setTarget(int newNode) {            
149
            if (direction == DIR_LEFT) {
150
    	        leftSubTree[startNode] = newNode;
151
            } else if (direction == DIR_RIGHT) {
152
                rightSubTree[startNode] = newNode;
153
            } else if (direction == DIR_UNSORTED) {
154
                nextUnsorted[startNode] = newNode;
155
            } else if (direction == DIR_ROOT) {
156
                root = newNode;
157
            } else if (direction == DIR_UNUSED) {
158
                firstUnusedNode = newNode;
159
            }
160
            
161
	        if (newNode != -1) {
162
	            parentTree[newNode] = startNode;
163
	        }
164
        }
165
        
166
        private void advance(int direction) {
167
            startNode = getTarget();
168
            this.direction = direction;
169
        }
170
    }
171
172
    private void setRootNode(int node) {
173
        root = node;
174
        if (node != -1) {
175
            parentTree[node] = -1;
176
        }
177
    }
178
    
179
    private void setNextUnsorted(int parent, int next) {
180
        nextUnsorted[parent] = next;
181
        if (next != -1) {
182
            parentTree[next] = parent;
183
        }
184
        recomputeTreeSize(parent);
185
    }
186
    
187
    public LazySortedCollection(Comparator c) {
188
        this.comparator = c;
189
    }
190
    
191
    public void print() {
192
        StringBuffer buf = new StringBuffer();
193
        System.out.println("----");
194
        print(0, root);
195
        
196
        System.out.println(buf.toString());
197
    }
198
    
199
    private String indent(int indent) {
200
        String output = "";
201
        
202
        for(int i = 0; i < indent; i++) {
203
            output += "  ";
204
        }
205
        
206
        return output;
207
    }
208
    
209
    public void print(int indent, int nodeToPrint) {
210
        
211
        String indentString = indent(indent);
212
        if (nodeToPrint == -1) {
213
            System.out.println("null\n");
214
            return;
215
        }
216
217
        if (leftSubTree[nodeToPrint] != -1) {
218
	        System.out.println(indentString + "left " + leftSubTree[nodeToPrint]);
219
	        print(indent + 1, leftSubTree[nodeToPrint]);
220
        }
221
222
        System.out.println(indentString + "value {" + contents[nodeToPrint] + "}");
223
        int next = nextUnsorted[nodeToPrint];
224
        while (next != -1) {
225
            System.out.println(indentString + "unsorted " + next + " {" + contents[next] + "}");
226
            next = nextUnsorted[next];
227
        }
228
        
229
        if (rightSubTree[nodeToPrint] != -1) {
230
	        System.out.println(indentString + "right " + rightSubTree[nodeToPrint]);
231
	        print(indent + 1, rightSubTree[nodeToPrint]);
232
        }
233
    }
234
    
235
    public void testInvariants() {
236
        if (enableRandomization) {
237
            return;
238
        }
239
        
240
        testInvariants(root);
241
    }
242
    
243
    private void testInvariants(int node) {
244
        if (node == -1) {
245
            return;
246
        }
247
        
248
        // Get the current tree size (we will later force the tree size
249
        // to be recomputed from scratch -- if everything works properly, then
250
        // there should be no change.
251
        int treeSize = getSubtreeSize(node);
252
253
        int left = leftSubTree[node];
254
        int right = rightSubTree[node];
255
        int unsorted = nextUnsorted[node];
256
        
257
        if (isUnsorted(node)) {
258
            Assert.isTrue(left == -1, "unsorted nodes shouldn't have a left subtree");
259
            Assert.isTrue(right == -1, "unsorted nodes shouldn't have a right subtree");
260
        }
261
        
262
        if (left != -1) {
263
            testInvariants(left);
264
            Assert.isTrue(parentTree[left] == node, "left node has invalid parent pointer");
265
        }
266
        if (right != -1) {
267
            testInvariants(right);
268
            Assert.isTrue(parentTree[right] == node, "right node has invalid parent pointer");            
269
        }
270
271
        int previous = node;
272
        while (unsorted != -1) {
273
            int oldTreeSize = this.treeSize[unsorted];
274
            recomputeTreeSize(unsorted);
275
            
276
            Assert.isTrue(this.treeSize[unsorted] == oldTreeSize, 
277
                    "Invalid node size for unsorted node");
278
            Assert.isTrue(leftSubTree[unsorted] == -1, "unsorted nodes shouldn't have left subtrees");
279
            Assert.isTrue(rightSubTree[unsorted] == -1, "unsorted nodes shouldn't have right subtrees");
280
            Assert.isTrue(parentTree[unsorted] == previous, "unsorted node has invalid parent pointer");
281
            Assert.isTrue(contents[unsorted] != lazyRemovalFlag, "unsorted nodes should not be lazily removed");
282
            previous = unsorted;
283
            unsorted = nextUnsorted[unsorted];
284
        }
285
        
286
        // Note that we've already tested that the child sizes are correct... if our size is
287
        // correct, then recomputing it now should not cause any change.
288
        recomputeTreeSize(node);
289
        
290
        if (treeSize != getSubtreeSize(node)) {
291
            print();
292
        }
293
        
294
        Assert.isTrue(treeSize == getSubtreeSize(node), "invalid tree size");
295
    }
296
    
297
    private boolean isUnsorted(int node) {
298
        int parent = parentTree[node];
299
        
300
        if (parent != -1) {
301
            return nextUnsorted[parent] == node;
302
        }
303
        
304
        return false;
305
    }
306
    
307
    private final boolean isLess(int element1, int element2) {
308
        return comparator.compare(contents[element1], contents[element2]) < 0;
309
    }
310
    
311
    /**
312
     * Adds the given element to the given subtree. Returns the new
313
     * root of the subtree.
314
     * 
315
     * @param subTree index of the subtree to insert elementToAdd into. If -1, 
316
     *                then a new subtree will be created for elementToAdd
317
     * @param idxToAdd index of the element to add to the subtree. If -1, this method
318
     *                 is a NOP.
319
     * @since 3.1
320
     */
321
    private final int addUnsorted(int subTree, int elementToAdd) {
322
        if (elementToAdd == -1) {
323
            return subTree;
324
        }
325
        
326
        if (subTree == -1) {
327
            nextUnsorted[elementToAdd] = -1;
328
            treeSize[elementToAdd] = 1;
329
            return elementToAdd;
330
        }
331
        
332
        // If the subTree is empty (ie: it only contains nodes flagged for lazy removal),
333
        // chop it off.
334
        if (treeSize[subTree] == 0) {
335
            removeSubTree(subTree);
336
            nextUnsorted[elementToAdd] = -1;
337
            treeSize[elementToAdd] = 1;
338
            return elementToAdd;
339
        }
340
        
341
        int addedTreeSize = treeSize[elementToAdd];
342
        
343
        // If neither subtree has any children, add a pseudorandom chance of the
344
        // newly added element becoming the new pivot for this node. Note: instead
345
        // of a real pseudorandom generator, we simply use a counter here.
346
        if (enableRandomization && leftSubTree[subTree] == -1 && rightSubTree[subTree] == -1 
347
                && leftSubTree[elementToAdd] == -1 && rightSubTree[elementToAdd] == -1) {
348
	        counter--;
349
	        
350
	        if (counter % treeSize[subTree] == 0) {
351
	            // Make the new node into the new pivot 
352
	            nextUnsorted[elementToAdd] = subTree;
353
	            parentTree[elementToAdd] = parentTree[subTree];
354
	            parentTree[subTree] = elementToAdd;
355
	            treeSize[elementToAdd] = treeSize[subTree] + 1;
356
	            return elementToAdd;
357
	        }
358
        }
359
        
360
        int oldNextUnsorted = nextUnsorted[subTree];
361
        nextUnsorted[elementToAdd] = oldNextUnsorted;
362
        
363
        if (oldNextUnsorted == -1) {
364
            treeSize[elementToAdd] = 1;
365
        } else {
366
            treeSize[elementToAdd] = treeSize[oldNextUnsorted] + 1;
367
            parentTree[oldNextUnsorted] = elementToAdd;
368
        }
369
        
370
        parentTree[elementToAdd] = subTree;
371
        
372
        nextUnsorted[subTree] = elementToAdd;
373
        treeSize[subTree]++;        
374
        return subTree;
375
    }
376
    
377
    /**
378
     * Returns the number of elements in the collection
379
     * 
380
     * @return
381
     * @since 3.1
382
     */
383
    public int size() {
384
        int result = getSubtreeSize(root);
385
        
386
        testInvariants();
387
        
388
        return result;
389
    }
390
    
391
    /**
392
     * Given a tree and one of its unsorted children, this sorts the child by moving
393
     * it into the left or right subtrees. Returns the next unsorted child or -1 if none
394
     * 
395
     * @param subTree parent tree
396
     * @param toMove child (unsorted) subtree
397
     * @since 3.1
398
     */
399
    private final int partition(int subTree, int toMove) {
400
        int result = nextUnsorted[toMove];
401
        
402
        if (isLess(toMove, subTree)) {
403
            int nextLeft = addUnsorted(leftSubTree[subTree], toMove);
404
            leftSubTree[subTree] = nextLeft;
405
            parentTree[nextLeft] = subTree;
406
        } else {
407
            int nextRight = addUnsorted(rightSubTree[subTree], toMove);
408
            rightSubTree[subTree] = nextRight;
409
            parentTree[nextRight] = subTree;
410
        }
411
        
412
        return result;
413
    }
414
    
415
    /**
416
     * Partitions the given subtree. Moves all unsorted elements at the given node
417
     * to either the left or right subtrees. If the node itself was scheduled for
418
     * lazy removal, this will force the node to be removed immediately. Returns
419
     * the new subTree.
420
     * 
421
     * @param subTree
422
     * @return the replacement node (this may be different from subTree if the subtree
423
     * was replaced during the removal)
424
     * @since 3.1
425
     */
426
    private final int partition(int subTree, FastProgressReporter mon) throws InterruptedException {
427
        if (subTree == -1) {
428
            return -1;
429
        }
430
        
431
        if (contents[subTree] == lazyRemovalFlag) {
432
            subTree = removeNode(subTree);
433
            if (subTree == -1) {
434
                return -1;
435
            }
436
        }
437
        
438
        for (int idx = nextUnsorted[subTree]; idx != -1;) { 
439
            idx = partition(subTree, idx);
440
            nextUnsorted[subTree] = idx;
441
            if (idx != -1) {
442
                parentTree[idx] = subTree;
443
            }
444
            
445
            if (mon.isCanceled()) {
446
                throw new InterruptedException();
447
            }
448
        }
449
        
450
        // At this point, there are no remaining unsorted nodes in this subtree
451
        nextUnsorted[subTree] = -1;
452
        
453
        return subTree;
454
    }
455
    
456
    private final int getSubtreeSize(int subTree) {
457
        if (subTree == -1) {
458
            return 0;
459
        }
460
        return treeSize[subTree];
461
    }
462
    
463
    /**
464
     * Increases the capacity of this collection, if necessary, so that it can hold the given number of
465
     * elements. This cannot be used to reduce the amount of memory used by the collection.
466
     *
467
     * @param newSize
468
     * @since 3.1
469
     */
470
    public final void setCapacity(int newSize) {
471
        if (newSize > contents.length) {
472
            setArraySize(newSize);
473
        }
474
    }
475
    
476
    /**
477
     * Adjusts the capacity of the array.
478
     * 
479
     * @param newCapacity
480
     * @since 3.1
481
     */
482
    private final void setArraySize(int newCapacity) {
483
        Object[] newContents = new Object[newCapacity];
484
        System.arraycopy(contents, 0, newContents, 0, lastNode);
485
        contents = newContents;
486
        
487
        int[] newLeftSubTree = new int[newCapacity];
488
        System.arraycopy(leftSubTree, 0, newLeftSubTree, 0, lastNode);
489
        leftSubTree = newLeftSubTree;
490
        
491
        int[] newRightSubTree = new int[newCapacity];
492
        System.arraycopy(rightSubTree, 0, newRightSubTree, 0, lastNode);
493
        rightSubTree = newRightSubTree;
494
        
495
        int[] newNextUnsorted = new int[newCapacity];
496
        System.arraycopy(nextUnsorted, 0, newNextUnsorted, 0, lastNode);
497
        nextUnsorted = newNextUnsorted;
498
        
499
        int[] newTreeSize = new int[newCapacity];
500
        System.arraycopy(treeSize, 0, newTreeSize, 0, lastNode);
501
        treeSize = newTreeSize;
502
        
503
        int[] newParentTree = new int[newCapacity];
504
        System.arraycopy(parentTree, 0, newParentTree, 0, lastNode);
505
        parentTree = newParentTree;
506
    }
507
    
508
    /**
509
     * Creates a new node with the given value. Returns the index of the newly
510
     * created node.
511
     * 
512
     * @param value
513
     * @return
514
     * @since 3.1
515
     */
516
    private final int createNode(Object value) {
517
        int result = -1;
518
519
        if (firstUnusedNode == -1) {
520
            // If there are no unused nodes from prior removals, then 
521
            // we add a node at the end
522
            result = lastNode;
523
            
524
            // If this would cause the array to overflow, reallocate the array 
525
            if (contents.length <= lastNode) {
526
                setCapacity(lastNode * 2);
527
            }
528
            
529
            lastNode++;
530
        } else {
531
            // Reuse a node from a prior removal
532
            result = firstUnusedNode;
533
            firstUnusedNode = nextUnsorted[result];
534
        }
535
        
536
        contents[result] = value;
537
        treeSize[result] = 1;
538
        
539
        // Clear pointers
540
        leftSubTree[result] = -1;
541
        rightSubTree[result] = -1;
542
        nextUnsorted[result] = -1;
543
        
544
        // As long as we have a hash table of values onto tree indices, incrementally
545
        // update the hash table. Note: the table is only constructed as needed, and it
546
        // is destroyed whenever the arrays are reallocated instead of reallocating it.
547
        if (objectIndices != null) {
548
            objectIndices.put(value, result);
549
        }
550
        
551
        return result;
552
    }
553
    
554
    /**
555
     * Returns the current tree index for the given object.
556
     * 
557
     * @param value
558
     * @return
559
     * @since 3.1
560
     */
561
    private int getObjectIndex(Object value) {
562
        // If we don't have a map of values onto tree indices, build the map now.
563
        if (objectIndices == null) {
564
            int result = -1;
565
            
566
            objectIndices = new IntHashMap((int)(contents.length / loadFactor) + 1, loadFactor);
567
            
568
            for (int i = 0; i < lastNode; i++) {
569
                Object element = contents[i];
570
                
571
                if (element != null && element != lazyRemovalFlag) {
572
                    objectIndices.put(element, i);
573
                    
574
                    if (value == element) {
575
                        result = i;
576
                    }
577
                }
578
            }
579
            
580
            return result;
581
        }
582
        
583
        // If we have a map of values onto tree indices, return the result by looking it up in
584
        // the map
585
        return objectIndices.get(value, -1);
586
    }
587
    
588
    /**
589
     * Redirects any pointers from the original to the replacement. If the replacement
590
     * causes a change in the number of elements in the parent tree, the changes are
591
     * propogated toward the root.
592
     * 
593
     * @param nodeToReplace
594
     * @param replacementNode
595
     * @since 3.1
596
     */
597
    private void replaceNode(int nodeToReplace, int replacementNode) {
598
        int parent = parentTree[nodeToReplace];
599
        
600
        if (parent == -1) {
601
            if (root == nodeToReplace) {
602
                setRootNode(replacementNode);
603
            }
604
        } else {
605
            if (leftSubTree[parent] == nodeToReplace) {
606
                leftSubTree[parent] = replacementNode;
607
            } else if (rightSubTree[parent] == nodeToReplace) {
608
                rightSubTree[parent] = replacementNode;
609
            } else if (nextUnsorted[parent] == nodeToReplace) {
610
                nextUnsorted[parent] = replacementNode;
611
            }
612
            if (replacementNode != -1) {
613
                parentTree[replacementNode] = parent;
614
            }
615
        }
616
    }
617
    
618
    /**
619
     * Returns the Edge whose target is the given node
620
     * @param targetNode
621
     * @return
622
     * @since 3.1
623
     */
624
    private Edge getEdgeTo(int targetNode) {
625
        int parent = parentTree[targetNode];
626
627
        int direction = DIR_LEFT;
628
        
629
        if (parent == -1) {
630
            if (root == targetNode) {
631
                direction = DIR_ROOT;
632
            } else if (firstUnusedNode == targetNode){
633
                direction = DIR_UNUSED;
634
            }
635
        } else {
636
            if (leftSubTree[parent] == targetNode) {
637
                direction = DIR_LEFT;
638
            } else if (rightSubTree[parent] == targetNode) {
639
                direction = DIR_RIGHT;
640
            } else if (nextUnsorted[parent] == targetNode) {
641
                direction = DIR_UNSORTED;
642
            }
643
        }
644
        
645
        return new Edge(parent, direction);
646
    }
647
    
648
    private void recomputeAncestorTreeSizes(int node) {
649
        while (node != -1) {
650
            int oldSize = treeSize[node];
651
            
652
            recomputeTreeSize(node);
653
            
654
            if (treeSize[node] == oldSize) {
655
                break;
656
            }
657
            
658
            node = parentTree[node];
659
        }        
660
    }
661
    
662
    /**
663
     * Recomputes the tree size for the given node.
664
     * 
665
     * @param toRecompute
666
     * @param stopAtNode
667
     * @since 3.1
668
     */
669
    private void recomputeTreeSize(int node) {
670
        if (node == -1) {
671
            return;
672
        }
673
        treeSize[node] = getSubtreeSize(leftSubTree[node])
674
    		+ getSubtreeSize(rightSubTree[node])
675
    		+ getSubtreeSize(nextUnsorted[node])
676
    		+ (contents[node] == lazyRemovalFlag ? 0 : 1); 
677
    }
678
    
679
    /**
680
     * 
681
     * @param toRecompute
682
     * @param whereToStop
683
     * @since 3.1
684
     */
685
    private void forceRecomputeTreeSize(int toRecompute, int whereToStop) {
686
        while (toRecompute != -1 && toRecompute != whereToStop) {
687
	        recomputeTreeSize(toRecompute);
688
	        
689
	        toRecompute = parentTree[toRecompute];
690
        }
691
    }
692
    
693
    /**
694
     * @param subTree
695
     * @since 3.1
696
     */
697
    private void destroyNode(int nodeToDestroy) {
698
        // If we're maintaining a map of values onto tree indices, remove this entry from
699
        // the map
700
        if (objectIndices != null) {
701
            Object oldContents = contents[nodeToDestroy];
702
            if (oldContents != lazyRemovalFlag) {
703
                objectIndices.remove(oldContents);
704
            }
705
        }
706
        
707
        contents[nodeToDestroy] = null;
708
        leftSubTree[nodeToDestroy] = -1;
709
        rightSubTree[nodeToDestroy] = -1;
710
        
711
        if (firstUnusedNode == -1) {
712
            treeSize[nodeToDestroy] = 1;
713
        } else {
714
            treeSize[nodeToDestroy] = treeSize[firstUnusedNode] + 1;
715
            parentTree[firstUnusedNode] = nodeToDestroy;
716
        }
717
        
718
        nextUnsorted[nodeToDestroy] = firstUnusedNode;
719
        
720
        firstUnusedNode = nodeToDestroy; 
721
    }
722
    
723
    /**
724
     * Frees up memory by clearing the list of nodes that have been freed up through removals.
725
     * 
726
     * @since 3.1
727
     */
728
    private final void pack() {
729
        
730
        // If there are no unused nodes, then there is nothing to do
731
        if (firstUnusedNode == -1) {
732
            return;
733
        }
734
        
735
        int reusableNodes = getSubtreeSize(firstUnusedNode);
736
        int nonPackableNodes = lastNode - reusableNodes;
737
        
738
        // Only pack the array if we're utilizing less than 1/4 of the array (note:
739
        // this check is important, or it will change the time bounds for removals)
740
        if (contents.length < MIN_CAPACITY || nonPackableNodes > contents.length / 4) {
741
            return;
742
        }
743
        
744
        // Rather than update the entire map, just null it out. If it is needed,
745
        // it will be recreated lazily later. This will save some memory if the
746
        // map isn't needed, and it takes a similar amount of time to recreate the
747
        // map as to update all the indices.
748
        objectIndices = null;
749
        
750
        // Maps old index -> new index
751
        int[] mapNewIdxOntoOld = new int[contents.length];
752
        int[] mapOldIdxOntoNew = new int[contents.length];
753
        
754
        int nextNewIdx = 0;
755
        // Compute the mapping. Determine the new index for each element 
756
        for (int oldIdx = 0; oldIdx < lastNode; oldIdx++) {
757
            if (contents[oldIdx] != null) {
758
                mapOldIdxOntoNew[oldIdx] = nextNewIdx;
759
                mapNewIdxOntoOld[nextNewIdx] = oldIdx;
760
                nextNewIdx++;
761
            } else {
762
                mapOldIdxOntoNew[oldIdx] = -1;
763
            }
764
        }
765
        
766
        // Make the actual array size double the number of nodes to allow
767
        // for expansion.
768
        int newNodes = nextNewIdx;
769
        int newCapacity = Math.max(newNodes * 2, MIN_CAPACITY);
770
        
771
        // Allocate new arrays
772
        Object[] newContents = new Object[newCapacity];
773
        int[] newTreeSize = new int[newCapacity];
774
        int[] newNextUnsorted = new int[newCapacity];
775
        int[] newLeftSubTree = new int[newCapacity];
776
        int[] newRightSubTree = new int[newCapacity];
777
        int[] newParentTree = new int[newCapacity];
778
        
779
        for (int newIdx = 0; newIdx < newNodes; newIdx++) {
780
            int oldIdx = mapNewIdxOntoOld[newIdx];
781
            newContents[newIdx] = contents[oldIdx];
782
            newTreeSize[newIdx] = treeSize[oldIdx];
783
            
784
            int left = leftSubTree[oldIdx];
785
            if (left == -1) {
786
                newLeftSubTree[newIdx] = -1;
787
            } else {
788
                newLeftSubTree[newIdx] = mapOldIdxOntoNew[left];
789
            }
790
            
791
            int right = rightSubTree[oldIdx];
792
            if (right == -1) {
793
                newRightSubTree[newIdx] = -1;                
794
            } else {
795
                newRightSubTree[newIdx] = mapOldIdxOntoNew[right];
796
            }
797
798
            int unsorted = nextUnsorted[oldIdx];
799
            if (unsorted == -1) {
800
                newNextUnsorted[newIdx] = -1;
801
            } else {
802
                newNextUnsorted[newIdx] = mapOldIdxOntoNew[unsorted];
803
            }
804
            
805
            int parent = parentTree[oldIdx];
806
            if (parent == -1) {
807
                newParentTree[newIdx] = -1;
808
            } else {
809
                newParentTree[newIdx] = mapOldIdxOntoNew[parent];
810
            }
811
        }
812
        
813
        contents = newContents;
814
        nextUnsorted = newNextUnsorted;
815
        treeSize = newTreeSize;
816
        leftSubTree = newLeftSubTree;
817
        rightSubTree = newRightSubTree;
818
        parentTree = newParentTree;
819
        
820
        if (root != -1) {
821
            root = mapOldIdxOntoNew[root];
822
        }
823
        
824
        // All unused nodes have been removed
825
        firstUnusedNode = -1;
826
        lastNode = newNodes;
827
    }
828
    
829
    /**
830
     * Adds the given object to the collection. Runs in O(1) amortized time.
831
     * 
832
     * @param toAdd
833
     * @since 3.1
834
     */
835
    public final void add(Object toAdd) {
836
        // Create the new node
837
        int newIdx = createNode(toAdd);
838
        
839
        // Insert the new node into the root tree
840
        setRootNode(addUnsorted(root, newIdx));
841
        
842
        testInvariants();
843
    }
844
    
845
    /**
846
     * Adds all items from the given collection. 
847
     * 
848
     * @param toAdd
849
     * @since 3.1
850
     */
851
    public final void addAll(Collection toAdd) {
852
        Iterator iter = toAdd.iterator();
853
        while (iter.hasNext()) {
854
            add(iter.next());
855
        }
856
        
857
        testInvariants();
858
    }
859
    
860
    /**
861
     * Adds all items from the given array to the collection
862
     * @param toAdd
863
     * @since 3.1
864
     */
865
    public final void addAll(Object[] toAdd) {
866
        for (int i = 0; i < toAdd.length; i++) {
867
            Object object = toAdd[i];
868
            
869
            add(object);
870
        }
871
        
872
        testInvariants();
873
    }
874
    
875
    /**
876
     * Returns true iff the collection is empty
877
     * 
878
     * @return
879
     * @since 3.1
880
     */
881
    public final boolean isEmpty() {
882
        boolean result = (root == -1);
883
        
884
        testInvariants();
885
        
886
        return result;
887
    }
888
    
889
    /**
890
     * Removes the given object from the collection
891
     * 
892
     * @param toRemove
893
     * @since 3.1
894
     */
895
    public final void remove(Object toRemove) {
896
        internalRemove(toRemove);
897
        
898
        pack();
899
        
900
        testInvariants();
901
    }
902
    
903
    /**
904
     * @param toRemove
905
     * @since 3.1
906
     */
907
    private void internalRemove(Object toRemove) {
908
        int objectIndex = getObjectIndex(toRemove);
909
        
910
        if (objectIndex != -1) {
911
            int parent = parentTree[objectIndex];
912
            lazyRemoveNode(objectIndex);
913
            //Edge parentEdge = getEdgeTo(objectIndex);
914
            //parentEdge.setTarget(lazyRemoveNode(objectIndex));
915
            recomputeAncestorTreeSizes(parent);
916
        }
917
        
918
        //testInvariants();
919
    }
920
921
    public final void removeAll(Collection toRemove, IProgressMonitor mon) throws InterruptedException {
922
        for (Iterator iter = toRemove.iterator(); iter.hasNext();) {
923
            Object next = (Object) iter.next();
924
925
            internalRemove(next);
926
        }
927
        
928
        testInvariants();
929
        
930
        pack();
931
        
932
        testInvariants();
933
    }
934
    
935
    public final void removeAll(Object[] toRemove, IProgressMonitor mon) throws InterruptedException {
936
        for (int i = 0; i < toRemove.length; i++) {
937
            Object object = toRemove[i];
938
            
939
            internalRemove(object);
940
        }
941
        
942
        testInvariants();
943
        
944
        pack();
945
        
946
        testInvariants();
947
    }
948
    
949
    /**
950
     * Retains the first n items in the collection, removing the rest. When
951
     * this method returns, the size of the collection will be n. Note that
952
     * this is a no-op if n > the current size of the collection.
953
     * 
954
     * @param toRetain
955
     * @return
956
     * @since 3.1
957
     */
958
    public final void retainFirst(int n, FastProgressReporter mon) throws InterruptedException {
959
        int sz = size();
960
        
961
        if (n >= sz) {
962
            return;
963
        }
964
        
965
        removeRange(n, sz - n, mon);
966
        
967
        testInvariants();
968
    }
969
    
970
    public final void retainFirst(int n) {
971
        try {
972
            retainFirst(n, new FastProgressReporter());
973
        } catch (InterruptedException e) {
974
        }
975
        
976
        testInvariants();
977
    }
978
    
979
    public final void removeRange(int first, int length) {
980
        try {
981
            removeRange(first, length, new FastProgressReporter());
982
        } catch (InterruptedException e) {
983
        }
984
        
985
        testInvariants();
986
    }
987
    
988
    public final void removeRange(int first, int length, FastProgressReporter mon) throws InterruptedException {
989
    	removeRange(root, first, length, mon);
990
    	
991
    	pack();
992
    	
993
    	testInvariants();
994
    }
995
    
996
    private final void removeRange(int node, int rangeStart, int rangeLength, FastProgressReporter mon) throws InterruptedException {
997
    	if (rangeLength == 0) {
998
    		return;
999
    	}
1000
    	
1001
    	int size = getSubtreeSize(node);
1002
    	
1003
    	if (size <= rangeStart) {
1004
    		return;
1005
    	}
1006
    	
1007
    	// If we can chop off this entire subtree without any sorting, do so.
1008
    	if (rangeStart == 0 && rangeLength >= size) {
1009
    		removeSubTree(node);
1010
    		return;
1011
    	}
1012
    	try {
1013
	    	// Partition any unsorted nodes
1014
    	    node = partition(node, mon);
1015
	    	
1016
	    	int left = leftSubTree[node];
1017
	    	int leftSize = getSubtreeSize(left);
1018
	    	
1019
	    	int toRemoveFromLeft = Math.min(leftSize - rangeStart, rangeLength);
1020
	    	
1021
	    	// If we're removing anything from the left node
1022
	    	if (toRemoveFromLeft >= 0) {
1023
	    		removeRange(leftSubTree[node], rangeStart, toRemoveFromLeft, mon);
1024
	    		
1025
	    		// Check if we're removing from both sides
1026
	    		int toRemoveFromRight = rangeStart + rangeLength - leftSize - 1;
1027
	    		
1028
	    		if (toRemoveFromRight >= 0) {
1029
	    			// Remove from right subtree
1030
	    			removeRange(rightSubTree[node], 0, toRemoveFromRight, mon);
1031
	    			
1032
	    			// ... removing from both sides means we need to remove the node itself too
1033
	    			removeNode(node);
1034
	    			return;
1035
	    		}
1036
	    	} else {
1037
	    		// If removing from the right side only
1038
	    		removeRange(rightSubTree[node], rangeStart - leftSize - 1, rangeLength, mon);
1039
	    	}
1040
    	} finally {
1041
    	    recomputeTreeSize(node);
1042
    	}
1043
    }
1044
    
1045
    /**
1046
     * Prunes the given subtree (and all child nodes, sorted or unsorted).
1047
     * 
1048
     * @param subTree
1049
     * @since 3.1
1050
     */
1051
    private final void removeSubTree(int subTree) {
1052
        if (subTree == -1) {
1053
            return;
1054
        }
1055
        
1056
        // Destroy all unsorted nodes
1057
        for (int next = nextUnsorted[subTree]; next != -1;) {
1058
            int current = next;
1059
            next = nextUnsorted[next];
1060
            
1061
            // Destroy this unsorted node
1062
            destroyNode(current);
1063
        }
1064
        
1065
        // Destroy left subtree
1066
        removeSubTree(leftSubTree[subTree]);
1067
        
1068
        // Destroy right subtree
1069
        removeSubTree(rightSubTree[subTree]);
1070
        
1071
        replaceNode(subTree, -1);
1072
        // Destroy pivot node
1073
        destroyNode(subTree);
1074
    }
1075
    
1076
    /**
1077
     * Schedules the node for removal. If the node can be removed in constant time,
1078
     * it is removed immediately.
1079
     * 
1080
     * @param subTree
1081
     * @return the replacement node
1082
     * @since 3.1
1083
     */
1084
    private final int lazyRemoveNode(int subTree) {
1085
        int left = leftSubTree[subTree];
1086
        int right = rightSubTree[subTree];
1087
1088
        // If this is a leaf node, remove it immediately
1089
        if (left == -1 && right == -1) {
1090
            int result = nextUnsorted[subTree];
1091
            replaceNode(subTree, result);
1092
            destroyNode(subTree);
1093
            return result;
1094
        }
1095
        
1096
        // Otherwise, flag it for future removal
1097
        Object value = contents[subTree];
1098
        contents[subTree] = lazyRemovalFlag;
1099
        treeSize[subTree]--;
1100
        if (objectIndices != null) {
1101
            objectIndices.remove(value);
1102
        }
1103
        
1104
        return subTree;
1105
    }
1106
    
1107
    /**
1108
     * Removes the given subtree, replacing it with one of its children.
1109
     * Returns the new root of the subtree
1110
     * 
1111
     * @param subTree
1112
     * @return
1113
     * @since 3.1
1114
     */
1115
    private final int removeNode(int subTree) {
1116
        int left = leftSubTree[subTree];
1117
        int right = rightSubTree[subTree];
1118
        
1119
        if (left == -1 || right == -1) {
1120
            int result = -1;
1121
            
1122
            if (left == -1 && right == -1) {
1123
                // If this is a leaf node, replace it with its first unsorted child
1124
                result = nextUnsorted[subTree];
1125
            } else {
1126
                // Either the left or right child is missing -- replace with the remaining child  
1127
                if (left == -1) {
1128
                    result = right;
1129
                } else {
1130
                    result = left;
1131
                }
1132
1133
                try {
1134
                    result = partition(result, new FastProgressReporter());
1135
                } catch (InterruptedException e) {
1136
                    
1137
                }
1138
                if (result == -1) {
1139
                    result = nextUnsorted[subTree];
1140
                } else {
1141
	                int unsorted = nextUnsorted[subTree];
1142
	                nextUnsorted[result] = unsorted;
1143
	                int additionalNodes = 0;
1144
	                if (unsorted != -1) {
1145
	                    parentTree[unsorted] = result;
1146
	                    additionalNodes = treeSize[unsorted];
1147
	                }
1148
	                treeSize[result] += additionalNodes;
1149
                }
1150
            }
1151
            
1152
            replaceNode(subTree, result);
1153
            destroyNode(subTree);
1154
            return result;
1155
        }
1156
                
1157
        // Find the edges that lead to the next-smallest and
1158
        // next-largest nodes
1159
        Edge nextSmallest = new Edge(subTree, DIR_LEFT);
1160
        while (!nextSmallest.isNull()) {
1161
            nextSmallest.advance(DIR_RIGHT);
1162
        }
1163
        
1164
        Edge nextLargest = new Edge(subTree, DIR_RIGHT);
1165
        while (!nextLargest.isNull()) {
1166
            nextLargest.advance(DIR_LEFT);
1167
        }
1168
        
1169
        // Index of the replacement node
1170
        int replacementNode = -1;
1171
        
1172
        // Count of number of nodes moved to the right
1173
        
1174
        int leftSize = getSubtreeSize(left);
1175
        int rightSize = getSubtreeSize(right);
1176
        
1177
        // Swap with a child from the larger subtree
1178
        if (leftSize > rightSize) {
1179
            replacementNode = nextSmallest.getStart();
1180
1181
            // Move any unsorted nodes that are larger than the replacement node into
1182
            // the left subtree of the next-largest node
1183
            Edge unsorted = new Edge(replacementNode, DIR_UNSORTED);
1184
            while (!unsorted.isNull()) {
1185
                int target = unsorted.getTarget();
1186
                
1187
                if (!isLess(target, replacementNode)) {
1188
                    unsorted.setTarget(nextUnsorted[target]);
1189
                    nextLargest.setTarget(addUnsorted(nextLargest.getTarget(), target));
1190
                } else {
1191
                    unsorted.advance(DIR_UNSORTED);
1192
                }
1193
            }
1194
            
1195
            forceRecomputeTreeSize(unsorted.getStart(), replacementNode);
1196
            forceRecomputeTreeSize(nextLargest.getStart(), subTree);
1197
        } else {
1198
            replacementNode = nextLargest.getStart();
1199
1200
            // Move any unsorted nodes that are smaller than the replacement node into
1201
            // the right subtree of the next-smallest node
1202
            Edge unsorted = new Edge(replacementNode, DIR_UNSORTED);
1203
            while (!unsorted.isNull()) {
1204
                int target = unsorted.getTarget();
1205
                
1206
                if (isLess(target, replacementNode)) {
1207
                    unsorted.setTarget(nextUnsorted[target]);
1208
                    nextSmallest.setTarget(addUnsorted(nextSmallest.getTarget(), target));
1209
                } else {
1210
                    unsorted.advance(DIR_UNSORTED);
1211
                }
1212
            }
1213
            
1214
            forceRecomputeTreeSize(unsorted.getStart(), replacementNode);
1215
            forceRecomputeTreeSize(nextSmallest.getStart(), subTree);
1216
        }
1217
        
1218
        // Now all the affected treeSize[...] elements should be updated to reflect the
1219
        // unsorted nodes that moved. Swap nodes. 
1220
        Object replacementContent = contents[replacementNode];
1221
        contents[replacementNode] = contents[subTree];
1222
        contents[subTree] = replacementContent;
1223
        
1224
        if (objectIndices != null) {
1225
            objectIndices.put(replacementContent, subTree);
1226
            // Note: currently we don't bother updating the index of the replacement
1227
            // node since we're going to remove it immediately afterwards and there's
1228
            // no good reason to search for the index in a method that was given the
1229
            // index as a parameter...
1230
            
1231
            // objectIndices.put(contents[replacementNode], replacementNode)
1232
        }
1233
        
1234
        int replacementParent = parentTree[replacementNode]; 
1235
        
1236
        replaceNode(replacementNode, removeNode(replacementNode));
1237
        //Edge parentEdge = getEdgeTo(replacementNode);
1238
        //parentEdge.setTarget(removeNode(replacementNode));
1239
1240
        forceRecomputeTreeSize(replacementParent, subTree);
1241
        recomputeTreeSize(subTree);
1242
        
1243
        //testInvariants();
1244
        
1245
        return subTree;
1246
    }
1247
   
1248
    /**
1249
     * Removes all elements from the collection
1250
     * 
1251
     * @since 3.1
1252
     */
1253
    public final void clear() {
1254
        lastNode = 0;
1255
        setArraySize(MIN_CAPACITY);
1256
        root = -1;
1257
        firstUnusedNode = -1;
1258
        objectIndices = null;
1259
        
1260
        testInvariants();
1261
    }
1262
    
1263
    public Comparator getComparator() {
1264
        return comparator;
1265
    }
1266
    
1267
    /**
1268
     * Fills in an array of size n with the n smallest elements from the collection.
1269
     * Note that the returned elements are not sorted, however these would be the first
1270
     * elements in the list if the collection were sorted. Returns the number of elements
1271
     * inserted into the result array (this will always equal the length of the array if
1272
     * the array is smaller than the size of the collection).
1273
     * 
1274
     * <p>
1275
     * When returning a sorted array, the algorithm runs in O(s + n * lg (n)) and when returning
1276
     * an unsorted array, the algorithm runs in O(s + n) time, where s is the size of the collection
1277
     * and n is the size of the output. The partial sort order computed in previous calls to
1278
     * this method is remembered and used to speed up subsequent calls. That is, if this method is
1279
     * called twice on the same range, its runtime will be closer to O(n) on subsequent calls (regardless
1280
     * of sorting).
1281
     * </p>
1282
     * 
1283
     * @param result 
1284
     * @since 3.1
1285
     */
1286
    public final int getFirst(Object[] result, boolean sorted, FastProgressReporter mon) throws InterruptedException {
1287
        int returnValue = getRange(result, 0, sorted, mon);
1288
        
1289
        testInvariants();
1290
        
1291
        return returnValue;
1292
    }
1293
    
1294
    public final int getFirst(Object[] result, boolean sorted) {
1295
        int returnValue = 0;
1296
        
1297
        try {
1298
            returnValue = getFirst(result, sorted, new FastProgressReporter());
1299
        } catch (InterruptedException e) {
1300
        }
1301
        
1302
        testInvariants();
1303
        
1304
        return returnValue;
1305
    }
1306
    
1307
    /**
1308
     * Given a position defined by k and an array of size n, this fills in the array with
1309
     * the kth smallest element through to the (k+n)th smallest element. For example, 
1310
     * getRange(myArray, 10) would fill in myArray starting with the 10th smallest item
1311
     * in the collection.
1312
     * 
1313
     * <p>
1314
     * When returning a sorted array, the algorithm runs in O(s + n * lg (n)) and when returning
1315
     * an unsorted array, the algorithm runs in O(s + n) time, where s is the size of the collection
1316
     * and n is the size of the output. The partial sort order computed in previous calls to
1317
     * this method is remembered and used to speed up subsequent calls. That is, if this method is
1318
     * called twice on the same range, its runtime will be closer to O(n) on subsequent calls (regardless
1319
     * of sorting).
1320
     * </p>
1321
     *   
1322
     * Returns the number of elements
1323
     * inserted into the result array (this will always equal the length of the array if
1324
     * the array is smaller than the size of the collection).
1325
     * 
1326
     * @param result 
1327
     * @since 3.1
1328
     */
1329
    public final int getRange(Object[] result, int rangeStart, boolean sorted, FastProgressReporter mon) throws InterruptedException {
1330
        return getRange(result, 0, rangeStart, root, sorted, mon);
1331
    }
1332
    
1333
    public final int getRange(Object[] result, int rangeStart, boolean sorted) {
1334
        int returnValue = 0;
1335
        
1336
        try {
1337
            returnValue = getRange(result, rangeStart, sorted, new FastProgressReporter());
1338
        } catch (InterruptedException e) {
1339
        }
1340
        
1341
        testInvariants();
1342
        
1343
        return returnValue;
1344
    }
1345
    
1346
    /**
1347
     * Returns the item at the given index
1348
     * 
1349
     * @param index
1350
     * @return
1351
     * @since 3.1
1352
     */
1353
    public final Object getItem(int index) {
1354
        Object[] result = new Object[1];
1355
        try {
1356
            getRange(result, index, false, new FastProgressReporter());
1357
        } catch (InterruptedException e) {
1358
            // shouldn't happen
1359
        }
1360
        Object returnValue = result[0];
1361
        
1362
        testInvariants();
1363
        
1364
        return returnValue;
1365
    }
1366
    
1367
    public final Object[] getItems(boolean sorted) {
1368
        Object[] result = new Object[size()];
1369
        
1370
        getRange(result, 0, sorted);
1371
        
1372
        return result;
1373
    }
1374
    
1375
    private final int getRange(Object[] result, int resultIdx, int rangeStart, int node, boolean sorted, FastProgressReporter mon) throws InterruptedException {
1376
        if (node == -1) {
1377
            return 0;
1378
        }
1379
1380
        int availableSpace = result.length - resultIdx;
1381
        
1382
        // If we're asking for all children of the current node, simply call getChildren
1383
        if (rangeStart == 0) {
1384
            if (treeSize[node] <= availableSpace) {
1385
                return getChildren(result, resultIdx, node, sorted, mon);
1386
            }
1387
        }
1388
        
1389
        node = partition(node, mon);
1390
        if (node == -1) {
1391
            return 0;
1392
        }
1393
        
1394
        int inserted = 0;
1395
        
1396
        int numberLessThanNode = getSubtreeSize(leftSubTree[node]);
1397
                
1398
        if (rangeStart < numberLessThanNode) {
1399
            if (inserted < availableSpace) {
1400
                inserted += getRange(result, resultIdx, rangeStart, leftSubTree[node], sorted, mon);
1401
            }
1402
        }
1403
        
1404
        if (rangeStart <= numberLessThanNode) {
1405
	        if (inserted < availableSpace) {
1406
	            result[resultIdx + inserted] = contents[node];
1407
	            inserted++;
1408
	        }	        
1409
        } 
1410
        
1411
        if (inserted < availableSpace) {
1412
            inserted += getRange(result, resultIdx + inserted,
1413
                Math.max(rangeStart - numberLessThanNode - 1, 0), rightSubTree[node], sorted, mon);
1414
        }
1415
        
1416
        return inserted;
1417
    }
1418
    
1419
    /**
1420
     * Fills in the available space in the given array with all children of the given node.
1421
     * 
1422
     * @param result 
1423
     * @param resultIdx index in the result array where we will begin filling in children
1424
     * @param node
1425
     * @return the number of children added to the array
1426
     * @since 3.1
1427
     */
1428
    private final int getChildren(Object[] result, int resultIdx, int node, boolean sorted, FastProgressReporter mon) throws InterruptedException {
1429
        if (node == -1) {
1430
            return 0;
1431
        }
1432
        
1433
        int tempIdx = resultIdx;
1434
        
1435
        if (sorted) {
1436
            node = partition(node, mon);
1437
            if (node == -1) {
1438
                return 0;
1439
            }
1440
        }
1441
        
1442
        // Add child nodes smaller than this one
1443
        if (tempIdx < result.length) {
1444
            tempIdx += getChildren(result, tempIdx, leftSubTree[node], sorted, mon);
1445
        }
1446
        
1447
        // Add the pivot
1448
        if (tempIdx < result.length) {
1449
            Object value = contents[node];
1450
            if (value != lazyRemovalFlag) {
1451
                result[tempIdx++] = value;
1452
            }
1453
        }
1454
        
1455
        // Add child nodes larger than this one
1456
        if (tempIdx < result.length) {
1457
            tempIdx += getChildren(result, tempIdx, rightSubTree[node], sorted, mon);
1458
        }
1459
        
1460
        // Add unsorted children (should be empty if the sorted flag was true)
1461
        for (int unsortedNode = nextUnsorted[node]; unsortedNode != -1 && tempIdx < result.length; 
1462
        	unsortedNode = nextUnsorted[unsortedNode]) {
1463
            
1464
            result[tempIdx++] = contents[unsortedNode];
1465
        }
1466
        
1467
        return tempIdx - resultIdx;
1468
    }
1469
1470
    /**
1471
     * @param item
1472
     * @return
1473
     * @since 3.1
1474
     */
1475
    public boolean contains(Object item) {
1476
        boolean returnValue = (getObjectIndex(item) != -1);
1477
        
1478
        testInvariants();
1479
        
1480
        return returnValue;
1481
    }
1482
}
(-)src/org/eclipse/jface/viewers/deferred/SynchronousTableManager.java (+96 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.Comparator;
16
17
import org.eclipse.core.runtime.IProgressMonitor;
18
19
/**
20
 * @since 3.1
21
 */
22
public class SynchronousTableManager extends TableManager {
23
    
24
    private int limit = 100;
25
    private IConcurrentContentProvider content;
26
    private IConcurrentTable table;
27
    private Comparator sortOrder;
28
    private Collection allElement = new ArrayList();
29
    private Object[] sentElements = new Object[0];
30
    private int rangeStart = 0;
31
    private int rangeLength = 0;
32
    
33
    public void SynchronousTableManager(IConcurrentTable table, IConcurrentContentProvider contentProvider, Comparator sortOrder) {
34
        content = contentProvider;
35
        this.table = table;
36
        this.sortOrder = sortOrder;
37
    }
38
    
39
    /* (non-Javadoc)
40
     * @see org.eclipse.jface.viewers.deferred.TableManager#add(java.util.Collection, org.eclipse.core.runtime.IProgressMonitor)
41
     */
42
    public void add(Collection items, IProgressMonitor mon) {
43
        
44
    }
45
46
    /* (non-Javadoc)
47
     * @see org.eclipse.jface.viewers.deferred.TableManager#remove(java.util.Collection, org.eclipse.core.runtime.IProgressMonitor)
48
     */
49
    public void remove(Collection items, IProgressMonitor mon) {
50
51
    }
52
53
    /* (non-Javadoc)
54
     * @see org.eclipse.jface.viewers.deferred.TableManager#update(java.util.Collection, org.eclipse.core.runtime.IProgressMonitor)
55
     */
56
    public void update(Collection items, IProgressMonitor mon) {
57
58
    }
59
60
    /* (non-Javadoc)
61
     * @see org.eclipse.jface.viewers.deferred.TableManager#refresh(org.eclipse.core.runtime.IProgressMonitor)
62
     */
63
    public void refresh(IProgressMonitor monitor) {
64
65
    }
66
67
    /* (non-Javadoc)
68
     * @see org.eclipse.jface.viewers.deferred.TableManager#setSortOrder(java.util.Comparator)
69
     */
70
    public void setSortOrder(Comparator sorter) {
71
72
    }
73
74
    /* (non-Javadoc)
75
     * @see org.eclipse.jface.viewers.deferred.TableManager#setRangeOfInterest(int, int)
76
     */
77
    public void setRangeOfInterest(int start, int length) {
78
        rangeStart = start;
79
        rangeLength = length;
80
    }
81
82
    /* (non-Javadoc)
83
     * @see org.eclipse.jface.viewers.deferred.TableManager#setLimit(int)
84
     */
85
    public void setLimit(int maxTableSize) {
86
87
    }
88
89
    /* (non-Javadoc)
90
     * @see org.eclipse.jface.viewers.deferred.TableManager#getLimit()
91
     */
92
    public int getLimit() {
93
        return 0;
94
    }
95
96
}
(-)src/org/eclipse/jface/viewers/deferred/TableManager.java (+32 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.viewers.deferred;
12
13
import java.util.Collection;
14
import java.util.Comparator;
15
16
import org.eclipse.core.runtime.IProgressMonitor;
17
18
/**
19
 * 
20
 * Not intended to be subclassed by clients
21
 * @since 3.1
22
 */
23
public abstract class TableManager {
24
	public abstract void add(Collection items, IProgressMonitor mon);
25
	public abstract void remove(Collection items, IProgressMonitor mon);	
26
	public abstract void update(Collection items, IProgressMonitor mon);
27
	public abstract void refresh(IProgressMonitor monitor);
28
    public abstract void setSortOrder(Comparator sorter);
29
	public abstract void setRangeOfInterest(int start, int length);	
30
	public abstract void setLimit(int maxTableSize);
31
	public abstract int getLimit();
32
}

Return to bug 72358