Changeset 7884 in josm
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java
r7523 r7884 6 6 7 7 import java.text.MessageFormat; 8 import java.util.ArrayList;9 8 import java.util.Collection; 9 import java.util.EnumSet; 10 10 import java.util.HashMap; 11 import java.util.HashSet;12 11 import java.util.LinkedList; 13 import java.util.List; 14 import java.util.Set; 12 import java.util.Map; 15 13 16 14 import org.openstreetmap.josm.command.Command; 17 15 import org.openstreetmap.josm.command.DeleteCommand; 18 import org.openstreetmap.josm.data.osm.Node;19 16 import org.openstreetmap.josm.data.osm.OsmPrimitive; 17 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 20 18 import org.openstreetmap.josm.data.osm.Relation; 21 19 import org.openstreetmap.josm.data.osm.RelationMember; 22 import org.openstreetmap.josm.data.osm.Way;23 20 import org.openstreetmap.josm.data.validation.Severity; 24 21 import org.openstreetmap.josm.data.validation.Test; … … 31 28 import org.openstreetmap.josm.gui.tagging.TaggingPresetType; 32 29 import org.openstreetmap.josm.gui.tagging.TaggingPresets; 30 import org.openstreetmap.josm.tools.Utils; 33 31 34 32 /** … … 86 84 } 87 85 86 private static class RolePreset { 87 public RolePreset(LinkedList<Role> roles, String name) { 88 this.roles = roles; 89 this.name = name; 90 } 91 private final LinkedList<Role> roles; 92 private final String name; 93 } 94 88 95 private static class RoleInfo { 89 96 private int total = 0; 90 private Collection<Node> nodes = new LinkedList<>();91 private Collection<Way> ways = new LinkedList<>();92 private Collection<Way> openways = new LinkedList<>();93 private Collection<Relation> relations = new LinkedList<>();94 97 } 95 98 96 99 @Override 97 100 public void visit(Relation n) { 98 LinkedList<Role> allroles = buildAllRoles(n);101 Map<String, RolePreset> allroles = buildAllRoles(n); 99 102 if (allroles.isEmpty() && n.hasTag("type", "route") 100 103 && n.hasTag("route", "train", "subway", "monorail", "tram", "bus", "trolleybus", "aerialway", "ferry")) { … … 106 109 } 107 110 108 HashMap<String, RoleInfo> map = buildRoleInfoMap(n);111 Map<String, RoleInfo> map = buildRoleInfoMap(n); 109 112 if (map.isEmpty()) { 110 113 errors.add(new TestError(this, Severity.ERROR, tr("Relation is empty"), RELATION_EMPTY, n)); … … 114 117 } 115 118 116 private HashMap<String, RoleInfo> buildRoleInfoMap(Relation n) {117 HashMap<String,RoleInfo> map = new HashMap<>();119 private Map<String, RoleInfo> buildRoleInfoMap(Relation n) { 120 Map<String,RoleInfo> map = new HashMap<>(); 118 121 for (RelationMember m : n.getMembers()) { 119 122 String role = m.getRole(); … … 121 124 if (ri == null) { 122 125 ri = new RoleInfo(); 126 map.put(role, ri); 123 127 } 124 128 ri.total++; 125 if (m.isRelation()) {126 ri.relations.add(m.getRelation());127 } else if(m.isWay()) {128 ri.ways.add(m.getWay());129 if (!m.getWay().isClosed()) {130 ri.openways.add(m.getWay());131 }132 }133 else if (m.isNode()) {134 ri.nodes.add(m.getNode());135 }136 map.put(role, ri);137 129 } 138 130 return map; 139 131 } 140 132 141 private LinkedList<Role> buildAllRoles(Relation n) { 142 LinkedList<Role> allroles = new LinkedList<>(); 133 // return Roles grouped by key 134 private Map<String, RolePreset> buildAllRoles(Relation n) { 135 Map<String, RolePreset> allroles = new HashMap<>(); 136 143 137 for (TaggingPreset p : relationpresets) { 144 138 boolean matches = true; … … 156 150 } 157 151 if (matches && r != null) { 158 allroles.addAll(r.roles); 152 for(Role role: r.roles) { 153 String key = role.key; 154 LinkedList<Role> roleGroup = null; 155 if (allroles.containsKey(key)) { 156 roleGroup = allroles.get(key).roles; 157 } else { 158 roleGroup = new LinkedList<>(); 159 allroles.put(key, new RolePreset(roleGroup, p.name)); 160 } 161 roleGroup.add(role); 162 } 159 163 } 160 164 } … … 162 166 } 163 167 164 private void checkRoles(Relation n, LinkedList<Role> allroles, HashMap<String, RoleInfo> map) { 165 List<String> done = new LinkedList<>(); 166 // Remove empty roles if several exist (like in route=hiking, see #9844) 167 List<Role> emptyRoles = new LinkedList<>(); 168 for (Role r : allroles) { 169 if ("".equals(r.key)) { 170 emptyRoles.add(r); 171 } 172 } 173 if (emptyRoles.size() > 1) { 174 allroles.removeAll(emptyRoles); 175 } 176 for (Role r : allroles) { 177 done.add(r.key); 178 String keyname = r.key; 179 if ("".equals(keyname)) { 180 keyname = tr("<empty>"); 181 } 182 RoleInfo ri = map.get(r.key); 183 checkRoleCounts(n, r, keyname, ri); 184 if (ri != null) { 185 if (r.types != null) { 186 checkRoleTypes(n, r, keyname, ri); 187 } 188 if (r.memberExpression != null) { 189 checkRoleMemberExpressions(n, r, keyname, ri); 190 } 191 } 192 } 168 private boolean checkMemberType(Role r, RelationMember member) { 169 if (r.types != null) { 170 if (member.getDisplayType().equals(OsmPrimitiveType.NODE)) { 171 return r.types.contains(TaggingPresetType.NODE); 172 } 173 if (member.getDisplayType().equals(OsmPrimitiveType.CLOSEDWAY)) { 174 return r.types.contains(TaggingPresetType.CLOSEDWAY); 175 } 176 if (member.getDisplayType().equals(OsmPrimitiveType.WAY)) { 177 return r.types.contains(TaggingPresetType.WAY); 178 } 179 if (member.getDisplayType().equals(OsmPrimitiveType.RELATION)) { 180 return r.types.contains(TaggingPresetType.RELATION); 181 } 182 // not matching type 183 return false; 184 } else { 185 // if no types specified, then test is passed 186 return true; 187 } 188 } 189 190 /** 191 * get all role definition for specified key and check, if some definition matches 192 * 193 * @param rolePreset containing preset for role of the member 194 * @param member to be verified 195 * @param n relation to be verified 196 * @return <tt>true</tt> if member passed any of definition within preset 197 * 198 */ 199 private boolean checkMemberExpressionAndType(RolePreset rolePreset, RelationMember member, Relation n) { 200 TestError possibleMatchError = null; 201 if (rolePreset == null || rolePreset.roles == null) { 202 // no restrictions on role types 203 return true; 204 } 205 // iterate through all of the role definition within preset 206 // and look for any matching definition 207 for (Role r: rolePreset.roles) { 208 if (checkMemberType(r, member)) { 209 // member type accepted by role definition 210 if (r.memberExpression == null) { 211 // no member expression - so all requirements met 212 return true; 213 } else { 214 // verify if preset accepts such member 215 OsmPrimitive primitive = member.getMember(); 216 if(!primitive.isUsable()) { 217 // if member is not usable (i.e. not present in working set) 218 // we can't verify expression - so we just skip it 219 return true; 220 } else { 221 // verify expression 222 if(r.memberExpression.match(primitive)) { 223 return true; 224 } else { 225 // possible match error 226 // we still need to iterate further, as we might have 227 // different present, for which memberExpression will match 228 // but stash the error in case no better reason will be found later 229 String s = marktr("Role member does not match expression {0} in template {1}"); 230 possibleMatchError = new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG, 231 tr(s, r.memberExpression, rolePreset.name), s, WRONG_TYPE, 232 member.getMember().isUsable() ? member.getMember() : n); 233 234 } 235 } 236 } 237 } 238 } 239 240 if( possibleMatchError != null) { 241 // if any error found, then assume that member type was correct 242 // and complain about not matching the memberExpression 243 // (the only failure, that we could gather) 244 errors.add(possibleMatchError); 245 } else { 246 // no errors found till now. So member at least failed at matching the type 247 // it could also fail at memberExpression, but we can't guess at which 248 String s = marktr("Role member type {0} does not match accepted list of {1} in template {2}"); 249 250 // prepare Set of all accepted types in template 251 EnumSet<TaggingPresetType> types = EnumSet.noneOf(TaggingPresetType.class); 252 for (Role r: rolePreset.roles) { 253 types.addAll(r.types); 254 } 255 256 // convert in localization friendly way to string of accepted types 257 String typesStr = Utils.join("/", Utils.transform(types, new Utils.Function<TaggingPresetType, Object>() { 258 public Object apply(TaggingPresetType x) { 259 return tr(x.getName()); 260 } 261 })); 262 263 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG, 264 tr(s, member.getType(), typesStr, rolePreset.name), s, WRONG_TYPE, 265 member.getMember().isUsable() ? member.getMember() : n)); 266 } 267 return false; 268 } 269 270 /** 271 * 272 * @param n relation to validate 273 * @param allroles contains presets for specified relation 274 * @param map contains statistics of occurances of specified role types in relation 275 */ 276 private void checkRoles(Relation n, Map<String, RolePreset> allroles, Map<String, RoleInfo> map) { 277 // go through all members of relation 278 for (RelationMember member: n.getMembers()) { 279 String role = member.getRole(); 280 281 // error reporting done inside 282 checkMemberExpressionAndType(allroles.get(role), member, n); 283 } 284 285 // verify role counts based on whole role sets 286 for(RolePreset rp: allroles.values()) { 287 for (Role r: rp.roles) { 288 String keyname = r.key; 289 if (keyname.isEmpty()) { 290 keyname = tr("<empty>"); 291 } 292 checkRoleCounts(n, r, keyname, map.get(r.key)); 293 } 294 } 295 // verify unwanted members 193 296 for (String key : map.keySet()) { 194 if (!done.contains(key)) { 297 if (!allroles.containsKey(key)) { 298 String templates = Utils.join("/", Utils.transform(allroles.keySet(), new Utils.Function<String, Object>() { 299 public Object apply(String x) { 300 return tr(x); 301 } 302 })); 303 195 304 if (key.length() > 0) { 196 String s = marktr("Role {0} unknown"); 305 String s = marktr("Role {0} unknown in templates {1}"); 306 197 307 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG, 198 tr(s, key ), MessageFormat.format(s, key), ROLE_UNKNOWN, n));308 tr(s, key, templates.toString()), MessageFormat.format(s, key), ROLE_UNKNOWN, n)); 199 309 } else { 200 String s = marktr("Empty role found");310 String s = marktr("Empty role type found when expecting one of {0}"); 201 311 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG, 202 tr(s), s, ROLE_EMPTY, n)); 203 } 204 } 205 } 206 } 207 208 private void checkRoleMemberExpressions(Relation n, Role r, String keyname, RoleInfo ri) { 209 Set<OsmPrimitive> notMatching = new HashSet<>(); 210 Collection<OsmPrimitive> allPrimitives = new ArrayList<>(); 211 allPrimitives.addAll(ri.nodes); 212 allPrimitives.addAll(ri.ways); 213 allPrimitives.addAll(ri.relations); 214 for (OsmPrimitive p : allPrimitives) { 215 if (p.isUsable() && !r.memberExpression.match(p)) { 216 notMatching.add(p); 217 } 218 } 219 if (!notMatching.isEmpty()) { 220 String s = marktr("Member for role ''{0}'' does not match ''{1}''"); 221 LinkedList<OsmPrimitive> highlight = new LinkedList<>(notMatching); 222 highlight.addFirst(n); 223 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG, 224 tr(s, keyname, r.memberExpression), MessageFormat.format(s, keyname, r.memberExpression), WRONG_TYPE, 225 highlight, notMatching)); 226 } 227 } 228 229 private void checkRoleTypes(Relation n, Role r, String keyname, RoleInfo ri) { 230 Set<OsmPrimitive> wrongTypes = new HashSet<>(); 231 if (!r.types.contains(TaggingPresetType.WAY)) { 232 wrongTypes.addAll(r.types.contains(TaggingPresetType.CLOSEDWAY) ? ri.openways : ri.ways); 233 } 234 if (!r.types.contains(TaggingPresetType.NODE)) { 235 wrongTypes.addAll(ri.nodes); 236 } 237 if (!r.types.contains(TaggingPresetType.RELATION)) { 238 wrongTypes.addAll(ri.relations); 239 } 240 if (!wrongTypes.isEmpty()) { 241 String s = marktr("Member for role {0} of wrong type"); 242 LinkedList<OsmPrimitive> highlight = new LinkedList<>(wrongTypes); 243 highlight.addFirst(n); 244 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG, 245 tr(s, keyname), MessageFormat.format(s, keyname), WRONG_TYPE, 246 highlight, wrongTypes)); 312 tr(s, templates), s, ROLE_EMPTY, n)); 313 } 314 } 247 315 } 248 316 } -
trunk/test/unit/org/openstreetmap/josm/data/validation/tests/RelationCheckerTest.groovy
r7526 r7884 55 55 def errors = testRelation(r) 56 56 assert errors.size() == 1 57 assert errors.get(0).getDescription() == "Role outer2 unknown "57 assert errors.get(0).getDescription() == "Role outer2 unknown in templates outer/inner" 58 58 } 59 59 … … 76 76 def errors = testRelation(r) 77 77 assert errors.size() == 1 78 assert errors.get(0).getDescription() == " Member for role via of wrong type"78 assert errors.get(0).getDescription() == "Role member type relation does not match accepted list of node/way in template Turn Restriction" 79 79 } 80 80 … … 100 100 def errors = testRelation(r) 101 101 assert errors.size() == 1 102 assert errors.get(0).getDescription() == "Empty role found"102 assert errors.get(0).getDescription().startsWith("Empty role type found when expecting one of") 103 103 } 104 104 … … 109 109 def errors = testRelation(r) 110 110 assert errors.size() == 1 111 assert errors.get(0).getDescription() == " Member for role '<empty>' does not match 'power'"111 assert errors.get(0).getDescription() == "Role member does not match expression power in template Power Route" 112 112 } 113 113 }
Note:
See TracChangeset
for help on using the changeset viewer.