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