001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm;
003
004import java.util.Arrays;
005import java.util.function.Predicate;
006import java.util.stream.Collectors;
007import java.util.stream.Stream;
008
009import org.openstreetmap.josm.command.Command;
010import org.openstreetmap.josm.data.UndoRedoHandler;
011
012/**
013 * Exception thrown when a primitive or data set does not pass its integrity checks.
014 * @since 2399
015 */
016public class DataIntegrityProblemException extends RuntimeException {
017
018    private final String htmlMessage;
019
020    /**
021     * Constructs a new {@code DataIntegrityProblemException}.
022     * @param message the detail message
023     */
024    public DataIntegrityProblemException(String message) {
025        this(message, null);
026    }
027
028    /**
029     * Constructs a new {@code DataIntegrityProblemException}.
030     * @param message the detail message
031     * @param htmlMessage HTML-formatted error message. Can be null
032     * @param p the primitive involved in this integrity problem (used for constructing a detailed message)
033     */
034    public DataIntegrityProblemException(String message, String htmlMessage, OsmPrimitive... p) {
035        super(message + relevantCommands(p));
036        this.htmlMessage = htmlMessage;
037    }
038
039    /**
040     * Returns the HTML-formatted error message.
041     * @return the HTML-formatted error message, or null
042     */
043    public String getHtmlMessage() {
044        return htmlMessage;
045    }
046
047    private static String relevantCommands(OsmPrimitive... p) {
048        if (p == null || p.length == 0) {
049            return "";
050        }
051        Predicate<Command> isParticipating = c -> Arrays.stream(p).anyMatch(c.getParticipatingPrimitives()::contains);
052        Stream<String> undo = UndoRedoHandler.getInstance().getUndoCommands().stream()
053                .filter(isParticipating)
054                .map(c -> "[" + c.getDescriptionText() + "]");
055        Stream<String> redo = UndoRedoHandler.getInstance().getRedoCommands().stream()
056                .filter(isParticipating)
057                .map(c -> "[" + c.getDescriptionText() + " (undone)]");
058        return Stream.concat(undo, redo)
059                .collect(Collectors.joining(", ", " (changed by the following commands: ", ")"));
060    }
061}