001// License: GPL. For details, see LICENSE file.
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.Locale;
008
009import org.openstreetmap.josm.command.Command;
010import org.openstreetmap.josm.data.osm.KeyValueVisitor;
011import org.openstreetmap.josm.data.osm.Node;
012import org.openstreetmap.josm.data.osm.OsmPrimitive;
013import org.openstreetmap.josm.data.osm.Tagged;
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 nodes with uninteresting tags that are in no way
020 *
021 * @author frsantos
022 */
023public class UntaggedNode extends Test implements KeyValueVisitor {
024
025    protected static final int UNTAGGED_NODE_BLANK = 201;
026    protected static final int UNTAGGED_NODE_FIXME = 202;
027    protected static final int UNTAGGED_NODE_NOTE = 203;
028    protected static final int UNTAGGED_NODE_CREATED_BY = 204;
029    protected static final int UNTAGGED_NODE_WATCH = 205;
030    protected static final int UNTAGGED_NODE_SOURCE = 206;
031    protected static final int UNTAGGED_NODE_OTHER = 207;
032    protected static final String ERROR_MESSAGE = tr("Unconnected nodes without physical tags");
033
034    /**
035     * Constructor
036     */
037    public UntaggedNode() {
038        super(tr("Untagged and unconnected nodes"),
039                tr("This test checks for untagged nodes that are not part of any way."));
040    }
041
042    @Override
043    public void visit(Node n) {
044        if (n.isUsable() && !n.isTagged() && n.getReferrers().isEmpty()) {
045
046            if (!n.hasKeys() && IN_DOWNLOADED_AREA.test(n)) {
047                errors.add(TestError.builder(this, Severity.WARNING, UNTAGGED_NODE_BLANK)
048                        .message(ERROR_MESSAGE, marktr("No tags"))
049                        .primitives(n)
050                        .build());
051                return;
052            }
053            n.visitKeys(this);
054        }
055    }
056
057    private static OsmPrimitive[] castPrim(Tagged n) {
058        return n instanceof OsmPrimitive ? (new OsmPrimitive[]{(OsmPrimitive) n}) : (new OsmPrimitive[0]);
059    }
060
061    @Override
062    public void visitKeyValue(Tagged n, String key, String value) {
063        if (key.toLowerCase(Locale.ENGLISH).contains("fixme") || value.toLowerCase(Locale.ENGLISH).contains("fixme")) {
064            /* translation note: don't translate quoted words */
065            errors.add(TestError.builder(this, Severity.WARNING, UNTAGGED_NODE_FIXME)
066                    .message(ERROR_MESSAGE, marktr("Has tag containing ''fixme''"))
067                    .primitives(castPrim(n))
068                    .build());
069            return;
070        }
071
072        String msg = null;
073        int code = 0;
074        if (key.startsWith("note") || key.startsWith("comment") || key.startsWith("description")) {
075            /* translation note: don't translate quoted words */
076            msg = marktr("Has key ''note'' or ''comment'' or ''description''");
077            code = UNTAGGED_NODE_NOTE;
078        } else if (key.startsWith("created_by") || key.startsWith("converted_by")) {
079            /* translation note: don't translate quoted words */
080            msg = marktr("Has key ''created_by'' or ''converted_by''");
081            code = UNTAGGED_NODE_CREATED_BY;
082        } else if (key.startsWith("watch")) {
083            /* translation note: don't translate quoted words */
084            msg = marktr("Has key ''watch''");
085            code = UNTAGGED_NODE_WATCH;
086        } else if (key.startsWith("source")) {
087            /* translation note: don't translate quoted words */
088            msg = marktr("Has key ''source''");
089            code = UNTAGGED_NODE_SOURCE;
090        }
091        if (msg != null) {
092            errors.add(TestError.builder(this, Severity.WARNING, code)
093                    .message(ERROR_MESSAGE, msg)
094                    .primitives(castPrim(n))
095                    .build());
096            return;
097        }
098        // Does not happen, but just to be sure. Maybe definition of uninteresting tags changes in future.
099        errors.add(TestError.builder(this, Severity.WARNING, UNTAGGED_NODE_OTHER)
100                .message(ERROR_MESSAGE, marktr("Other"))
101                .primitives(castPrim(n))
102                .build());
103    }
104
105    @Override
106    public Command fixError(TestError testError) {
107        return deletePrimitivesIfNeeded(testError.getPrimitives());
108    }
109
110    @Override
111    public boolean isFixable(TestError testError) {
112        if (testError.getTester() instanceof UntaggedNode) {
113            int code = testError.getCode();
114            switch (code) {
115            case UNTAGGED_NODE_BLANK:
116            case UNTAGGED_NODE_CREATED_BY:
117            case UNTAGGED_NODE_WATCH:
118            case UNTAGGED_NODE_SOURCE:
119                return true;
120            }
121        }
122        return false;
123    }
124}