View Javadoc
1   /*******************************************************************************
2    * JDateButton: JavaFX/Swing Date Button
3    * Copyright 2012,2015 Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   * ------------------------------------------------------------
17   * SubVersion Revision Information:
18   * $URL: https://svn.code.sf.net/p/jdatebutton/code/tags/v2.1.0-b3/jdatebutton-core/src/main/java/net/sourceforge/jdatebutton/JDateBaseConfig.java $
19   * $Revision: 34 $
20   * $Author: tonywasher $
21   * $Date: 2015-12-01 16:21:13 +0000 (Tue, 01 Dec 2015) $
22   ******************************************************************************/
23  package net.sourceforge.jdatebutton;
24  
25  import java.time.LocalDate;
26  import java.time.format.DateTimeFormatter;
27  import java.util.Locale;
28  
29  /**
30   * Provides Date Management support for JDateButton.
31   * @param <T> the owning button
32   */
33  public abstract class JDateBaseConfig<T extends JDateBaseButton> {
34      /**
35       * Name of the Date property.
36       */
37      public static final String PROPERTY_DATE = "SelectedDate";
38  
39      /**
40       * The Locale.
41       */
42      private Locale theLocale = null;
43  
44      /**
45       * The format string.
46       */
47      private String theFormat = null;
48  
49      /**
50       * The formatter.
51       */
52      private JDateFormatter theFormatter = null;
53  
54      /**
55       * The Simple Date format for the locale and format string.
56       */
57      private DateTimeFormatter theDateFormat = null;
58  
59      /**
60       * Show Narrow days.
61       */
62      private Boolean doShowNarrowDays = false;
63  
64      /**
65       * Allow null date selection.
66       */
67      private boolean allowNullDateSelection = false;
68  
69      /**
70       * The earliest select-able date (or null if no lower bound).
71       */
72      private LocalDate theEarliest = null;
73  
74      /**
75       * The latest select-able date (or null if no upper bound).
76       */
77      private LocalDate theLatest = null;
78  
79      /**
80       * The active display month.
81       */
82      private LocalDate theMonth = null;
83  
84      /**
85       * The button to notify of Date changes.
86       */
87      private T theOwner = null;
88  
89      /**
90       * Constructor.
91       */
92      protected JDateBaseConfig() {
93          /* Set default locale and format */
94          setLocale(Locale.getDefault());
95          setFormat("dd-MMM-yyyy");
96      }
97  
98      /**
99       * Constructor.
100      * @param pFormatter the date formatter
101      */
102     protected JDateBaseConfig(final JDateFormatter pFormatter) {
103         /* Set formatter */
104         setFormatter(pFormatter);
105     }
106 
107     /**
108      * Get the locale.
109      * @return the locale
110      */
111     public Locale getLocale() {
112         return theLocale;
113     }
114 
115     /**
116      * Get the owner.
117      * @return the owner
118      */
119     public T getOwner() {
120         return theOwner;
121     }
122 
123     /**
124      * Get the selected date.
125      * @return the Selected date
126      */
127     public abstract LocalDate getSelectedDate();
128 
129     /**
130      * Get the earliest select-able date.
131      * @return the Earliest date
132      */
133     public LocalDate getEarliestDate() {
134         return theEarliest;
135     }
136 
137     /**
138      * Get the latest select-able date.
139      * @return the Latest date
140      */
141     public LocalDate getLatestDate() {
142         return theLatest;
143     }
144 
145     /**
146      * Get the current month.
147      * @return the current month
148      */
149     public LocalDate getCurrentMonth() {
150         return theMonth;
151     }
152 
153     /**
154      * Allow Null Date selection.
155      * @return true/false
156      */
157     public boolean allowNullDateSelection() {
158         return allowNullDateSelection;
159     }
160 
161     /**
162      * Show Narrow Days.
163      * @return true/false
164      */
165     public boolean showNarrowDays() {
166         return doShowNarrowDays;
167     }
168 
169     /**
170      * Check that we have no formatter.
171      */
172     protected final void checkNoFormatter() {
173         /* Not allowed if we have a formatter */
174         if (theFormatter != null) {
175             throw new UnsupportedOperationException();
176         }
177     }
178 
179     /**
180      * Set Locale.
181      * @param pLocale the Locale
182      */
183     public final void setLocale(final Locale pLocale) {
184         /* Not allowed if we have a formatter */
185         checkNoFormatter();
186 
187         /* Store locale */
188         setTheLocale(pLocale);
189     }
190 
191     /**
192      * Set the Locale.
193      * @param pLocale the Locale
194      */
195     public void setTheLocale(final Locale pLocale) {
196         /* Store locale */
197         theLocale = pLocale;
198 
199         /* Update the date format if available */
200         if (theFormat != null) {
201             setFormat(theFormat);
202         }
203 
204         /* Request rebuild of names */
205         rebuildNames();
206     }
207 
208     /**
209      * Set the date format.
210      * @param pFormat the format string
211      */
212     public final void setFormat(final String pFormat) {
213         /* Not allowed if we have a formatter */
214         checkNoFormatter();
215 
216         /* Store the format string */
217         theFormat = pFormat;
218 
219         /* Create the simple date format */
220         theDateFormat = DateTimeFormatter.ofPattern(theFormat, theLocale);
221 
222         /* Refresh the display */
223         refreshText();
224     }
225 
226     /**
227      * Refresh the text.
228      */
229     protected final void refreshText() {
230         /* Refresh the display */
231         if (theOwner != null) {
232             theOwner.refreshText();
233         }
234     }
235 
236     /**
237      * Set the date formatter.
238      * @param pFormatter the formatter
239      */
240     public final void setFormatter(final JDateFormatter pFormatter) {
241         /* Store the formatter */
242         theFormatter = pFormatter;
243 
244         /* Release format and date format */
245         theFormat = null;
246         theDateFormat = null;
247 
248         /* Set the locale */
249         setTheLocale(pFormatter.getLocale());
250     }
251 
252     /**
253      * Allow null date selection. If this flag is set an additional button will be displayed
254      * allowing the user to explicitly select no date, thus setting the SelectedDate to null.
255      * @param pAllowNullDateSelection true/false
256      */
257     public void setAllowNullDateSelection(final boolean pAllowNullDateSelection) {
258         allowNullDateSelection = pAllowNullDateSelection;
259     }
260 
261     /**
262      * Show Narrow Days. If this flag is set Days are show in narrow rather tyhan short form.
263      * @param pShowNarrowDays true/false
264      */
265     public void setShowNarrowDays(final boolean pShowNarrowDays) {
266         /* Set options */
267         doShowNarrowDays = pShowNarrowDays;
268 
269         /* Request rebuild of names */
270         rebuildNames();
271     }
272 
273     /**
274      * Rebuild names.
275      */
276     protected void rebuildNames() {
277         /* Request rebuild of names */
278         if (theOwner != null) {
279             theOwner.reBuildNames();
280         }
281     }
282 
283     /**
284      * Set the Button owner.
285      * @param pOwner the owning button
286      */
287     public void setOwner(final T pOwner) {
288         theOwner = pOwner;
289     }
290 
291     /**
292      * Set the earliest date. This is the earliest date that may be selected. If the configured
293      * latest date is earlier than this date, it will be set to this date to ensure a valid range.
294      * @param pEarliest the Earliest select-able date (or null if unlimited)
295      */
296     public final void setEarliestDate(final LocalDate pEarliest) {
297         /* Default the field to null */
298         theEarliest = null;
299 
300         /* If we have an earliest */
301         if (pEarliest != null) {
302             /* Store the date */
303             theEarliest = pEarliest;
304 
305             /* If we have a latest date, reset if necessary */
306             if ((theLatest != null)
307                 && (theLatest.compareTo(theEarliest) < 0)) {
308                 theLatest = theEarliest;
309             }
310         }
311     }
312 
313     /**
314      * Set the latest date. This is the latest date that may be selected. If the configured earliest
315      * date is later than this date, it will be set to this date to ensure a valid range.
316      * @param pLatest the Latest select-able date (or null if unlimited)
317      */
318     public final void setLatestDate(final LocalDate pLatest) {
319         /* Null the field */
320         theLatest = null;
321 
322         /* If we have an earliest */
323         if (pLatest != null) {
324             /* Store the date */
325             theLatest = pLatest;
326 
327             /* If we have an earliest date, reset if necessary */
328             if ((theEarliest != null)
329                 && (theLatest.compareTo(theEarliest) < 0)) {
330                 theEarliest = theLatest;
331             }
332         }
333     }
334 
335     /**
336      * Format a date according to configured rules.
337      * @param pDate the date to format
338      * @return the formatted date
339      */
340     public String formatDate(final LocalDate pDate) {
341         /* Handle null */
342         if (pDate == null) {
343             return null;
344         }
345 
346         /* If we have a formatter */
347         if (theFormatter != null) {
348             /* Use the formatter */
349             return theFormatter.formatLocalDate(pDate);
350         }
351 
352         /* Format the date */
353         return pDate.format(theDateFormat);
354     }
355 
356     /**
357      * Capitalise first letter of string.
358      * @param pValue the string to capitalise the first letter of
359      * @return the capitalised string
360      */
361     public String capitaliseString(final String pValue) {
362         String myValue = pValue;
363 
364         /* If we are showing pretty weekdays and the first UniCode item is lowerCase */
365         if (Character.isLowerCase(pValue.codePointAt(0))) {
366             /* Locate the length of the first character */
367             int iCharLen = pValue.offsetByCodePoints(0, 1);
368 
369             /* UpperCase the first iCharLen letters */
370             myValue = pValue.substring(0, iCharLen).toUpperCase(theLocale)
371                       + pValue.substring(iCharLen);
372         }
373 
374         /* Return the capitalised value */
375         return myValue;
376     }
377 
378     /**
379      * Obtain current date.
380      * @return the current date
381      */
382     public LocalDate currentDate() {
383         return LocalDate.now();
384     }
385 
386     /**
387      * Obtain current day of month or zero if not current month.
388      * @return the current month day
389      */
390     public int getCurrentDay() {
391         LocalDate myDate = currentDate();
392         return isSameMonth(myDate, theMonth)
393                                              ? myDate.getDayOfMonth()
394                                              : 0;
395     }
396 
397     /**
398      * Obtain Selected day of month or zero if not current month.
399      * @return the selected month day
400      */
401     public int getSelectedDay() {
402         LocalDate myDate = getSelectedDate();
403         return isSameMonth(myDate, theMonth)
404                                              ? myDate.getDayOfMonth()
405                                              : 0;
406     }
407 
408     /**
409      * Obtain Earliest day of month or zero if not current month.
410      * @return the earliest month day
411      */
412     public int getEarliestDay() {
413         return isSameMonth(theEarliest, theMonth)
414                                                   ? theEarliest.getDayOfMonth()
415                                                   : 0;
416     }
417 
418     /**
419      * Obtain Latest day of month or zero if not current month.
420      * @return the latest month day
421      */
422     public int getLatestDay() {
423         return isSameMonth(theLatest, theMonth)
424                                                 ? theLatest.getDayOfMonth()
425                                                 : 0;
426     }
427 
428     /**
429      * Adjust current month to previous month.
430      */
431     public void previousMonth() {
432         theMonth = theMonth.minusMonths(1);
433     }
434 
435     /**
436      * Adjust current month to next month.
437      */
438     public void nextMonth() {
439         theMonth = theMonth.plusMonths(1);
440     }
441 
442     /**
443      * Adjust current month to previous year.
444      */
445     public void previousYear() {
446         theMonth = theMonth.minusYears(1);
447         if ((theEarliest != null)
448             && (theMonth.compareTo(theEarliest) < 0)) {
449             theMonth = theEarliest.withDayOfMonth(1);
450         }
451     }
452 
453     /**
454      * Adjust current month to next year.
455      */
456     public void nextYear() {
457         theMonth = theMonth.plusYears(1);
458         if ((theLatest != null)
459             && (theMonth.compareTo(theLatest) > 0)) {
460             theMonth = theLatest.withDayOfMonth(1);
461         }
462     }
463 
464     /**
465      * Set the selected date.
466      * @param pDate the Selected date
467      */
468     public final void setSelectedDate(final LocalDate pDate) {
469         LocalDate myOld = getSelectedDate();
470 
471         /* Ignore if there is no change */
472         if (!isDateChanged(myOld, pDate)) {
473             return;
474         }
475 
476         /* Store the date */
477         storeSelectedDate(pDate);
478         if (theOwner != null) {
479             theOwner.refreshText();
480         }
481     }
482 
483     /**
484      * Store the selected date.
485      * @param pDate the Selected date
486      */
487     protected abstract void storeSelectedDate(final LocalDate pDate);
488 
489     /**
490      * Store the explicit date.
491      * @param pDate the explicit date
492      */
493     protected void storeExplicitDate(final LocalDate pDate) {
494         storeSelectedDate(pDate);
495     }
496 
497     /**
498      * Set selected day in current month (called from dialog).
499      * @param pDay the selected day
500      */
501     public void setSelectedDay(final int pDay) {
502         LocalDate myOld = getSelectedDate();
503         LocalDate myNew = null;
504 
505         /* If we are selecting a proper date */
506         if (pDay > 0) {
507             /* Build the new selected date */
508             myNew = theMonth.withDayOfMonth(pDay);
509         }
510 
511         /* Ignore if there is no change */
512         if (!isDateChanged(myOld, myNew)) {
513             return;
514         }
515 
516         /* Store the explicitly selected date */
517         storeExplicitDate(myNew);
518         if (theOwner != null) {
519             theOwner.refreshText();
520         }
521     }
522 
523     /**
524      * Initialise the current month.
525      */
526     public void initialiseCurrent() {
527         /* Access Selected Date */
528         LocalDate myDate = getInitialDate();
529 
530         /* Move to start date if we are earlier */
531         if ((theEarliest != null)
532             && (myDate.compareTo(theEarliest) < 0)) {
533             myDate = theEarliest;
534         }
535 
536         /* Move to end date if we are later */
537         if ((theLatest != null)
538             && (myDate.compareTo(theLatest) > 0)) {
539             myDate = theLatest;
540         }
541 
542         /* Set to 1st of month and record it */
543         theMonth = myDate.withDayOfMonth(1);
544     }
545 
546     /**
547      * Obtain initial date.
548      * @return the initial date
549      */
550     protected abstract LocalDate getInitialDate();
551 
552     /**
553      * Has the date changed?
554      * @param pFirst the first date
555      * @param pSecond the second date
556      * @return <code>true/false</code>
557      */
558     public static boolean isDateChanged(final LocalDate pFirst,
559                                         final LocalDate pSecond) {
560         if (pFirst == null) {
561             return pSecond != null;
562         } else {
563             return !pFirst.equals(pSecond);
564         }
565     }
566 
567     /**
568      * Are the dates in the same month.
569      * @param pFirst the first date (maybe null)
570      * @param pSecond the second date
571      * @return true/false
572      */
573     public static boolean isSameMonth(final LocalDate pFirst,
574                                       final LocalDate pSecond) {
575         if (!isSameYear(pFirst, pSecond)) {
576             return false;
577         } else {
578             return pFirst.getMonthValue() == pSecond.getMonthValue();
579         }
580     }
581 
582     /**
583      * Are the dates in the same year.
584      * @param pFirst the first date (maybe null)
585      * @param pSecond the second date
586      * @return true/false
587      */
588     public static boolean isSameYear(final LocalDate pFirst,
589                                      final LocalDate pSecond) {
590         if (pFirst == null) {
591             return false;
592         }
593         return pFirst.getYear() == pSecond.getYear();
594     }
595 }