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