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.Main;
015import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
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            return;
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                Main.trace(e);
099            }
100        }
101        return null;
102    }
103}