Changeset 16651 in josm for trunk/src/org/openstreetmap/josm/data
- Timestamp:
- 2020-06-16T10:27:46+02:00 (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java
r16616 r16651 7 7 import java.util.ArrayList; 8 8 import java.util.Collection; 9 import java.util.Collections; 9 10 import java.util.EnumSet; 10 11 import java.util.HashMap; 12 import java.util.Iterator; 11 13 import java.util.LinkedHashMap; 14 import java.util.LinkedHashSet; 12 15 import java.util.LinkedList; 13 16 import java.util.List; … … 15 18 import java.util.stream.Collectors; 16 19 20 import org.openstreetmap.josm.command.ChangeCommand; 17 21 import org.openstreetmap.josm.command.Command; 18 22 import org.openstreetmap.josm.command.DeleteCommand; … … 62 66 /** Type ''{0}'' of relation member with role ''{1}'' does not match accepted types ''{2}'' in preset {3} */ 63 67 public static final int WRONG_TYPE = 1709; 68 /** Relations build circular dependencies */ 69 public static final int RELATION_LOOP = 1710; 64 70 // CHECKSTYLE.ON: SingleSpaceSeparator 65 71 … … 71 77 private boolean ignoreMultiPolygons; 72 78 private boolean ignoreTurnRestrictions; 73 79 private final List<List<Relation>> loops = new ArrayList<>(); 74 80 /** 75 81 * Constructor … … 155 161 checkRoles(n, allroles, map); 156 162 } 163 checkLoop(n); 157 164 } 158 165 … … 370 377 Collection<? extends OsmPrimitive> primitives = testError.getPrimitives(); 371 378 if (isFixable(testError) && !primitives.iterator().next().isDeleted()) { 372 return new DeleteCommand(primitives); 379 if (testError.getCode() == RELATION_EMPTY) { 380 return new DeleteCommand(primitives); 381 } 382 if (testError.getCode() == RELATION_LOOP) { 383 Relation old = (Relation) primitives.iterator().next(); 384 Relation mod = new Relation(old); 385 mod.removeMembersFor(primitives); 386 return new ChangeCommand(old, mod); 387 } 373 388 } 374 389 return null; … … 378 393 public boolean isFixable(TestError testError) { 379 394 Collection<? extends OsmPrimitive> primitives = testError.getPrimitives(); 380 return testError.getCode() == RELATION_EMPTY && !primitives.isEmpty() && primitives.iterator().next().isNew(); 395 return (testError.getCode() == RELATION_EMPTY && !primitives.isEmpty() && primitives.iterator().next().isNew()) 396 || (testError.getCode() == RELATION_LOOP && primitives.size() == 1); 381 397 } 382 398 … … 386 402 initializePresets(); 387 403 } 404 405 @Override 406 public void endTest() { 407 loops.forEach(loop -> errors.add(TestError.builder(this, Severity.ERROR, RELATION_LOOP) 408 .message(loop.size() == 2 ? tr("Relation contains itself as a member") 409 : tr("Relations generate circular dependency of parent/child elements")) 410 .primitives(new LinkedHashSet<>(loop)) 411 .build())); 412 loops.clear(); 413 super.endTest(); 414 } 415 416 /** 417 * Check if a given relation is part of a circular dependency loop. 418 * @param r the relation 419 */ 420 private void checkLoop(Relation r) { 421 checkLoop(r, new LinkedList<>()); 422 } 423 424 private void checkLoop(Relation parent, List<Relation> path) { 425 if (path.contains(parent)) { 426 Iterator<List<Relation>> iter = loops.iterator(); 427 while (iter.hasNext()) { 428 List<Relation> loop = iter.next(); 429 if (loop.size() > path.size() && loop.containsAll(path)) { 430 // remove same loop with irrelevant parent 431 iter.remove(); 432 } else if (path.size() >= loop.size() && path.containsAll(loop)) { 433 // same or smaller loop is already known 434 return; 435 } 436 } 437 if (path.get(0).equals(parent)) { 438 path.add(parent); 439 loops.add(path); 440 } 441 return; 442 } 443 path.add(parent); 444 for (Relation sub : parent.getMemberPrimitives(Relation.class)) { 445 if (sub.isUsable() && !sub.isIncomplete()) { 446 checkLoop(sub, new LinkedList<>(path)); 447 } 448 } 449 } 450 451 /** 452 * Check if adding one relation to another would produce a circular dependency. 453 * @param parent the relation which would be changed 454 * @param child the child relation which should be added to parent 455 * @return An unmodifiable list of relations which is empty when no circular dependency was found, 456 * else it contains the relations that form circular dependencies. 457 * The list then contains at least two items. Normally first and last item are both {@code parent}, 458 * but if child is already part of a circular dependency the returned list may not end with {@code parent}. 459 */ 460 public static List<Relation> checkAddMember(Relation parent, Relation child) { 461 if (parent == null || child == null || child.isIncomplete()) 462 return Collections.emptyList(); 463 RelationChecker test = new RelationChecker(); 464 LinkedList<Relation> path = new LinkedList<>(); 465 path.add(parent); 466 test.checkLoop(child, path); 467 if (test.loops.isEmpty()) 468 return Collections.emptyList(); 469 else 470 return Collections.unmodifiableList(test.loops.iterator().next()); 471 } 472 388 473 }
Note:
See TracChangeset
for help on using the changeset viewer.