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}