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.HashSet;
007import java.util.Set;
008
009import org.openstreetmap.josm.data.osm.Node;
010import org.openstreetmap.josm.data.osm.Way;
011import org.openstreetmap.josm.data.validation.Severity;
012import org.openstreetmap.josm.data.validation.Test;
013import org.openstreetmap.josm.data.validation.TestError;
014
015/**
016 * Checks for self-intersecting ways.
017 */
018public class SelfIntersectingWay extends Test {
019
020    protected static final int SELF_INTERSECT = 401;
021
022    /**
023     * Constructs a new {@code SelfIntersectingWay} test.
024     */
025    public SelfIntersectingWay() {
026        super(tr("Self-intersecting ways"),
027                tr("This test checks for ways " +
028                        "that contain some of their nodes more than once."));
029    }
030
031    @Override
032    public void visit(Way w) {
033        int last = w.getNodesCount();
034        if (last < 2)
035            return;
036        Set<Node> nodes = new HashSet<>();
037        nodes.add(w.firstNode());
038        int countFirst = 0;
039        int countLast = 0;
040        for (int i = 1; i < last; i++) {
041            Node n = w.getNode(i);
042            if (nodes.contains(n)) {
043                boolean ok = false;
044                if (n == w.firstNode()) {
045                    if (countFirst++ == 0)
046                        ok = true;
047                } else if (i + 1 == last) {
048                    if (countLast++ == 0)
049                        ok = true;
050                }
051                if (!ok || countFirst + countLast > 1) {
052                    errors.add(TestError.builder(this, Severity.WARNING, SELF_INTERSECT)
053                            .message(tr("Self-intersecting ways"))
054                            .primitives(w)
055                            .highlight(n)
056                            .build());
057                    break;
058                }
059            } else {
060                nodes.add(n);
061            }
062        }
063    }
064}