diff options
Diffstat (limited to 'dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java')
-rw-r--r-- | dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java new file mode 100644 index 00000000000..bbf9bb8b921 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src_preprocess/org/eclipse/cdt/examples/dsf/dataviewer/AsyncDataViewer.java @@ -0,0 +1,276 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +//#ifdef exercises +package org.eclipse.cdt.examples.dsf.dataviewer; +//#else +//#package org.eclipse.cdt.examples.dsf.dataviewer.answers; +//#endif + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.ui.concurrent.DisplayDsfExecutor; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; + +/** + * Data viewer based on a table, which reads data using asynchronous methods. + * <p> + * This viewer implements the {@link ILazyContentProvider} interface + * which is used by the JFace TableViewer class to populate a Table. This + * interface contains separate asynchronous methods for requesting the count + * and values for individual indexes, which neatly correspond to the methods + * in {@link IDataGenerator}. As an added optimization, this viewer + * implementation checks for the range of visible items in the view upon each + * request, and it cancels old requests which scroll out of view but have not + * been completed yet. However, it is up to the data generator implementation + * to check the canceled state of the requests and ignore them. + * </p> + */ +@ConfinedToDsfExecutor("fDisplayExecutor") +public class AsyncDataViewer + implements ILazyContentProvider, IDataGenerator.Listener +{ + // Executor to use instead of Display.asyncExec(). + @ThreadSafe + final private DsfExecutor fDisplayExecutor; + + // The viewer and generator that this content provider using. + final private TableViewer fViewer; + final private IDataGenerator fDataGenerator; + + // Fields used in request cancellation logic. + private List<ValueDataRequestMonitor> fItemDataRequestMonitors = new LinkedList<ValueDataRequestMonitor>(); + private Set<Integer> fIndexesToCancel = new HashSet<Integer>(); + private int fCancelCallsPending = 0; + + public AsyncDataViewer(TableViewer viewer, IDataGenerator generator) { + fViewer = viewer; + fDisplayExecutor = DisplayDsfExecutor.getDisplayDsfExecutor(fViewer.getTable().getDisplay()); + fDataGenerator = generator; + fDataGenerator.addListener(this); + } + + public void dispose() { + fDataGenerator.removeListener(this); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Set the initial count to the viewer after the input is set. + queryItemCount(); + } + + public void updateElement(final int index) { + // Calculate the visible index range. + final int topIdx = fViewer.getTable().getTopIndex(); + final int botIdx = topIdx + getVisibleItemCount(topIdx); + + // Request the item for the given index. + queryValue(index); + + // Invoke a cancel task with a delay. The delay allows multiple cancel + // calls to be combined together improving performance of the viewer. + fCancelCallsPending++; + fDisplayExecutor.schedule( + new Runnable() { public void run() { + cancelStaleRequests(topIdx, botIdx); + }}, + 1, TimeUnit.MILLISECONDS); + } + + private int getVisibleItemCount(int top) { + Table table = fViewer.getTable(); + int itemCount = table.getItemCount(); + return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top); + } + + @ThreadSafe + public void countChanged() { + queryItemCount(); + } + + @ThreadSafe + public void valuesChanged(final Set<Integer> indexes) { + // Mark the changed items in table viewer as dirty, this will + // trigger update requests for these indexes if they are + // visible in the viewer. + final TableViewer tableViewer = fViewer; + fDisplayExecutor.execute( new Runnable() { + public void run() { + if (!fViewer.getTable().isDisposed()) { + for (Integer index : indexes) { + tableViewer.clear(index); + } + } + }}); + } + + + private void queryItemCount() { + // Request count from data provider. When the count is returned, we + // have to re-dispatch into the display thread to avoid calling + // the table widget on the DSF dispatch thread. + fIndexesToCancel.clear(); + fDataGenerator.getCount( + // Use the display executor to construct the request monitor, this + // will cause the handleCompleted() method to be automatically + // called on the display thread. + new DataRequestMonitor<Integer>(fDisplayExecutor, null) { + @Override + protected void handleCompleted() { + if (!fViewer.getTable().isDisposed()) { + fViewer.setItemCount(getData()); + fViewer.getTable().clearAll(); + } + } + }); + + } + + + // Dedicated class for data item requests. This class holds the index + // argument so it can be examined when canceling stale requests. + private class ValueDataRequestMonitor extends DataRequestMonitor<String> { + + /** Index is used when canceling stale requests. */ + int fIndex; + + ValueDataRequestMonitor(int index) { + super(fDisplayExecutor, null); + fIndex = index; + } + + @Override + protected void handleCompleted() { + fItemDataRequestMonitors.remove(this); + + // Check if the request completed successfully, otherwise ignore it. + if (isSuccess()) { + if (!fViewer.getTable().isDisposed()) { + fViewer.replace(getData(), fIndex); + } + } + } + } + + private void queryValue(final int index) { + ValueDataRequestMonitor rm = new ValueDataRequestMonitor(index); + fItemDataRequestMonitors.add(rm); + fDataGenerator.getValue(index, rm); + } + + private void cancelStaleRequests(int topIdx, int botIdx) { + // Decrement the count of outstanding cancel calls. + fCancelCallsPending--; + + // Must check again, in case disposed while re-dispatching. + if (fDataGenerator == null || fViewer.getTable().isDisposed()) return; + + // Go through the outstanding requests and cancel any that + // are not visible anymore. + for (Iterator<ValueDataRequestMonitor> itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) { + ValueDataRequestMonitor item = itr.next(); + if (item.fIndex < topIdx || item.fIndex > botIdx) { + // Set the item to canceled status, so that the data provider + // will ignore it. + item.cancel(); + + // Add the item index to list of indexes that were canceled, + // which will be sent to the table widget. + fIndexesToCancel.add(item.fIndex); + + // Remove the item from the outstanding cancel requests. + itr.remove(); + } + } + if (!fIndexesToCancel.isEmpty() && fCancelCallsPending == 0) { + Set<Integer> canceledIdxs = fIndexesToCancel; + fIndexesToCancel = new HashSet<Integer>(); + + // Clear the indexes of the canceled request, so that the + // viewer knows to request them again when needed. + // Note: clearing using TableViewer.clear(int) seems very + // inefficient, it's better to use Table.clear(int[]). + int[] canceledIdxsArray = new int[canceledIdxs.size()]; + int i = 0; + for (Integer index : canceledIdxs) { + canceledIdxsArray[i++] = index; + } + fViewer.getTable().clear(canceledIdxsArray); + } + } + + + public static void main(String[] args) { + // Create the shell to hold the viewer. + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new GridLayout()); + GridData data = new GridData(GridData.FILL_BOTH); + shell.setLayoutData(data); + Font font = new Font(display, "Courier", 10, SWT.NORMAL); + + // Create the table viewer. + TableViewer tableViewer = new TableViewer(shell, SWT.BORDER | SWT.VIRTUAL); + tableViewer.getControl().setLayoutData(data); + + // Create the data generator. + final IDataGenerator generator = new DataGeneratorWithExecutor(); + + // Create the content provider which will populate the viewer. + AsyncDataViewer contentProvider = new AsyncDataViewer(tableViewer, generator); + tableViewer.setContentProvider(contentProvider); + tableViewer.setInput(new Object()); + + // Open the shell and service the display dispatch loop until user + // closes the shell. + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + + // The IDataGenerator.shutdown() method is asynchronous, this requires + // using a query again in order to wait for its completion. + Query<Object> shutdownQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + generator.shutdown(rm); + } + }; + ImmediateExecutor.getInstance().execute(shutdownQuery); + try { + shutdownQuery.get(); + } catch (Exception e) {} + + // Shut down the display. + font.dispose(); + display.dispose(); + } +} |