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 }