001// License: GPL. See LICENSE file for details.
002package org.openstreetmap.josm.data.validation.tests;
003
004import static org.openstreetmap.josm.tools.I18n.marktr;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.util.Collection;
008import java.util.Map;
009
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.validation.Severity;
014import org.openstreetmap.josm.data.validation.Test;
015import org.openstreetmap.josm.data.validation.TestError;
016
017/**
018 * Checks for nodes with uninteresting tags that are in no way
019 *
020 * @author frsantos
021 */
022public class UntaggedNode extends Test {
023
024    protected static final int UNTAGGED_NODE_BLANK = 201;
025    protected static final int UNTAGGED_NODE_FIXME = 202;
026    protected static final int UNTAGGED_NODE_NOTE = 203;
027    protected static final int UNTAGGED_NODE_CREATED_BY = 204;
028    protected static final int UNTAGGED_NODE_WATCH = 205;
029    protected static final int UNTAGGED_NODE_SOURCE = 206;
030    protected static final int UNTAGGED_NODE_OTHER = 207;
031
032    /**
033     * Constructor
034     */
035    public UntaggedNode() {
036        super(tr("Untagged and unconnected nodes"),
037                tr("This test checks for untagged nodes that are not part of any way."));
038    }
039
040    @Override
041    public void visit(Collection<OsmPrimitive> selection) {
042        for (OsmPrimitive p : selection) {
043            if (p.isUsable() && p instanceof Node) {
044                p.accept(this);
045            }
046        }
047    }
048
049    @Override
050    public void visit(Node n) {
051        if (n.isUsable() && !n.isTagged() && n.getReferrers().isEmpty()) {
052            String errorMessage = tr("Unconnected nodes without physical tags");
053            if (!n.hasKeys()) {
054                String msg = marktr("No tags");
055                errors.add(new TestError(this, Severity.WARNING, errorMessage, tr(msg), msg, UNTAGGED_NODE_BLANK, n));
056                return;
057            }
058            for (Map.Entry<String, String> tag : n.getKeys().entrySet()) {
059                String key = tag.getKey();
060                if (contains(tag, "fixme") || contains(tag, "FIXME")) {
061                    /* translation note: don't translate quoted words */
062                    String msg = marktr("Has tag containing ''fixme'' or ''FIXME''");
063                    errors.add(new TestError(this, Severity.WARNING, errorMessage, tr(msg), msg, UNTAGGED_NODE_FIXME, n));
064                    return;
065                }
066
067                String msg = null;
068                int code = 0;
069                if (key.startsWith("note") || key.startsWith("comment") || key.startsWith("description")) {
070                    /* translation note: don't translate quoted words */
071                    msg = marktr("Has key ''note'' or ''comment'' or ''description''");
072                    code = UNTAGGED_NODE_NOTE;
073                } else if (key.startsWith("created_by") || key.startsWith("converted_by")) {
074                    /* translation note: don't translate quoted words */
075                    msg = marktr("Has key ''created_by'' or ''converted_by''");
076                    code = UNTAGGED_NODE_CREATED_BY;
077                } else if (key.startsWith("watch")) {
078                    /* translation note: don't translate quoted words */
079                    msg = marktr("Has key ''watch''");
080                    code = UNTAGGED_NODE_WATCH;
081                } else if (key.startsWith("source")) {
082                    /* translation note: don't translate quoted words */
083                    msg = marktr("Has key ''source''");
084                    code = UNTAGGED_NODE_SOURCE;
085                }
086                if (msg != null) {
087                    errors.add(new TestError(this, Severity.WARNING, errorMessage, tr(msg), msg, code, n));
088                    return;
089                }
090            }
091            // Does not happen, but just to be sure. Maybe definition of uninteresting tags changes in future.
092            errors.add(new TestError(this, Severity.WARNING, errorMessage, tr("Other"), "Other", UNTAGGED_NODE_OTHER, n));
093        }
094    }
095
096    private boolean contains(Map.Entry<String, String> tag, String s) {
097        return tag.getKey().indexOf(s) != -1 || tag.getValue().indexOf(s) != -1;
098    }
099
100    @Override
101    public Command fixError(TestError testError) {
102        return deletePrimitivesIfNeeded(testError.getPrimitives());
103    }
104
105    @Override
106    public boolean isFixable(TestError testError) {
107        if (testError.getTester() instanceof UntaggedNode) {
108            int code = testError.getCode();
109            switch (code) {
110            case UNTAGGED_NODE_BLANK:
111            case UNTAGGED_NODE_CREATED_BY:
112            case UNTAGGED_NODE_WATCH:
113            case UNTAGGED_NODE_SOURCE:
114                return true;
115            }
116        }
117        return false;
118    }
119}