Package org.locationtech.udig.tools.merge.internal.view

Source Code of org.locationtech.udig.tools.merge.internal.view.MergeComposite

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2012, Refractions Research Inc.
* (C) 2006, Axios Engineering S.L. (Axios)
* (C) 2006, County Council of Gipuzkoa, Department of Environment and Planning
*
* 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 Axios BSD
* License v1.0 (http://udig.refractions.net/files/asd3-v10.html).
*/
package org.locationtech.udig.tools.merge.internal.view;

import java.text.MessageFormat;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.locationtech.udig.project.ILayer;
import org.locationtech.udig.project.command.UndoableMapCommand;
import org.locationtech.udig.project.ui.ApplicationGIS;
import org.locationtech.udig.project.ui.tool.IToolContext;

import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ViewForm;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
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.TableItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.geotools.factory.CommonFactoryFinder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.Id;
import org.opengis.filter.identity.FeatureId;

import org.locationtech.udig.tools.internal.i18n.Messages;
import org.locationtech.udig.tools.internal.ui.util.InfoMessage;
import org.locationtech.udig.tools.internal.ui.util.LayerUtil;
import org.locationtech.udig.tools.merge.MergeContext;

/**
* Merge Controls for composite
* <p>
* Presents the source features in tree view and result feature in table. The user can customize the
* merge.
* </p>
*
* @author Aritz Davila (www.axios.es)
* @author Mauricio Pazos (www.axios.es)
* @author Marco Foi (www.mcfoi.it)
*/
class MergeComposite extends Composite {

    private SashForm sashForm = null;

    private Composite compositeSourceFeatures = null;

    private CLabel cLabelSources = null;

    private Tree treeFeatures = null;

    private Composite compositeMergeFeature = null;

    private CLabel cLabelTarget = null;

    private Table tableMergeFeature = null;

    private Composite compositeMergeControls = null;

    private Label labelResult = null;

    private Label labelResultGeometry = null;

    private ViewForm viewForm = null;

    private Composite infoComposite = null;

    private String message = null;

    private CLabel messageTitle = null;

    private ImageRegistry registry = null;

    private Button trashButton = null;

    private MergeView mergeView = null;

    private Menu menu;

    /** data handle */
    private MergeFeatureBuilder mergeBuilder = null;

    /**
     * Union geometry
     */
    private static final String UNION = "Union"; //$NON-NLS-1$

    /**
     * Index of the column holding the attribute names in both views
     */
    private static final int NAME_COLUMN = 0;

    /**
     * Index of the column holding the attribute values in both views
     */
    private static final int VALUE_COLUMN = 1;

    /**
     * Label to use as attribute value in the merged view when an attribute is {@code null}
     */
    private static final String NULL_LABEL = "<null>"; //$NON-NLS-1$

    private CLabel messagePanel;

    public MergeComposite(Composite parent, int style) {

        super(parent, style);
        createContent();
    }

    /**
     * Creates the widget of this composite.
     */
    private void createContent() {

        this.setLayout(new FillLayout());

        viewForm = new ViewForm(this, SWT.NONE);
        viewForm.setLayout(new FillLayout());

        infoComposite = new Composite(viewForm, SWT.NONE);

        createCompositeInformation();
        viewForm.setTopLeft(infoComposite);

        sashForm = new SashForm(viewForm, SWT.V_SCROLL);
        sashForm.setOrientation(SWT.HORIZONTAL);

        sashForm.setLayout(new FillLayout());

        createCompositeSourceFeatures();
        createCompositeMergeFeature();
        createContextMenu();
        viewForm.setContent(sashForm);
    }

    /**
     * The composite that shows the information.
     */
    private void createCompositeInformation() {

        GridLayout gridLayout1 = new GridLayout();
        gridLayout1.numColumns = 2;

        infoComposite.setLayout(gridLayout1);

        GridData gridData = new GridData();
        gridData.horizontalAlignment = GridData.FILL;
        gridData.grabExcessHorizontalSpace = true;
        gridData.grabExcessVerticalSpace = true;
        gridData.horizontalSpan = 2;
        gridData.verticalAlignment = GridData.FILL;

        messageTitle = new CLabel(infoComposite, SWT.BOLD);
        messageTitle.setLayoutData(gridData);

        GridData gridData2 = new GridData();
        gridData2.horizontalAlignment = GridData.FILL;
        gridData2.grabExcessHorizontalSpace = true;
        gridData2.grabExcessVerticalSpace = true;
        gridData2.verticalAlignment = GridData.FILL;

        this.messagePanel = new CLabel(infoComposite, SWT.NONE);
        this.messagePanel.setLayoutData(gridData2);

    }

    /**
     * The composite that shows the resultant merge feature.
     */
    private void createCompositeMergeFeature() {

        GridData gridData = new GridData();
        gridData.horizontalAlignment = GridData.FILL;
        gridData.grabExcessHorizontalSpace = true;
        gridData.grabExcessVerticalSpace = true;
        gridData.verticalAlignment = GridData.FILL;

        GridLayout gridLayout1 = new GridLayout();
        gridLayout1.numColumns = 1;
        gridLayout1.makeColumnsEqualWidth = true;

        compositeMergeFeature = new Composite(sashForm, SWT.BORDER);
        compositeMergeFeature.setLayout(gridLayout1);

        cLabelTarget = new CLabel(compositeMergeFeature, SWT.NONE);
        cLabelTarget.setText(Messages.MergeFeaturesComposite_merge_feature);

        tableMergeFeature = new Table(compositeMergeFeature, SWT.MULTI);
        tableMergeFeature.setHeaderVisible(true);
        tableMergeFeature.setLayoutData(gridData);
        tableMergeFeature.setLinesVisible(true);

        createCompositeMergeControls();

        TableColumn tableColumnName = new TableColumn(tableMergeFeature, SWT.NONE);
        tableColumnName.setWidth(150);
        tableColumnName.setText(Messages.MergeFeaturesComposite_property);

        TableColumn tableColumnValue = new TableColumn(tableMergeFeature, SWT.NONE);
        tableColumnValue.setWidth(60);
        tableColumnValue.setText(Messages.MergeFeaturesComposite_value);
    }

    /**
     * The composite that shows the merge feature geometry.
     */
    private void createCompositeMergeControls() {

        GridData gridData3 = new GridData();
        gridData3.horizontalAlignment = GridData.FILL;
        gridData3.grabExcessHorizontalSpace = true;
        gridData3.verticalAlignment = GridData.CENTER;

        GridData gridData2 = new GridData();
        gridData2.horizontalAlignment = GridData.FILL;
        gridData2.grabExcessHorizontalSpace = true;
        gridData2.verticalAlignment = GridData.CENTER;

        GridData gridData1 = new GridData();
        gridData1.horizontalAlignment = GridData.FILL;
        gridData1.grabExcessHorizontalSpace = true;
        gridData1.verticalAlignment = GridData.CENTER;

        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 2;
        gridLayout.makeColumnsEqualWidth = true;

        compositeMergeControls = new Composite(compositeMergeFeature, SWT.NONE);
        compositeMergeControls.setLayout(gridLayout);
        compositeMergeControls.setLayoutData(gridData1);

        labelResult = new Label(compositeMergeControls, SWT.NONE);
        labelResult.setText(Messages.MergeFeaturesComposite_result_geometry);
        labelResult.setLayoutData(gridData2);

        labelResultGeometry = new Label(compositeMergeControls, SWT.NONE);
        labelResultGeometry.setText(Messages.MergeFeaturesComposite_result);
        labelResultGeometry.setLayoutData(gridData3);

    }

    /**
     * The composite that shows the tree with the source features.
     */
    private void createCompositeSourceFeatures() {

        GridData gridData4 = new GridData();
        gridData4.horizontalAlignment = GridData.FILL;
        gridData4.grabExcessHorizontalSpace = true;
        gridData4.grabExcessVerticalSpace = true;
        gridData4.horizontalSpan = 2;
        gridData4.verticalAlignment = GridData.FILL;

        GridData gridData3 = new GridData();
        gridData3.horizontalAlignment = GridData.FILL;
        gridData3.grabExcessHorizontalSpace = false;
        gridData3.grabExcessVerticalSpace = false;
        gridData3.verticalAlignment = GridData.FILL;

        GridData gridData2 = new GridData();
        gridData2.horizontalAlignment = GridData.END;
        gridData2.grabExcessHorizontalSpace = false;
        gridData2.grabExcessVerticalSpace = false;
        gridData2.verticalAlignment = GridData.END;

        GridLayout gridLayout2 = new GridLayout();
        gridLayout2.numColumns = 2;
        gridLayout2.makeColumnsEqualWidth = true;

        compositeSourceFeatures = new Composite(sashForm, SWT.BORDER);
        compositeSourceFeatures.setLayout(gridLayout2);

        cLabelSources = new CLabel(compositeSourceFeatures, SWT.NONE);
        cLabelSources.setText(Messages.MergeFeaturesComposite_source);
        cLabelSources.setLayoutData(gridData3);

        createImageRegistry();

        trashButton = new Button(compositeSourceFeatures, SWT.NONE);
        trashButton.setLayoutData(gridData2);
        trashButton.setToolTipText(Messages.MergeView_remove_tool_tip);
        trashButton.setImage(getImage());
        trashButton.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseUp(MouseEvent e) {

                deleteSourceFeatures();
            }
        });

        treeFeatures = new Tree(compositeSourceFeatures, SWT.SINGLE | SWT.CHECK);
        treeFeatures.setHeaderVisible(true);
        treeFeatures.setLayoutData(gridData4);
        treeFeatures.setLinesVisible(true);

        treeFeatures.addListener(SWT.Selection, new Listener() {

            public void handleEvent(Event event) {

                if (event.detail == SWT.CHECK) {
                    handleTreeEvent(event);
                }
            }
        });

        treeFeatures.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                MergeContext mergeContextSingleton = MergeContext.getInstance();
                if (mergeContextSingleton.getMergeMode() == MergeContext.MERGEMODE_TOOL) {
                    // The handleTreeEventClick function selects in map the clicked treeFeature:
                    // such behaviour is not compatible with MERGEMODE_OPERATION due to filterChange
                    // listeners in place when in that mode, so the function call is allowed just
                    // when in TOOL mode
                handleTreeEventClick(e);
                }
            }
        });

        treeFeatures.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseDown(MouseEvent e) {

                if (e.button == 3) {
                    showContextMenu(e);
                }

            }
        });

        TreeColumn treeColumnFeature = new TreeColumn(treeFeatures, SWT.NONE);
        treeColumnFeature.setWidth(150);
        treeColumnFeature.setText(Messages.MergeFeaturesComposite_feature);

        TreeColumn treeColumnValue = new TreeColumn(treeFeatures, SWT.NONE);
        treeColumnValue.setWidth(60);
        treeColumnValue.setText(Messages.MergeFeaturesComposite_value);
    }

    /**
     * Removes the set of features selected
     */
    private void deleteSourceFeatures() {

        TreeItem[] items = this.treeFeatures.getSelection();
        for (int i = 0; i < items.length; i++) {
            String id = items[i].getText();

            List<SimpleFeature> sourceFeatures = this.mergeBuilder.getSourceFeatures();
            for (SimpleFeature feature : sourceFeatures) {

                if (feature.getID().equals(id)) {

                    this.mergeBuilder.removeFromSourceFeatures(feature);

                    unselect(feature);

                    break;
                }
            }

            // deletes from three view
            items[i].dispose();
        }
        changed();

    }

    /**
     * deselects the merged features
     *
     * @param unselectedFeature
     */
    private void unselect(SimpleFeature unselectedFeature) {

        IToolContext context = this.mergeView.getContext();
        UndoableMapCommand unselectCommand = context.getSelectionFactory().createNoSelectCommand();

        context.sendASyncCommand(unselectCommand);
    }

    /**
     * Creates the context menu that will be showed when user do a right click on the
     * treeSourceFeatures.
     */
    private void createContextMenu() {

        final MenuManager contextMenu = new MenuManager();

        contextMenu.setRemoveAllWhenShown(true);

        this.menu = contextMenu.createContextMenu(compositeSourceFeatures);
    }

    private Image getImage() {

        return registry.get("trash"); //$NON-NLS-1$
    }

    private void createImageRegistry() {

        registry = new ImageRegistry();

        String imgFile = "images/" + "trash" + ".gif"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        registry.put("trash", ImageDescriptor.createFromFile(MergeComposite.class, imgFile)); //$NON-NLS-1$
    }

    /**
     * Display in this composite the features of the indeed layer
     *
     * @param selectedFeatures
     * @param layer
     */
    public void display(List<SimpleFeature> selectedFeatures, ILayer layer) {

        addSourceFeatures(selectedFeatures);
        display();
    }

    /**
     * Populate its data.
     */
    public void display() {

        //assert mergeBuilder != null : "the merge builder was not been set"; //$NON-NLS-1$
        this.mergeBuilder = getMergeBuilder();

        // presents the source in tree view
        populateSourceFeaturesView();

        populateMergeFeatureView();

        changed();
    }

    // /**
    // * Clears source tree and merge features table data.
    // */
    // private void init() {
    //
    // treeFeatures.removeAll();
    // treeFeatures.clearAll(true);
    // tableMergeFeature.removeAll();
    // tableMergeFeature.clearAll();
    // }

    // /**
    // * Set the builder and adds a change listener.
    // */
    // private MergeFeatureBuilder getBuilder() {
    //
    // if(this.mergeBuilder != null){
    // return this.mergeBuilder;
    // }
    // // it is required a new builder
    // this.mergeBuilder = new MergeFeatureBuilder(this.mergeView.getContext().getSelectedLayer());
    // this.mergeBuilder
    // .addChangeListener(new MergeFeatureBuilder.ChangeListener() {
    //
    // public void attributeChanged(MergeFeatureBuilder builder,
    // int attributeIndex, Object oldValue) {
    //
    // if (attributeIndex == builder.getDefaultGeometryIndex()) {
    // mergeGeometryChanged(builder);
    // }
    // changed();
    // }
    //
    // });
    // // set up initial feedback
    // mergeGeometryChanged(mergeBuilder);
    //
    // // display();
    // return this.mergeBuilder;
    //
    // }

    /**
     * Call back function to report a change in the merged geometry attribute
     *
     * @param builder
     */
    private void mergeGeometryChanged(MergeFeatureBuilder builder) {

        String geomName = builder.getPrittyMergeGeometry();
        if (builder.isGeometriesUnion()) {
            labelResultGeometry.setText(UNION);
        } else {
            labelResultGeometry.setText(geomName.toString());
        }

        final String msg = MessageFormat.format(Messages.MergeFeaturesComposite_result_will_be,
                geomName);

        setMessage(msg, InfoMessage.Type.INFORMATION);
    }

    /**
     * Set the message showed on the information view.
     *
     * @param usrMessage
     * @param type
     */
    public void setMessage(final String usrMessage, final InfoMessage.Type type) {

        InfoMessage info = new InfoMessage(usrMessage, type);
        messagePanel.setImage(info.getImage());
        messagePanel.setText(info.getText());
        messageTitle.setText(Messages.MergeFeaturesComposite_merge_result_title);
    }

    /**
     * Call back function invoked every time a user interface event occurs over an item of the
     * source features view.
     * <p>
     * Applies the following logic:
     * <ul>
     * <li>If the TreeItem state change <code>event</code> was produced on a Feature item, selects
     * or deselects all the attributes of the corresponding feature
     * <li>If the TreeItem state change <code>event</code> was produced on an Attribute item, that
     * same item checked state will be respected, and all the attribute items at the same index for
     * the rest of the source features will become <b>unchecked</b>
     * <li>The internal {@link MergeFeatureBuilder} state will be updated accordingly, whether all
     * the attributes of a given feature has to be set for the target feature, or just the attribute
     * selected, depending on if the event were raised at a Feature item or an Attribute item
     * </ul>
     * </p>
     * <p>
     * No manipulation of the target feature view is done here. Instead, as this method calls
     * {@link #setAttributeValue(int, int, boolean)}, the {@link MergeFeatureBuilder} will raise
     * change events that will be catched up by {@link #changed()}
     * </p>
     *
     * @param event
     * @see #setSelectedFeature(int, boolean)
     * @see #selectAttributePropagate(int, int, boolean)
     * @see #setAttributeValue(int, int, boolean)
     */
    private void handleTreeEvent(Event event) {

        TreeItem item = (TreeItem) event.item;
        final boolean isFeatureItem = isFeatureItem(item);
        final boolean checked = item.getChecked();
        if (isFeatureItem) {
            int featureIndex = ((Integer) item.getData()).intValue();
            setSelectedFeature(featureIndex, checked);
        } else {
            TreeItem featureItem = item.getParentItem();
            int featureIndex = ((Integer) featureItem.getData()).intValue();
            int attributeIndex = ((Integer) item.getData()).intValue();
            selectAttributePropagate(featureIndex, attributeIndex, checked);
            setAttributeValue(featureIndex, attributeIndex, checked);
        }
    }

    /**
     * Called when a click is done on the sources tree. If the click was done over the name of a
     * feature, this feature is selected on the map.
     *
     * @param event
     */
    private void handleTreeEventClick(SelectionEvent event) {

        TreeItem item = (TreeItem) event.item;
        final boolean isFeatureItem = isFeatureItem(item);
        if (isFeatureItem) {
            Object obj = item.getData();
           
            if (obj instanceof Integer) {
                ILayer layer = mergeBuilder.getLayer();
                Filter filter = getSelectedFeatureFilter((Integer) obj);
                LayerUtil.presentSelection(layer, filter);
            }
           
        }
    }

    /**
     * Get the filter of the desired feature.
     *
     * @param index The index of this feature.
     * @return The geometry filter for this feature.
     */
    private Filter getSelectedFeatureFilter(Integer index) {

        FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
        String id = this.mergeBuilder.getFeature(index).getID();
        FeatureId fid = ff.featureId(id);
        Set<FeatureId> ids = new HashSet<FeatureId>(1);
        ids.add(fid);
        Id filter = ff.id(ids);

        return filter;
    }

    /**
     * Get the selected item, if it is a feature item, show the context menu with the option of
     * remove that feature from the tree list.
     *
     * @param e
     */
    private void showContextMenu(MouseEvent e) {

        Tree tree = (Tree) e.getSource();
        TreeItem selection = tree.getSelection()[0];

        boolean isFather = isFeatureItem(selection);
        if (isFather) {

            menu.setVisible(true);
        }
    }

    /**
     * Called whenever a merged feature attribute value changed to update the merge feature view
     */
    private void changed() {

        updateMergePanel();

        updateCommandButtonStatus();
    }

    /**
     * Updates the merge's attributes using the values present in the {@link MergeFeatureBuilder}
     * object.
     */
    private void updateMergePanel() {

        List<SimpleFeature> sourceFeatures = this.mergeBuilder.getSourceFeatures();
        if (sourceFeatures.size() == 0) {

            this.tableMergeFeature.removeAll();

        } else {

            final int attributeCount = this.mergeBuilder.getAttributeCount();

            for (int attIndex = 0; attIndex < attributeCount; attIndex++) {

                TableItem attrItem = this.tableMergeFeature.getItem(attIndex);

                String strValue;
                if (attIndex == this.mergeBuilder.getDefaultGeometryIndex()) {

                    strValue = this.mergeBuilder.getPrittyMergeGeometry();

                } else {
                    Object mergeFeatureProperty = this.mergeBuilder.getMergeAttribute(attIndex);

                    strValue = (mergeFeatureProperty == null) ? NULL_LABEL : String
                            .valueOf(mergeFeatureProperty);
                }
                attrItem.setText(VALUE_COLUMN, strValue);
            }
        }
    }

    private void updateCommandButtonStatus() {

        canMerge();
        canDelete();
    }

    private void canDelete() {

        this.trashButton.setEnabled(!this.mergeBuilder.getSourceFeatures().isEmpty());
    }

    /**
     * This is the single point where the {@link MergeFeatureBuilder} state is modified. This method
     * is called whenever a UI event in the source features view implies to change the value of an
     * attribute for the merge feature.
     * <p>
     * As a result of calling {@link MergeFeatureBuilder#copyAttributeToMerge(int, int)} or
     * {@link MergeFeatureBuilder#clearMergeAttribute(int)}, the change event will be caught up by
     * {@link #changed()} to reflect the change in the merge feature view
     * </p>
     *
     * @param sourceFeatureIndex the index of the source feature where the UI event event occurred
     * @param attributeIndex the index of the attribute of the source feature where the event
     *        occurred
     * @param setValue whether to set or clear the target feature attribute value at index
     *        <code>attributeIndex</code>
     * @see MergeFeatureBuilder#copyAttributeToMerge(int, int)
     * @see MergeFeatureBuilder#clearMergeAttribute(int)
     */
    private void setAttributeValue(int sourceFeatureIndex, int attributeIndex, boolean setValue) {

        if (setValue) {
            mergeBuilder.copyAttributeToMerge(sourceFeatureIndex, attributeIndex);
        } else {
            mergeBuilder.clearMergeAttribute(attributeIndex);
        }
    }

    private void setSelectedFeature(final int featureIndex, final boolean checked) {

        final int numFeatures = mergeBuilder.getFeatureCount();
        final int numAttributes = mergeBuilder.getAttributeCount();
        final TreeItem[] featureItems = treeFeatures.getItems();

        assert numFeatures == featureItems.length;

        for (int currFeatureIdx = 0; currFeatureIdx < numFeatures; currFeatureIdx++) {
            TreeItem featureItem = featureItems[currFeatureIdx];
            final boolean checkIt = checked && currFeatureIdx == featureIndex;
            featureItem.setChecked(checkIt);

            for (int attIdx = 0; attIdx < numAttributes; attIdx++) {
                selectAttribute(currFeatureIdx, attIdx, checkIt);
                if (currFeatureIdx == featureIndex) {
                    setAttributeValue(featureIndex, attIdx, checkIt);
                }
            }
        }
    }

    /**
     * Simply selects or deselects an item in the source features view, does not make any state
     * change in the {@link MergeFeatureBuilder}
     *
     * @param featureIndex the index of the feature item the desired attribute item belongs to
     * @param attributeIndex the index of the attribute to change the checked state
     * @param checked whether to check or uncheck the pointed attribute item
     */
    private void selectAttribute(final int featureIndex, final int attributeIndex,
            final boolean checked) {

        TreeItem featureItem = treeFeatures.getItem(featureIndex);
        TreeItem attItem = featureItem.getItem(attributeIndex);
        attItem.setChecked(checked);
    }

    /**
     * Selects a source feature attribute item in the source features view, propagating the contrary
     * effect to the TreeItems for the attributes of the other features at the same attribute index.
     * In other words, if selecting an attribute of one feature, deselects the same attribute of the
     * other features.
     *
     * @param featureIndex
     * @param attributeIndex
     * @param checked
     * @see #selectAttribute(int, int, boolean)
     */
    private void selectAttributePropagate(final int featureIndex, final int attributeIndex,
            final boolean checked) {

        final int numFeatures = mergeBuilder.getFeatureCount();

        for (int currFIndex = 0; currFIndex < numFeatures; currFIndex++) {
            boolean checkIt = checked && currFIndex == featureIndex;
            selectAttribute(currFIndex, attributeIndex, checkIt);
        }
    }

    /**
     * Checks if <code>item</code> corresponds to the root item of a source feature (a.k.a, it has
     * children)
     *
     * @param item
     * @return <code>true</code> if item is the root item of a Feature, <code>false</code> otherwise
     */
    private boolean isFeatureItem(final TreeItem item) {

        Object itemData = item.getData();
        boolean isFeatureItem = item.getItemCount() > 0;
        isFeatureItem = isFeatureItem && itemData instanceof Integer;
        return isFeatureItem;
    }

    /**
     * Populates the source feature panel.
     * <p>
     * Feature item's {@link TreeItem#getData() data} are <code>Integer</code> values with the
     * corresponding feature index in the {@link MergeFeatureBuilder}. Feature's attribute data are
     * the attribute value as per {@link MergeFeatureBuilder#getAttribute(int, int)}
     * </p>
     */
    private void populateSourceFeaturesView() {

        this.treeFeatures.removeAll();

        final int featureCount = mergeBuilder.getFeatureCount();
        // add feature as parent
        for (int featureIndex = 0; featureIndex < featureCount; featureIndex++) {
            TreeItem featureItem = new TreeItem(this.treeFeatures, SWT.NONE);
            // store the
            featureItem.setData(Integer.valueOf(featureIndex));
            featureItem.setText(mergeBuilder.getID(featureIndex));

            final int geometryIndex = mergeBuilder.getDefaultGeometryIndex();
            boolean isFisrtFeature = featureIndex == 0;
            // adds feature's attribute as child items
            for (int attIndex = 0; attIndex < mergeBuilder.getAttributeCount(); attIndex++) {

                TreeItem attrItem = new TreeItem(featureItem, SWT.NONE);

                // sets Name
                String attrName = mergeBuilder.getAttributeName(attIndex);
                attrItem.setText(0, attrName);
                attrItem.setData(Integer.valueOf(attIndex));

                // sets value
                Object attrValue = mergeBuilder.getAttribute(featureIndex, attIndex);
                String strValue = attrValue == null ? NULL_LABEL : String.valueOf(attrValue);
                attrItem.setText(VALUE_COLUMN, strValue);

                // check geometry only if it is not union and it is the first
                // feature
                if (isFisrtFeature && attIndex == geometryIndex) {
                    attrItem.setChecked(!mergeBuilder.isGeometriesUnion());
                } else {
                    attrItem.setChecked(isFisrtFeature);
                }
            }
            featureItem.setExpanded(isFisrtFeature);
        }
    }

    /**
     * Adds the feature as last element in the tree view that shows the source feature list.
     *
     * @param feature
     */
    private void displaySourceFeature(SimpleFeature feature) {

        setMessage("", InfoMessage.Type.NULL); //$NON-NLS-1$

        MergeFeatureBuilder builder = getMergeBuilder();

        if (!builder.canMerge(feature)) {
            this.message = Messages.MergeFeatureBehaviour_must_intersect;
            setMessage(this.message, InfoMessage.Type.WARNING);
            return;
        }
        int position = this.mergeBuilder.addSourceFeature(feature);
        if (position == -1) {
            // it was inserted previously y the source feature list.
            return;
        }

        TreeItem featureItem = new TreeItem(this.treeFeatures, SWT.NONE);
        // store the feature id
        featureItem.setData(position);
        featureItem.setText(builder.getID(position));

        // adds feature's attribute as child items
        for (int attIndex = 0; attIndex < builder.getAttributeCount(); attIndex++) {

            TreeItem attrItem = new TreeItem(featureItem, SWT.NONE);

            // sets Name
            String attrName = builder.getAttributeName(attIndex);
            attrItem.setText(0, attrName);
            attrItem.setData(Integer.valueOf(attIndex));

            // sets value
            Object attrValue = builder.getAttribute(position, attIndex);
            String strValue = attrValue == null ? NULL_LABEL : String.valueOf(attrValue);
            attrItem.setText(VALUE_COLUMN, strValue);

        }
        featureItem.setExpanded(true);
    }

    /**
     * Adds the target feature and its attributes. The merge feature view {@link TableItem}s value
     * property will hold integers representing the index of each attribute in the target feature's
     * schema
     */
    private void populateMergeFeatureView() {

        this.tableMergeFeature.removeAll();

        final int attributeCount = mergeBuilder.getAttributeCount();
        for (int attIndex = 0; attIndex < attributeCount; attIndex++) {

            TableItem attrItem = new TableItem(this.tableMergeFeature, SWT.NONE);
            attrItem.setData(attIndex);
            String attrName = mergeBuilder.getAttributeName(attIndex);
            attrItem.setText(NAME_COLUMN, attrName);
        }
    }

    public void setView(MergeView mergeView) {

        this.mergeView = mergeView;

    }

    /**
     * Adds the features to the existent source feature set.
     *
     * @param featureList
     */
    public void addSourceFeatures(List<SimpleFeature> featureList) {

        // If in operation mode, clean tree-view before adding new features
        if (MergeContext.getInstance().getMergeMode() == MergeContext.MERGEMODE_OPERATION) {
            this.mergeBuilder = getMergeBuilder();
            mergeBuilder.removeFromSourceFeaturesAll();
        }

        for (SimpleFeature feature : featureList) {
            displaySourceFeature(feature);
        }

    }

    public void addSourceFeature(SimpleFeature newFeature) {
        displaySourceFeature(newFeature);
    }

    /**
     * Checks the conditions to execute the merge operation.
     *
     * @return true if the features could be merge
     */
    private boolean canMerge() {

        boolean valid = true;

        // Must select two or more feature
        if (this.mergeBuilder.getSourceFeatures().size() < 2) {
            this.message = Messages.MergeFeatureBehaviour_select_two_or_more;
            setMessage(this.message, InfoMessage.Type.WARNING);
            valid = false;
        }
        this.mergeView.canMerge(valid);

        return valid;
    }

    /**
     * The builder used to merge the features. This is a factory method if the builder instance is
     * null a new one will be created
     *
     * @return {@link MergeFeatureBuilder}
     */
    public MergeFeatureBuilder getMergeBuilder() {
        if (this.mergeBuilder != null)
            return this.mergeBuilder;

        // create a new merge builder
        if (this.mergeView.isOperationMode()) {
            this.mergeBuilder = new MergeFeatureBuilder(
                    this.mergeView.getCurrentEventTriggeringLayer());
        } else {
            this.mergeBuilder = new MergeFeatureBuilder(this.mergeView.getContext()
                    .getSelectedLayer());
        }

        this.mergeBuilder.addChangeListener(new MergeFeatureBuilder.ChangeListener() {

            public void attributeChanged(MergeFeatureBuilder builder, int attributeIndex,
                    Object oldValue) {

                if (attributeIndex == builder.getDefaultGeometryIndex()) {
                    mergeGeometryChanged(builder);
                }
                changed();
            }
        });
        return this.mergeBuilder;
    }
}
TOP

Related Classes of org.locationtech.udig.tools.merge.internal.view.MergeComposite

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.