Java tutorial
/* Spatial Operations & Editing Tools for uDig * * Axios Engineering under a funding contract with: * Diputacin Foral de Gipuzkoa, Ordenacin Territorial * * http://b5m.gipuzkoa.net * http://www.axios.es * * (C) 2006, Diputacin Foral de Gipuzkoa, Ordenacin Territorial (DFG-OT). * DFG-OT agrees to licence under Lesser General Public License (LGPL). * * You can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software * Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package eu.udig.tools.merge.internal.view; import java.text.MessageFormat; import java.util.HashSet; import java.util.List; import java.util.Set; import net.refractions.udig.project.ILayer; import net.refractions.udig.project.command.UndoableMapCommand; import net.refractions.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 eu.udig.tools.internal.i18n.Messages; import eu.udig.tools.internal.ui.util.InfoMessage; import eu.udig.tools.internal.ui.util.LayerUtil; /** * 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) */ 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) { 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) { 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 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; } }