Ticket #23397: 23397-beta2.patch

File 23397-beta2.patch, 17.0 KB (added by GerdP, 13 months ago)

like 23397-beta.patch, but makes sure that errors coming from MapCss rules contain parent object so that filtering works

  • src/org/openstreetmap/josm/actions/ValidateAction.java

     
    1212import org.openstreetmap.josm.data.validation.OsmValidator;
    1313import org.openstreetmap.josm.data.validation.Test;
    1414import org.openstreetmap.josm.data.validation.ValidationTask;
    15 import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor;
    1615import org.openstreetmap.josm.gui.MainApplication;
    1716import org.openstreetmap.josm.gui.MapFrame;
    1817import org.openstreetmap.josm.tools.Shortcut;
     
    7069                selection = getLayerManager().getActiveDataSet().allNonDeletedPrimitives();
    7170                lastSelection = null;
    7271            } else {
    73                 AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor();
    74                 selection = v.visit(selection);
    7572                lastSelection = selection;
    7673            }
    7774        } else {
  • src/org/openstreetmap/josm/actions/upload/ValidateUploadHook.java

     
    66import java.awt.Dimension;
    77import java.awt.GridBagLayout;
    88import java.util.Collection;
     9import java.util.HashSet;
    910import java.util.List;
    1011import java.util.concurrent.atomic.AtomicBoolean;
    1112
     
    1718import org.openstreetmap.josm.data.validation.OsmValidator;
    1819import org.openstreetmap.josm.data.validation.TestError;
    1920import org.openstreetmap.josm.data.validation.ValidationTask;
    20 import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor;
    2121import org.openstreetmap.josm.gui.ExtendedDialog;
    2222import org.openstreetmap.josm.gui.MainApplication;
    2323import org.openstreetmap.josm.gui.dialogs.validator.ValidatorTreePanel;
     
    4545    @Override
    4646    public boolean checkUpload(APIDataSet apiDataSet) {
    4747        AtomicBoolean returnCode = new AtomicBoolean();
    48         AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor();
    49         v.visit(apiDataSet.getPrimitivesToAdd());
    50         Collection<OsmPrimitive> visited = v.visit(apiDataSet.getPrimitivesToUpdate());
     48        Collection<OsmPrimitive> toCheck = new HashSet<>();
     49        toCheck.addAll(apiDataSet.getPrimitivesToAdd());
     50        toCheck.addAll(apiDataSet.getPrimitivesToUpdate());
    5151        OsmValidator.initializeTests();
    5252        new ValidationTask(errors -> {
    5353            if (errors.stream().allMatch(TestError::isIgnored)) {
     
    5858                // of the progress monitor.
    5959                GuiHelper.runInEDTAndWait(() -> returnCode.set(displayErrorScreen(errors)));
    6060            }
    61         }, null, OsmValidator.getEnabledTests(true), visited, null, true).run();
     61        }, null, OsmValidator.getEnabledTests(true), toCheck, null, true).run();
    6262
    6363        return returnCode.get();
    6464    }
  • src/org/openstreetmap/josm/data/validation/Test.java

     
    66import java.awt.GridBagConstraints;
    77import java.util.ArrayList;
    88import java.util.Collection;
     9import java.util.HashSet;
    910import java.util.List;
    1011import java.util.Optional;
     12import java.util.Set;
    1113import java.util.function.Predicate;
    1214import java.util.stream.Collectors;
    1315
     
    383385    public Object getSource() {
    384386        return "Java: " + this.getClass().getName();
    385387    }
     388
     389    /**
     390     * Filter the list of errors, remove all which do not concern the given list of primitives
     391     * @param given the list of primitives
     392     * @since xxx
     393     */
     394    public void removeIrrelevantErrors(Collection<? extends OsmPrimitive> given) {
     395        if (errors == null || errors.isEmpty())
     396            return;
     397        // filter errors for those which are needed, don't show errors for objects which were not in the selection
     398        final Set<? extends OsmPrimitive> relevant;
     399        if (given instanceof Set) {
     400            relevant = (Set<? extends OsmPrimitive>) given;
     401        } else {
     402            relevant = new HashSet<>(given);
     403        }
     404        errors.removeIf(e -> !e.isConcerned(relevant));
     405    }
    386406}
  • src/org/openstreetmap/josm/data/validation/TestError.java

     
    1212import java.util.List;
    1313import java.util.Locale;
    1414import java.util.Map;
     15import java.util.Set;
    1516import java.util.TreeSet;
    1617import java.util.function.Supplier;
    1718import java.util.stream.Collectors;
     
    666667                ", code=" + code + ", message=" + message + ']';
    667668    }
    668669
     670    /**
     671     * Check if any of the primitives in this error occurs in the given set of primitives.
     672     * @param given the set of primitives
     673     * @return true if any of the primitives in this error occurs in the given set of primitives, else false
     674     * @since xxx
     675     */
     676    public boolean isConcerned(Set<? extends OsmPrimitive> given) {
     677        for (OsmPrimitive p : getPrimitives()) {
     678            if (given.contains(p)) {
     679                return true;
     680            }
     681        }
     682        return false;
     683    }
    669684}
  • src/org/openstreetmap/josm/data/validation/ValidationTask.java

     
    66import java.awt.GraphicsEnvironment;
    77import java.util.ArrayList;
    88import java.util.Collection;
     9import java.util.HashSet;
    910import java.util.List;
    1011import java.util.function.BiConsumer;
    1112import java.util.function.Consumer;
     
    1213
    1314import javax.swing.JOptionPane;
    1415
     16import org.openstreetmap.josm.data.osm.Node;
    1517import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1618import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
     19import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor;
    1720import org.openstreetmap.josm.gui.MainApplication;
    1821import org.openstreetmap.josm.gui.MapFrame;
    1922import org.openstreetmap.josm.gui.Notification;
     
    3033public class ValidationTask extends PleaseWaitRunnable {
    3134    private final Consumer<List<TestError>> onFinish;
    3235    private Collection<Test> tests;
    33     private final Collection<OsmPrimitive> validatedPrimitives;
     36    private final Collection<OsmPrimitive> initialPrimitives;
    3437    private final Collection<OsmPrimitive> formerValidatedPrimitives;
    3538    private final boolean beforeUpload;
    3639    private boolean canceled;
     
    7174                progressMonitor != null ? progressMonitor : new PleaseWaitProgressMonitor(tr("Validating")),
    7275                false /*don't ignore exceptions */);
    7376        this.onFinish = onFinish;
    74         this.validatedPrimitives = validatedPrimitives;
     77        this.initialPrimitives = validatedPrimitives;
    7578        this.formerValidatedPrimitives = formerValidatedPrimitives;
    7679        this.tests = tests;
    7780        this.beforeUpload = beforeUpload;
    7881    }
    7982
     83    /**
     84     * Create extended list
     85     * - add child objects because MapCss tests may need them to work properly
     86     * - add parent objects of modified nodes as they may mean a geometry change
     87     * @param primitives the primitives
     88     * @return extended list of primitives
     89     */
     90    private static Collection<OsmPrimitive> extendList(Collection<OsmPrimitive> primitives) {
     91        Collection<OsmPrimitive> extendedList = new HashSet<>(primitives);
     92        for (OsmPrimitive p : primitives) {
     93            if (p instanceof Node && p.isModified() && !p.isNew()) {
     94                for (OsmPrimitive parent : p.getReferrers()) {
     95                    if (!parent.isDeleted()) {
     96                        extendedList.add(parent);
     97                    }
     98                }
     99            }
     100        }
     101        AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor();
     102        return v.visit(extendedList);
     103    }
     104
    80105    protected ValidationTask(ProgressMonitor progressMonitor,
    81106            Collection<Test> tests,
    82107            Collection<OsmPrimitive> validatedPrimitives,
     
    122147    protected void realRun() {
    123148        if (Utils.isEmpty(tests))
    124149            return;
     150        int testCounter = 0;
     151        final boolean isPartial = this.beforeUpload || formerValidatedPrimitives != null;
     152
     153        Collection<OsmPrimitive> validatedPrimitives = isPartial ? extendList(initialPrimitives) : initialPrimitives;
    125154        getProgressMonitor().setTicksCount(tests.size() * validatedPrimitives.size());
    126         int testCounter = 0;
     155
    127156        for (Test test : tests) {
    128157            if (canceled)
    129158                return;
     
    131160            getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(), test.getName()));
    132161            test.setBeforeUpload(this.beforeUpload);
    133162            // Pre-upload checks only run on a partial selection.
    134             test.setPartialSelection(this.beforeUpload || formerValidatedPrimitives != null);
     163            test.setPartialSelection(isPartial);
    135164            test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false));
    136165            test.visit(validatedPrimitives);
    137166            test.endTest();
     167            if (isPartial) {
     168                // #23397: remove errors for objects which were not in the initial list of primitives
     169                test.removeIrrelevantErrors(initialPrimitives);
     170            }
    138171            errors.addAll(test.getErrors());
    139172            if (this.testConsumer != null) {
    140173                this.testConsumer.accept(this, test);
  • src/org/openstreetmap/josm/data/validation/tests/CrossingWays.java

     
    66import java.awt.geom.Point2D;
    77import java.util.ArrayList;
    88import java.util.Arrays;
     9import java.util.Collection;
    910import java.util.HashMap;
    1011import java.util.HashSet;
    1112import java.util.List;
     
    1617
    1718import org.openstreetmap.josm.data.coor.EastNorth;
    1819import org.openstreetmap.josm.data.coor.ILatLon;
     20import org.openstreetmap.josm.data.osm.DataSet;
     21import org.openstreetmap.josm.data.osm.OsmDataManager;
    1922import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2023import org.openstreetmap.josm.data.osm.OsmUtils;
    2124import org.openstreetmap.josm.data.osm.Relation;
     
    8184    private final Map<Point2D, List<WaySegment>> cellSegments = new HashMap<>(1000);
    8285    /** The already detected ways in error */
    8386    private final Map<List<Way>, List<WaySegment>> seenWays = new HashMap<>(50);
     87    private final Set<Way> waysToTest = new HashSet<>();
    8488
    8589    protected final int code;
    8690
     
    305309
    306310    @Override
    307311    public void endTest() {
    308         super.endTest();
     312        final Collection<Way> selection;
     313        if (this instanceof SelfCrossing || !partialSelection) {
     314            selection = waysToTest;
     315        } else {
     316            selection = new HashSet<>();
     317            DataSet ds = OsmDataManager.getInstance().getActiveDataSet();
     318            if (ds != null) {
     319                for (Way w: waysToTest) {
     320                    selection.addAll(ds.searchWays(w.getBBox()));
     321                }
     322            }
     323        }
     324        for (Way w : selection) {
     325            if (!w.isDeleted() && isPrimitiveUsable(w)) {
     326                testWay(w);
     327            }
     328        }
     329        // free storage
    309330        cellSegments.clear();
    310331        seenWays.clear();
     332        waysToTest.clear();
     333        super.endTest();
    311334    }
    312335
    313336    static boolean isCoastline(OsmPrimitive w) {
     
    344367
    345368    @Override
    346369    public void visit(Way w) {
     370        waysToTest.add(w);
     371    }
     372
     373    private void testWay(Way w) {
    347374        boolean findSelfCrossingOnly = this instanceof SelfCrossing;
    348375        if (findSelfCrossingOnly) {
    349376            // free memory, we are not interested in previous ways
     
    482509        CheckParameterUtil.ensureParameterNotNull(way, "way");
    483510        SelfCrossing test = new SelfCrossing();
    484511        test.visit(way);
     512        test.endTest();
    485513        return !test.getErrors().isEmpty();
    486514    }
    487515}
  • src/org/openstreetmap/josm/data/validation/tests/DuplicateWay.java

     
    77import java.util.Collection;
    88import java.util.Collections;
    99import java.util.HashSet;
     10import java.util.LinkedHashSet;
    1011import java.util.LinkedList;
    1112import java.util.List;
    1213import java.util.Map;
     
    103104
    104105    /** Set of known hashcodes for list of coordinates **/
    105106    private Set<Integer> knownHashCodes;
     107    private List<Way> waysToCheck;
    106108
    107109    /**
    108110     * Constructor
     
    115117    @Override
    116118    public void startTest(ProgressMonitor monitor) {
    117119        super.startTest(monitor);
     120        waysToCheck = new ArrayList<>();
    118121        ways = new MultiMap<>(1000);
    119122        waysNoTags = new MultiMap<>(1000);
    120123        knownHashCodes = new HashSet<>(1000);
     
    122125
    123126    @Override
    124127    public void endTest() {
    125         super.endTest();
     128        if (partialSelection && !waysToCheck.isEmpty()) {
     129            // make sure that we have the error candidates even if not selected
     130            Set<Way> extended = new LinkedHashSet<>(waysToCheck);
     131            for (Way w : waysToCheck) {
     132                // select a node, anyone can be used but a middle node is less likely to have many parent ways
     133                final Node n = w.getNode(w.getNodesCount()/2);
     134                // check the ways which might be in the same position
     135                for (Way other : n.getParentWays()) {
     136                    if (other != w && !other.isDeleted() && other.isUsable()
     137                            && other.getNodesCount() == w.getNodesCount())
     138                        extended.add(other);
     139                }
     140            }
     141            extended.forEach(this::checkWay);
     142        }
     143
    126144        for (Set<OsmPrimitive> duplicated : ways.values()) {
    127145            if (duplicated.size() > 1) {
    128146                TestError testError = TestError.builder(this, Severity.ERROR, DUPLICATE_WAY)
     
    162180                errors.add(testError);
    163181            }
    164182        }
     183
    165184        ways = null;
    166185        waysNoTags = null;
    167186        knownHashCodes = null;
     187        waysToCheck = null;
     188        super.endTest();
    168189    }
    169190
    170191    /**
     
    181202    public void visit(Way w) {
    182203        if (!w.isUsable())
    183204            return;
     205        if (partialSelection)
     206            waysToCheck.add(w);
     207        else
     208            checkWay(w);
     209    }
     210
     211    private void checkWay(Way w) {
    184212        List<LatLon> wLat = getOrderedNodes(w);
    185213        // If this way has not direction-dependant keys, make sure the list is ordered the same for all ways (fix #8015)
    186214        if (!w.hasDirectionKeys()) {
  • src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java

     
    377377                        res.add(errorBuilder.primitives(p, (OsmPrimitive) c).build());
    378378                    }
    379379                }
     380            } else if (env.parent != null) {
     381                res.add(errorBuilder.primitives(p, (OsmPrimitive) env.parent).highlight(p).build());
    380382            } else {
    381383                res.add(errorBuilder.primitives(p).build());
    382384            }
  • src/org/openstreetmap/josm/data/validation/util/AggregatePrimitivesVisitor.java

     
    1414/**
    1515 * A visitor that aggregates all primitives it visits.
    1616 * <p>
    17  * The primitives are sorted according to their type: first nodes, then ways.
    1817 *
    1918 * @author frsantos
    2019 */
     
    2423
    2524    /**
    2625     * Visits a collection of primitives
    27      * @param data The collection of primitives
     26     * @param data The collection of primitives in no specific order.
    2827     * @return The aggregated primitives
    2928     */
    3029    public Collection<OsmPrimitive> visit(Collection<OsmPrimitive> data) {