Package org.locationtech.udig.ui

Source Code of org.locationtech.udig.ui.FeatureTableContentProvider

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2004, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.ui;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
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.widgets.Display;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.PlatformUI;
import org.geotools.data.FeatureEvent;
import org.geotools.data.FeatureListener;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.locationtech.udig.core.IProvider;
import org.locationtech.udig.core.feature.AdaptableFeatureCollection;
import org.locationtech.udig.internal.ui.Trace;
import org.locationtech.udig.internal.ui.UiPlugin;
import org.locationtech.udig.ui.internal.Messages;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

/**
* ContentProvider that agressively listens to FeatureCollection (using FeatureSource FeatureEvents).
*
* @author Jody Garnett
*/
class FeatureTableContentProvider implements ILazyContentProvider, IProvider<Collection<SimpleFeature>> {

    private static final IProgressMonitor NULL = new NullProgressMonitor();
    /** FeatureContentProvider owningFeatureTableControl field */
    private final FeatureTableControl owningFeatureTableControl;

    private volatile IProgressMonitor monitor = NULL;
    private IProvider<IProgressMonitor> progressMonitorProvider;

    public FeatureTableContentProvider( FeatureTableControl control, IProvider<IProgressMonitor> progressMonitorProvider ) {
        owningFeatureTableControl = control;
        this.progressMonitorProvider=progressMonitorProvider;
    }
   
    /**
     * Listens for changes in FeatureSource, updates the viewer if content provider changes.
     * <p>
     * If we were extra cool we could filter the incoming FeatureEvents based on if they
     * are actually included in our ContentProvider. As it is we will refresh everything.
     */
    private FeatureListener listener = new FeatureListener() {

        @Override
        public void changed(FeatureEvent event) {
            if (listener == null) {
                event.getFeatureSource().removeFeatureListener(this);
                return;
            }

            TableViewer viewer = FeatureTableContentProvider.this.owningFeatureTableControl
                    .getViewer();

            switch (event.getType()) {
            case ADDED:
                try {
                    SimpleFeatureCollection changed = (SimpleFeatureCollection) event
                            .getFeatureSource().getFeatures(event.getFilter());
                    FeatureIterator<SimpleFeature> iterator = changed.features();
                    try {
                        while (iterator.hasNext()) {
                            SimpleFeature newFeature = iterator.next();
                            features.add(newFeature);
                        }
                        viewer.setItemCount(event.getFeatureSource().getCount(Query.ALL));
                        viewer.getTable().clearAll();
                    } finally {
                        iterator.close();
                    }
                } catch (IOException accessError) {
                }
                break;
            case REMOVED:
                for (Iterator<SimpleFeature> iter = features.iterator(); iter.hasNext();) {
                    SimpleFeature feature = iter.next();
                    if (event.getFilter().evaluate(feature)) {
                        iter.remove(); // event indicated this feature has been removed
                    }
                }
                viewer.setItemCount(features.size());
                viewer.getTable().clearAll();
                break;
            case CHANGED:
                try {
                    SimpleFeatureCollection changed = (SimpleFeatureCollection) event
                            .getFeatureSource().getFeatures(event.getFilter());

                    FeatureIterator<SimpleFeature> iterator = changed.features();
                    try {
                        while (iterator.hasNext()) {
                            SimpleFeature changedFeature = iterator.next();
                            SCAN: for (ListIterator<SimpleFeature> iter = features.listIterator(); iter
                                    .hasNext();) {
                                SimpleFeature item = iter.next();
                                if (item.getID().equals(changedFeature.getID())) {
                                    iter.set(changedFeature);
                                    break SCAN;
                                }
                            }
                        }
                        viewer.setItemCount(event.getFeatureSource().getCount(Query.ALL));
                        viewer.getTable().clearAll();
                    } finally {
                        iterator.close();
                    }
                } catch (IOException accessError) {
                }
                viewer.getTable().clearAll();
                break;
            case COMMIT:
                //TBD
                break;
            case ROLLBACK:
                //TBD
                break;

            default:
                break;
            }
        }
    };

    /** Memory bound cache of features for table.
     * May be sorted according to FID or any of the attributes so don't rely on any given order because
     * its liable to change.  User Lookup instead for quickly locating a features
     */
    List<SimpleFeature> features = Collections.synchronizedList( new ArrayList<SimpleFeature>());

    /**
     * Contains same features as Features but sorted by id
     */
    Map<String, SimpleFeature> lookup = new HashMap<String, SimpleFeature>();
    /**
     * If true then an edit has occurred and the table is being updated.
     */
    private volatile boolean updating=false;
    private boolean disposed=false;
    /**
     * Does nothing.
     *
     * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
     *      java.lang.Object, java.lang.Object)
     * @param viewer
     * @param oldInput
     * @param newInput
     */
    public void inputChanged( Viewer viewer, Object oldInput, final Object newInput ) {

        synchronized (this) {
            if (monitor != NULL) {
                monitor.setCanceled(true);

                UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableContentProvider.class,
                        "#inputChanged(): cancelled monitor", null); //$NON-NLS-1$
                try {
                    PlatformGIS.wait(500, -1, new WaitCondition(){
                       
                        public boolean isTrue() {
                            return monitor == NULL;
                        }
                       
                    }, this);
                } catch (InterruptedException e) {
                    UiPlugin.log("Interrupted", e); //$NON-NLS-1$
                    return;
                }
            }
            features.clear();
           
            if (oldInput != null && oldInput instanceof AdaptableFeatureCollection) {
                AdaptableFeatureCollection old = (AdaptableFeatureCollection) oldInput;
                FeatureSource source = (FeatureSource) old.getAdapter(FeatureSource.class);
                if (source != null) {
                    source.removeFeatureListener(listener);
                }
            }
            if (newInput != null && newInput instanceof AdaptableFeatureCollection) {
                AdaptableFeatureCollection input = (AdaptableFeatureCollection) newInput;
                FeatureSource source = (FeatureSource) input.getAdapter(FeatureSource.class);
                if (source != null) {
                    source.addFeatureListener(listener);
                } else {
                    UiPlugin.trace(UiPlugin.ID, FeatureTableContentProvider.class,
                            "Unable to adapt to FeatureSource (to listen for changes):" + input,
                            null);
                }
            } else {
                UiPlugin.trace(UiPlugin.ID, FeatureTableContentProvider.class,
                        "Unable to access FeatureSource (to listen for changes):" + newInput, null);
            }

            if (newInput == null)
                return;

            monitor = progressMonitorProvider.get();
            monitor.setCanceled(false);
            owningFeatureTableControl.message(null);
            owningFeatureTableControl.notifyLoadingListeners(new LoadingEvent(false, monitor, true));

            final FeatureCollection<SimpleFeatureType, SimpleFeature> input = (FeatureCollection<SimpleFeatureType, SimpleFeature>) newInput;
            Display display = Display.getCurrent();
            owningFeatureTableControl.message(Messages.FeatureTableContentProvider_loading, display
                    .getSystemColor(SWT.COLOR_INFO_BACKGROUND), display
                    .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
            PlatformGIS.run(new ContentLoader(input));
        }
    }
    public void dispose() {
        synchronized( this ){
            if( disposed )
                return;
           
            disposed=true;
        }
        features.clear();

        if (monitor != NULL) {
            monitor.setCanceled(true);
            try {
                PlatformGIS.wait(200, -1, new WaitCondition(){

                    public boolean isTrue() {
                        return monitor == NULL;
                    }

                }, this);
            } catch (InterruptedException e) {
                UiPlugin.log("Interrupted", e); //$NON-NLS-1$
                return;
            }
        }
    }
    public Collection<SimpleFeature> get(Object... params) {
        return features;
    }
    public void updateElement( int index ) {
        if (index >= features.size()) {
            owningFeatureTableControl.getViewer().replace("", index); //$NON-NLS-1$
        } else if (monitor != NULL && index == 0 && !updating) {
            owningFeatureTableControl.getViewer().replace(FeatureTableControl.LOADING, 0);
        } else {
            int resolvedIndex=index;
            if( owningFeatureTableControl.getViewer().getTable().getSortDirection()==SWT.UP )
                resolvedIndex=features.size()-index-1;
            SimpleFeature feature = features.get(resolvedIndex);
            owningFeatureTableControl.getViewer().replace(feature, index);
        }
    }

    private class ContentLoader implements ISafeRunnable {

        private final FeatureCollection<SimpleFeatureType, SimpleFeature> input;

        public ContentLoader( FeatureCollection<SimpleFeatureType, SimpleFeature> input ) {
            this.input = input;
        }

        public void handleException( Throwable exception ) {
            UiPlugin.log("Error loading features", exception); //$NON-NLS-1$
        }

        public void run() throws Exception {
            if (cancel())
                return;
            UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableContentProvider.class,
                    "Starting ContentLoader", null); //$NON-NLS-1$
            setEnabled(false);
            int i = 0;
            final int[] monitorUpdate = new int[1];
            monitorUpdate[0] = 0;
            boolean updated = false;
            long start = System.currentTimeMillis();
            FeatureIterator<SimpleFeature> iterator = null;
            try {
                iterator = input.features();
                while( iterator.hasNext() ) {
                    if (System.currentTimeMillis() - start > 500) {
                        if (!updated) {
                            updated = true;
                            updateTable(input, false);
                        }
                        start = System.currentTimeMillis();
                        updateMonitor(i, monitorUpdate);
                    }
                    if (cancel())
                        return;
                    SimpleFeature next = iterator.next();
                    features.add(next);
                    lookup.put(next.getID(), next);
                    i++;
                }
            } catch (OutOfMemoryError error) {
                error(input, i + " " + Messages.FeatureTableContentProvider_outOfMemory, true); //$NON-NLS-1$
                UiPlugin.log("Out of memory error in table view", error); //$NON-NLS-1$
                return;
            } catch (IndexOutOfBoundsException e) {
                error(input, Messages.FeatureTableContentProvider_unexpectedErro
                        + " " + Messages.FeatureTableContentProvider_probablecharseterror, false);
                UiPlugin.log("error loading features in table view", e); //$NON-NLS-1$
                return;
            } catch (Throwable t) {
                error(input, Messages.FeatureTableContentProvider_unexpectedErro
                        + t.getLocalizedMessage(), false);
                UiPlugin.log("error loading features in table view", t); //$NON-NLS-1$
                return;
            } finally {
                if (iterator != null)
                    iterator.close();
                UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableContentProvider.class,
                        "Ending ContentLoader, Cancel state is:"+monitor.isCanceled(), null); //$NON-NLS-1$
            }
            if (!cancel()) {
                updateTable(input, true);
                setEnabled(true);
            }
        }

        /**
         * will setenable and set an error message on the feature table control.
         */
        private void error( final FeatureCollection<SimpleFeatureType, SimpleFeature> input, final String string,
                final boolean clearFeatures ) {
            UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableContentProvider.class,
                    "ContentLoader#error: Error occurred in ContentLoader:\n"+string, null); //$NON-NLS-1$

            final Display display = owningFeatureTableControl.getViewer().getControl().getDisplay();
            display.asyncExec(new Runnable(){
                public void run() {
                    monitor.setCanceled(true);
                }
            });
            done();
            display.asyncExec(new Runnable(){
                public void run() {
                    owningFeatureTableControl.message(string, display
                            .getSystemColor(SWT.COLOR_INFO_BACKGROUND), display
                            .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
                }
            });
        }

        private void updateMonitor( int i, final int[] monitorUpdate ) {
            final int j = i;
            owningFeatureTableControl.getViewer().getControl().getDisplay().asyncExec(
                    new Runnable(){
                        public void run() {
                            monitor
                                    .subTask(Messages.FeatureTableContentProvider_loadedFeatures
                                            + j);
                            monitor.worked(j - monitorUpdate[0]);
                            monitorUpdate[0] = j;
                        }
                    });
        }

        /**
         * If enabled it will finish the {@link IProgressMonitor}, null it out, notify listeners
         * and enable the table control. if not enabled it will begin the progress task and disable
         * the Table control
         *
         * @param enabled
         */
        private void setEnabled( final boolean enabled ) {

            final Scrollable control = owningFeatureTableControl.getViewer().getTable();
            final int size;
            if (!enabled)
                size = input.size();
            else
                size = IProgressMonitor.UNKNOWN;

            control.getDisplay().asyncExec(new Runnable(){
                public void run() {

                    UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableContentProvider.class,
                            "ContentLoader#setEnabled():"+enabled, null); //$NON-NLS-1$
                    if (enabled) {
                        done();
                    } else {
                        monitor.beginTask(Messages.FeatureTableControl_loading1
                                + input.getSchema().getName().getLocalPart()
                                + Messages.FeatureTableControl_loading2, size + 1);
                        monitor.worked(1);
                    }
                }
            });
        }
        private void done() {
            final Table control = owningFeatureTableControl.getViewer().getTable();
            Runnable runnable = new Runnable(){
                public void run() {

                    UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableContentProvider.class,
                            "ContentLoader#done()|run():", null); //$NON-NLS-1$
                   
                    monitor.done();
                    synchronized (FeatureTableContentProvider.this) {
                        monitor = NULL;
                        FeatureTableContentProvider.this.notifyAll();
                        if (!control.isDisposed())
                            control.getVerticalBar().setEnabled(true);
                        if (control.getItemCount() > 0) {
                            owningFeatureTableControl.getViewer().replace(features.get(0), 0);
                        }
                        owningFeatureTableControl.notifyLoadingListeners(new LoadingEvent(monitor
                                .isCanceled(), null, false));
                    }
                }
            };
            if (Display.getCurrent() != control.getDisplay()) {
                control.getDisplay().asyncExec(runnable);
            } else {
                runnable.run();
            }
        }

        private void updateTable( final FeatureCollection<SimpleFeatureType, SimpleFeature> newInput, final boolean done ) {
            final Table table = owningFeatureTableControl.getViewer().getTable();
            table.getDisplay().asyncExec(new Runnable(){
                public void run() {

                    UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableContentProvider.class,
                            "ContentLoader#updateTable(): done="+done, null); //$NON-NLS-1$
                   
                    owningFeatureTableControl.message(null);
                    int size = features.size();
                    owningFeatureTableControl.getViewer().setItemCount(size);
                    if (!done && !table.isDisposed())
                        table.getVerticalBar().setEnabled(false);
                }
            });
        }

        private boolean cancel() {
            synchronized (FeatureTableContentProvider.this) {
                if (monitor.isCanceled() || PlatformUI.getWorkbench().isClosing()) {
                    done();
                    return true;
                }
                return false;
            }
        }

    }

    /**
     * Updates the features that have the same feature ID to match the new feature or adds the features if they are not part of the
     * current collection. 
     *
     * @param features2 the feature collection that contains the modified or new features. 
     */
    public void update( FeatureCollection<SimpleFeatureType, SimpleFeature> features2 ) throws IllegalArgumentException{
        if( features==null )
            return;
       
        if( !owningFeatureTableControl.features.getSchema().equals(features2.getSchema()) )
            throw new IllegalArgumentException( "The feature type of the SimpleFeature Collection passed as a parameter does not have the same" + //$NON-NLS-1$
                    " feature type as the features in the table so it cannot be used to update the features." ); //$NON-NLS-1$
       
        ContentUpdater updater=new ContentUpdater(features2);
        PlatformGIS.run(updater);
    }
   
    SimpleFeature findFeature(String featureId ){
        return lookup.get(featureId);
    }
   
    private class ContentUpdater implements ISafeRunnable{

        private FeatureCollection<SimpleFeatureType, SimpleFeature> newFeatures;
        private int loaded=0;

        public ContentUpdater( FeatureCollection<SimpleFeatureType, SimpleFeature> features2 ) {
            this.newFeatures=features2;
        }

        public void handleException( Throwable exception ) {
            UiPlugin.log("Exception while updating the features in the FeatureTableControl", exception); //$NON-NLS-1$
        }

        public void run() throws Exception {
            synchronized( FeatureTableContentProvider.this){
                updating=true;
                if (monitor != NULL) {
                    // wait until finished loading
                    try {
                        PlatformGIS.wait(500, -1, new WaitCondition(){
   
                            public boolean isTrue() {
                                return monitor == NULL;
                            }
   
                        }, FeatureTableContentProvider.this);
                    } catch (InterruptedException e) {
                        UiPlugin.log("Interrupted", e); //$NON-NLS-1$
                        return;
                    }
                }
               
                startLoading();
               
                SimpleFeatureType schema = newFeatures.getSchema();
                FeatureIterator<SimpleFeature> iter=newFeatures.features();
                try{
                    boolean featuresWereAdded=false;
                    while( iter.hasNext() ){
                        if( monitor.isCanceled() )
                            break;
                        SimpleFeature newValue = iter.next();
                        SimpleFeature oldValue = findFeature(newValue.getID());
                        if( oldValue==null ){
                            featuresWereAdded=true;
                            features.add(newValue);
                            lookup.put(newValue.getID(), newValue);
                        }else{
                            for( int i = 0; i < schema.getAttributeCount(); i++ ) {
                                oldValue.setAttribute(i, newValue.getAttribute(i));
                            }
                        }
                        loaded++;
                        updateMonitor(loaded+Messages.FeatureTableContentProvider_updatingFeatures);
                    }
                   
                    updateTable(featuresWereAdded);
                }finally{
                    iter.close();
                }
               
            }
        }
       
        private void updateTable(final boolean featuresWereAdded) {
            final Table table = owningFeatureTableControl.getViewer().getTable();
            table.getDisplay().asyncExec(new Runnable(){
                public void run() {
                    if( featuresWereAdded ){
                        updateMonitor(Messages.FeatureTableContentProvider_sortTable);
                        owningFeatureTableControl.sort(false);
                        owningFeatureTableControl.getViewer().setItemCount(features.size());
                    }else{
                        table.clearAll();
                    }
                    monitor.done();
                    boolean cancelled = monitor.isCanceled();
                    monitor=NULL;
                    updating=false;
                    synchronized (FeatureTableContentProvider.this) {
                        FeatureTableContentProvider.this.notifyAll();
                    }
                    owningFeatureTableControl.notifyLoadingListeners(new LoadingEvent(cancelled, null, false));
                }
            });
        }


        private void updateMonitor(final String subTask) {
            Display display = owningFeatureTableControl.getControl().getDisplay();
            display.asyncExec(new Runnable(){
                public void run() {
                    monitor.subTask(subTask);
                    monitor.worked(1);
                }
            });
        }

        private void startLoading() {
            Display display = owningFeatureTableControl.getControl().getDisplay();
            display.asyncExec(new Runnable(){
                public void run() {
                    owningFeatureTableControl.notifyLoadingListeners(new LoadingEvent(false, monitor, true));
                    monitor=progressMonitorProvider.get();
                    monitor.setCanceled(false);
                    monitor.beginTask(Messages.FeatureTableContentProvider_updateTaskName, IProgressMonitor.UNKNOWN);
                }
            });
        }
    }
   
    /**
     * Checks the lookup table and the feature list to ensure that they have the same number of features and the same features.
     * An exception will be thrown otherwise.
     */
    public void assertInternallyConsistent(){
        if( features.size()!=lookup.size())
            throw new AssertionError("lookup table has "+lookup.size()+" features while feature list has "+features.size()+" features"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
       
        for( SimpleFeature feature : features ) {
            SimpleFeature lookupFeature = lookup.get(feature.getID());
            if( lookup==null ){
                throw new AssertionError("Lookup table is missing "+feature); //$NON-NLS-1$
            }
            if( lookupFeature!=feature )
                throw new AssertionError("Lookup table contains: "+lookupFeature+" while feature list contains"+feature+".  They are" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                        " not the same instance"); //$NON-NLS-1$
        }
    }
    /**
     * Removes the selected features (the features selected by the owning {@link FeatureTableControl}).
     * @return returns a collection of the deleted features
     */
    public FeatureCollection<SimpleFeatureType, SimpleFeature> deleteSelection() {
        final DefaultFeatureCollection deletedFeatures = new DefaultFeatureCollection();
        Runnable updateTable = new Runnable(){
            @SuppressWarnings("unchecked")
            public void run() {
                Collection<String> selectionFids = owningFeatureTableControl.getSelectionProvider().getSelectionFids();
               
                for( Iterator<SimpleFeature> iter = features.iterator(); iter.hasNext(); ) {
                    SimpleFeature feature =  iter.next();
                    if( selectionFids.contains(feature.getID()) ){
                        deletedFeatures.add(feature);
                        iter.remove();
                        lookup.remove(feature.getID());
                    }
                }
               
                selectionFids.clear();
                owningFeatureTableControl.getViewer().getTable().clearAll();
            }
        };
       
        if( Display.getCurrent()==null ){
            PlatformGIS.syncInDisplayThread(owningFeatureTableControl.getControl().getDisplay(), updateTable);
        }else{
            updateTable.run();
        }
       
        return deletedFeatures;
    }
}
TOP

Related Classes of org.locationtech.udig.ui.FeatureTableContentProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.