1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing.components;
20
21 import java.awt.event.FocusAdapter;
22 import java.awt.event.FocusEvent;
23 import java.awt.event.KeyEvent;
24 import java.text.ParsePosition;
25 import java.text.SimpleDateFormat;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.Vector;
29
30 import javax.swing.JOptionPane;
31 import javax.swing.JTextField;
32
33
34 /***
35 * DateTextField is a "smart" text field that restricts the user's input. The
36 * input is restructed to a string representing a date format.
37 *
38 * @author rob@straylight.princeton.com
39 * @author $Author: cgruber $
40 * @version $Revision: 904 $
41 */
42 public class DateTextField extends JTextField
43 {
44
45 /********************************
46 * CONSTANTS
47 *******************************/
48
49 /***
50 * Use the current date for this text field.
51 */
52 public static final int CURRENT_DATE = 0;
53
54 /***
55 * Use blanks for this text field.
56 */
57 public static final int BLANKS = 1;
58
59 /***
60 * Use underscores for this text field.
61 */
62 public static final int UNDERSCORES = 2;
63
64 /***
65 * Use just a 4-digit year for this text field.
66 */
67 public static final int YEAR = 3;
68
69 private static final int BACKSPACE = 8;
70 private static final int DELETE = 127;
71 private static final int PASTE = 22;
72 private static final int CUT = 24;
73
74
75 /********************************
76 * DATA MEMEBERS
77 *******************************/
78 private int defaultType = CURRENT_DATE;
79
80 private boolean warningMessageActive = false;
81
82
83 /********************************
84 * PUBLIC METHODS
85 *******************************/
86
87 /***
88 * Default Constructor.
89 */
90 public DateTextField()
91 {
92 this(1, 1, 1999, 0);
93
94 Calendar rightNow = Calendar.getInstance();
95
96 super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
97 rightNow.get(Calendar.DATE),
98 rightNow.get(Calendar.YEAR)));
99 }
100
101 /***
102 * Constructor.
103 * @param month Number of the month, January being 1.
104 * @param data The day of the month.
105 * @param year The year.
106 */
107 public DateTextField(int month, int date, int year)
108 {
109 this(month, date, year, 0);
110 }
111
112 /***
113 * Constructor.
114 * @param columns Width of the text field (in characters).
115 */
116 public DateTextField(int columns)
117 {
118 this(1, 1, 1998, columns);
119
120 Calendar rightNow = Calendar.getInstance();
121
122 super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
123 rightNow.get(Calendar.DATE),
124 rightNow.get(Calendar.YEAR)));
125 }
126
127 /***
128 * Constructor.
129 * @param month Number of the month, January being 1.
130 * @param data The day of the month.
131 * @param year The year.
132 * @param columns Width of the text field (in characters).
133 */
134 public DateTextField(int month, int date, int year, int columns)
135 {
136 super("", columns);
137
138 super.setText(createDateString(month, date, year));
139
140 this.addFocusListener(new FocusAdapter()
141 {
142 public void focusLost(FocusEvent e)
143 {
144 if (!(e.isTemporary()))
145 {
146 validateDateString(e);
147 }
148 }
149 });
150 }
151
152 /***
153 * Sets the date type to display when the user has not entered any date yet.
154 * Default is the current date.
155 * @see #CURRENT_DATE
156 * @see #BLANKS
157 * @see #UNDERSCORES
158 * @param newDefaultType The type of date to display when there is no date data.
159 */
160 public void setDefaultType(int newDefaultType)
161 {
162 if (newDefaultType == BLANKS)
163 {
164 defaultType = BLANKS;
165 super.setText(" / / ");
166 }
167 else if (newDefaultType == UNDERSCORES)
168 {
169 defaultType = UNDERSCORES;
170 super.setText("__/__/____");
171 }
172 else if (newDefaultType == YEAR)
173 {
174 defaultType = YEAR;
175 super.setText("0000");
176 }
177 else
178 {
179 defaultType = CURRENT_DATE;
180
181 Calendar rightNow = Calendar.getInstance();
182
183 super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
184 rightNow.get(Calendar.DATE),
185 rightNow.get(Calendar.YEAR)));
186 }
187 }
188
189 /***
190 * Returns the type of date to display when there is no user input.
191 * @see #CURRENT_DATE
192 * @see #BLANKS
193 * @see #UNDERSCORES
194 * @return The type of date to display when there is no date to display.
195 */
196 public int getDefaultType()
197 {
198 return defaultType;
199 }
200
201 /***
202 * Sets the text field to the string representation of the specified date.
203 * @param aDate The date to set the text field to.
204 */
205 public void setDate(Date aDate)
206 {
207 Calendar aCalendar = Calendar.getInstance();
208
209 aCalendar.setTime(aDate);
210
211 super.setText(createDateString(aCalendar.get(Calendar.MONTH) + 1,
212 aCalendar.get(Calendar.DATE),
213 aCalendar.get(Calendar.YEAR)));
214 }
215
216 /***
217 * Sets the text field directly from a Date object.
218 * @param aDate The date to set the text field to.
219 */
220 public void setText( Date aDate )
221 {
222 setDate( aDate );
223 }
224
225 /***
226 * Sets the text field to the date specified in the string. This is overridden
227 * from the parent class to insure a valid date is inputted. The format of the
228 * date expected is the type of date format this text field is currently set to.
229 * @param aString A string representing a date in this text field current format.
230 */
231 public void setText( String aString )
232 {
233 Date testDate = null;
234
235 if ( aString != null )
236 {
237 ParsePosition position = new ParsePosition( 0 );
238
239 if ( defaultType == YEAR )
240 {
241 SimpleDateFormat yearFormatter = new SimpleDateFormat( "yyyy" );
242 testDate = yearFormatter.parse( aString, position );
243 }
244 else
245 {
246 SimpleDateFormat fullDateFormatter = new SimpleDateFormat( "MM/dd/yyyy" );
247 testDate = fullDateFormatter.parse( aString, position );
248 }
249 }
250
251
252 if ( testDate == null )
253 {
254 Calendar aCalendar = Calendar.getInstance();
255
256 testDate = aCalendar.getTime();
257 }
258
259 setDate( testDate );
260 }
261
262 /***
263 * Returns the date as represented by the date string in the text field.
264 * @return The date in the text field.
265 */
266 public Date getDate() throws NumberFormatException
267 {
268 Calendar aCalendar = Calendar.getInstance();
269 int year = 1980;
270 int month = 0;
271 int date = 1;
272 int[] tempArray = {1,3,5,7,8,10,12};
273 Vector monthsWith31Days = new Vector(7);
274
275 for (int i = 0; i < tempArray.length; ++i)
276 {
277 monthsWith31Days.addElement(new Integer(tempArray[i]));
278 }
279
280 aCalendar.set(year, month, date, 12, 0, 0);
281
282 try
283 {
284 String dateString = getText();
285 NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + dateString));
286
287 if (defaultType == YEAR)
288 {
289 year = Integer.parseInt(dateString);
290
291 aCalendar.set(year, 0, 1, 12, 0, 0);
292
293 return aCalendar.getTime();
294 }
295
296 month = Integer.parseInt(dateString.substring(0, 2).trim());
297 date = Integer.parseInt(dateString.substring(3, 5).trim());
298 year = Integer.parseInt(dateString.substring(6).trim());
299
300 if ((month < 1) || (month > 12))
301 {
302 throw nfException;
303 }
304
305 if ((date < 1) || (date > 31))
306 {
307 throw nfException;
308 }
309
310 if ((date == 31) && (!(monthsWith31Days.contains(new Integer(month)))))
311 {
312 throw nfException;
313 }
314
315 if ((date == 30) && (month == 2))
316 {
317 throw nfException;
318 }
319
320 if ((date == 29) && (month == 2))
321 {
322 if ((year % 100) == 0)
323 {
324 if ((year % 400) != 0)
325 {
326 throw nfException;
327 }
328 }
329 else
330 {
331 if ((year % 4) != 0)
332 {
333 throw nfException;
334 }
335 }
336 }
337 }
338 catch (IndexOutOfBoundsException ioobe)
339 {
340 NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText()));
341 throw nfException;
342 }
343 catch (NumberFormatException nfe)
344 {
345 NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText()));
346 throw nfException;
347 }
348
349 aCalendar.set(year, (month - 1), date, 12, 0, 0);
350
351 return aCalendar.getTime();
352 }
353
354 public void processKeyEvent(KeyEvent e)
355 {
356 String currentString = "";
357 String testString = "";
358 char newChar = e.getKeyChar();
359 int currentLength = 0;
360 int currentCaretPosition = 0;
361 int selectionStart = 0;
362 int selectionEnd = 0;
363 int modifierPosition = 0;
364 int modifierDirection = 1;
365 char modifierCharacter;
366 boolean backspace = false;
367 boolean delete = false;
368 boolean paste = false;
369 boolean cut = false;
370 boolean keyPressed = false;
371
372 backspace = (newChar == BACKSPACE);
373 delete = (newChar == DELETE);
374 paste = (newChar == PASTE);
375 cut = (newChar == CUT);
376
377 keyPressed = (e.paramString().startsWith("KEY_PRESSED"));
378
379 if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste) || (cut)))
380 {
381 if (isValidCharacter(newChar))
382 {
383 if ((isPrintableCharacter(newChar)) || (backspace) || (delete))
384 {
385
386
387
388 if (((backspace) || (delete)) && (!(keyPressed)))
389 {
390
391 }
392 else
393 {
394
395 currentString = getText();
396 currentLength = currentString.length();
397
398 char[] tempText = new char[currentLength];
399
400 currentCaretPosition = getCaretPosition();
401
402 selectionStart = getSelectionStart();
403 selectionEnd = getSelectionEnd();
404
405
406
407 if (selectionStart != selectionEnd)
408 {
409 selectionEnd = selectionStart;
410 setSelectionEnd(selectionEnd);
411
412 currentCaretPosition = selectionStart;
413 setCaretPosition(currentCaretPosition);
414 }
415
416 if (currentCaretPosition <= currentLength)
417 {
418
419
420
421
422
423
424 if (((currentCaretPosition == 0) && (backspace)) ||
425 ((currentCaretPosition == currentLength) && (!(backspace))))
426 {
427
428 }
429 else
430 {
431 modifierPosition = currentCaretPosition;
432 if (backspace)
433 {
434 modifierDirection = -1;
435 modifierPosition += modifierDirection;
436 }
437
438
439
440
441 if (defaultType != YEAR)
442 {
443 modifierCharacter =
444 ((delete)||(backspace)) ?
445 ((defaultType == UNDERSCORES) ? '_' : ' ') :
446 newChar;
447 }
448 else
449 {
450
451
452
453 modifierCharacter = ((delete)||(backspace)) ? ('0') : newChar;
454 }
455
456 if (currentString.charAt(modifierPosition) == '/')
457 {
458 modifierPosition += modifierDirection;
459 }
460
461 for (int i = 0; i < currentLength; ++i)
462 {
463 if (i == modifierPosition)
464 {
465 tempText[i] = modifierCharacter;
466 }
467 else
468 {
469 tempText[i] = currentString.charAt(i);
470 }
471 }
472
473 testString = new String(tempText);
474 if (isValidString(testString))
475 {
476 super.setText(testString);
477 if (backspace)
478 {
479 setCaretPosition(modifierPosition);
480 }
481 else
482 {
483 setCaretPosition(modifierPosition + 1);
484 }
485 }
486 }
487 }
488 }
489
490 e.consume();
491 }
492 else if ((cut) || (paste))
493 {
494 e.consume();
495 }
496
497 }
498 else
499 {
500 e.consume();
501 }
502 }
503
504 super.processKeyEvent(e);
505 }
506
507 private boolean isValidCharacter(char aChar)
508 {
509 if (((aChar >= '!') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~')))
510 {
511 return false;
512 }
513 return true;
514 }
515
516 private boolean isPrintableCharacter(char inputChar)
517 {
518 if ((inputChar >= ' ') && (inputChar <= '~'))
519 {
520 return true;
521 }
522 return false;
523 }
524
525 private boolean isValidDate(int month, int date, int year)
526 {
527 if ((month < 1) || (month > 12))
528 {
529 return false;
530 }
531
532 if ((date < 1) || (date > 31))
533 {
534 return false;
535 }
536
537 if ((year < 0) || (year > 9999))
538 {
539 return false;
540 }
541
542 return true;
543 }
544
545 private boolean isValidString(String aString)
546 {
547 return true;
548 }
549
550 private String createDateString(int month, int date, int year)
551 {
552 String dateString = "";
553
554 if (isValidDate(month, date, year))
555 {
556 if (defaultType != YEAR)
557 {
558 if (month < 10)
559 {
560 dateString = "0";
561 }
562
563 dateString += String.valueOf(month);
564 dateString += "/";
565
566 if (date < 10)
567 {
568 dateString += "0";
569 }
570
571 dateString += String.valueOf(date);
572 dateString += "/";
573 }
574
575 if (year < 1000)
576 {
577 dateString += "0";
578 if (year < 100)
579 {
580 dateString += "0";
581 if (year < 10)
582 {
583 dateString += "0";
584 }
585 }
586 }
587
588 dateString += String.valueOf(year);
589 }
590 else
591 {
592 if (defaultType == YEAR)
593 {
594 dateString = "1999";
595 }
596 else
597 {
598 dateString = "01/01/1999";
599 }
600 }
601
602 return dateString;
603 }
604
605 private void validateDateString(FocusEvent e)
606 {
607 if (!(warningMessageActive))
608 {
609 try
610 {
611 getDate();
612 }
613 catch (NumberFormatException nfe)
614 {
615 System.out.println("Invalid Date String!!!");
616 warningMessageActive = true;
617 JOptionPane.showMessageDialog(this, "Invald Date: " + getText(), "Warning", JOptionPane.WARNING_MESSAGE);
618 warningMessageActive = false;
619 if (defaultType == YEAR)
620 {
621 super.setText("1999");
622 }
623 else
624 {
625 super.setText("01/01/1999");
626 }
627 }
628 }
629 }
630 }