Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ILazyContentProvider updating everything at each viewer.setItemCount()

Greetings dear Stackoverflowians,

A couple of months ago I was dealing with a ILazyTreeContentProvider, and finally fixed it as per Eclipse RCP - ILazyTreeContentProvider implementation is unexpectedly eager

But I am facing the exact same problem with a ILazyContentProvider, and despite having followed similar steps as with the tree, I am at a loss. In this table I am adding around 1000 elements per second in the table, and triggering a refresh via setItemCount() on the viewer every 100 ms.

The window size is smaller than 100 rows, and hence the updateElement() method should not start from the first index every time I call setItemCount() on the viewer.

Unfortunately, though, it does. It updates from 0 till the last index, each time.

Here's the code:

package manyelementscontentprovider;

import java.util.List;
import java.util.Vector;
import org.eclipse.jface.viewers.ILazyContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class LargeDataSetTable {
    private class MyContentProvider implements ILazyContentProvider {
        private TableViewer viewer;
        public List<MyEntity> elements;
        private int lastIndex=0;
        public MyContentProvider(TableViewer viewer) {
            this.viewer = viewer;
        }

        public void dispose() {

        }

        @SuppressWarnings("unchecked")
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            this.elements = (List<MyEntity>) newInput;
        }
        @Override
        public void updateElement(int index) {
            System.out.println(index);
            if (!viewer.isBusy())
                viewer.replace(elements.get(index), index);
        }

    }

    public static class MyEntity {
        public int counter;
        public MyEntity(int counter) {
            this.counter = counter;
        }

        public String toString() {
            return "Item " + this.counter;
        }
    }

    List<MyEntity> model;

    private int counter;
    private Display display;
    private TableViewer v;
    public LargeDataSetTable(Shell shell, Display display) {
        model = createModel();
        this.display=display;
        v= new TableViewer(shell, SWT.VIRTUAL);
        v.setLabelProvider(new LabelProvider());
        v.setContentProvider(new MyContentProvider(v));
        v.setInput(null);
        v.setUseHashlookup(true);
        counter = 0;

        v.setInput(model);
        v.setItemCount(model.size());

        v.getTable().setLinesVisible(true);
    }
    private void startSomeShit() {
        final Runnable gooeiUpdate = new Runnable() {

            @Override
            public void run() {
                long timeA = System.currentTimeMillis();
                v.setItemCount(counter);                            
                v.setSelection( new StructuredSelection( model.get(counter-1) ), true );
                v.setSelection(null);
                long timeB = System.currentTimeMillis();
                System.out.println("Paint lasted:"+(timeB-timeA));
            }

        };

        Runnable addThingsToModel = new Runnable() {

            public void run() {
                long currentTime=System.currentTimeMillis();
                long howManyGotIn =0;
                while (counter<4000000) {
                    for (int i = 0; i< 10; i++){
                        final MyEntity m = new MyEntity(counter);
                        model.add(m);
                        counter++;
                    }

                    if (System.currentTimeMillis()-currentTime>100) {
                        howManyGotIn=counter - howManyGotIn;
                        display.syncExec(gooeiUpdate);
                        currentTime=System.currentTimeMillis();
                        System.out.println("How many got in = "+howManyGotIn);
                        howManyGotIn=counter;
                    }

                    try {
                        Thread.sleep(0,25);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread th = new Thread(addThingsToModel);
        th.start();
    }
    private List<MyEntity> createModel() {
        List<MyEntity> list = new Vector<MyEntity>(4000000);
        return list;
    }
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());
        LargeDataSetTable viewerCica = new LargeDataSetTable(shell,display);
        shell.open();
        viewerCica.startSomeShit();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }
}

Any sort of suggestions, opinions and options are very appreciated. You guys rock!

like image 471
Vlad Ilie Avatar asked Mar 21 '23 17:03

Vlad Ilie


2 Answers

The javadoc for

TableViewer.setSelection(ISelection selection, boolean reveal)

states the following:

Sets a new selection for this viewer and optionally makes it visible. The TableViewer implementation of this method is inefficient for the ILazyContentProvider as lookup is done by indices rather than elements and may require population of the entire table in worse case.

Use Table#setSelection(int[] indices) and Table#showSelection() if you wish to set selection more efficiently when using a ILazyContentProvider.

Therefore, you could write something like this:

v.getTable().setSelection(counter - 1);
v.getTable().showSelection();

Using this approach, the paint operation takes an average time of 10ms.

like image 52
Danut Avatar answered Apr 07 '23 13:04

Danut


Here is some code snippet from the AbstractTableViewer#virtualSetSelectionToWidget(List list, boolean reveal), which is called, when you use v.setSelection(new StructuredSelection(model.get(counter - 1)), true);:

if (getContentProvider() instanceof ILazyContentProvider) {
            ILazyContentProvider provider = (ILazyContentProvider) getContentProvider();

            // Now go through it again until all is done or we are no longer
            // virtual
            // This may create all items so it is not a good
            // idea in general.
            // Use #setSelection (int [] indices,boolean reveal) instead
            for (int i = 0; virtualElements.size() > 0 && i < doGetItemCount(); i++) {
                provider.updateElement(i);
                Item item = doGetItem(i);
                if (virtualElements.contains(item.getData())) {
                    indices[count++] = i;
                    virtualElements.remove(item.getData());
                    if (firstItem == null) {
                        firstItem = item;
                    }
                }
            }
        }

as you can see it always iterates over all elements (confessing, that It might not be the best idea), as per Eclipse 3.x. Tree viewer has different implementation (which is actually understandable, that there you actually have kind of visibility levels and in table you don't have those).

I think, that refreshing of elements in general could be handled without dependency on content provider, so that only visible elements are refreshed (at least on demand).

like image 28
Alex K. Avatar answered Apr 07 '23 14:04

Alex K.