/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package de.cismet.cismap.commons.util; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import de.cismet.cismap.commons.features.DefaultFeatureCollection; import de.cismet.cismap.commons.features.DefaultFeatureServiceFeature; import de.cismet.cismap.commons.features.Feature; import de.cismet.cismap.commons.features.FeatureCollectionEvent; import de.cismet.cismap.commons.features.FeatureCollectionListener; import de.cismet.cismap.commons.features.FeatureServiceFeature; import de.cismet.cismap.commons.features.FeatureWithId; import de.cismet.cismap.commons.featureservice.AbstractFeatureService; import de.cismet.cismap.commons.gui.MappingComponent; import de.cismet.cismap.commons.gui.attributetable.AttributeTable; import de.cismet.cismap.commons.gui.piccolo.PFeature; import de.cismet.cismap.commons.gui.piccolo.eventlistener.SelectionListener; import de.cismet.cismap.commons.interaction.CismapBroker; import de.cismet.cismap.commons.retrieval.RepaintEvent; import de.cismet.cismap.commons.retrieval.RepaintListener; /** * Determines the selected features and sort them by their corresponding service. * * @author therter * @version $Revision$, $Date$ */ public class SelectionManager implements FeatureCollectionListener, ListSelectionListener { //~ Instance fields -------------------------------------------------------- // this maps contains pre-calculated values private final Map<AbstractFeatureService, Set<Feature>> selectedFeatures = new HashMap<AbstractFeatureService, Set<Feature>>(); private final Map<AbstractFeatureService, Integer> modifiableFeaturesCount = new HashMap<AbstractFeatureService, Integer>(); // The selected elements of this table will be considered private final HashMap<AbstractFeatureService, AttributeTable> consideredAttributeTables = new HashMap<AbstractFeatureService, AttributeTable>(); private List<AbstractFeatureService> editableServices = new ArrayList<AbstractFeatureService>(); private final List<SelectionChangedListener> listener = new ArrayList<SelectionChangedListener>(); private boolean selectionChangeInProgress = false; //~ Constructors ----------------------------------------------------------- /** * Creates a new SelectionManager object. */ private SelectionManager() { editableServices = Collections.synchronizedList(editableServices); final MappingComponent mc = CismapBroker.getInstance().getMappingComponent(); mc.getFeatureCollection().addFeatureCollectionListener(this); mc.addRepaintListener(new RepaintListener() { @Override public void repaintStart(final RepaintEvent e) { } @Override public void repaintComplete(final RepaintEvent e) { if (e.getRetrievalEvent().getRetrievalService() instanceof AbstractFeatureService) { synchronizeSelectionWithMap( (AbstractFeatureService)e.getRetrievalEvent().getRetrievalService()); } } @Override public void repaintError(final RepaintEvent e) { } }); } //~ Methods ---------------------------------------------------------------- /** * synchronizes the table selection with the PFeatures in the map. * * @param service DOCUMENT ME! */ private void synchronizeSelectionWithMap(final AbstractFeatureService service) { final List<PFeature> features = new ArrayList<PFeature>(); if (service.getPNode() != null) { features.addAll(service.getPNode().getChildrenReference()); } final List<Feature> selectedServiceFeatures = getSelectedFeatures(service); if (selectedServiceFeatures != null) { final int[] selectedFeatureIds = new int[selectedServiceFeatures.size()]; final List<Feature> featuresToSelect = new ArrayList<Feature>(); final List<Feature> featuresToUnselect = new ArrayList<Feature>(); int index = -1; for (final Feature f : selectedServiceFeatures) { if (f instanceof FeatureWithId) { selectedFeatureIds[++index] = ((FeatureWithId)f).getId(); } } Arrays.sort(selectedFeatureIds); final SelectionListener sl = (SelectionListener)CismapBroker.getInstance().getMappingComponent() .getInputEventListener() .get(MappingComponent.SELECT); for (final PFeature pfeature : features) { final Feature feature = pfeature.getFeature(); if (feature instanceof FeatureWithId) { final boolean selected = Arrays.binarySearch( selectedFeatureIds, ((FeatureWithId)feature).getId()) >= 0; if (selected != pfeature.isSelected()) { pfeature.setSelected(selected); } if (selected) { sl.addSelectedFeature(pfeature); featuresToSelect.add(feature); } else { sl.removeSelectedFeature(pfeature); featuresToUnselect.add(feature); } } } selectionChangeInProgress = true; CismapBroker.getInstance().getMappingComponent().getFeatureCollection().addToSelection(featuresToSelect); CismapBroker.getInstance().getMappingComponent().getFeatureCollection().unselect(featuresToUnselect); selectionChangeInProgress = false; } } /** * DOCUMENT ME! * * @param featureList DOCUMENT ME! */ public void addSelectedFeatures(final List<? extends Feature> featureList) { setSelectedFeatures(featureList, false, true); fireSelectionChangedEvent(); } /** * DOCUMENT ME! * * @param featureList DOCUMENT ME! */ public void setSelectedFeatures(final List<? extends Feature> featureList) { setSelectedFeatures(featureList, true, true); fireSelectionChangedEvent(); } /** * DOCUMENT ME! * * @param service DOCUMENT ME! * @param featureList DOCUMENT ME! */ public void setSelectedFeaturesForService(final AbstractFeatureService service, final List<? extends Feature> featureList) { selectedFeatures.put(service, null); removeSelectionFromMap(service); for (final Feature f : featureList) { if (f instanceof DefaultFeatureServiceFeature) { final DefaultFeatureServiceFeature fsf = (DefaultFeatureServiceFeature)f; if ((fsf.getLayerProperties() != null) && (fsf.getLayerProperties().getFeatureService() != null)) { Set<Feature> list = selectedFeatures.get(fsf.getLayerProperties().getFeatureService()); if (list == null) { list = new HashSet<Feature>(); selectedFeatures.put(fsf.getLayerProperties().getFeatureService(), list); } list.add(fsf); } } } synchronizeSelectionWithMap(service); fireSelectionChangedEvent(); } /** * Removes the given feature from the selection. * * @param feature the feature to remove */ public void removeSelectedFeatures(final Feature feature) { removeSelectedFeatures(Collections.nCopies(1, feature)); } /** * Removes the given features from the selection. * * @param featureList the features to remove */ public void removeSelectedFeatures(final List<? extends Feature> featureList) { final Map<AbstractFeatureService, TreeSet<DefaultFeatureServiceFeature>> selectedFeaturesToRemove = new HashMap<AbstractFeatureService, TreeSet<DefaultFeatureServiceFeature>>(); if (featureList == null) { return; } // save the features ordered by the corresponding service for (final Feature f : featureList) { if (f instanceof DefaultFeatureServiceFeature) { final DefaultFeatureServiceFeature fsf = (DefaultFeatureServiceFeature)f; if ((fsf.getLayerProperties() != null) && (fsf.getLayerProperties().getFeatureService() != null)) { TreeSet<DefaultFeatureServiceFeature> list = selectedFeaturesToRemove.get( fsf.getLayerProperties().getFeatureService()); if (list == null) { list = new TreeSet<DefaultFeatureServiceFeature>(); selectedFeaturesToRemove.put(fsf.getLayerProperties().getFeatureService(), list); } list.add(fsf); } } } for (final AbstractFeatureService service : selectedFeaturesToRemove.keySet()) { final TreeSet<DefaultFeatureServiceFeature> list = selectedFeaturesToRemove.get(service); final Set<Feature> features = selectedFeatures.get(service); if (features != null) { for (final DefaultFeatureServiceFeature f : list) { if (features.contains(f)) { features.remove(f); } } } // remove selected features from the map final MappingComponent map = CismapBroker.getInstance().getMappingComponent(); final SelectionListener sl = (SelectionListener)map.getInputEventListener().get(MappingComponent.SELECT); final List<PFeature> sel = sl.getAllSelectedPFeatures(); final List<Feature> toBeUnselected = new ArrayList<Feature>(); for (final PFeature feature : sel) { if (feature.getFeature() instanceof FeatureServiceFeature) { final FeatureServiceFeature fsf = (FeatureServiceFeature)feature.getFeature(); if ((fsf.getLayerProperties() != null) && featureList.contains(fsf) && feature.isSelected()) { feature.setSelected(false); sl.removeSelectedFeature(feature); toBeUnselected.add(feature.getFeature()); } } } selectionChangeInProgress = true; ((DefaultFeatureCollection)CismapBroker.getInstance().getMappingComponent().getFeatureCollection()) .unselect( toBeUnselected); selectionChangeInProgress = false; fireSelectionChangedEvent(); } } /** * Set the selected features of a specific service. * * @param featureList DOCUMENT ME! * @param removeOldSelection service DOCUMENT ME! * @param syncMap DOCUMENT ME! */ private void setSelectedFeatures(final List<? extends Feature> featureList, final boolean removeOldSelection, final boolean syncMap) { if (removeOldSelection) { selectedFeatures.clear(); if (syncMap) { removeSelectionFromMap(); } } for (final Feature f : featureList) { if (f instanceof DefaultFeatureServiceFeature) { final DefaultFeatureServiceFeature fsf = (DefaultFeatureServiceFeature)f; if ((fsf.getLayerProperties() != null) && (fsf.getLayerProperties().getFeatureService() != null)) { Set<Feature> list = selectedFeatures.get(fsf.getLayerProperties().getFeatureService()); if (list == null) { list = new HashSet<Feature>(); selectedFeatures.put(fsf.getLayerProperties().getFeatureService(), list); } list.add(fsf); } } } if (syncMap) { for (final AbstractFeatureService service : selectedFeatures.keySet()) { synchronizeSelectionWithMap(service); } } } /** * DOCUMENT ME! */ private void removeSelectionFromMap() { final MappingComponent map = CismapBroker.getInstance().getMappingComponent(); final SelectionListener sl = (SelectionListener)map.getInputEventListener().get(MappingComponent.SELECT); final List<PFeature> sel = sl.getAllSelectedPFeatures(); final List<Feature> toBeUnselected = new ArrayList<Feature>(); for (final PFeature feature : sel) { if (feature.isSelected()) { feature.setSelected(false); sl.removeSelectedFeature(feature); toBeUnselected.add(feature.getFeature()); } } selectionChangeInProgress = true; ((DefaultFeatureCollection)CismapBroker.getInstance().getMappingComponent().getFeatureCollection()).unselect( toBeUnselected); selectionChangeInProgress = false; } /** * DOCUMENT ME! * * @param service DOCUMENT ME! */ private void removeSelectionFromMap(final AbstractFeatureService service) { final MappingComponent map = CismapBroker.getInstance().getMappingComponent(); final SelectionListener sl = (SelectionListener)map.getInputEventListener().get(MappingComponent.SELECT); final List<PFeature> sel = sl.getAllSelectedPFeatures(); final List<Feature> toBeUnselected = new ArrayList<Feature>(); for (final PFeature feature : sel) { if (feature.getFeature() instanceof FeatureServiceFeature) { final FeatureServiceFeature fsf = (FeatureServiceFeature)feature.getFeature(); if ((fsf.getLayerProperties() != null) && fsf.getLayerProperties().getFeatureService().equals(service) && feature.isSelected()) { feature.setSelected(false); sl.removeSelectedFeature(feature); toBeUnselected.add(feature.getFeature()); } } } selectionChangeInProgress = true; ((DefaultFeatureCollection)CismapBroker.getInstance().getMappingComponent().getFeatureCollection()).unselect( toBeUnselected); selectionChangeInProgress = false; } /** * DOCUMENT ME! */ public void clearSelection() { selectedFeatures.clear(); removeSelectionFromMap(); fireSelectionChangedEvent(); } /** * DOCUMENT ME! * * @param service DOCUMENT ME! */ public void clearSelection(final AbstractFeatureService service) { selectedFeatures.put(service, null); removeSelectionFromMap(service); fireSelectionChangedEvent(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public List<Feature> getSelectedFeatures() { final List<Feature> features = new ArrayList<Feature>(); for (final AbstractFeatureService service : selectedFeatures.keySet()) { final Set<Feature> serviceFeatures = selectedFeatures.get(service); if (serviceFeatures != null) { features.addAll(serviceFeatures); } } return features; } /** * Returns the selected features of the given service. * * @param service all selected features of this service will be returned * * @return The selected features of the given service */ public List<Feature> getSelectedFeatures(final AbstractFeatureService service) { final Set set = selectedFeatures.get(service); if (set != null) { return new ArrayList<Feature>(set); } else { return null; } } /** * Returns the instance of the SelectionManager and creates a new one, if required. * * @return The instance of the SelectionManager */ public static SelectionManager getInstance() { return LazyInitialiser.INSTANCE; } /** * Determines the number of selected features for the given service. * * @param service DOCUMENT ME! * * @return The number of selected features of the given service */ public Integer getSelectedFeaturesCount(final AbstractFeatureService service) { if (service == null) { return null; } final Set<Feature> features = selectedFeatures.get(service); if (features == null) { return 0; } else { return features.size(); } } /** * DOCUMENT ME! * * @param service DOCUMENT ME! * * @return The number of modifiable features of the given service */ public Integer getModifiableFeaturesCount(final AbstractFeatureService service) { if (service == null) { return null; } if (editableServices.contains(service)) { return getSelectedFeaturesCount(service); } else { return null; } } /** * Switch the processing mode of the given service. * * @param service DOCUMENT ME! */ public void switchProcessingMode(final AbstractFeatureService service) { if (editableServices.contains(service)) { editableServices.remove(service); } else { editableServices.add(service); } fireSelectionChangedEvent(); } /** * DOCUMENT ME! * * @return All modifiable services */ public List<AbstractFeatureService> getEditableServices() { return editableServices; } /** * Adds an AttributeTable that should be considered, when the selected features will be determined. * * @param table DOCUMENT ME! */ public void addConsideredAttributeTable(final AttributeTable table) { consideredAttributeTables.put(table.getFeatureService(), table); table.addListSelectionListener(this); } /** * DOCUMENT ME! * * @param table DOCUMENT ME! */ public void removeConsideredAttributeTable(final AttributeTable table) { consideredAttributeTables.remove(table.getFeatureService()); table.removeListSelectionListener(this); } @Override public void featuresAdded(final FeatureCollectionEvent fce) { } @Override public void allFeaturesRemoved(final FeatureCollectionEvent fce) { } @Override public void featuresRemoved(final FeatureCollectionEvent fce) { } @Override public void featuresChanged(final FeatureCollectionEvent fce) { } @Override public void featureSelectionChanged(final FeatureCollectionEvent fce) { // for (final AttributeTable table : consideredAttributeTables.values()) { // table.applySelection(this, new ArrayList<Feature>(), true); // } // // selectedStandaloneFeatures.clear(); if (selectionChangeInProgress) { return; } final MappingComponent map = CismapBroker.getInstance().getMappingComponent(); final SelectionListener sl = (SelectionListener)map.getInputEventListener().get(MappingComponent.SELECT); final boolean featuresAdded = sl.isFeatureAdded(); if ((fce != null) && (fce.getFeatureCollection() != null) && (fce.getFeatureCollection().getSelectedFeatures() != null)) { final List<Feature> selectedMapFeatures = new ArrayList<Feature>((Collection<Feature>) fce.getFeatureCollection().getSelectedFeatures()); setSelectedFeatures(selectedMapFeatures, !featuresAdded, false); if (sl.getLastUnselectedFeatures() != null) { final Set<Feature> deselectedFeatures = new HashSet<Feature>(); deselectedFeatures.addAll(sl.getLastUnselectedFeatures()); deselectedFeatures.removeAll(selectedMapFeatures); final List<Feature> featuresToDeselect = new ArrayList(deselectedFeatures); if (!featuresToDeselect.isEmpty()) { removeSelectedFeatures(featuresToDeselect); } } } fireSelectionChangedEvent(); } @Override public void featureReconsiderationRequested(final FeatureCollectionEvent fce) { } @Override public void featureCollectionChanged() { } @Override public void valueChanged(final ListSelectionEvent e) { // if (!e.getValueIsAdjusting()) { // fireSelectionChangedEvent(); // } } /** * DOCUMENT ME! * * @param l DOCUMENT ME! */ public void addSelectionChangedListener(final SelectionChangedListener l) { this.listener.add(l); } /** * DOCUMENT ME! * * @param l DOCUMENT ME! */ public void removeSelectionChangedListener(final SelectionChangedListener l) { this.listener.remove(l); } /** * DOCUMENT ME! */ private void fireSelectionChangedEvent() { final SelectionChangedEvent e = new SelectionChangedEvent(this); for (final SelectionChangedListener l : listener) { l.selectionChanged(e); } } //~ Inner Classes ---------------------------------------------------------- /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ private static final class LazyInitialiser { //~ Static fields/initializers ----------------------------------------- static final SelectionManager INSTANCE = new SelectionManager(); } }