// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeSet;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Way;
/**
* Auxiliary class for the {@link SelectNonBranchingWaySequencesAction}.
*
* @author Marko Mäkelä
*/
public class SelectNonBranchingWaySequences {
/**
* outer endpoints of selected ways
*/
Set<Node> outerNodes;
/**
* endpoints of selected ways
*/
Set<Node> nodes;
/**
* Creates a way selection
*
* @param ways selection a selection of ways
*/
public SelectNonBranchingWaySequences(final Collection<Way> ways) {
if (ways.isEmpty()) {
// The selection cannot be extended.
outerNodes = null;
nodes = null;
} else {
nodes = new TreeSet<>();
outerNodes = new TreeSet<>();
for (Way way : ways)
addNodes(way);
}
}
/**
* Add a way endpoint to nodes, outerNodes
*
* @param node a way endpoint
*/
private void addNodes(Node node) {
if (node == null) return;
else if (!nodes.add(node))
outerNodes.remove(node);
else
outerNodes.add(node);
}
/**
* Add the endpoints of the way to nodes, outerNodes
*
* @param way a way whose endpoints are added
*/
private void addNodes(Way way) {
addNodes(way.firstNode());
addNodes(way.lastNode());
}
/**
* Find out if the selection can be extended
*
* @return true if the selection can be extended
*/
public boolean canExtend() {
return outerNodes != null && !outerNodes.isEmpty();
}
/**
* Finds out if the current selection can be extended.
*
* @param selection current selection (ways and others)
* @param node perimeter node from which to extend the selection
* @return a way by which to extend the selection, or null
*/
private static Way findWay(Collection<OsmPrimitive> selection, Node node) {
Way foundWay = null;
for (Way way : OsmPrimitive.getFilteredList(node.getReferrers(),
Way.class)) {
if (way.getNodesCount() < 2 || !way.isFirstLastNode(node)
|| selection.contains(way))
continue;
/* A previously unselected way was found that is connected
to the node. */
if (foundWay != null)
/* This is not the only qualifying way. There is a
branch at the node, and we cannot extend the selection. */
return null;
/* Remember the first found qualifying way. */
foundWay = way;
}
/* Return the only way found, or null if none was found. */
return foundWay;
}
/**
* Finds out if the current selection can be extended.
* <p>
* The members outerNodes, nodes must have been initialized.
* How to update these members when extending the selection, @see extend().
* </p>
* @param selection current selection
* @return a way by which to extend the selection, or null
*/
private Way findWay(Collection<OsmPrimitive> selection) {
for (Node node : outerNodes) {
Way way = findWay(selection, node);
if (way != null)
return way;
}
return null;
}
/**
* Extend the current selection
*
* @param data the data set in which to extend the selection
*/
public void extend(DataSet data) {
Collection<OsmPrimitive> currentSelection;
LinkedList<OsmPrimitive> selection;
boolean selectionChanged = false;
Way way;
if (!canExtend())
return;
currentSelection = data.getSelected();
way = findWay(currentSelection);
if (way == null)
return;
selection = new LinkedList<>();
for (OsmPrimitive primitive : currentSelection)
selection.add(primitive);
do {
if (!selection.add(way))
break;
selectionChanged = true;
addNodes(way);
way = findWay(selection);
} while (way != null);
if (selectionChanged)
data.setSelected(selection, true);
}
}