001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.validation.tests; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.ArrayList; 007import java.util.Collections; 008import java.util.Iterator; 009import java.util.List; 010 011import org.openstreetmap.josm.command.ChangeNodesCommand; 012import org.openstreetmap.josm.command.Command; 013import org.openstreetmap.josm.data.osm.Node; 014import org.openstreetmap.josm.data.osm.OsmPrimitive; 015import org.openstreetmap.josm.data.osm.Way; 016import org.openstreetmap.josm.data.validation.Severity; 017import org.openstreetmap.josm.data.validation.Test; 018import org.openstreetmap.josm.data.validation.TestError; 019 020/** 021 * Checks for ways with identical consecutive nodes. 022 * @since 3669 023 */ 024public class DuplicatedWayNodes extends Test { 025 protected static final int DUPLICATE_WAY_NODE = 501; 026 027 /** 028 * Constructs a new {@code DuplicatedWayNodes} test. 029 */ 030 public DuplicatedWayNodes() { 031 super(tr("Duplicated way nodes"), 032 tr("Checks for ways with identical consecutive nodes.")); 033 } 034 035 @Override 036 public void visit(Way w) { 037 if (!w.isUsable()) return; 038 039 Node lastN = null; 040 for (Node n : w.getNodes()) { 041 if (lastN == null) { 042 lastN = n; 043 continue; 044 } 045 if (lastN == n) { 046 errors.add(TestError.builder(this, Severity.ERROR, DUPLICATE_WAY_NODE) 047 .message(tr("Duplicated way nodes")) 048 .primitives(w) 049 .highlight(n) 050 .build()); 051 break; 052 } 053 lastN = n; 054 } 055 } 056 057 @Override 058 public Command fixError(TestError testError) { 059 // primitives list can be empty if all primitives have been purged 060 Iterator<? extends OsmPrimitive> it = testError.getPrimitives().iterator(); 061 if (it.hasNext()) { 062 Way w = (Way) it.next(); 063 Node lastN = null; 064 List<Node> modNodes = new ArrayList<>(); 065 for (Node n : w.getNodes()) { 066 if (n != lastN) { 067 modNodes.add(n); 068 } 069 lastN = n; 070 } 071 if (modNodes.size() < 2) 072 // Empty way, delete 073 return deletePrimitivesIfNeeded(Collections.singleton(w)); 074 else 075 return new ChangeNodesCommand(w, modNodes); 076 } 077 return null; 078 } 079 080 @Override 081 public boolean isFixable(TestError testError) { 082 return testError.getTester() instanceof DuplicatedWayNodes; 083 } 084}