001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.changeset.query;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.time.LocalDate;
007import java.time.ZoneId;
008import java.time.ZonedDateTime;
009import java.time.format.DateTimeFormatter;
010import java.time.format.DateTimeParseException;
011import java.time.format.FormatStyle;
012
013import javax.swing.text.JTextComponent;
014
015import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
016import org.openstreetmap.josm.tools.Logging;
017
018/**
019 * Validates dates entered as text in a {@link JTextComponent}. Validates the input
020 * on the fly and gives feedback about whether the date is valid or not.
021 *
022 * Dates can be entered in one of four standard formats defined for the current locale.
023 * @since 11326 (extracted from AdvancedChangesetQueryPanel)
024 */
025public class DateValidator extends AbstractTextComponentValidator {
026
027    /**
028     * Constructs a new {@code DateValidator} for the given text component.
029     * @param tc text component
030     */
031    public DateValidator(JTextComponent tc) {
032        super(tc);
033    }
034
035    /**
036     * Decorates the given text component.
037     * @param tc text component to decorate
038     * @return new date validator attached to {@code tc}
039     */
040    public static DateValidator decorate(JTextComponent tc) {
041        return new DateValidator(tc);
042    }
043
044    @Override
045    public boolean isValid() {
046        return getDate() != null;
047    }
048
049    /**
050     * Returns the standard tooltip text as HTML.
051     * @return the standard tooltip text as HTML
052     */
053    public String getStandardTooltipTextAsHtml() {
054        return "<html>" + getStandardTooltipText() + "</html>";
055    }
056
057    /**
058     * Returns the standard tooltip text.
059     * @return the standard tooltip text
060     */
061    public String getStandardTooltipText() {
062        final ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault());
063        return tr(
064                "Please enter a date in the usual format for your locale.<br>"
065                + "Example: {0}<br>"
066                + "Example: {1}<br>"
067                + "Example: {2}<br>"
068                + "Example: {3}<br>",
069                DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(now),
070                DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(now),
071                DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(now),
072                DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(now)
073        );
074    }
075
076    @Override
077    public void validate() {
078        if (!isValid()) {
079            String msg = "<html>The current value isn't a valid date.<br>" + getStandardTooltipText()+ "</html>";
080            feedbackInvalid(msg);
081        } else {
082            String msg = "<html>" + getStandardTooltipText() + "</html>";
083            feedbackValid(msg);
084        }
085    }
086
087    /**
088     * Returns the date.
089     * @return the date
090     */
091    public LocalDate getDate() {
092        for (final FormatStyle format: FormatStyle.values()) {
093            DateTimeFormatter df = DateTimeFormatter.ofLocalizedDate(format);
094            try {
095                return LocalDate.parse(getComponent().getText(), df);
096            } catch (DateTimeParseException e) {
097                // Try next format
098                Logging.trace(e);
099            }
100        }
101        return null;
102    }
103}