Coverage Report - net.wotonomy.ui.swing.components.DateTextField
 
Classes in this File Line Coverage Branch Coverage Complexity
DateTextField
0% 
0% 
4.556
 
 1  0
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Blacksmith, Inc.
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 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  0
 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;  // Ctl-V
 72  
    private static final int CUT = 24;  // Ctl-X
 73  
 
 74  
 
 75  
 /*******************************
 76  
 * DATA MEMEBERS
 77  
 *******************************/
 78  0
    private int defaultType = CURRENT_DATE;
 79  
 
 80  0
    private boolean warningMessageActive = false;
 81  
 
 82  
 
 83  
 /*******************************
 84  
 * PUBLIC METHODS
 85  
 *******************************/
 86  
 
 87  
 /**
 88  
 * Default Constructor.
 89  
 */
 90  
    public DateTextField()
 91  
    {
 92  0
       this(1, 1, 1999, 0);
 93  
 
 94  0
       Calendar rightNow = Calendar.getInstance();
 95  
 
 96  0
       super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
 97  0
                                      rightNow.get(Calendar.DATE),
 98  0
                                      rightNow.get(Calendar.YEAR)));
 99  0
    }
 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  0
       this(month, date, year, 0);
 110  0
    }
 111  
 
 112  
 /**
 113  
 * Constructor.
 114  
 * @param columns Width of the text field (in characters).
 115  
 */
 116  
    public DateTextField(int columns)
 117  
    {
 118  0
       this(1, 1, 1998, columns);
 119  
 
 120  0
       Calendar rightNow = Calendar.getInstance();
 121  
 
 122  0
       super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
 123  0
                                      rightNow.get(Calendar.DATE),
 124  0
                                      rightNow.get(Calendar.YEAR)));
 125  0
    }
 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  0
       super("", columns);
 137  
 
 138  0
       super.setText(createDateString(month, date, year));
 139  
 
 140  0
       this.addFocusListener(new FocusAdapter()
 141  
       {
 142  0
          public void focusLost(FocusEvent e)
 143  
          {
 144  0
             if (!(e.isTemporary()))
 145  
             {
 146  0
                validateDateString(e);
 147  
             }
 148  0
          }
 149  
       });
 150  0
    }
 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  0
       if (newDefaultType == BLANKS)
 163  
       {
 164  0
          defaultType = BLANKS;
 165  0
          super.setText("  /  /    ");
 166  0
       }
 167  0
       else if (newDefaultType == UNDERSCORES)
 168  
       {
 169  0
          defaultType = UNDERSCORES;
 170  0
          super.setText("__/__/____");
 171  0
       }
 172  0
       else if (newDefaultType == YEAR)
 173  
       {
 174  0
           defaultType = YEAR;
 175  0
           super.setText("0000");
 176  0
       }
 177  
       else
 178  
       {
 179  0
          defaultType = CURRENT_DATE;
 180  
 
 181  0
          Calendar rightNow = Calendar.getInstance();
 182  
 
 183  0
          super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
 184  0
                                         rightNow.get(Calendar.DATE),
 185  0
                                         rightNow.get(Calendar.YEAR)));
 186  
       }
 187  0
    }
 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  0
       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  0
        Calendar aCalendar = Calendar.getInstance();
 208  
 
 209  0
        aCalendar.setTime(aDate);
 210  
 
 211  0
        super.setText(createDateString(aCalendar.get(Calendar.MONTH) + 1,
 212  0
                                       aCalendar.get(Calendar.DATE),
 213  0
                                       aCalendar.get(Calendar.YEAR)));
 214  0
    }
 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  0
        setDate( aDate );
 223  0
    }
 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  0
        Date testDate = null;
 234  
 
 235  0
        if ( aString != null )
 236  
        {
 237  0
            ParsePosition position = new ParsePosition( 0 );
 238  
 
 239  0
            if  ( defaultType == YEAR )
 240  
            {
 241  0
                SimpleDateFormat yearFormatter = new SimpleDateFormat( "yyyy" );
 242  0
                testDate = yearFormatter.parse( aString, position );
 243  0
            }
 244  
            else
 245  
            {
 246  0
                SimpleDateFormat fullDateFormatter = new SimpleDateFormat( "MM/dd/yyyy" );
 247  0
                testDate = fullDateFormatter.parse( aString, position );
 248  
            }
 249  
        }
 250  
 
 251  
        // The string is not a valid date, use default value for date then.
 252  0
        if ( testDate == null )
 253  
        {
 254  0
            Calendar aCalendar = Calendar.getInstance();
 255  
 
 256  0
            testDate = aCalendar.getTime();
 257  
        }
 258  
 
 259  0
        setDate( testDate );
 260  0
    }
 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  0
       Calendar aCalendar = Calendar.getInstance();
 269  0
       int year = 1980;
 270  0
       int month = 0;
 271  0
       int date = 1;
 272  0
       int[] tempArray = {1,3,5,7,8,10,12};
 273  0
       Vector monthsWith31Days = new Vector(7);
 274  
 
 275  0
       for (int i = 0; i < tempArray.length; ++i)
 276  
       {
 277  0
          monthsWith31Days.addElement(new Integer(tempArray[i]));
 278  
       }
 279  
 
 280  0
       aCalendar.set(year, month, date, 12, 0, 0);
 281  
 
 282  
       try
 283  
       {
 284  0
          String dateString = getText();
 285  0
          NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + dateString));
 286  
 
 287  0
          if (defaultType == YEAR)
 288  
          {
 289  0
              year = Integer.parseInt(dateString);
 290  
              
 291  0
              aCalendar.set(year, 0, 1, 12, 0, 0);
 292  
 
 293  0
              return aCalendar.getTime();
 294  
          }
 295  
 
 296  0
          month = Integer.parseInt(dateString.substring(0, 2).trim());
 297  0
          date = Integer.parseInt(dateString.substring(3, 5).trim());
 298  0
          year = Integer.parseInt(dateString.substring(6).trim());
 299  
 
 300  0
          if ((month < 1) || (month > 12))
 301  
          {
 302  0
             throw nfException;
 303  
          }
 304  
 
 305  0
          if ((date < 1) || (date > 31))
 306  
          {
 307  0
             throw nfException;
 308  
          }
 309  
 
 310  0
          if ((date == 31) && (!(monthsWith31Days.contains(new Integer(month)))))
 311  
          {
 312  0
             throw nfException;
 313  
          }
 314  
 
 315  0
          if ((date == 30) && (month == 2))
 316  
          {
 317  0
             throw nfException;
 318  
          }
 319  
 
 320  0
          if ((date == 29) && (month == 2))
 321  
          {
 322  0
             if ((year % 100) == 0)
 323  
             {
 324  0
                if ((year % 400) != 0)
 325  
                {
 326  0
                   throw nfException;
 327  
                }
 328  
             }
 329  
             else
 330  
             {
 331  0
                if ((year % 4) != 0)
 332  
                {
 333  0
                   throw nfException;
 334  
                }
 335  
             }
 336  
          }
 337  
       }
 338  0
       catch (IndexOutOfBoundsException ioobe)
 339  
       {
 340  0
          NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText()));
 341  0
          throw nfException;
 342  
       }
 343  0
       catch (NumberFormatException nfe)
 344  
       {
 345  0
          NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText()));
 346  0
          throw nfException;
 347  0
       }
 348  
 
 349  0
       aCalendar.set(year, (month - 1), date, 12, 0, 0);
 350  
 
 351  0
       return aCalendar.getTime();
 352  
    }
 353  
 
 354  
    public void processKeyEvent(KeyEvent e)
 355  
    {
 356  0
       String currentString = "";
 357  0
       String testString = "";
 358  0
       char newChar = e.getKeyChar();
 359  0
       int currentLength = 0;
 360  0
       int currentCaretPosition = 0;
 361  0
       int selectionStart = 0;
 362  0
       int selectionEnd = 0;
 363  0
       int modifierPosition = 0;
 364  0
       int modifierDirection = 1;
 365  
       char modifierCharacter;
 366  0
       boolean backspace = false;
 367  0
       boolean delete = false;
 368  0
       boolean paste = false;
 369  0
       boolean cut = false;
 370  0
       boolean keyPressed = false;
 371  
 
 372  0
       backspace = (newChar == BACKSPACE);
 373  0
       delete = (newChar == DELETE);
 374  0
       paste = (newChar == PASTE);
 375  0
       cut = (newChar == CUT);
 376  
 
 377  0
       keyPressed = (e.paramString().startsWith("KEY_PRESSED"));
 378  
 
 379  0
       if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste) || (cut)))  // A "key-typed" event
 380  
       {
 381  0
          if (isValidCharacter(newChar))
 382  
          {
 383  0
             if ((isPrintableCharacter(newChar)) || (backspace) || (delete))
 384  
             {
 385  
                // Both the key "pressed" and key "released" events get passed
 386  
                // in here for the delete and backspace key.  Only processes
 387  
                // these keys if the event is key "pressed".
 388  0
                if (((backspace) || (delete)) && (!(keyPressed)))
 389  
                {
 390  
                   // Don't do anything, pass through to consumption.
 391  0
                }
 392  
                else
 393  
                {
 394  
                   // Analyze the current contents of the field
 395  0
                   currentString = getText();
 396  0
                   currentLength = currentString.length();
 397  
 
 398  0
                   char[] tempText = new char[currentLength];
 399  
 
 400  0
                   currentCaretPosition = getCaretPosition();
 401  
 
 402  0
                   selectionStart = getSelectionStart();
 403  0
                   selectionEnd = getSelectionEnd();
 404  
 
 405  
                   // if a range is selected, then get rid of it and place the caret
 406  
                   // at the begginning of the range and continue processing.
 407  0
                   if (selectionStart != selectionEnd)
 408  
                   {
 409  0
                      selectionEnd = selectionStart;
 410  0
                      setSelectionEnd(selectionEnd);
 411  
 
 412  0
                      currentCaretPosition = selectionStart;
 413  0
                      setCaretPosition(currentCaretPosition);
 414  
                   }
 415  
 
 416  0
                   if (currentCaretPosition <= currentLength)
 417  
                   {
 418  
                      // a number of delete or backspace was pressed, delete and
 419  
                      // backspace deletes a number and places a "space" there
 420  
 
 421  
                      // if caret at start of string and the backspace pressed OR
 422  
                      // caret at end of string and delete or number pressed THEN
 423  
                      // don't do anything, otherwise process key stroke
 424  0
                      if (((currentCaretPosition == 0) && (backspace)) ||
 425  0
                          ((currentCaretPosition == currentLength) && (!(backspace))))
 426  
                      {
 427  
                         // Don't do any processing.
 428  0
                      }
 429  
                      else
 430  
                      {
 431  0
                         modifierPosition = currentCaretPosition;
 432  0
                         if (backspace)
 433  
                         {
 434  0
                            modifierDirection = -1;
 435  0
                            modifierPosition += modifierDirection;
 436  
                         }
 437  
 
 438  
                         // Overwrite the current position with the new character
 439  
                         // inputted or overwrite using a space or underscore if
 440  
                         // the backspace or delete key was pressed.
 441  0
                         if (defaultType != YEAR)
 442  
                         {
 443  0
                             modifierCharacter =
 444  0
                                    ((delete)||(backspace)) ?
 445  0
                                    ((defaultType == UNDERSCORES) ? '_' : ' ') :
 446  0
                                    newChar;
 447  0
                         }
 448  
                         else
 449  
                         {
 450  
                             // We are dealing with a 4-digit year.  Overwrite
 451  
                             // with new character or "0" if delete or backspace
 452  
                             // was pressed.
 453  0
                             modifierCharacter = ((delete)||(backspace)) ? ('0') : newChar;
 454  
                         }
 455  
 
 456  0
                         if (currentString.charAt(modifierPosition) == '/')
 457  
                         {
 458  0
                            modifierPosition += modifierDirection;
 459  
                         }
 460  
 
 461  0
                         for (int i = 0; i < currentLength; ++i)
 462  
                         {
 463  0
                            if (i == modifierPosition)
 464  
                            {
 465  0
                               tempText[i] = modifierCharacter;
 466  0
                            }
 467  
                            else
 468  
                            {
 469  0
                               tempText[i] = currentString.charAt(i);
 470  
                            }
 471  
                         }
 472  
 
 473  0
                         testString = new String(tempText);
 474  0
                         if (isValidString(testString))
 475  
                         {
 476  0
                            super.setText(testString);
 477  0
                            if (backspace)
 478  
                            {
 479  0
                               setCaretPosition(modifierPosition);
 480  0
                            }
 481  
                            else
 482  
                            {
 483  0
                               setCaretPosition(modifierPosition + 1);
 484  
                            }
 485  
                         }
 486  
                      }
 487  
                   }
 488  
                }
 489  
 
 490  0
                e.consume();
 491  0
             }
 492  0
             else if ((cut) || (paste))
 493  
             {
 494  0
                e.consume();
 495  0
             }
 496  
             // else its a non-printable character, let it pass through
 497  
          }
 498  
          else
 499  
          {
 500  0
             e.consume();
 501  
          }
 502  
       }
 503  
 
 504  0
       super.processKeyEvent(e);
 505  0
    }
 506  
 
 507  
    private boolean isValidCharacter(char aChar)
 508  
    {
 509  0
       if (((aChar >= '!') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~')))
 510  
       {
 511  0
          return false;
 512  
       }
 513  0
       return true;
 514  
    }
 515  
 
 516  
    private boolean isPrintableCharacter(char inputChar)
 517  
    {
 518  0
       if ((inputChar >= ' ') && (inputChar <= '~'))
 519  
       {
 520  0
          return true;
 521  
       }
 522  0
       return false;
 523  
    }
 524  
 
 525  
    private boolean isValidDate(int month, int date, int year)
 526  
    {
 527  0
       if ((month < 1) || (month > 12))
 528  
       {
 529  0
          return false;
 530  
       }
 531  
 
 532  0
       if ((date < 1) || (date > 31))
 533  
       {
 534  0
          return false;
 535  
       }
 536  
 
 537  0
       if ((year < 0) || (year > 9999))
 538  
       {
 539  0
          return false;
 540  
       }
 541  
 
 542  0
       return true;
 543  
    }
 544  
 
 545  
    private boolean isValidString(String aString)
 546  
    {
 547  0
       return true;
 548  
    }
 549  
 
 550  
    private String createDateString(int month, int date, int year)
 551  
    {
 552  0
       String dateString = "";
 553  
 
 554  0
       if (isValidDate(month, date, year))
 555  
       {
 556  0
          if (defaultType != YEAR)
 557  
          {
 558  0
              if (month < 10)
 559  
              {
 560  0
                 dateString = "0";
 561  
              }
 562  
 
 563  0
              dateString += String.valueOf(month);
 564  0
              dateString += "/";
 565  
 
 566  0
              if (date < 10)
 567  
              {
 568  0
                 dateString += "0";
 569  
              }
 570  
 
 571  0
              dateString += String.valueOf(date);
 572  0
              dateString += "/";
 573  
          }
 574  
 
 575  0
          if (year < 1000)
 576  
          {
 577  0
             dateString += "0";
 578  0
             if (year < 100)
 579  
             {
 580  0
                dateString += "0";
 581  0
                if (year < 10)
 582  
                {
 583  0
                   dateString += "0";
 584  
                }
 585  
             }
 586  
          }
 587  
 
 588  0
          dateString += String.valueOf(year);
 589  0
       }
 590  
       else
 591  
       {
 592  0
          if (defaultType == YEAR)
 593  
          {
 594  0
              dateString = "1999";
 595  0
          }
 596  
          else
 597  
          {
 598  0
              dateString = "01/01/1999";
 599  
          }
 600  
       }
 601  
 
 602  0
       return dateString;
 603  
    }
 604  
 
 605  0
    private void validateDateString(FocusEvent e)
 606  
    {
 607  0
       if (!(warningMessageActive))
 608  
       {
 609  
          try
 610  
          {
 611  0
             getDate();
 612  
          }
 613  0
          catch (NumberFormatException nfe)
 614  
          {
 615  0
             System.out.println("Invalid Date String!!!");
 616  0
             warningMessageActive = true;
 617  0
             JOptionPane.showMessageDialog(this, "Invald Date: " + getText(), "Warning", JOptionPane.WARNING_MESSAGE);
 618  0
             warningMessageActive = false;
 619  0
             if (defaultType == YEAR)
 620  
             {
 621  0
                 super.setText("1999");
 622  0
             }
 623  
             else
 624  
             {
 625  0
                 super.setText("01/01/1999");
 626  
             }
 627  0
          }
 628  
       }
 629  0
    }
 630  
 }