From 8da327919874c55be8878866f33d901ecb6c6e06 Mon Sep 17 00:00:00 2001 From: Taylor Smock Date: Thu, 26 Sep 2019 15:09:42 -0600 Subject: [PATCH] Add undo/redo functionality * Add various commands --- .classpath | 5 + .gitlab-ci.yml | 2 +- build.gradle | 10 + .../rapid/backend/AddNodeToWayCommand.java | 58 ++++++ .../rapid/backend/AddPrimitivesCommand.java | 111 +++++++++++ .../backend/CreateConnectionsCommand.java | 147 +++++++++++++++ .../backend/DeletePrimitivesCommand.java | 102 ++++++++++ .../backend/MovePrimitiveDataSetCommand.java | 86 +++++++++ .../plugins/rapid/backend/RapiDAction.java | 21 ++- .../rapid/backend/RapiDAddCommand.java | 178 +++--------------- .../plugins/rapid/backend/RapiDDataUtils.java | 2 +- .../plugins/rapid/backend/RapiDLayer.java | 1 + .../rapid/backend/RapiDMoveAction.java | 8 + .../plugins/rapid/RapiDDataUtilsTest.java | 21 +-- .../backend/AddNodeToWayCommandTest.java | 66 +++++++ .../backend/AddPrimitivesCommandTest.java | 66 +++++++ .../backend/DeletePrimitivesCommandTest.java | 71 +++++++ .../MovePrimitiveDataSetCommandTest.java | 55 ++++++ .../rapid/backend/RapiDActionTest.java | 61 ++++++ .../{ => backend}/RapiDAddComandTest.java | 54 +++++- .../rapid/backend/RapiDMoveActionTest.java | 56 ++++++ 21 files changed, 998 insertions(+), 183 deletions(-) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommand.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommand.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/rapid/backend/CreateConnectionsCommand.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommand.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommand.java create mode 100644 test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommandTest.java create mode 100644 test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommandTest.java create mode 100644 test/unit/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommandTest.java create mode 100644 test/unit/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommandTest.java create mode 100644 test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDActionTest.java rename test/unit/org/openstreetmap/josm/plugins/rapid/{ => backend}/RapiDAddComandTest.java (63%) create mode 100644 test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveActionTest.java diff --git a/.classpath b/.classpath index 98fa24d..710897b 100644 --- a/.classpath +++ b/.classpath @@ -12,6 +12,11 @@ + + + + + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a29a4d5..4d23bc2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -121,7 +121,7 @@ junit tests: - ./gradlew test artifacts: reports: - junit: build/test-results/test/**/TEST-*.xml + junit: build/test-results/test/TEST-*.xml ################ # Deploy stage # diff --git a/build.gradle b/build.gradle index e112857..8aa21fa 100644 --- a/build.gradle +++ b/build.gradle @@ -42,10 +42,12 @@ def versions = [ pmd: "6.6.0", spotbugs: "3.1.12", wiremock: "2.24.1", + jmockit: "1.48", ] repositories { jcenter() + mavenCentral() } dependencies { if (!JavaVersion.current().isJava9Compatible()) { @@ -56,10 +58,12 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:${versions.junit}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${versions.junit}") testImplementation("org.junit.vintage:junit-vintage-engine:${versions.junit}") + testCompile("org.jmockit:jmockit:${versions.jmockit}") testImplementation("com.github.spotbugs:spotbugs-annotations:${versions.spotbugs}") testImplementation("org.openstreetmap.josm:josm-unittest:"){changing=true} testImplementation("com.github.tomakehurst:wiremock:${versions.wiremock}") testImplementation("org.awaitility:awaitility:${versions.awaitility}") + testCompile("org.jmockit:jmockit:${versions.jmockit}") } // Add dependencies from ivy.xml @@ -75,6 +79,12 @@ test { testLogging.exceptionFormat = 'full' } +tasks.getByName("test") { + project.afterEvaluate { + jvmArgs("-javaagent:${classpath.find { it.name.contains("jmockit") }.absolutePath}") + } +} + sourceSets { test { java { diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommand.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommand.java new file mode 100644 index 0000000..8491f20 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommand.java @@ -0,0 +1,58 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.Arrays; +import java.util.Collection; + +import org.openstreetmap.josm.command.Command; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitive; +import org.openstreetmap.josm.data.osm.Way; + +public class AddNodeToWayCommand extends Command { + private final Node toAddNode; + private final Way way; + private final Node first; + private final Node second; + + /** + * Add a node to a way in an undoable manner + * + * @param toAddNode The node to add + * @param way The way to add the node to + * @param first The node that comes before the node to add + * @param second The node that comes after the node to add + */ + public AddNodeToWayCommand(Node toAddNode, Way way, Node first, Node second) { + super(way.getDataSet()); + this.toAddNode = toAddNode; + this.way = way; + this.first = first; + this.second = second; + } + + @Override + public boolean executeCommand() { + int index = Math.max(way.getNodes().indexOf(first), way.getNodes().indexOf(second)); + way.addNode(index, toAddNode); + return true; + } + + @Override + public void undoCommand() { + way.removeNode(toAddNode); + } + + @Override + public String getDescriptionText() { + return tr("Add node to way"); + } + + @Override + public void fillModifiedData(Collection modified, Collection deleted, + Collection added) { + modified.addAll(Arrays.asList(toAddNode, way)); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommand.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommand.java new file mode 100644 index 0000000..5b5841e --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommand.java @@ -0,0 +1,111 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.stream.Collectors; + +import org.openstreetmap.josm.command.Command; +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.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.tools.Utils; + +public class AddPrimitivesCommand extends Command { + private final Collection add; + private final Collection actuallyAdded; + private final Collection selection; + + /** + * Add source primitives (not {@code PrimitiveData}) to a dataset. + * + * @param data The destination dataset + * @param add The primitives to add. It must be complete. + * @param selection The primitives to select in the dataset. May be null. + */ + public AddPrimitivesCommand(DataSet data, Collection add, Collection selection) { + super(data); + this.add = add; + this.selection = selection; + actuallyAdded = new ArrayList<>(); + } + + @Override + public boolean executeCommand() { + actuallyAdded.addAll(addPrimitives(getAffectedDataSet(), add)); + if (selection != null) { + getAffectedDataSet().setSelected(selection); + } + return true; + } + + @Override + public void undoCommand() { + DataSet ds = getAffectedDataSet(); + Utils.filteredCollection(actuallyAdded, Relation.class).stream().filter(ds::containsRelation) + .forEach(ds::removePrimitive); + Utils.filteredCollection(actuallyAdded, Way.class).stream().filter(ds::containsWay) + .forEach(ds::removePrimitive); + Utils.filteredCollection(actuallyAdded, Node.class).stream().filter(ds::containsNode) + .forEach(ds::removePrimitive); + } + + /** + * Add primitives to a dataset + * + * @param ds The dataset to add primitives to + * @param primitives A collection of primitives to add + * @return The primitives actually added + */ + public static Collection addPrimitives(DataSet ds, Collection primitives) { + Collection returnCollection = new ArrayList<>(); + returnCollection.addAll(addRelations(ds, Utils.filteredCollection(primitives, Relation.class))); + returnCollection.addAll(addWays(ds, Utils.filteredCollection(primitives, Way.class))); + returnCollection.addAll(addNodes(ds, Utils.filteredCollection(primitives, Node.class))); + return returnCollection; + } + + private static Collection addNodes(DataSet ds, Collection nodes) { + Collection toAdd = nodes.stream().filter(node -> node.getDataSet() == null) + .collect(Collectors.toList()); + toAdd.stream().forEach(ds::addPrimitive); + return toAdd; + } + + private static Collection addWays(DataSet ds, Collection ways) { + Collection toAdd = new ArrayList<>(); + ways.stream().map(Way::getNodes).forEach(list -> toAdd.addAll(addNodes(ds, list))); + ways.stream() + .filter(way -> way.getDataSet() == null + && way.getNodes().stream().filter(node -> node.getDataSet() != ds).count() == 0) + .forEach(way -> { + ds.addPrimitive(way); + toAdd.add(way); + }); + return toAdd; + } + + // This might break with relations. TODO (not needed right now) + private static Collection addRelations(DataSet ds, Collection relations) { + Collection toAdd = relations.stream().filter(relation -> relation.getDataSet() != null) + .collect(Collectors.toList()); + toAdd.forEach(ds::addPrimitive); + return toAdd; + } + + @Override + public String getDescriptionText() { + return tr("Add OSM Primitives to a dataset"); + } + + @Override + public void fillModifiedData(Collection modified, Collection deleted, + Collection added) { + added.addAll(actuallyAdded); + } + +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/CreateConnectionsCommand.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/CreateConnectionsCommand.java new file mode 100644 index 0000000..571df3b --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/CreateConnectionsCommand.java @@ -0,0 +1,147 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.openstreetmap.josm.actions.MergeNodesAction; +import org.openstreetmap.josm.command.ChangePropertyCommand; +import org.openstreetmap.josm.command.Command; +import org.openstreetmap.josm.command.SequenceCommand; +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.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.rapid.RapiDPlugin; +import org.openstreetmap.josm.tools.Logging; +import org.openstreetmap.josm.tools.Utils; + +public class CreateConnectionsCommand extends Command { + private final Collection primitives; + public final static String DUPE_KEY = "dupe"; + public final static String CONN_KEY = "conn"; + private Command command = null; + + public CreateConnectionsCommand(DataSet data, Collection primitives) { + super(data); + this.primitives = primitives; + } + + @Override + public boolean executeCommand() { + command = createConnections(getAffectedDataSet(), primitives); + if (command != null) { + command.executeCommand(); + } + return true; + } + + @Override + public void undoCommand() { + if (command != null) { + command.undoCommand(); + } + } + + /** + * Create connections based off of current RapiD syntax + * + * @param dataSet The {@link DataSet} that should have the primitives we are + * connecting to + * @param collection The primitives with connection information (currently only + * checks Nodes) + * @return + */ + public SequenceCommand createConnections(DataSet dataSet, Collection collection) { + Collection nodes = Utils.filteredCollection(collection, Node.class); + List changedKeyList = new ArrayList<>(); + for (Node node : nodes) { + if (node.hasKey(CONN_KEY)) { + // Currently w,n,n + OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(CONN_KEY)); + for (int i = 0; i < primitiveConnections.length / 3; i++) { + if (primitiveConnections[i] instanceof Way && primitiveConnections[i + 1] instanceof Node + && primitiveConnections[i + 2] instanceof Node) { + changedKeyList.add(addNodesToWay(node, (Way) primitiveConnections[i], + (Node) primitiveConnections[i + 1], (Node) primitiveConnections[i + 2])); + } else { + Logging.error("{0}: {1}, {2}: {3}, {4}: {5}", i, primitiveConnections[i].getClass(), i + 1, + primitiveConnections[i + 1].getClass(), i + 2, primitiveConnections[i + 2].getClass()); + } + } + Logging.debug("RapiD: Removing conn from {0} in {1}", node, dataSet.getName()); + changedKeyList.add(new ChangePropertyCommand(node, CONN_KEY, null)); + } + if (node.hasKey(DUPE_KEY)) { + OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(DUPE_KEY)); + if (primitiveConnections.length != 1) { + Logging.error("RapiD: dupe connection connected to more than one node? (dupe={0})", + node.get(DUPE_KEY)); + } + changedKeyList.add(replaceNode(node, (Node) primitiveConnections[0])); + } + } + if (!changedKeyList.isEmpty()) + return new SequenceCommand(getDescriptionText(), changedKeyList); + return null; + } + + /** + * Get the primitives from a dataset with specified ids + * + * @param dataSet The dataset holding the primitives (hopefully) + * @param ids The ids formated like n,r,w + * @return The primitives that the ids point to, if in the dataset. + */ + private static OsmPrimitive[] getPrimitives(DataSet dataSet, String ids) { + String[] connections = ids.split(",", -1); + OsmPrimitive[] primitiveConnections = new OsmPrimitive[connections.length]; + for (int i = 0; i < connections.length; i++) { + String member = connections[i]; + long id = Long.parseLong(member.substring(1)); + char firstChar = member.charAt(0); + if (firstChar == 'w') { + primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.WAY); + } else if (firstChar == 'n') { + primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.NODE); + } else if (firstChar == 'r') { + primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.RELATION); + } + } + return primitiveConnections; + } + + /** + * Add a node to a way + * + * @param toAddNode The node to add + * @param way The way to add the node to + * @param first The first node in a waysegment (the node is between this and + * the second node) + * @param second The second node in a waysegemnt + */ + public static Command addNodesToWay(Node toAddNode, Way way, Node first, Node second) { + return new AddNodeToWayCommand(toAddNode, way, first, second); + } + + public static Command replaceNode(Node original, Node newNode) { + return MergeNodesAction.mergeNodes(Arrays.asList(original), newNode, newNode); + } + + @Override + public String getDescriptionText() { + return tr("Create connections from {0} data", RapiDPlugin.NAME); + } + + @Override + public void fillModifiedData(Collection modified, Collection deleted, + Collection added) { + // Don't touch the collectioins, since we haven't modified anything -- the + // subcommands should do that. + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommand.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommand.java new file mode 100644 index 0000000..131c47f --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommand.java @@ -0,0 +1,102 @@ +package org.openstreetmap.josm.plugins.rapid.backend; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import org.openstreetmap.josm.command.Command; +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; + +public class DeletePrimitivesCommand extends Command { + private final Collection selection; + private final DataSet from; + private final Collection removed; + private final Collection commands; + private final boolean deleteChildren; + + /** + * Delete primitives from a DataSet + * + * @param from The {@link DataSet} to remove primitives from + * @param selection The primitives to remove + */ + public DeletePrimitivesCommand(DataSet from, Collection selection) { + this(from, selection, false); + } + + /** + * Delete primitives from a DataSet + * + * @param from The {@link DataSet} to remove primitives from + * @param selection The primitives to remove + * @param removeAll {@code true} if all children should be removed (for ways). + */ + public DeletePrimitivesCommand(DataSet from, Collection selection, boolean removeAll) { + super(from); + this.from = from; + this.selection = selection; + commands = new ArrayList<>(); + removed = new ArrayList<>(); + this.deleteChildren = removeAll; + } + + @Override + public boolean executeCommand() { + for (OsmPrimitive primitive : selection) { + DataSet dataSet = primitive.getDataSet(); + + if (from.equals(dataSet)) { + dataSet.removePrimitive(primitive); + removed.add(primitive); + } + if (primitive instanceof Way) { + List nodes = ((Way) primitive).getNodes().stream() + .filter(node -> (!node.hasKeys() || deleteChildren) && !selection.contains(node)) + .collect(Collectors.toList()); + DeletePrimitivesCommand delNodes = new DeletePrimitivesCommand(from, nodes); + delNodes.executeCommand(); + commands.add(delNodes); + } + } + return true; + } + + @Override + public void undoCommand() { + for (Command command : commands) { + command.undoCommand(); + } + for (OsmPrimitive primitive : removed) { + DataSet removedPrimitiveDataSet = primitive.getDataSet(); + if (removedPrimitiveDataSet != null) { + removedPrimitiveDataSet.removePrimitive(primitive); + } + if (primitive instanceof Way) { + for (Node node : ((Way) primitive).getNodes()) { + if (node.getDataSet() == null) { + from.addPrimitive(node); + } + } + } + from.addPrimitive(primitive); + } + } + + @Override + public String getDescriptionText() { + return tr("Remove primitives from data set"); + } + + @Override + public void fillModifiedData(Collection modified, Collection deleted, + Collection added) { + deleted.addAll(removed); + } + +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommand.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommand.java new file mode 100644 index 0000000..78ac73c --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommand.java @@ -0,0 +1,86 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import static org.openstreetmap.josm.tools.I18n.tr; +import static org.openstreetmap.josm.tools.I18n.trn; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.openstreetmap.josm.command.Command; +import org.openstreetmap.josm.command.SequenceCommand; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.data.osm.OsmPrimitive; +import org.openstreetmap.josm.plugins.rapid.RapiDPlugin; +import org.openstreetmap.josm.tools.Logging; + +/** + * Move primitives between datasets (*not* a copy) + * + * @author Taylor Smock + */ +public class MovePrimitiveDataSetCommand extends Command { + private final DataSet to; + private final DataSet from; + private final Collection primitives; + private SequenceCommand command; + List commands; + + public MovePrimitiveDataSetCommand(DataSet to, DataSet from, Collection primitives) { + super(to); + this.to = to; + this.from = from; + this.primitives = primitives; + command = null; + commands = new ArrayList<>(); + } + + @Override + public boolean executeCommand() { + if (to.equals(from)) + return false; + command = moveCollection(from, to, primitives); + command.executeCommand(); + return true; + } + /** + * Move primitives from one dataset to another + * + * @param to The receiving dataset + * @param from The sending dataset + * @param selection The primitives to move + */ + public SequenceCommand moveCollection(DataSet from, DataSet to, Collection selection) { + if (from == null || to.isLocked() || from.isLocked()) { + Logging.error("{0}: Cannot move primitives from {1} to {2}", RapiDPlugin.NAME, from, to); + return null; + } + + Collection allNeededPrimitives = new ArrayList<>(); + RapiDDataUtils.addPrimitivesToCollection(allNeededPrimitives, selection); + + commands.add(new DeletePrimitivesCommand(from, selection, true)); + AddPrimitivesCommand addPrimitivesCommand = new AddPrimitivesCommand(to, allNeededPrimitives, selection); + commands.add(addPrimitivesCommand); + + return new SequenceCommand(trn("Move {0} OSM Primitive between data sets", + "Move {0} OSM Primitives between data sets", selection.size(), selection.size()), commands); + } + + @Override + public void undoCommand() { + command.undoCommand(); + } + + @Override + public String getDescriptionText() { + return tr("Move OsmPrimitives between layers"); + } + + @Override + public void fillModifiedData(Collection modified, Collection deleted, + Collection added) { + modified.addAll(primitives); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAction.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAction.java index ba3c0ed..49bf5ea 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAction.java +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAction.java @@ -39,10 +39,10 @@ public class RapiDAction extends JosmAction { * @return A RapiDLayer, or a new RapiDLayer if none exist. May return * {@code null} if {@code create} is {@code false}. */ - public RapiDLayer getLayer(boolean create) { + public static RapiDLayer getLayer(boolean create) { final List rapidLayers = MainApplication.getLayerManager().getLayersOfType(RapiDLayer.class); RapiDLayer layer; - synchronized (this) { + synchronized (layerLock) { if (rapidLayers.isEmpty() && create) { layer = new RapiDLayer(new DataSet(), RapiDPlugin.NAME, null); MainApplication.getLayerManager().addLayer(layer); @@ -60,7 +60,7 @@ public class RapiDAction extends JosmAction { * * @param layer The {@link RapiDLayer} to add data to */ - public void getRapiDData(RapiDLayer layer) { + public static void getRapiDData(RapiDLayer layer) { final List osmLayers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class); for (final OsmDataLayer osmLayer : osmLayers) { if (!osmLayer.isLocked()) { @@ -75,13 +75,18 @@ public class RapiDAction extends JosmAction { * @param layer A pre-existing {@link RapiDLayer} * @param osmLayer The osm datalayer with a set of bounds */ - public void getRapiDData(RapiDLayer layer, OsmDataLayer osmLayer) { + public static void getRapiDData(RapiDLayer layer, OsmDataLayer osmLayer) { final DataSet editSet = osmLayer.getDataSet(); final List editSetBounds = editSet.getDataSourceBounds(); final DataSet rapidSet = layer.getDataSet(); final List rapidBounds = rapidSet.getDataSourceBounds(); for (final Bounds bound : editSetBounds) { - if (!rapidBounds.contains(bound)) { + for (final Bounds rapidBound : rapidBounds) { + if (bound.equals(rapidBound)) { + continue; + } + } + if (rapidBounds.parallelStream().filter(bound::equals).count() == 0) { final DataSet newData = RapiDDataUtils.getData(bound.toBBox()); /* Microsoft buildings don't have a source, so we add one */ RapiDDataUtils.addSourceTags(newData, "building", "Microsoft"); @@ -96,10 +101,6 @@ public class RapiDAction extends JosmAction { @Override protected void updateEnabledState() { - if (getLayerManager().getEditDataSet() == null) { - setEnabled(false); - } else { - setEnabled(true); - } + setEnabled(getLayerManager().getEditDataSet() != null); } } diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAddCommand.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAddCommand.java index 1bf205b..82b29d2 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAddCommand.java +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDAddCommand.java @@ -3,38 +3,23 @@ package org.openstreetmap.josm.plugins.rapid.backend; import static org.openstreetmap.josm.tools.I18n.tr; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; -import java.util.List; -import java.util.TreeSet; -import java.util.stream.Collectors; -import org.openstreetmap.josm.command.AddPrimitivesCommand; import org.openstreetmap.josm.command.Command; +import org.openstreetmap.josm.command.SequenceCommand; 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.OsmPrimitiveType; -import org.openstreetmap.josm.data.osm.PrimitiveData; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.RelationMember; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor; -import org.openstreetmap.josm.gui.MainApplication; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.plugins.rapid.RapiDPlugin; import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Pair; -import org.openstreetmap.josm.tools.Utils; public class RapiDAddCommand extends Command { DataSet editable; DataSet rapid; Collection primitives; - AddPrimitivesCommand addPrimitivesCommand; Collection modifiedPrimitives; + Command command = null; OsmDataLayer editLayer = null; @@ -72,16 +57,18 @@ public class RapiDAddCommand extends Command { primitives = new HashSet<>(primitives); RapiDDataUtils.addPrimitivesToCollection(/* collection= */ primitives, /* primitives= */ primitives); synchronized (this) { - rapid.unlock(); - Collection newPrimitives = new TreeSet<>(moveCollection(rapid, editable, primitives)); - createConnections(editable, newPrimitives); - RapiDDataUtils.removePrimitivesFromDataSet(primitives); - rapid.lock(); - } - if (editLayer != null && RapiDDataUtils.getSwitchLayers()) { - MainApplication.getLayerManager().setActiveLayer(editLayer); - editable.setSelected( - editable.getSelected().stream().filter(OsmPrimitive::isTagged).collect(Collectors.toSet())); + boolean locked = rapid.isLocked(); + if (locked) { + rapid.unlock(); + } + Command tCommand = new MovePrimitiveDataSetCommand(editable, rapid, primitives); + Command createConnectionsCommand = createConnections(editable, primitives); + command = new SequenceCommand(getDescriptionText(), tCommand, createConnectionsCommand); + command.executeCommand(); + + if (locked) { + rapid.lock(); + } } return true; } @@ -94,134 +81,22 @@ public class RapiDAddCommand extends Command { * @param collection The primitives with connection information (currently only * checks Nodes) */ - public static void createConnections(DataSet dataSet, Collection collection) { - Collection nodes = Utils.filteredCollection(collection, Node.class); - for (Node node : nodes) { - if (node.hasKey("conn")) { - // Currently w,n,n - OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get("conn")); - for (int i = 0; i < primitiveConnections.length / 3; i++) { - if (primitiveConnections[i] instanceof Way && primitiveConnections[i + 1] instanceof Node - && primitiveConnections[i + 2] instanceof Node) { - addNodesToWay(node, (Way) primitiveConnections[i], (Node) primitiveConnections[i + 1], - (Node) primitiveConnections[i + 2]); - } else { - Logging.error("{0}: {1}, {2}: {3}, {4}: {5}", i, primitiveConnections[i].getClass(), i + 1, - primitiveConnections[i + 1].getClass(), i + 2, primitiveConnections[i + 2].getClass()); - } - } - Logging.debug("RapiD: Removing conn from {0} in {1}", node, dataSet.getName()); - node.remove("conn"); - } - if (node.hasKey("dupe")) { - OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get("dupe")); - if (primitiveConnections.length != 1) { - Logging.error("RapiD: dupe connection connected to more than one node? (dupe={0})", - node.get("dupe")); - } - replaceNode(node, (Node) primitiveConnections[0]); - } - } + public static Command createConnections(DataSet dataSet, Collection collection) { + return new CreateConnectionsCommand(dataSet, collection); } - /** - * Get the primitives from a dataset with specified ids - * - * @param dataSet The dataset holding the primitives (hopefully) - * @param ids The ids formated like n,r,w - * @return The primitives that the ids point to, if in the dataset. - */ - private static OsmPrimitive[] getPrimitives(DataSet dataSet, String ids) { - String[] connections = ids.split(",", -1); - OsmPrimitive[] primitiveConnections = new OsmPrimitive[connections.length]; - for (int i = 0; i < connections.length; i++) { - String member = connections[i]; - long id = Long.parseLong(member.substring(1)); - char firstChar = member.charAt(0); - if (firstChar == 'w') { - primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.WAY); - } else if (firstChar == 'n') { - primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.NODE); - } else if (firstChar == 'r') { - primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.RELATION); - } + @Override + public void undoCommand() { + boolean locked = rapid.isLocked(); + if (locked) { + rapid.unlock(); } - return primitiveConnections; - } - - /** - * Add a node to a way - * - * @param toAddNode The node to add - * @param way The way to add the node to - * @param first The first node in a waysegment (the node is between this and - * the second node) - * @param second The second node in a waysegemnt - */ - public static void addNodesToWay(Node toAddNode, Way way, Node first, Node second) { - int index = Math.max(way.getNodes().indexOf(first), way.getNodes().indexOf(second)); - way.addNode(index, toAddNode); - } - - public static void replaceNode(Node original, Node newNode) { - for (OsmPrimitive primitive : original.getReferrers()) { - if (primitive instanceof Way) { - Way way = (Way) primitive; - List indexes = new ArrayList<>(); - List nodes = way.getNodes(); - for (int i = 0; i < nodes.size(); i++) { - if (nodes.get(i).equals(original)) { - indexes.add(i); - } - } - while (way.getNodes().contains(original)) { - way.removeNode(original); - } - for (int index : indexes) { - way.addNode(index, newNode); - } - } else if (primitive instanceof Relation) { - List> replaceMembers = new ArrayList<>(); - Relation relation = (Relation) primitive; - List relationMembers = relation.getMembers(); - for (int i = 0; i < relationMembers.size(); i++) { - RelationMember member = relationMembers.get(i); - if (member.getMember().equals(original)) { - replaceMembers.add(new Pair<>(i, new RelationMember(member.getRole(), newNode))); - } - } - relation.removeMembersFor(original); - for (Pair pair : replaceMembers) { - relation.addMember(pair.a, pair.b); - } - } + if (command != null) { + command.undoCommand(); } - original.getDataSet().removePrimitive(original); - } - - /** - * Move primitives from one dataset to another - * - * @param to The receiving dataset - * @param from The sending dataset - * @param selection The primitives to move - * @return true if the primitives have moved datasets - */ - public Collection moveCollection(DataSet from, DataSet to, - Collection selection) { - if (from == null || to.isLocked() || from.isLocked()) { - Logging.error("RapiD: Cannot move primitives from {0} to {1}", from, to); - return Collections.emptySet(); + if (locked) { + rapid.lock(); } - Collection originalSelection = from.getSelected(); - from.setSelected(selection); - MergeSourceBuildingVisitor mergeBuilder = new MergeSourceBuildingVisitor(from); - List primitiveDataList = mergeBuilder.build().allPrimitives().stream().map(OsmPrimitive::save) - .collect(Collectors.toList()); - from.setSelected(originalSelection); - addPrimitivesCommand = new AddPrimitivesCommand(primitiveDataList, primitiveDataList, to); - addPrimitivesCommand.executeCommand(); - return addPrimitivesCommand.getParticipatingPrimitives(); } @Override @@ -232,7 +107,6 @@ public class RapiDAddCommand extends Command { @Override public void fillModifiedData(Collection modified, Collection deleted, Collection added) { - // TODO Auto-generated method stub - + modified.addAll(primitives); } } diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDDataUtils.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDDataUtils.java index 1c440b4..a0aaf8b 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDDataUtils.java +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDDataUtils.java @@ -35,7 +35,7 @@ import org.openstreetmap.josm.tools.Logging; * */ public final class RapiDDataUtils { - public static final String DEFAULT_RAPID_API = "https://www.facebook.com/maps/ml_roads?conflate_with_osm=true&theme=ml_road_vector&collaborator=fbid&token=ASZUVdYpCkd3M6ZrzjXdQzHulqRMnxdlkeBJWEKOeTUoY_Gwm9fuEd2YObLrClgDB_xfavizBsh0oDfTWTF7Zb4C&hash=ASYM8LPNy8k1XoJiI7A&result_type=road_building_vector_xml&bbox={bbox}"; + public static final String DEFAULT_RAPID_API = "https://www.facebook.com/maps/ml_roads?conflate_with_osm=true&theme=ml_road_vector&collaborator=fbid&token=ASZUVdYpCkd3M6ZrzjXdQzHulqRMnxdlkeBJWEKOeTUoY_Gwm9fuEd2YObLrClgDB_xfavizBsh0oDfTWTF7Zb4C&hash=ASYM8LPNy8k1XoJiI7A&result_type=road_building_vector_xml&bbox={bbox}&crop_bbox={bbox}"; private RapiDDataUtils() { // Hide the constructor diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDLayer.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDLayer.java index 40f8593..d0e693c 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDLayer.java +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDLayer.java @@ -21,6 +21,7 @@ public class RapiDLayer extends OsmDataLayer { public RapiDLayer(DataSet data, String name, File associatedFile) { super(data, name, associatedFile); this.lock(); + this.setUploadDiscouraged(true); } // @Override only JOSM > 15323 diff --git a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveAction.java b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveAction.java index 37e9a06..c15a027 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveAction.java +++ b/src/main/java/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveAction.java @@ -6,9 +6,11 @@ import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import org.openstreetmap.josm.actions.JosmAction; import org.openstreetmap.josm.data.UndoRedoHandler; +import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.gui.MainApplication; import org.openstreetmap.josm.gui.layer.Layer; @@ -41,6 +43,12 @@ public class RapiDMoveAction extends JosmAction { if (editLayer != null) { RapiDAddCommand command = new RapiDAddCommand(rapid, editLayer, selected); UndoRedoHandler.getInstance().add(command); + if (RapiDDataUtils.getSwitchLayers()) { + MainApplication.getLayerManager().setActiveLayer(editLayer); + DataSet editable = editLayer.getDataSet(); + editable.setSelected( + editable.getSelected().stream().filter(OsmPrimitive::isTagged).collect(Collectors.toSet())); + } } } } diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/RapiDDataUtilsTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/RapiDDataUtilsTest.java index 4906113..9b22c73 100644 --- a/test/unit/org/openstreetmap/josm/plugins/rapid/RapiDDataUtilsTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/RapiDDataUtilsTest.java @@ -38,24 +38,19 @@ public class RapiDDataUtilsTest { @Test public void testAddSourceTags() { - BBox testBBox = getTestBBox(); - DataSet ds = new DataSet(RapiDDataUtils.getData(testBBox)); - Assert.assertEquals(1, ds.getWays().size()); - Way way1 = (Way) ds.getWays().toArray()[0]; - String originalSource = way1.get("source"); - Assert.assertNotNull(originalSource); + Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1))); + DataSet ds = new DataSet(way1.firstNode(), way1.lastNode(), way1); + String source = "random source"; - way1.remove("source"); Assert.assertNull(way1.get("source")); - - RapiDDataUtils.addSourceTags(ds, "highway", originalSource); - Assert.assertEquals(originalSource, way1.get("source")); + RapiDDataUtils.addSourceTags(ds, "highway", source); + Assert.assertEquals(source, way1.get("source")); } - private static BBox getTestBBox() { + public static BBox getTestBBox() { BBox testBBox = new BBox(); - testBBox.add(new LatLon(39.0768984, -108.5462553)); - testBBox.add(new LatLon(39.0776276, -108.5452918)); + testBBox.add(new LatLon(39.076, -108.547)); + testBBox.add(new LatLon(39.078, -108.545)); return testBBox; } diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommandTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommandTest.java new file mode 100644 index 0000000..582a927 --- /dev/null +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddNodeToWayCommandTest.java @@ -0,0 +1,66 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.openstreetmap.josm.TestUtils; +import org.openstreetmap.josm.data.coor.LatLon; +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; +import org.openstreetmap.josm.testutils.JOSMTestRules;; + +public class AddNodeToWayCommandTest { + private Node toAdd; + private Way way; + private AddNodeToWayCommand command; + @Rule + public JOSMTestRules test = new JOSMTestRules(); + + @Before + public void setupArea() { + toAdd = new Node(new LatLon(0.1, 0.1)); + way = TestUtils.newWay("", new Node(new LatLon(0, 0)), new Node(new LatLon(-0.1, -0.1))); + new DataSet(toAdd, way.firstNode(), way.lastNode(), way); + command = new AddNodeToWayCommand(toAdd, way, way.firstNode(), way.lastNode()); + } + + @Test + public void testAddNodeToWay() { + command.executeCommand(); + Assert.assertEquals(3, way.getNodesCount()); + + command.undoCommand(); + Assert.assertEquals(2, way.getNodesCount()); + + command = new AddNodeToWayCommand(toAdd, way, way.lastNode(), way.firstNode()); + + command.executeCommand(); + Assert.assertEquals(3, way.getNodesCount()); + + command.undoCommand(); + Assert.assertEquals(2, way.getNodesCount()); + } + + @Test + public void testDescription() { + Assert.assertNotNull(command.getDescriptionText()); + } + + @Test + public void testModifiedAddedDeleted() { + List added = new ArrayList<>(); + List modified = new ArrayList<>(); + List deleted = new ArrayList<>(); + command.fillModifiedData(modified, deleted, added); + Assert.assertTrue(deleted.isEmpty()); + Assert.assertTrue(added.isEmpty()); + Assert.assertEquals(2, modified.size()); + } +} diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommandTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommandTest.java new file mode 100644 index 0000000..8ec1b0c --- /dev/null +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/AddPrimitivesCommandTest.java @@ -0,0 +1,66 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import java.util.Collection; +import java.util.Collections; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.openstreetmap.josm.TestUtils; +import org.openstreetmap.josm.data.coor.LatLon; +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; +import org.openstreetmap.josm.testutils.JOSMTestRules; + +public class AddPrimitivesCommandTest { + @Rule + public JOSMTestRules test = new JOSMTestRules(); + + @Test + public void testAddPrimitives() { + DataSet dataSet = new DataSet(); + Way way1 = TestUtils.newWay("highway=secondary", new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1))); + Assert.assertNull(way1.getDataSet()); + + Collection added = AddPrimitivesCommand.addPrimitives(dataSet, Collections.singleton(way1)); + Assert.assertEquals(3, added.size()); + Assert.assertSame(dataSet, way1.getDataSet()); + } + + @SuppressWarnings("UndefinedEquals") + @Test + public void testUndoRedo() { + DataSet dataSet = new DataSet(); + Way way1 = TestUtils.newWay("highway=secondary", new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, -0.1))); + AddPrimitivesCommand command = new AddPrimitivesCommand(dataSet, Collections.singleton(way1), null); + Collection selection = dataSet.getAllSelected(); + Assert.assertNull(way1.getDataSet()); + + command.executeCommand(); + Assert.assertSame(dataSet, way1.getDataSet()); + Assert.assertEquals(selection, dataSet.getAllSelected()); + + command.undoCommand(); + Assert.assertNull(way1.getDataSet()); + Assert.assertEquals(selection, dataSet.getAllSelected()); + + command.executeCommand(); + Assert.assertSame(dataSet, way1.getDataSet()); + Assert.assertEquals(selection, dataSet.getAllSelected()); + + command.undoCommand(); + + command = new AddPrimitivesCommand(dataSet, Collections.singleton(way1), Collections.singleton(way1)); + + command.executeCommand(); + Assert.assertSame(dataSet, way1.getDataSet()); + Assert.assertNotEquals(selection, dataSet.getAllSelected()); + + command.undoCommand(); + Assert.assertNull(way1.getDataSet()); + Assert.assertEquals(selection, dataSet.getAllSelected()); + } +} diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommandTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommandTest.java new file mode 100644 index 0000000..97c4142 --- /dev/null +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/DeletePrimitivesCommandTest.java @@ -0,0 +1,71 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import java.util.Collections; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.openstreetmap.josm.TestUtils; +import org.openstreetmap.josm.data.coor.LatLon; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.testutils.JOSMTestRules; + +public class DeletePrimitivesCommandTest { + @Rule + public JOSMTestRules test = new JOSMTestRules(); + + @Test + public void testDeletePrimitives() { + DataSet ds = new DataSet(); + Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(-0.1, 0.1))); + way1.getNodes().forEach(node -> ds.addPrimitive(node)); + ds.addPrimitive(way1); + + Assert.assertTrue(ds.containsWay(way1)); + + DeletePrimitivesCommand delete = new DeletePrimitivesCommand(ds, Collections.singleton(way1)); + delete.executeCommand(); + Assert.assertFalse(ds.containsWay(way1)); + Assert.assertEquals(0, ds.allPrimitives().size()); + + delete.undoCommand(); + + Assert.assertTrue(ds.containsWay(way1)); + Assert.assertEquals(3, ds.allPrimitives().size()); + + Node tNode = new Node(new LatLon(0.1, 0.1)); + ds.addPrimitive(tNode); + Assert.assertEquals(4, ds.allPrimitives().size()); + + delete.executeCommand(); + Assert.assertFalse(ds.containsWay(way1)); + Assert.assertEquals(1, ds.allPrimitives().size()); + + delete.undoCommand(); + Assert.assertEquals(4, ds.allPrimitives().size()); + + way1.firstNode().put("highway", "stop"); + + delete.executeCommand(); + Assert.assertFalse(ds.containsWay(way1)); + Assert.assertEquals(2, ds.allPrimitives().size()); + + delete.undoCommand(); + Assert.assertTrue(way1.firstNode().hasKey("highway")); + + delete = new DeletePrimitivesCommand(ds, Collections.singleton(way1), true); + + delete.executeCommand(); + Assert.assertFalse(ds.containsWay(way1)); + Assert.assertEquals(1, ds.allPrimitives().size()); + + delete.undoCommand(); + Assert.assertEquals(4, ds.allPrimitives().size()); + + Assert.assertTrue(way1.firstNode().hasKey("highway")); + } + +} diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommandTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommandTest.java new file mode 100644 index 0000000..129581f --- /dev/null +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/MovePrimitiveDataSetCommandTest.java @@ -0,0 +1,55 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import java.util.Collections; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.openstreetmap.josm.TestUtils; +import org.openstreetmap.josm.data.coor.LatLon; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.testutils.JOSMTestRules; + +public class MovePrimitiveDataSetCommandTest { + @Rule + public JOSMTestRules test = new JOSMTestRules(); + + @Test + public void testMovePrimitives() { + DataSet to = new DataSet(); + DataSet from = new DataSet(); + Way way1 = TestUtils.newWay("highway=tertiary", new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1))); + way1.getNodes().stream().forEach(node -> from.addPrimitive(node)); + from.addPrimitive(way1); + from.addPrimitive(new Node(new LatLon(-0.1, 0.1))); + + MovePrimitiveDataSetCommand move = new MovePrimitiveDataSetCommand(to, from, Collections.singleton(way1)); + Assert.assertEquals(0, to.allPrimitives().size()); + Assert.assertEquals(4, from.allPrimitives().size()); + + move.executeCommand(); + Assert.assertEquals(1, from.allPrimitives().size()); + Assert.assertEquals(3, to.allPrimitives().size()); + Assert.assertEquals(to, way1.getDataSet()); + + move.undoCommand(); + Assert.assertEquals(0, to.allPrimitives().size()); + Assert.assertEquals(4, from.allPrimitives().size()); + Assert.assertEquals(from, way1.getDataSet()); + + way1.firstNode().put("highway", "stop"); + + move.executeCommand(); + Assert.assertEquals(1, from.allPrimitives().size()); + Assert.assertEquals(3, to.allPrimitives().size()); + Assert.assertEquals(to, way1.getDataSet()); + + move.undoCommand(); + Assert.assertEquals(0, to.allPrimitives().size()); + Assert.assertEquals(4, from.allPrimitives().size()); + Assert.assertEquals(from, way1.getDataSet()); + } +} diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDActionTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDActionTest.java new file mode 100644 index 0000000..9e88227 --- /dev/null +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDActionTest.java @@ -0,0 +1,61 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.openstreetmap.josm.data.Bounds; +import org.openstreetmap.josm.data.DataSource; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.gui.MainApplication; +import org.openstreetmap.josm.gui.layer.Layer; +import org.openstreetmap.josm.gui.layer.OsmDataLayer; +import org.openstreetmap.josm.testutils.JOSMTestRules; + +public class RapiDActionTest { + @Rule + public JOSMTestRules test = new JOSMTestRules().preferences().main().projection(); + + @Test + public void testGetLayer() { + Layer rapid = RapiDAction.getLayer(false); + Assert.assertNull(rapid); + + rapid = RapiDAction.getLayer(true); + Assert.assertEquals(RapiDLayer.class, rapid.getClass()); + + Layer tRapid = RapiDAction.getLayer(false); + Assert.assertSame(rapid, tRapid); + + tRapid = RapiDAction.getLayer(true); + Assert.assertSame(rapid, tRapid); + } + + @Test + public void testGetData() { + RapiDLayer rapid = RapiDAction.getLayer(true); + OsmDataLayer osm = new OsmDataLayer(new DataSet(), "test", null); + MainApplication.getLayerManager().addLayer(osm); + RapiDAction.getRapiDData(rapid, osm); + + Assert.assertTrue(rapid.getDataSet().getDataSourceBounds().isEmpty()); + + osm.getDataSet().addDataSource(new DataSource(new Bounds(0, 0, 0.001, 0.001), "random test")); + + osm.lock(); + RapiDAction.getRapiDData(rapid); + Assert.assertTrue(rapid.getDataSet().getDataSourceBounds().isEmpty()); + osm.unlock(); + + RapiDAction.getRapiDData(rapid); + Assert.assertFalse(rapid.getDataSet().getDataSourceBounds().isEmpty()); + Assert.assertEquals(1, rapid.getDataSet().getDataSourceBounds().parallelStream().distinct().count()); + + osm.getDataSet().addDataSource(new DataSource(new Bounds(-0.001, -0.001, 0, 0), "random test")); + RapiDAction.getRapiDData(rapid); + Assert.assertEquals(2, rapid.getDataSet().getDataSourceBounds().parallelStream().distinct().count()); + + RapiDAction.getRapiDData(rapid); + Assert.assertEquals(2, rapid.getDataSet().getDataSourceBounds().parallelStream().distinct().count()); + } +} diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/RapiDAddComandTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDAddComandTest.java similarity index 63% rename from test/unit/org/openstreetmap/josm/plugins/rapid/RapiDAddComandTest.java rename to test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDAddComandTest.java index 420c364..e69bed6 100644 --- a/test/unit/org/openstreetmap/josm/plugins/rapid/RapiDAddComandTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDAddComandTest.java @@ -1,5 +1,5 @@ // License: GPL. For details, see LICENSE file. -package org.openstreetmap.josm.plugins.rapid; +package org.openstreetmap.josm.plugins.rapid.backend; import java.util.Arrays; import java.util.Collections; @@ -11,8 +11,8 @@ import org.openstreetmap.josm.TestUtils; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.Tag; import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.plugins.rapid.backend.RapiDAddCommand; import org.openstreetmap.josm.testutils.JOSMTestRules; public class RapiDAddComandTest { @@ -55,6 +55,12 @@ public class RapiDAddComandTest { Assert.assertTrue(ds2.containsNode(way3.firstNode())); Assert.assertTrue(ds2.containsNode(way3.lastNode())); Assert.assertFalse(ds1.containsWay(way3)); + + command.undoCommand(); + Assert.assertFalse(ds2.containsWay(way3)); + Assert.assertFalse(ds2.containsNode(way3.firstNode())); + Assert.assertFalse(ds2.containsNode(way3.lastNode())); + Assert.assertTrue(ds1.containsWay(way3)); } @Test @@ -66,12 +72,12 @@ public class RapiDAddComandTest { way2.firstNode().put("conn", "w".concat(Long.toString(way1.getUniqueId())).concat(",n") .concat(Long.toString(way1.firstNode().getUniqueId())).concat(",n") - .concat(Long.toString(way1.lastNode().getUniqueId()))); + .concat(Long.toString(way1.lastNode().getUniqueId()))); way1.getNodes().forEach(node -> ds1.addPrimitive(node)); way2.getNodes().forEach(node -> ds1.addPrimitive(node)); ds1.addPrimitive(way2); ds1.addPrimitive(way1); - RapiDAddCommand.createConnections(ds1, Collections.singletonList(way2.firstNode())); + RapiDAddCommand.createConnections(ds1, Collections.singletonList(way2.firstNode())).executeCommand(); Assert.assertEquals(3, way1.getNodesCount()); Assert.assertFalse(way1.isFirstLastNode(way2.firstNode())); @@ -81,9 +87,45 @@ public class RapiDAddComandTest { way3.getNodes().forEach(node -> ds1.addPrimitive(node)); ds1.addPrimitive(way3); Node way3Node1 = way3.firstNode(); - RapiDAddCommand.createConnections(ds1, Collections.singletonList(way3.firstNode())); + RapiDAddCommand.createConnections(ds1, Collections.singletonList(way3.firstNode())).executeCommand(); Assert.assertNotEquals(way3Node1, way3.firstNode()); Assert.assertEquals(way1.firstNode(), way3.firstNode()); - Assert.assertFalse(ds1.containsNode(way3Node1)); + Assert.assertTrue(way3Node1.isDeleted()); + } + + @Test + public void testCreateConnectionsUndo() { + DataSet osmData = new DataSet(); + DataSet rapidData = new DataSet(); + Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1))); + Way way2 = TestUtils.newWay("highway=residential", new Node(new LatLon(-0.1, -0.1)), + new Node(new LatLon(0.1, 0.1))); + way1.getNodes().forEach(node -> rapidData.addPrimitive(node)); + way2.getNodes().forEach(node -> osmData.addPrimitive(node)); + osmData.addPrimitive(way2); + rapidData.addPrimitive(way1); + rapidData.setSelected(way1); + + Assert.assertEquals(3, osmData.allPrimitives().size()); + Assert.assertEquals(3, rapidData.allPrimitives().size()); + + RapiDAddCommand command = new RapiDAddCommand(rapidData, osmData, rapidData.getSelected()); + command.executeCommand(); + Assert.assertEquals(6, osmData.allPrimitives().size()); + Assert.assertTrue(rapidData.allPrimitives().isEmpty()); + + command.undoCommand(); + Assert.assertEquals(3, osmData.allPrimitives().size()); + Assert.assertEquals(3, rapidData.allPrimitives().size()); + + Tag dupe = new Tag("dupe", "n".concat(Long.toString(way2.lastNode().getUniqueId()))); + way1.lastNode().put(dupe); + command.executeCommand(); + Assert.assertEquals(6, osmData.allPrimitives().size()); + Assert.assertTrue(rapidData.allPrimitives().isEmpty()); + + command.undoCommand(); + Assert.assertEquals(3, osmData.allPrimitives().size()); + Assert.assertEquals(3, rapidData.allPrimitives().size()); } } diff --git a/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveActionTest.java b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveActionTest.java new file mode 100644 index 0000000..932d11b --- /dev/null +++ b/test/unit/org/openstreetmap/josm/plugins/rapid/backend/RapiDMoveActionTest.java @@ -0,0 +1,56 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.rapid.backend; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.openstreetmap.josm.TestUtils; +import org.openstreetmap.josm.data.UndoRedoHandler; +import org.openstreetmap.josm.data.coor.LatLon; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.gui.MainApplication; +import org.openstreetmap.josm.gui.layer.OsmDataLayer; +import org.openstreetmap.josm.plugins.rapid.RapiDDataUtilsTest; +import org.openstreetmap.josm.testutils.JOSMTestRules; + +public class RapiDMoveActionTest { + RapiDMoveAction moveAction; + + @Rule + public JOSMTestRules test = new JOSMTestRules().preferences().main().projection(); + + @Before + public void setup() { + moveAction = new RapiDMoveAction(); + } + + @Test + public void testMoveAction() { + DataSet osmData = new DataSet(); + DataSet rapidData = new DataSet(); + Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1))); + Way way2 = TestUtils.newWay("highway=residential", new Node(new LatLon(-0.1, -0.1)), + new Node(new LatLon(0.1, 0.1))); + way1.getNodes().forEach(node -> rapidData.addPrimitive(node)); + way2.getNodes().forEach(node -> osmData.addPrimitive(node)); + osmData.addPrimitive(way2); + rapidData.addPrimitive(way1); + + OsmDataLayer osmLayer = new OsmDataLayer(osmData, "osm", null); + RapiDLayer rapidLayer = new RapiDLayer(RapiDDataUtils.getData(RapiDDataUtilsTest.getTestBBox()), "rapid", null); + MainApplication.getLayerManager().addLayer(osmLayer); + MainApplication.getLayerManager().addLayer(rapidLayer); + MainApplication.getLayerManager().setActiveLayer(rapidLayer); + + rapidData.addSelected(way1); + + moveAction.actionPerformed(null); + + Assert.assertEquals(osmLayer, MainApplication.getLayerManager().getActiveLayer()); + + UndoRedoHandler.getInstance().undo(); + } +}