Package org.locationtech.udig.ui

Source Code of org.locationtech.udig.ui.FeatureTableSelectionProvider$Abort

/* 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.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import org.locationtech.udig.core.IProvider;
import org.locationtech.udig.core.internal.FeatureUtils;
import org.locationtech.udig.internal.ui.Trace;
import org.locationtech.udig.internal.ui.UiPlugin;
import org.locationtech.udig.ui.internal.Messages;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.GeoTools;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.filter.FilterAttributeExtractor;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.Id;
import org.opengis.filter.identity.Identifier;

/**
* Manages selection for the {@link FeatureTableControl}
*
* @author Jesse
* @since 1.1.0
*/
class FeatureTableSelectionProvider implements ISelectionProvider {

    private FeatureTableControl owner;
    private Set<String> selectionFids = new HashSet<String>();
    private Set<ISelectionChangedListener> selectionChangedListeners = new CopyOnWriteArraySet<ISelectionChangedListener>();

    /**
     * if null then no set selection is running if not null then it must be cancelled.
     */
    volatile IProgressMonitor progressMonitor;
    private IProvider<IProgressMonitor> progressMonitorProvider;

    public FeatureTableSelectionProvider( FeatureTableControl control, IProvider<IProgressMonitor> progressMonitorProvider  ) {
        this.owner = control;
        this.progressMonitorProvider=progressMonitorProvider;
    }

    public void addSelectionChangedListener( ISelectionChangedListener listener ) {
        selectionChangedListeners.add(listener);
    }

    public ISelection getSelection() {
        checkWidget();
        if (selectionFids.isEmpty())
            return new StructuredSelection();
        return new StructuredSelection(getId());

    }

    public void removeSelectionChangedListener( ISelectionChangedListener listener ) {
        selectionChangedListeners.remove(listener);
    }

    public void setSelection( ISelection selection ) {
        setSelection(selection, true);
    }

    void setSelection( final ISelection newSelection, final boolean reveal ) {
        checkWidget();
        if (progressMonitor != null) {
            progressMonitor.setCanceled(true);
            UiPlugin.trace(Trace.FEATURE_TABLE, FeatureTableSelectionProvider.class,
                    "#setSelection(): cancelled monitor", null); //$NON-NLS-1$

        }
        try {
            PlatformGIS.wait(500, -1, new WaitCondition(){

                public boolean isTrue() {
                    return progressMonitor==null;
                }
               
            }, this);
        } catch (InterruptedException e) {
            UiPlugin.log("Interrupted", e); //$NON-NLS-1$
            return;
        }

        synchronized (this) {
            progressMonitor = progressMonitorProvider.get();
            progressMonitor.setCanceled(false);

            PlatformGIS.run(new SelectionLoader(newSelection, reveal));
        }
    }

    protected void notifyListeners() {
        //      let listeners know the selection has changed.
        SelectionChangedEvent event = new SelectionChangedEvent(owner,
                getSelection());
        for( ISelectionChangedListener listener : selectionChangedListeners ) {
            try{
                listener.selectionChanged(event);
            }catch (Throwable e) {
                UiPlugin.log("", e); //$NON-NLS-1$
            }
        }
    }
    /**
     * Returns the fids.  It can be used to add new fids.  No notification of the change is raised.
     *
     * @return
     */
    public Collection<String> getSelectionFids() {
        checkWidget();
        return selectionFids;
    }

    public Id getId() {
        checkWidget();
        FilterFactory2 fac = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());
        Set<Identifier> ids = FeatureUtils.stringToId(fac, selectionFids);
        return fac.id(ids);
    }

    private void checkWidget() {
        if (Display.getCurrent() == null)
            SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS);
    }

    private class SelectionLoader implements ISafeRunnable {

        private final ISelection newSelection;
        private final boolean reveal;

        public SelectionLoader( ISelection newSelection, boolean reveal ) {
            this.newSelection = newSelection;
            this.reveal = reveal;
        }

        public void handleException( Throwable exception ) {
            UiPlugin.log("Error setting selection on table view", exception); //$NON-NLS-1$
        }

        public void run() throws Exception {
            startProgress();
            try {
                if (newSelection.isEmpty()) {
                    if (owner.getViewer().getControl().isDisposed()){
                        done();
                        return;
                    }
                    owner.getViewer().getControl().getDisplay().asyncExec(new Runnable(){
                        public void run() {
                            updateMonitor(3);
                            owner.getViewer().getTable().setSelection(new TableItem[0]);

                            selectionFids.clear();

                            updateMonitor(3);
                           
                            owner.getViewer().getTable().clearAll();
                            notifyListeners();
                        }

                    });

                } else if (newSelection instanceof IStructuredSelection) {
                    IStructuredSelection structured = (IStructuredSelection) newSelection;
                    final Set<String> fids = new HashSet<String>();
                    obtainFidsFromSelection(structured, fids);

                    // selection is equivalent to last selection so return
                    if (selectionFids.equals(fids))
                        return;

                    FeatureTableContentProvider provider = (FeatureTableContentProvider) owner
                            .getViewer().getContentProvider();

                    final List<SimpleFeature> features = provider.features;
                    int i = 0;
                    synchronized( provider.features ){
                        for( SimpleFeature feature : features ) {
                            if (fids.contains(feature.getID())) {
                                break;
                            }
                            i++;
                        }
                    }
                   
                    updateMonitor(1);

                    final int index = i;

                    owner.getViewer().getControl().getDisplay().asyncExec(new Runnable(){
                        public void run() {
                            updateMonitor(1);

                            owner.getViewer().getTable().setSelection(new TableItem[0]);

                            selectionFids = fids;

                            final Table table = owner.getViewer().getTable();
                            // clear non-virtual data so that it will be re-labelled. This is not
                            // too
                            // bad of an operation (I think)
                            table.clearAll();
                            if (reveal && index < features.size()) {
                                // show selection if there is one.
                                table.setTopIndex(index);
                            }

                            notifyListeners();
                        }
                    });
                }
            } catch (Abort e) {
                // its ok just aborting the finally will clean up
            }finally {
                done();
            }
        }

        private void updateMonitor( final int ticks ) {
            if( Display.getCurrent()!=null ){
                progressMonitor.worked(ticks);
            }else{
                owner.getControl().getDisplay().asyncExec(new Runnable(){
                    public void run() {
                        progressMonitor.worked(ticks);
                    }
                });
            }
        }

        private void done() {
            Runnable runnable = new Runnable(){
                public void run() {
                    synchronized (FeatureTableSelectionProvider.this) {
                        progressMonitor.done();
                        progressMonitor = null;
                        FeatureTableSelectionProvider.this.notifyAll();
                    }
                }
            };
            if( Display.getCurrent()!=null ){
                runnable.run();
            }else{
                owner.getControl().getDisplay().asyncExec(runnable);
            }
        }

        private void startProgress() {
            Runnable runnable = new Runnable(){
                public void run() {
                    progressMonitor.beginTask(Messages.FeatureTableSelectionProvider_loading_new_selection, 10);
                    progressMonitor.worked(1);
                }
            };
            if( Display.getCurrent()!=null ){
                runnable.run();
            }else{
                owner.getControl().getDisplay().asyncExec(runnable);
            }
        }

        private void obtainFidsFromSelection( IStructuredSelection structured,
                final Set<String> fids ) throws IOException, Abort {
            int usedTicks = 0;
            for( Iterator iter = structured.iterator(); iter.hasNext(); ) {
               
                if (progressMonitor.isCanceled())
                    throw new Abort();

                Object element = (Object) iter.next();

                if (element instanceof String) {
                    fids.add((String) element);
                } else if (element instanceof SimpleFeature) {
                    fids.add(((SimpleFeature) element).getID());
                } else if (element instanceof Id) {
                    String[] fids2 = ((Id) element).getIDs().toArray(new String[0]);
                    fids.addAll(Arrays.asList(fids2));
                } else if (element instanceof IAdaptable) {

                    obtainFidsFromAdaptable(fids, (IAdaptable) element);
                } else if (element instanceof Filter) {
                    UiPlugin
                            .log(
                                    "Tried to set selection on table but the selection contained a non-Fid Filter that could not " + //$NON-NLS-1$
                                            "adapt to a FeatureSource so the selection could not be applied.  Ignoring filter", //$NON-NLS-1$
                                    null);
                } else {
                    UiPlugin
                            .log(
                                    "Tried to set selection on table but the selection contained a " + element.getClass().getSimpleName() + //$NON-NLS-1$
                                            " but this type cannot be converted to Fids", null); //$NON-NLS-1$
                }
                if( usedTicks < 7 )
                    updateMonitor(1);
            }
            if( usedTicks < 7 )
                updateMonitor(7-usedTicks);
        }

        /**
         * Obtain fids from the features source if possible.
         *
         * @param fids the set to add fids to
         * @param adaptable the object that adapted to the filter. hopefully can adapt to a feature
         *        source as well
         * @param filter filter to use for obtaining fids.
         * @throws IOException
         * @throws Abort
         */
        private void obtainFidsFromAdaptable( final Set<String> fids, IAdaptable adaptable )
                throws IOException, Abort {
            Filter filter = null;
            if (adaptable.getAdapter(Filter.class) != null)
                filter = (Filter) adaptable.getAdapter(Filter.class);
            else if (adaptable.getAdapter(Query.class) != null)
                filter = ((Query) adaptable.getAdapter(Query.class)).getFilter();

            if (filter == null)
                return;

            FeatureSource<SimpleFeatureType, SimpleFeature> source = null;
            if (adaptable.getAdapter(FeatureSource.class) != null) {
                source = (FeatureSource<SimpleFeatureType, SimpleFeature>) adaptable.getAdapter(FeatureSource.class);
            }

            if (source == null) {
                UiPlugin
                        .log(
                                "last resource run filter on features in table view...  Might now work since table view" + //$NON-NLS-1$
                                        " does not have any Geometries", new Exception()); //$NON-NLS-1$

                if (owner.getViewer() != null && owner.getViewer().getInput() != null)
                    for( SimpleFeature feature : ((FeatureTableContentProvider) owner.getViewer()
                            .getContentProvider()).features ) {
                        if (progressMonitor.isCanceled())
                            throw new Abort();
                        if (filter.evaluate(feature))
                            fids.add(feature.getID());
                    }
            } else {
                DefaultQuery defaultQuery = new DefaultQuery(source.getSchema().getName().getLocalPart(),
                        filter, new String[0]);
                // TODO: Remove this workaround in 2.6.1 (note this has no performance impact)
                Set<String> required = (Set) filter.accept( new FilterAttributeExtractor(), null );               
                defaultQuery.setPropertyNames( required.toArray(new String[0]) );
               
                // get features that are just fids no attributes
                FeatureCollection<SimpleFeatureType, SimpleFeature>  features = source.getFeatures(defaultQuery);
                long start=System.currentTimeMillis();
               
                FeatureIterator<SimpleFeature> featureIterator = features.features();
                try {
                    while( featureIterator.hasNext() ) {
                        if (progressMonitor.isCanceled())
                            throw new Abort();
                        if( System.currentTimeMillis()-start>500){
                            start=System.currentTimeMillis();
                            owner.getViewer().getControl().getDisplay().asyncExec(new Runnable(){
                                public void run() {
                                    progressMonitor.subTask(fids.size()+" selected"); //$NON-NLS-1$
                                }
                            });
                        }
                        fids.add(featureIterator.next().getID());
                    }
                } finally {
                    featureIterator.close();
                }
            }
        }
    }

    private static class Abort extends Exception {

        /** long serialVersionUID field */
        private static final long serialVersionUID = 1L;
    }

}
TOP

Related Classes of org.locationtech.udig.ui.FeatureTableSelectionProvider$Abort

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.