private Collection<SimpleMatch> getSelectedFromTable() {
   ListSelectionModel lsm = matchTable.getSelectionModel();
   Collection<SimpleMatch> selMatches = new HashSet<>();
   for (int i = lsm.getMinSelectionIndex(); i <= lsm.getMaxSelectionIndex(); i++) {
     if (lsm.isSelectedIndex(i) && i < matches.size()) {
       selMatches.add(matches.get(i));
     }
   }
   return selMatches;
 }
  private SimpleMatchList generateMatches(SimpleMatchSettings settings) {
    JosmTaskMonitor monitor = new JosmTaskMonitor();
    monitor.beginTask("Generating matches");

    // create Features and collections from primitive selections
    Set<OsmPrimitive> allPrimitives = new HashSet<>();
    allPrimitives.addAll(settings.getReferenceSelection());
    allPrimitives.addAll(settings.getSubjectSelection());
    FeatureCollection allFeatures = createFeatureCollection(allPrimitives);
    FeatureCollection refColl = new FeatureDataset(allFeatures.getFeatureSchema());
    FeatureCollection subColl = new FeatureDataset(allFeatures.getFeatureSchema());
    for (Feature f : allFeatures.getFeatures()) {
      OsmFeature osmFeature = (OsmFeature) f;
      if (settings.getReferenceSelection().contains(osmFeature.getPrimitive()))
        refColl.add(osmFeature);
      if (settings.getSubjectSelection().contains(osmFeature.getPrimitive()))
        subColl.add(osmFeature);
    }

    // TODO: pass to MatchFinderPanel to use as hint/default for DistanceMatchers
    // get maximum possible distance so scores can be scaled (FIXME: not quite accurate)
    //        Envelope envelope = refColl.getEnvelope();
    //        envelope.expandToInclude(subColl.getEnvelope());
    //        double maxDistance = Point2D.distance(
    //            envelope.getMinX(),
    //            envelope.getMinY(),
    //            envelope.getMaxX(),
    //            envelope.getMaxY());

    // build matcher
    FCMatchFinder finder = settings.getMatchFinder();

    // FIXME: ignore/filter duplicate objects (i.e. same object in both sets)
    // FIXME: fix match functions to work on point/linestring features as well
    // find matches
    Map<Feature, Matches> map = finder.match(refColl, subColl, monitor);

    monitor.subTask("Finishing match list");

    // convert to simple one-to-one match
    SimpleMatchList list = new SimpleMatchList();
    for (Map.Entry<Feature, Matches> entry : map.entrySet()) {
      OsmFeature target = (OsmFeature) entry.getKey();
      OsmFeature subject = (OsmFeature) entry.getValue().getTopMatch();
      if (target != null && subject != null)
        list.add(
            new SimpleMatch(
                target.getPrimitive(), subject.getPrimitive(), entry.getValue().getTopScore()));
    }

    monitor.finishTask();
    monitor.close();
    return list;
  }
 private void updateTabTitles() {
   tabbedPane.setTitleAt(
       tabbedPane.indexOfComponent(matchTable), tr(marktr("Matches ({0})"), matches.size()));
   tabbedPane.setTitleAt(
       tabbedPane.indexOfComponent(referenceOnlyList),
       tr(marktr("Reference only ({0})"), referenceOnlyListModel.size()));
   tabbedPane.setTitleAt(
       tabbedPane.indexOfComponent(subjectOnlyList),
       tr(marktr("Subject only ({0})"), subjectOnlyListModel.size()));
 }
  private List<OsmPrimitive> getSelectedSubjectPrimitives() {
    List<OsmPrimitive> selection = new ArrayList<>();
    if (tabbedPane == null || tabbedPane.getSelectedComponent() == null) return selection;

    if (tabbedPane.getSelectedComponent().equals(matchTable)) {
      for (SimpleMatch c : matches.getSelected()) {
        selection.add(c.getSubjectObject());
      }
    } else if (tabbedPane.getSelectedComponent().equals(subjectOnlyList)) {
      selection.addAll(subjectOnlyList.getSelectedValuesList());
    }
    return selection;
  }
  private void performMatching() {
    matches = generateMatches(settings);

    // populate unmatched objects
    List<OsmPrimitive> referenceOnly = new ArrayList<>(settings.getReferenceSelection());
    List<OsmPrimitive> subjectOnly = new ArrayList<>(settings.getSubjectSelection());
    for (SimpleMatch match : matches) {
      referenceOnly.remove(match.getReferenceObject());
      subjectOnly.remove(match.getSubjectObject());
    }

    referenceOnlyListModel.clear();
    referenceOnlyListModel.addAll(referenceOnly);
    subjectOnlyListModel.clear();
    subjectOnlyListModel.addAll(subjectOnly);

    updateTabTitles();

    matchTableModel.setMatches(matches);
    matches.addConflationListChangedListener(matchTableModel);
    matches.addConflationListChangedListener(conflateAction);
    matches.addConflationListChangedListener(removeAction);
    matches.addConflationListChangedListener(this);
    settings.getSubjectDataSet().addDataSetListener(this);
    settings.getReferenceDataSet().addDataSetListener(this);
    // add conflation layer
    try {
      if (conflationLayer == null) {
        conflationLayer = new ConflationLayer(matches);
        Main.main.addLayer(conflationLayer);
      }
    } catch (Exception ex) {
      JOptionPane.showMessageDialog(
          Main.parent, ex.toString(), "Error adding conflation layer", JOptionPane.ERROR_MESSAGE);
    }
    //        matches.addConflationListChangedListener(conflationLayer);
  }
  @Override
  public void primitivesRemoved(PrimitivesRemovedEvent event) {
    List<? extends OsmPrimitive> prims = event.getPrimitives();
    for (OsmPrimitive p : prims) {
      // TODO: use hashmap
      for (SimpleMatch c : matches) {
        if (c.getReferenceObject().equals(p) || c.getSubjectObject().equals(p)) {
          matches.remove(c);
          break;
        }
      }

      referenceOnlyListModel.removeElement(p);
      subjectOnlyListModel.removeElement(p);
    }
  }
  @Override
  public void simpleMatchSelectionChanged(Collection<SimpleMatch> selected) {
    // adjust table selection to match match list selection
    // FIXME: is this really where I should be doing this?

    // selection is the same, don't do anything
    Collection<SimpleMatch> tableSelection = getSelectedFromTable();
    if (tableSelection.containsAll(selected) && tableSelection.size() == selected.size()) return;

    ListSelectionModel lsm = matchTable.getSelectionModel();
    lsm.setValueIsAdjusting(true);
    lsm.clearSelection();
    for (SimpleMatch c : selected) {
      int idx = matches.indexOf(c);
      lsm.addSelectionInterval(idx, idx);
    }
    lsm.setValueIsAdjusting(false);
  }