001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.tagging.presets;
003
004import static java.util.Collections.singleton;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.util.ArrayList;
008import java.util.Arrays;
009import java.util.Collection;
010import java.util.List;
011
012import javax.swing.JLabel;
013
014import org.openstreetmap.josm.command.Command;
015import org.openstreetmap.josm.data.osm.DataSet;
016import org.openstreetmap.josm.data.osm.FilterModel;
017import org.openstreetmap.josm.data.osm.INode;
018import org.openstreetmap.josm.data.osm.IRelation;
019import org.openstreetmap.josm.data.osm.IWay;
020import org.openstreetmap.josm.data.osm.OsmPrimitive;
021import org.openstreetmap.josm.data.osm.Tag;
022import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
023import org.openstreetmap.josm.data.validation.OsmValidator;
024import org.openstreetmap.josm.data.validation.TestError;
025import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
026import org.openstreetmap.josm.data.validation.tests.OpeningHourTest;
027import org.openstreetmap.josm.gui.MainApplication;
028import org.openstreetmap.josm.gui.util.GuiHelper;
029import org.openstreetmap.josm.tools.Logging;
030import org.openstreetmap.josm.tools.SubclassFilteredCollection;
031import org.openstreetmap.josm.tools.Utils;
032
033/**
034 * Validates the preset user input a the given primitive.
035 */
036interface TaggingPresetValidation {
037
038    /**
039     * Asynchronously validates the user input for the given primitive.
040     * @param original the primitive
041     * @param validationLabel the label for validation errors
042     * @param changedTags the list of tags that are set by this preset
043     */
044    static void validateAsync(OsmPrimitive original, JLabel validationLabel, List<Tag> changedTags) {
045        OsmPrimitive primitive = applyChangedTags(original, changedTags);
046        MainApplication.worker.execute(() -> validate(primitive, validationLabel));
047    }
048
049    /**
050     * Validates the user input for the given primitive.
051     * @param primitive the primitive
052     * @param validationLabel the label for validation errors
053     */
054    static void validate(OsmPrimitive primitive, JLabel validationLabel) {
055        try {
056            MapCSSTagChecker mapCSSTagChecker = OsmValidator.getTest(MapCSSTagChecker.class);
057            OpeningHourTest openingHourTest = OsmValidator.getTest(OpeningHourTest.class);
058            OsmValidator.initializeTests(Arrays.asList(mapCSSTagChecker, openingHourTest));
059
060            List<TestError> errors = new ArrayList<>();
061            openingHourTest.addErrorsForPrimitive(primitive, errors);
062            errors.addAll(mapCSSTagChecker.getErrorsForPrimitive(primitive, ValidatorPrefHelper.PREF_OTHER.get()));
063
064            boolean visible = !errors.isEmpty();
065            String toolTipText = "<html>" + Utils.joinAsHtmlUnorderedList(Utils.transform(errors, e ->
066                    e.getDescription() == null ? e.getMessage() : tr("{0} ({1})", e.getMessage(), e.getDescription())));
067            GuiHelper.runInEDTAndWait(() -> {
068                validationLabel.setVisible(visible);
069                validationLabel.setToolTipText(toolTipText);
070            });
071        } catch (Exception e) {
072            Logging.warn("Failed to validate {0}", primitive);
073            Logging.warn(e);
074        } finally {
075            primitive.getDataSet().clear();
076        }
077    }
078
079    static OsmPrimitive applyChangedTags(OsmPrimitive original, List<Tag> changedTags) {
080        DataSet ds = new DataSet();
081        Collection<OsmPrimitive> primitives = FilterModel.getAffectedPrimitives(singleton(original));
082        OsmPrimitive primitive = ds.clonePrimitives(
083                new SubclassFilteredCollection<>(primitives, INode.class::isInstance),
084                new SubclassFilteredCollection<>(primitives, IWay.class::isInstance),
085                new SubclassFilteredCollection<>(primitives, IRelation.class::isInstance))
086                .get(original);
087        Command command = TaggingPreset.createCommand(singleton(primitive), changedTags);
088        if (command != null) {
089            command.executeCommand();
090        }
091        return primitive;
092    }
093}