001 // License: GPL. See LICENSE file for details. 002 package org.openstreetmap.josm.data.validation.tests; 003 004 import static org.openstreetmap.josm.tools.I18n.marktr; 005 import static org.openstreetmap.josm.tools.I18n.tr; 006 007 import java.text.MessageFormat; 008 import java.util.Collection; 009 import java.util.HashMap; 010 import java.util.LinkedList; 011 012 import org.openstreetmap.josm.data.osm.Node; 013 import org.openstreetmap.josm.data.osm.OsmPrimitive; 014 import org.openstreetmap.josm.data.osm.Relation; 015 import org.openstreetmap.josm.data.osm.RelationMember; 016 import org.openstreetmap.josm.data.osm.Way; 017 import org.openstreetmap.josm.data.validation.Severity; 018 import org.openstreetmap.josm.data.validation.Test; 019 import org.openstreetmap.josm.data.validation.TestError; 020 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference; 021 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 022 import org.openstreetmap.josm.gui.tagging.TaggingPreset.PresetType; 023 024 /** 025 * Check for wrong relations 026 * 027 */ 028 public class RelationChecker extends Test { 029 030 protected static final int ROLE_UNKNOWN = 1701; 031 protected static final int ROLE_EMPTY = 1702; 032 protected static final int WRONG_TYPE = 1703; 033 protected static final int HIGH_COUNT = 1704; 034 protected static final int LOW_COUNT = 1705; 035 protected static final int ROLE_MISSING = 1706; 036 protected static final int RELATION_UNKNOWN = 1707; 037 protected static final int RELATION_EMPTY = 1708; 038 039 /** 040 * Constructor 041 */ 042 public RelationChecker() { 043 super(tr("Relation checker"), 044 tr("This plugin checks for errors in relations.")); 045 } 046 047 @Override 048 public void initialize() { 049 initializePresets(); 050 } 051 052 static Collection<TaggingPreset> relationpresets = new LinkedList<TaggingPreset>(); 053 054 /** 055 * Reads the presets data. 056 * 057 */ 058 public void initializePresets() { 059 Collection<TaggingPreset> presets = TaggingPresetPreference.taggingPresets; 060 if (presets != null) { 061 for (TaggingPreset p : presets) { 062 for (TaggingPreset.Item i : p.data) { 063 if (i instanceof TaggingPreset.Roles) { 064 relationpresets.add(p); 065 break; 066 } 067 } 068 } 069 } 070 } 071 072 public static class RoleInfo { 073 int total = 0; 074 Collection<Node> nodes = new LinkedList<Node>(); 075 Collection<Way> ways = new LinkedList<Way>(); 076 Collection<Way> closedways = new LinkedList<Way>(); 077 Collection<Way> openways = new LinkedList<Way>(); 078 Collection<Relation> relations = new LinkedList<Relation>(); 079 } 080 081 @Override 082 public void visit(Relation n) { 083 LinkedList<TaggingPreset.Role> allroles = new LinkedList<TaggingPreset.Role>(); 084 for (TaggingPreset p : relationpresets) { 085 boolean matches = true; 086 TaggingPreset.Roles r = null; 087 for (TaggingPreset.Item i : p.data) { 088 if (i instanceof TaggingPreset.Key) { 089 TaggingPreset.Key k = (TaggingPreset.Key) i; 090 if (!k.value.equals(n.get(k.key))) { 091 matches = false; 092 break; 093 } 094 } else if (i instanceof TaggingPreset.Roles) { 095 r = (TaggingPreset.Roles) i; 096 } 097 } 098 if (matches && r != null) { 099 allroles.addAll(r.roles); 100 } 101 } 102 if (allroles.size() == 0) { 103 errors.add( new TestError(this, Severity.WARNING, tr("Relation type is unknown"), 104 RELATION_UNKNOWN, n) ); 105 } else { 106 HashMap<String,RoleInfo> map = new HashMap<String, RoleInfo>(); 107 for (RelationMember m : n.getMembers()) { 108 String s = ""; 109 if (m.hasRole()) { 110 s = m.getRole(); 111 } 112 RoleInfo ri = map.get(s); 113 if (ri == null) { 114 ri = new RoleInfo(); 115 } 116 ri.total++; 117 if (m.isRelation()) { 118 ri.relations.add(m.getRelation()); 119 } else if(m.isWay()) { 120 ri.ways.add(m.getWay()); 121 if (m.getWay().isClosed()) { 122 ri.closedways.add(m.getWay()); 123 } else { 124 ri.openways.add(m.getWay()); 125 } 126 } 127 else if (m.isNode()) { 128 ri.nodes.add(m.getNode()); 129 } 130 map.put(s, ri); 131 } 132 if(map.isEmpty()) { 133 errors.add( new TestError(this, Severity.ERROR, tr("Relation is empty"), 134 RELATION_EMPTY, n) ); 135 } else { 136 LinkedList<String> done = new LinkedList<String>(); 137 for (TaggingPreset.Role r : allroles) { 138 done.add(r.key); 139 String keyname = r.key; 140 if ("".equals(keyname)) { 141 keyname = tr("<empty>"); 142 } 143 RoleInfo ri = map.get(r.key); 144 long count = (ri == null) ? 0 : ri.total; 145 long vc = r.getValidCount(count); 146 if (count != vc) { 147 if (count == 0) { 148 String s = marktr("Role {0} missing"); 149 errors.add(new TestError(this, Severity.WARNING, tr("Role verification problem"), 150 tr(s, keyname), MessageFormat.format(s, keyname), ROLE_MISSING, n)); 151 } 152 else if (vc > count) { 153 String s = marktr("Number of {0} roles too low ({1})"); 154 errors.add(new TestError(this, Severity.WARNING, tr("Role verification problem"), 155 tr(s, keyname, count), MessageFormat.format(s, keyname, count), LOW_COUNT, n)); 156 } else { 157 String s = marktr("Number of {0} roles too high ({1})"); 158 errors.add(new TestError(this, Severity.WARNING, tr("Role verification problem"), 159 tr(s, keyname, count), MessageFormat.format(s, keyname, count), HIGH_COUNT, n)); 160 } 161 } 162 if (ri != null) { 163 Collection<OsmPrimitive> wrongTypes = new LinkedList<OsmPrimitive>(); 164 if (!r.types.contains(PresetType.WAY)) { 165 wrongTypes.addAll(r.types.contains(PresetType.CLOSEDWAY) ? ri.openways : ri.ways); 166 } 167 if (!r.types.contains(PresetType.NODE)) { 168 wrongTypes.addAll(ri.nodes); 169 } 170 if (!r.types.contains(PresetType.RELATION)) { 171 wrongTypes.addAll(ri.relations); 172 } 173 if (!wrongTypes.isEmpty()) { 174 String s = marktr("Member for role {0} of wrong type"); 175 LinkedList<OsmPrimitive> highlight = new LinkedList<OsmPrimitive>(wrongTypes); 176 highlight.addFirst(n); 177 errors.add(new TestError(this, Severity.WARNING, tr("Role verification problem"), 178 tr(s, keyname), MessageFormat.format(s, keyname), WRONG_TYPE, 179 highlight, wrongTypes)); 180 } 181 } 182 } 183 for (String key : map.keySet()) { 184 if (!done.contains(key)) { 185 if (key.length() > 0) { 186 String s = marktr("Role {0} unknown"); 187 errors.add(new TestError(this, Severity.WARNING, tr("Role verification problem"), 188 tr(s, key), MessageFormat.format(s, key), ROLE_UNKNOWN, n)); 189 } else { 190 String s = marktr("Empty role found"); 191 errors.add(new TestError(this, Severity.WARNING, tr("Role verification problem"), 192 tr(s), s, ROLE_EMPTY, n)); 193 } 194 } 195 } 196 } 197 } 198 } 199 }