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}