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