Package org.locationtech.udig.ui

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

/*
*    uDig - User Friendly Desktop Internet GIS client
*    http://udig.refractions.net
*    (C) 2012, 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.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.locationtech.udig.core.IProvider;
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.jface.action.MenuManager;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ICellEditorListener;
import org.eclipse.jface.viewers.ICellEditorValidator;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.PageBook;
import org.geotools.feature.FeatureCollection;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.Filter;
import org.opengis.filter.Id;
import org.opengis.filter.identity.FeatureId;

import com.vividsolutions.jts.geom.Geometry;

/**
* A TreeViewer control for viewing a table of SimpleFeature attributes.
* <p>
* The object is used by using a FeatureCollection. In this case the control hangs on to a reference
* to the FeatureCollection and populates the table entries directory from it. This method results
* in a single page containing all features.
* </p>
* <p>
* If the FeatureCollection implements the {@link IAdaptable} interface and adapts to
* {@link ICellModifier} then the table is editable. The {@link ICellModifier} is used to modify the
* features. The Column properties passed to the {@link ICellModifier} are the attribute name of the
* attribute being modified.
* </p>
* <p>
* If the FeatureCollection implements the {@link IAdaptable} interface and adapts to
* {@link CellEditor[]} then the cell editors will be used to edit the cells. This is optional for
* editing. By default a {@link TextCellEditor} is used for editing most cells, and an
* {@link AttributeValidator} is used to validate the new values. The first column is for the fid
* column and will not be used since FIDS are assigned by the datastore and can not be modified. The
* number of Items the array (this is the same for the cell editor validators and cell editor
* listeners) must be either the number of attributes in the feature type or the number of
* attributes + 1 (one for the FID column). If the number of editors it Attributes+1 then the first
* element in the array will not be used as it is assumed to be a placeholder for the fid column.
* </p>
* <p>
* If the FeatureCollection implements the {@link IAdaptable} interface and adapts to
* {@link ICellEditorValidator[]} then the validators will be used to validate the cells.
* </p>
* <p>
* If the FeatureCollection implements the {@link IAdaptable} interface and adapts to
* {@link ICellEditorListener[]} then the listeners will be added to the {@link CellEditor}s.
* </p>
*
* @author jdeolive
* @author jeichar
* @since 0.3
*/
public class FeatureTableControl implements ISelectionProvider {

    public static final String FEATURE_ID_COLUMN_PROPERTY = "FeatureIDProperty"; //$NON-NLS-1$

    public static final Object ERROR_COLUMN_PROPERTY = "ErrorProperty"; //$NON-NLS-1$

    public static final Object LOADING = new Object();

    /** results per page * */
    private int pageSize = 10; // XXX: actual put this as a user pref

    /** table viewer control * */
    private TableViewer tableViewer;

    private PageBook book;

    private Text message;

    FeatureCollection<SimpleFeatureType, SimpleFeature> features;

    private final IProvider<IProgressMonitor> progressMonitorProvider;

    private FeatureTableSelectionProvider selectionProvider;

    private Color messageBackground;

    private Color messageForeground;

    private Set<IFeatureTableLoadingListener> loadingListeners = new CopyOnWriteArraySet<IFeatureTableLoadingListener>();

    private Comparator<SimpleFeature> currentComparator;

    private MenuManager contextMenu;

    private IProvider<RGB> selectionColor;

    private boolean shown;

    /**
     * Construct <code>FeatureTableControl</code>.
     * <p>
     * Must call setFeatures before use.
     * </p>
     */
    public FeatureTableControl() {
        this(ProgressManager.instance());
    }

    /**
     * Construct a <code>FeatureTableControl</code>.
     *
     * @param monitorProvider a provider that will provider progress monitors for displaying loading
     *        information.
     * @param fReader The FeatureReader that returns the actual features.
     * @param resPerPage Results per page to be shown in the table.
     */
    public FeatureTableControl( Composite parent,
            FeatureCollection<SimpleFeatureType, SimpleFeature> features ) {
        this(ProgressManager.instance(), parent, features);
    }
    /**
     * Construct <code>FeatureTableControl</code>.
     * <p>
     * Must call setFeatures before use.
     * </p>
     *
     * @param monitorProvider a provider that will provider progress monitors for displaying loading
     *        information.
     */
    public FeatureTableControl( final IProvider<IProgressMonitor> monitorProvider ) {
        this.progressMonitorProvider = monitorProvider;
        this.selectionProvider = new FeatureTableSelectionProvider(this, ProgressManager.instance());
    }

    /**
     * Construct a <code>FeatureTableControl</code>.
     *
     * @param monitorProvider a provider that will provider progress monitors for displaying loading
     *        information.
     * @param fReader The FeatureReader that returns the actual features.
     * @param resPerPage Results per page to be shown in the table.
     */
    public FeatureTableControl( final IProvider<IProgressMonitor> monitorProvider,
            Composite parent, FeatureCollection<SimpleFeatureType, SimpleFeature> features ) {
        this(monitorProvider);
        this.features = features;
        createTableControl(parent);
    }

    /**
     * Sets the number of features viewed in the table per page.
     *
     * @param resPerPage positive integer.
     */
    public void setPageSize( int resPerPage ) {
        this.pageSize = resPerPage;
    }

    /**
     * Returns the number of features viewed in the table per page.
     *
     * @return positive integer.
     */
    public int getPageSize() {
        return pageSize;
    }

    /**
     * Returns the control representing the table control.
     *
     * @return The internal table viewer control.
     */
    public Control getControl() {
        return book;
    }

    public void dispose() {
        disposeTableViewer();
    }

    /**
     * Creates the table control.
     *
     * @param parent The to be parent of the control.
     */
    public void createTableControl( Composite parent ) {
        book = new PageBook(parent, SWT.NONE);
        message = new Text(book, SWT.WRAP);
        messageBackground = message.getBackground();
        messageForeground = message.getForeground();
        createTableViewer(book);
    }

    /**
     * Key for indicating whether the warning should be displayed. false if the warning is displayed
     */
    public static final String CACHING_WARNING = "FEATURE_TABLE_CACHING_IN_MEMORY_WARNING"; //$NON-NLS-1$

    /**
     * Indicates that all attribute types will be searched by the select method
     *
     * @see #select(String, String[], boolean)
     */
    public static final String[] ALL = new String[0];

    private static final boolean SHOW_PATH = false;

    /**
     * show the warning about loading features into memory.
     *
     * @returns true if the user wishes to continue to load features; false otherwise.
     */
    public boolean showWarning( Display display ) {

        IPreferenceStore preferenceStore = UiPlugin.getDefault().getPreferenceStore();
        if (!preferenceStore.getBoolean(CACHING_WARNING) && !shown) {
            shown = true;

            MessageDialogWithToggle dialog = MessageDialogWithToggle.openOkCancelConfirm(display
                    .getActiveShell(), Messages.FeatureTableControl_warningTitle,
                    Messages.FeatureTableControl_warningMessage,
                    Messages.FeatureTableControl_warningToggle, false, null, null);
            // MessageDialogWithToggle dialog = MessageDialogWithToggle.openWarning(display
            // .getActiveShell(), Messages.FeatureTableControl_warningTitle,
            // Messages.FeatureTableControl_warningMessage,
            // Messages.FeatureTableControl_warningToggle, false, null, null);
            preferenceStore.setValue(CACHING_WARNING, dialog.getToggleState());
            if (dialog.getReturnCode() == MessageDialogWithToggle.OK) {
                return true;
            } else {
                return false;
            }

        }
        return true;

    }

    /**
     * Updates the table control with the current set of features.
     * <p>
     * This method will ensure that the column information gets updated
     * </p>
     */
    public void update() {
        checkWidget();
        if (tableViewer != null) {
            tableViewer.setInput(features);
            tableViewer.getTable().clearAll();
        }
    }

    /**
     * Creates the table control itself.
     *
     * @param parent
     */
    protected void createTableViewer( Composite parent ) {
        int style = SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.H_SCROLL | SWT.V_SCROLL;
        if (tableViewer != null) {
            disposeTableViewer();
        }
        final Table table = new Table(parent, style);
        table.setLinesVisible(true);
        TableLayout layout = new TableLayout();
        table.setLayout(layout);

        FeatureTableContentProvider ftp = new FeatureTableContentProvider(this,
                this.progressMonitorProvider);
        FeatureTableLabelProvider flp = new FeatureTableLabelProvider(this);
        flp.setSelectionColor(selectionColor);
        tableViewer = new TableViewer(table);

        tableViewer.setContentProvider(ftp);
        tableViewer.setLabelProvider(flp);

        // create columns after tableViewer is created because Column listeners need to access the
        // tableViewer.
        createAttributeColumns(table, tableViewer, layout);
        table.setHeaderVisible(true);

        addSelectionListener(table);
        if (features instanceof IAdaptable
                && ((IAdaptable) features).getAdapter(ICellModifier.class) != null) {

            IAdaptable adaptable = (IAdaptable) features;
            SimpleFeatureType schema = features.getSchema();
            int attributeCount = schema.getAttributeCount();
            setCellEditors(adaptable, attributeCount);

            setCellValidators(adaptable);
            addCellEditorListeners(adaptable);
            tableViewer.setCellModifier((ICellModifier) adaptable.getAdapter(ICellModifier.class));

            String[] properties = new String[attributeCount + 1];
            for( int i = 0; i < properties.length; i++ ) {
                if (i == 0)
                    properties[i] = FEATURE_ID_COLUMN_PROPERTY;
                else {
                    properties[i] = schema.getDescriptor(i - 1).getName().getLocalPart();
                }
            }
            tableViewer.setColumnProperties(properties);
        }
        if (contextMenu != null) {
            Menu menu = contextMenu.createContextMenu(tableViewer.getControl());
            tableViewer.getControl().setMenu(menu);
        }
        book.showPage(tableViewer.getControl());

        UiPlugin.trace(Trace.FEATURE_TABLE, getClass(),
                "createTableViewer(): showing table View", SHOW_PATH ? new Exception() : null); //$NON-NLS-1$

        if (features != null) {
            tableViewer.setInput(features);
        }

    }

    private void addSelectionListener( final Table table ) {
        // We are not using default selection provided by the table because it is too slow
        // so I am doing my own listening and based on which items are selected and what
        // keys are down I am simulating the selection behaviour.
        table.addListener(SWT.MouseDown, new Listener(){

            int lastIndex = -1;

            public void handleEvent( Event e ) {

                if (e.button != 1) {
                    return;
                }

                int index = table.getSelectionIndex();
                FeatureTableContentProvider provider = (FeatureTableContentProvider) tableViewer
                        .getContentProvider();
                Collection<String> selectionFids = selectionProvider.getSelectionFids();

                table.deselect(index);
                if ((e.stateMask & SWT.MOD2) != 0 && lastIndex != -1) {
                    if (lastIndex == index)
                        return;
                    handleSelecteRange(table, index, provider, selectionFids);
                } else if ((e.stateMask & SWT.MOD1) != 0) {
                    handleXORSelect(table, index, provider, selectionFids);
                } else {
                    if (lastIndex == index)
                        return;
                    handleDefault(table, index, provider, selectionFids);
                }

                selectionProvider.notifyListeners();
            }

            private void handleDefault( final Table table, int index,
                    FeatureTableContentProvider provider, Collection<String> selectionFids ) {
                if (index == -1) {
                    selectionFids.clear();
                    table.clearAll();
                } else {
                    String fid = provider.features.get(index).getID();
                    selectionFids.clear();
                    selectionFids.add(fid);
                    table.clearAll();
                }
                lastIndex = index;
            }

            private void handleXORSelect( final Table table, int index,
                    FeatureTableContentProvider provider, Collection<String> selectionFids ) {
                String fid = provider.features.get(index).getID();
                if (selectionFids.contains(fid)) {
                    selectionFids.remove(fid);
                } else {
                    selectionFids.add(fid);
                }
                table.clear(index);
                lastIndex = index;
            }

            private void handleSelecteRange( final Table table, int index,
                    FeatureTableContentProvider provider, Collection<String> selectionFids ) {
                selectionFids.clear();

                int low = Math.min(lastIndex, index);
                int high = Math.max(lastIndex, index);
                if (low == -1 || high == -1) {
                    table.clearAll();
                    return;
                }
                List<SimpleFeature> toAdd = provider.features.subList(low, high + 1);
                boolean foundUnselectedItem = false;
                int i = low;
                for( SimpleFeature feature : toAdd ) {
                    if (selectionFids.add(feature.getID())) {
                        foundUnselectedItem = true;
                    }
                    i++;
                }
                if (foundUnselectedItem) {
                    table.clearAll();
                }
            }

        });
    }

    private void disposeTableViewer() {
        if (tableViewer == null)
            return;
        IContentProvider contentProvider = tableViewer.getContentProvider();
        if (contentProvider != null)
            contentProvider.dispose();
        IBaseLabelProvider labelProvider = tableViewer.getLabelProvider();
        if (labelProvider != null)
            labelProvider.dispose();
        Control control = tableViewer.getControl();
        if (control != null)
            control.dispose();
        tableViewer = null;
    }

    private void setCellEditors( IAdaptable adaptable, int attributeCount ) {
        if (adaptable.getAdapter(CellEditor[].class) != null) {
            CellEditor[] editors = (CellEditor[]) adaptable.getAdapter(Array.class);
            if (editors.length < attributeCount) {
                UiPlugin.log(
                        "not enough cell editors for feature type so not used", new Exception()); //$NON-NLS-1$
                createCellEditors();
            } else {
                CellEditor[] copy = new CellEditor[editors.length + 1];
                if (editors.length == attributeCount) {
                    // there is an editor for each attribute. First element in copy if for the
                    // fid column (which is not editable).
                    System.arraycopy(editors, 0, copy, 1, attributeCount);
                } else {

                    // ignore 1st element in editors because it is for the FID column which is read
                    // only.
                    System.arraycopy(editors, 1, copy, 1, attributeCount);

                }
                tableViewer.setCellEditors(copy);
            }
        } else {
            createCellEditors();
        }
    }

    private void addCellEditorListeners( IAdaptable adaptable ) {
        CellEditor[] editors = tableViewer.getCellEditors();
        // offset is usually 1 but if the number of validators==number of attributes then the offset
        // is 0
        // because the first column is the FID column which doesn't have a listener.
        int offset = 1;

        ICellEditorListener[] listener = null;
        if (adaptable.getAdapter(ICellEditorListener[].class) != null) {
            listener = (ICellEditorListener[]) adaptable.getAdapter(ICellEditorListener[].class);
            int attributeCount = features.getSchema().getAttributeCount();
            if (listener.length < attributeCount) {
                UiPlugin.log(
                        "not enough cell editors for feature type so not used", new Exception()); //$NON-NLS-1$
                return;
            } else if (listener.length == attributeCount + 1) {
                offset = 0;
            }
        }

        for( int i = 0; i < editors.length - offset; i++ ) {
            final CellEditor editor = editors[i + offset];
            if (editor == null)
                continue;
            if (listener != null && listener[i] != null)
                editor.addListener(listener[i]);
            editor.addListener(new DisplayErrorCellListener(editor));
        }
    }

    private void setCellValidators( IAdaptable adaptable ) {
        CellEditor[] editors = tableViewer.getCellEditors();
        SimpleFeatureType schema = features.getSchema();
        // offset is usually 1 but if the number of validators==number of attributes then the offset
        // is 0
        // because the first column is the FID column which doesn't have a listener.
        int offset = 1;

        ICellEditorValidator[] validators = null;
        if (adaptable.getAdapter(ICellEditorValidator[].class) != null) {
            validators = (ICellEditorValidator[]) adaptable
                    .getAdapter(ICellEditorValidator[].class);
            int attributeCount = features.getSchema().getAttributeCount();
            if (validators.length < attributeCount) {
                UiPlugin.log(
                        "not enough cell editors for feature type so not used", new Exception()); //$NON-NLS-1$
                validators = null;
            } else if (validators.length == attributeCount) {
                offset = 0;
            }
        }

        for( int i = 0; i < editors.length - offset; i++ ) {
            CellEditor editor = editors[i + offset];
            if (editor == null)
                continue;
            if (validators != null && validators[i] != null)
                editor.setValidator(validators[i]);
            else
                editor.setValidator(new AttributeValidator(schema.getDescriptor(i), schema));
        }
    }

    @SuppressWarnings("unchecked")
    private void createCellEditors() {
        SimpleFeatureType schema = features.getSchema();
        org.eclipse.jface.viewers.CellEditor[] editors = new org.eclipse.jface.viewers.CellEditor[schema
                .getAttributeCount() + 1];

        for( int i = 0; i < schema.getAttributeCount(); i++ ) {
            AttributeDescriptor aType = schema.getDescriptor(i);
            Class< ? extends Object> concreteType = aType.getType().getBinding();
            Composite control = (Composite) tableViewer.getControl();
            if (concreteType.isAssignableFrom(String.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control, String.class);
                editors[i + 1] = textCellEditor;
            } else if (concreteType.isAssignableFrom(Integer.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control, Integer.class);
                editors[i + 1] = textCellEditor;
            } else if (concreteType.isAssignableFrom(Double.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control, Double.class);
                editors[i + 1] = textCellEditor;
            } else if (concreteType.isAssignableFrom(Float.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control, Float.class);
                editors[i + 1] = textCellEditor;

            } else if (concreteType.isAssignableFrom(Boolean.class)) {
                BooleanCellEditor textCellEditor = new BooleanCellEditor(control);
                editors[i + 1] = textCellEditor;

            } else if (concreteType.isAssignableFrom(Character.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control,
                        Character.class);
                editors[i + 1] = textCellEditor;

            }
            // else if( concreteType.isAssignableFrom(Date.class)){
            // WarningCellEditor textCellEditor = new WarningCellEditor(control, "The Date type does
            // not yet have a editor, please make a bug report for this Attribute Type");
            // editors[i+1]=textCellEditor;
            //               
            // }
            else if (concreteType.isAssignableFrom(Byte.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control, Byte.class);
                editors[i + 1] = textCellEditor;

            } else if (concreteType.isAssignableFrom(Short.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control, Short.class);
                editors[i + 1] = textCellEditor;

            } else if (concreteType.isAssignableFrom(Long.class)) {
                BasicTypeCellEditor textCellEditor = new BasicTypeCellEditor(control, Long.class);
                editors[i + 1] = textCellEditor;

            } else {
                WarningCellEditor textCellEditor = new WarningCellEditor(control,
                        Messages.FeatureTableControl_noEditor1 + concreteType.getSimpleName()
                                + Messages.FeatureTableControl_noEditor2);
                editors[i + 1] = textCellEditor;
            }
        }
        tableViewer.setCellEditors(editors);
    }

    private void createAttributeColumns( final Table table, TableViewer viewer, TableLayout layout ) {

        if (features == null) {
            TableColumn column = new TableColumn(table, SWT.CENTER | SWT.BORDER);
            column.setText(Messages.FeatureTableControl_1);
            layout.addColumnData(new ColumnWeightData(1));
        } else {

            SimpleFeatureType schema = features.getSchema();

            TableColumn column = new TableColumn(table, SWT.CENTER | SWT.BORDER);
            column.setText("FID"); //$NON-NLS-1$
            layout.addColumnData(new ColumnWeightData(1, 150, true));
            column.setMoveable(true);

            column.addListener(SWT.Selection, new AttributeColumnSortListener(this,
                    FEATURE_ID_COLUMN_PROPERTY));

            for( int i = 0; i < schema.getAttributeCount(); i++ ) {
                AttributeDescriptor aType = schema.getDescriptor(i);
                column = new TableColumn(table, SWT.CENTER | SWT.BORDER);
                if (Geometry.class.isAssignableFrom(aType.getType().getBinding())) { // was
                                                                                     // aType.isGeometry()
                    // jg: wot is this maddness? jd: paul said so
                    column.setText("GEOMETRY"); //$NON-NLS-1$
                } else
                    column.setText(aType.getName().getLocalPart());

                layout.addColumnData(new ColumnWeightData(1, 100, true));
                column.setMoveable(true);

                column.addListener(SWT.Selection, new AttributeColumnSortListener(this, aType
                        .getName().getLocalPart()));
            }

        }
    }

    /**
     * Does nothing.
     *
     * @see org.eclipse.ui.IWorkbenchPart#setFocus()
     */
    public void setFocus() {
        // do nothing.
    }

    /**
     * Contents of the current page of features
     *
     * @return
     */
    public FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatures() {
        return features;
    }

    /** Set up for a single page of content */
    public void setFeatures( FeatureCollection<SimpleFeatureType, SimpleFeature> features ) {
        checkWidget();
        if (this.features != null && this.features == features)
            return;

        this.features = features;

        createTableViewer(book);
    }

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

    /**
     * Don't display nothing :-)
     */
    public void clear() {
        features = null;
        selectionProvider.getSelectionFids().clear();
        update();
    }

    /**
     * Displays a message. If text == null or "" then the message is hidden and tableViewer is shown
     * again.
     *
     * @param text message to display
     * @param background color of the background of the text widget. If null the default color is
     *        used
     * @param foreground color of the foreground of the text widget. If null the default color is
     *        used
     */
    public void message( String text, Color background, Color foreground ) {
        checkWidget();
        Color background2 = background;
        Color foreground2 = foreground;
        if (background2 == null) {
            background2 = messageBackground;
        }
        if (foreground2 == null) {
            foreground2 = messageForeground;
        }
        message.setBackground(background2);
        message.setForeground(foreground2);
        if (text == null || text.trim().length() == 0) {
            message.setText(""); //$NON-NLS-1$
            if (tableViewer != null) {
                book.showPage(tableViewer.getControl());
                UiPlugin
                        .trace(
                                Trace.FEATURE_TABLE,
                                getClass(),
                                "message(String,Color,Color): showing table View", SHOW_PATH ? new Exception() : null); //$NON-NLS-1$
            }
        } else {
            message.setText(text);
            book.showPage(message);
            UiPlugin
                    .trace(
                            Trace.FEATURE_TABLE,
                            getClass(),
                            "message(String,Color,Color): showing message", SHOW_PATH ? new Exception() : null); //$NON-NLS-1$
        }
    }

    /**
     * Displays a message. If text == null or "" then the message is hidden and tableViewer is shown
     * again.
     *
     * @param text message to display
     */
    public void message( String text ) {
        message(text, null, null);
    }

    /**
     * Returns a selection with a single Id indicating the features selected
     */
    public ISelection getSelection() {
        checkWidget();
        return selectionProvider.getSelection();
    }

    public void addSelectionChangedListener( ISelectionChangedListener listener ) {
        selectionProvider.addSelectionChangedListener(listener);
    }
    public void removeSelectionChangedListener( ISelectionChangedListener listener ) {
        selectionProvider.removeSelectionChangedListener(listener);
    }

    /**
     * Useable selections are: selection of features, FIDS and Filters/Queries that adapt to a
     * FeatureSource
     */
    public void setSelection( final ISelection newSelection ) {
        checkWidget();
        selectionProvider.setSelection(newSelection);
    }

    /**
     * Sorts the table so that the selection is at the top of the table. It does not last. The next
     * selection will not be at the top.
     */
    public void promoteSelection() {
        checkWidget();
        tableViewer.cancelEditing();

        Table table = tableViewer.getTable();
        table.setSortColumn(null);

        Id filter = selectionProvider.getId();

        sort(new SelectionComparator(filter, SWT.UP, new FIDComparator(SWT.DOWN)), SWT.UP, null);
        table.setTopIndex(0);
    }

    /**
     * Sorts the features in the tableView.
     *
     * @param comparator comparator to use for the sorting.
     * @param dir the direction to set the column SWT.UP or SWT.DOWN. If SWT.UP then the table item
     *        with index 0 is at the top of the table otherwise it is at the bottom of the table.
     * @param sortColumn the column that is being sorted
     */
    public void sort( Comparator<SimpleFeature> comparator, int dir, TableColumn sortColumn ) {
        checkWidget();

        FeatureTableContentProvider provider = (FeatureTableContentProvider) tableViewer
                .getContentProvider();

        boolean sorted = false;
        if (!comparator.equals(currentComparator)) {
            sorted = true;
            currentComparator = comparator;
            Collections.sort(provider.features, currentComparator);
        }
        Table table = tableViewer.getTable();
        if (table.getSortColumn() != sortColumn) {
            sorted = true;
            table.setSortColumn(sortColumn);
            while( Display.getCurrent().readAndDispatch() );
        }
        if (table.getSortColumn() != null && dir != table.getSortDirection()) {
            sorted = true;
            table.setSortDirection(dir);
            while( Display.getCurrent().readAndDispatch() );
        }
        if (sorted) {
            table.deselectAll();
            table.clearAll();
        }

    }

    /**
     * Resorts the table using the last comparator. This is useful for cases where features have
     * been added to the table
     *
     * @param refreshTable
     */
    void sort( boolean refreshTable ) {
        if (currentComparator == null)
            return;

        FeatureTableContentProvider provider = (FeatureTableContentProvider) tableViewer
                .getContentProvider();

        Collections.sort(provider.features, currentComparator);

        tableViewer.getTable().deselectAll();
        if (refreshTable)
            tableViewer.getTable().clearAll();
    }

    public TableViewer getViewer() {
        return tableViewer;
    }

    FeatureTableSelectionProvider getSelectionProvider() {
        return selectionProvider;
    }

    public void setSelection( StructuredSelection selection, boolean reveal ) {
        selectionProvider.setSelection(selection, reveal);
    }

    public int getSelectionCount() {
        return selectionProvider.getSelectionFids().size();
    }

    public void select( Set<FeatureId> selection ) {
        getSelectionProvider().getSelectionFids().clear();
        int j = 0;
        int firstMatch = -1;
        for( FeatureId id : selection ) {
            selectionProvider.getSelectionFids().add(id.getID());
            if (firstMatch == -1) {
                firstMatch = j;
            }
            j++;
        }
        Table table = tableViewer.getTable();
        if (firstMatch != -1) {
            // display the selected item
            table.setTopIndex(firstMatch);
        }
        // trigger a refresh of table
        table.clearAll();
        // tell the world..
        selectionProvider.notifyListeners();
    }
    public void select( String cql, boolean selectAll ) throws CQLException {
        Filter filter = (Filter) CQL.toFilter(cql);

        FeatureTableContentProvider provider = (FeatureTableContentProvider) this.tableViewer
                .getContentProvider();
        List<SimpleFeature> toSearch = provider.features;

        IProgressMonitor progressMonitor = getSelectionProvider().progressMonitor;
        if (progressMonitor != null) {
            progressMonitor.setCanceled(true);
        }
        getSelectionProvider().getSelectionFids().clear();
        int j = 0;
        int firstMatch = -1;
        OUTER: for( SimpleFeature feature : toSearch ) {
            if (filter.evaluate(feature)) {
                selectionProvider.getSelectionFids().add(feature.getID());
                if (firstMatch == -1)
                    firstMatch = j;
                if (!selectAll)
                    break OUTER;
            }
            j++;
        }

        Table table = tableViewer.getTable();
        if (firstMatch != -1) {
            // display the selected item
            table.setTopIndex(firstMatch);
        }
        // trigger a refresh of table
        table.clearAll();
        // tell the world..
        selectionProvider.notifyListeners();
    }

    /**
     * select the features found that has the text. Only the attributes indicated are searched. If
     * {@link #ALL} is selected then all attributes will be searched
     *
     * @param text text to search for it will first be assumed that it is a reg ex expression
     * @param attributes the attributes to search. See {@link #ALL}
     * @param selectAll if true all matched features will be selected otherwise just the first
     *        feature
     */
    public void select( String text, String[] attributes, boolean selectAll )
            throws PatternSyntaxException {

        Pattern pattern = compilePattern(text);

        if (pattern == null) {
            return;
        }

        FeatureTableContentProvider provider = (FeatureTableContentProvider) this.tableViewer
                .getContentProvider();
        List<SimpleFeature> toSearch = provider.features;

        IProgressMonitor progressMonitor = getSelectionProvider().progressMonitor;
        if (progressMonitor != null) {
            progressMonitor.setCanceled(true);
        }
        getSelectionProvider().getSelectionFids().clear();
        int j = 0;
        int firstMatch = -1;
        OUTER: for( SimpleFeature feature : toSearch ) {
            if (searchFeature(feature, pattern, attributes)) {
                if (firstMatch == -1)
                    firstMatch = j;
                if (!selectAll)
                    break OUTER;
            }
            j++;
        }

        Table table = tableViewer.getTable();
        if (firstMatch != -1) {
            // display the selected item
            table.setTopIndex(firstMatch);
        }
        // trigger a refresh of table
        table.clearAll();
        // tell the world..
        selectionProvider.notifyListeners();
    }

    private Pattern compilePattern( final String text ) {

        String[] parts = text.split("\\|");

        StringBuilder builder = new StringBuilder();

        for( String string : parts ) {
            String pre = ".*";
            String post = ".*";
            if (string.startsWith("^") || string.startsWith(".") || string.startsWith("\\A")) {
                pre = "";
            }
            if (string.startsWith("&") || string.startsWith("\\Z") || string.startsWith("\\z")) {
                post = "";
            }
            builder.append(pre);
            builder.append(string);
            builder.append(post);
            builder.append('|');
        }
        if (builder.length() > 0) {
            builder.deleteCharAt(builder.length() - 1);
        }
        Pattern pattern;
        try {
            pattern = Pattern.compile(builder.toString(), Pattern.CASE_INSENSITIVE);
        } catch (IllegalArgumentException e) {
            try {
                pattern = Pattern.compile(".*" + convertToLiteral(text) + ".*"); //$NON-NLS-1$//$NON-NLS-2$
            } catch (IllegalArgumentException e2) {
                return null;
            }
        }
        return pattern;
    }

    private boolean searchFeature( SimpleFeature feature, Pattern pattern, String[] attributes ) {
        SimpleFeatureType featureType = feature.getFeatureType();
        if (attributes == ALL) {
            for( int i = 0; i < featureType.getAttributeCount(); i++ ) {
                if (matches(pattern, feature.getAttribute(i))) {
                    selectionProvider.getSelectionFids().add(feature.getID());
                    return true;
                }
            }
        }
        for( int i = 0; i < attributes.length; i++ ) {
            if (matches(pattern, feature.getAttribute(attributes[i]))) {
                getSelectionProvider().getSelectionFids().add(feature.getID());
                return true;

            }
        }
        return false;
    }

    private String convertToLiteral( String text ) {
        String text2 = text.replace("\\", "\\\\"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("*", "\\*"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("+", "\\+"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace(".", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("?", "\\?"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("[", "\\["); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("]", "\\]"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("^", "\\^"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("-", "\\-"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("&", "\\&"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("(", "\\("); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace(")", "\\)"); //$NON-NLS-1$ //$NON-NLS-2$
        text2 = text2.replace("|", "\\|"); //$NON-NLS-1$ //$NON-NLS-2$
        return text2;
    }

    private boolean matches( Pattern pattern, Object attribute ) {
        if (attribute == null) {
            attribute = "";
        }
        String stringValue = attribute.toString();
        return pattern.matcher(stringValue).matches();
    }

    public void addLoadingListener( IFeatureTableLoadingListener listener ) {
        loadingListeners.add(listener);
    }

    public void remove( IFeatureTableLoadingListener listener ) {
        loadingListeners.remove(listener);
    }

    protected void notifyLoadingListeners( LoadingEvent event ) {
        this.checkWidget();
        if (event.loading) {
            if (event.monitor == null)
                throw new NullPointerException();
            for( IFeatureTableLoadingListener listener : loadingListeners ) {
                try {
                    listener.loadingStarted(event.monitor);
                } catch (Throwable e) {
                    UiPlugin.log(listener + " threw an exception", e); //$NON-NLS-1$
                }
            }
        } else {
            for( IFeatureTableLoadingListener listener : loadingListeners ) {
                try {
                    listener.loadingStopped(event.canceled);
                } catch (Throwable e) {
                    UiPlugin.log(listener + " threw an exception", e); //$NON-NLS-1$
                }
            }
        }
    }

    /**
     * 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 ) {
        if (features == null)
            return; // nothing to update since the table is not in use... Should this be an
                    // exception?
        FeatureTableContentProvider provider = (FeatureTableContentProvider) tableViewer
                .getContentProvider();
        provider.update(features2);
    }

    /**
     * Checks all the lists, caches, content providers, etc... are consistent with each other. This
     * is an expensive method so should be called with care. A test is a good example.
     */
    public void assertInternallyConsistent() {
        if (tableViewer.getContentProvider() != null) {
            FeatureTableContentProvider provider = (FeatureTableContentProvider) tableViewer
                    .getContentProvider();
            provider.assertInternallyConsistent();
        }
    }

    /**
     * Removes the selected features (the features selected by the owning
     * {@link FeatureTableControl}).
     *
     * @return returns a collection of the deleted features
     * @see #setSelection(ISelection)
     * @see #setSelection(StructuredSelection, boolean)
     */
    public FeatureCollection<SimpleFeatureType, SimpleFeature> deleteSelection() {
        FeatureTableContentProvider provider = (FeatureTableContentProvider) tableViewer
                .getContentProvider();
        return provider.deleteSelection();
    }

    /**
     * Sets the context Menu used by the table view. Not menu is used for the message box.
     *
     * @param contextMenu menu manager used for creating the menu.
     */
    public void setMenuManager( MenuManager contextMenu ) {
        checkWidget();
        this.contextMenu = contextMenu;
        if (tableViewer != null && tableViewer.getControl() != null) {
            Menu oldMenu = tableViewer.getControl().getMenu();
            if (oldMenu != null)
                oldMenu.dispose();
            Menu menu = contextMenu.createContextMenu(tableViewer.getControl());
            tableViewer.getControl().setMenu(menu);
        }
    }

    public void setSelectionColor( IProvider<RGB> selectionColor ) {
        this.selectionColor = selectionColor;
        if (tableViewer != null) {
            FeatureTableLabelProvider labelProvider = (FeatureTableLabelProvider) tableViewer
                    .getLabelProvider();
            labelProvider.setSelectionColor(selectionColor);
        }
    }

}
TOP

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

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.