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