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 }