Ticket #19312: 19312.patch

File 19312.patch, 4.5 KB (added by GerdP, 6 years ago)
  • src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java

     
    88import java.util.Collection;
    99import java.util.EnumSet;
    1010import java.util.HashMap;
     11import java.util.Iterator;
    1112import java.util.LinkedHashMap;
    1213import java.util.LinkedList;
    1314import java.util.List;
     
    1415import java.util.Map;
    1516import java.util.stream.Collectors;
    1617
     18import org.openstreetmap.josm.command.ChangeCommand;
    1719import org.openstreetmap.josm.command.Command;
    1820import org.openstreetmap.josm.command.DeleteCommand;
    1921import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    6163    public static final int RELATION_EMPTY   = 1708;
    6264    /** Type ''{0}'' of relation member with role ''{1}'' does not match accepted types ''{2}'' in preset {3} */
    6365    public static final int WRONG_TYPE       = 1709;
     66    /** Relations build a loop */
     67    public static final int RELATION_LOOP    = 1710;
    6468    // CHECKSTYLE.ON: SingleSpaceSeparator
    6569
    6670    /**
     
    7074    public static final String ROLE_VERIF_PROBLEM_MSG = tr("Role verification problem");
    7175    private boolean ignoreMultiPolygons;
    7276    private boolean ignoreTurnRestrictions;
    73 
     77    private final List<List<Relation>> loops = new ArrayList<>();
    7478    /**
    7579     * Constructor
    7680     */
     
    154158        if (!map.isEmpty() && !allroles.isEmpty()) {
    155159            checkRoles(n, allroles, map);
    156160        }
     161        checkLoop(n);
    157162    }
    158163
    159164    private static Map<String, RoleInfo> buildRoleInfoMap(Relation n) {
     
    365370    public Command fixError(TestError testError) {
    366371        Collection<? extends OsmPrimitive> primitives = testError.getPrimitives();
    367372        if (isFixable(testError) && !primitives.iterator().next().isDeleted()) {
    368             return new DeleteCommand(primitives);
     373            if (testError.getCode() == RELATION_EMPTY) {
     374                return new DeleteCommand(primitives);
     375            }
     376            if (testError.getCode() == RELATION_LOOP) {
     377                Relation old = (Relation) primitives.iterator().next();
     378                Relation mod = new Relation(old);
     379                mod.removeMembersFor(primitives);
     380                return new ChangeCommand(old, mod);
     381            }
    369382        }
    370383        return null;
    371384    }
     
    373386    @Override
    374387    public boolean isFixable(TestError testError) {
    375388        Collection<? extends OsmPrimitive> primitives = testError.getPrimitives();
    376         return testError.getCode() == RELATION_EMPTY && !primitives.isEmpty() && primitives.iterator().next().isNew();
     389        return testError.getCode() == RELATION_EMPTY && !primitives.isEmpty() && primitives.iterator().next().isNew() ||
     390                testError.getCode() == RELATION_LOOP && primitives.size() == 1;
    377391    }
    378392
    379393    @Override
     
    381395        relationpresets.clear();
    382396        initializePresets();
    383397    }
     398
     399    @Override
     400    public void endTest() {
     401        loops.forEach(loop -> errors.add(TestError.builder(this, Severity.ERROR, RELATION_LOOP)
     402                .message(loop.size() == 1 ? tr("Relation contains itself as a member") : tr("Relations build a dependency loop"))
     403                .primitives(loop)
     404                .build()));
     405        loops.clear();
     406        super.endTest();
     407    }
     408
     409    private void checkLoop(Relation r) {
     410        checkLoop(r, new LinkedList<>());
     411    }
     412
     413    private void checkLoop(Relation parent, List<Relation> path) {
     414        if (path.contains(parent)) {
     415            Iterator<List<Relation>> iter = loops.iterator();
     416            while (iter.hasNext()) {
     417                List<Relation> loop = iter.next();
     418                if (loop.size() > path.size() && loop.containsAll(path)) {
     419                    // remove same loop with irrelevant parent
     420                    iter.remove();
     421                } else if (path.size() >= loop.size() && path.containsAll(loop)) {
     422                    // same or smaller loop is already known
     423                    return;
     424                }
     425            }
     426            loops.add(path);
     427            return;
     428        }
     429        path.add(parent);
     430        for (Relation sub : parent.getMemberPrimitives(Relation.class)) {
     431            if (sub.isUsable() && !sub.isIncomplete()) {
     432                checkLoop(sub, new LinkedList<>(path));
     433            }
     434        }
     435    }
     436
    384437}