View Javadoc

1   /*
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.datatransfer.DataFlavor;
22  import java.awt.datatransfer.Transferable;
23  import java.awt.datatransfer.UnsupportedFlavorException;
24  import java.awt.event.KeyEvent;
25  
26  import javax.swing.JPasswordField;
27  
28  /***
29   * SmartPasswordField is an extention of JPasswordField.  It does everything
30   * a JPassword does, as well as limit the number of characters.  The user
31   * of this class can specify that a password can only have a maximum of
32   * 10 characters for instance.
33   *
34   * @author rob@straylight.princeton.com
35   * @author $Author: cgruber $
36   * @version $Revision: 904 $
37   */
38  public class SmartPasswordField extends JPasswordField
39  {
40  
41  /********************************
42  * CONSTANTS
43  *******************************/
44     private static final int BACKSPACE = 8;
45     private static final int DELETE = 127;
46     private static final int SPACE = 32;
47     private static final int DASH = 45;
48     private static final int UNDERSCORE = 95;
49     private static final int PERIOD = 46;
50     private static final int PASTE = 22;  // Ctl-V
51  
52     private int passwordLength = Integer.MAX_VALUE;
53  
54  /********************************
55  * PUBLIC METHODS
56  *******************************/
57  
58  /***
59  * Default constructor.
60  */
61     public SmartPasswordField()
62     {
63         super();
64     }
65  
66  /***
67  * This constructor allows the user to set the maximum length of the password.
68  * @param aLength The maximum length of the password.
69  */
70     public SmartPasswordField( int aLength )
71     {
72         this();
73         setPasswordLength( aLength );
74     }
75  
76  /***
77  * Sets the maximum lenght of the password.  The value must be 0 or greater.
78  * If the length specified is less than 0, then no action occurs.
79  * @param aLength The maximum lenght of the password.
80  */
81      public void setPasswordLength( int aLength )
82      {
83          if ( aLength >= 0 )
84          {
85              passwordLength = aLength;
86          }
87      }
88  
89  /***
90  * Returns the current maximum length of the password.
91  * @return The current maximum length of the password.
92  */
93      public int getPasswordLength()
94      {
95          return passwordLength;
96      }
97  
98  /***
99  * This method processes a key event.  This event is generated by input from the
100 * keyboard when this text field has the focus.  This method is called for every
101 * key that is pressed and released on the keyboard.  This includes modifier
102 * keys like the shift and alt keys.  This class looks at the key and determines
103 * if the key is valid input given the restrictions of this class. <BR> <BR>
104 * @param e A key event generated by a keyboard action.
105 */
106    public void processKeyEvent(KeyEvent e)
107    {
108       String currentText = "";
109       String testString = "";
110       char newChar = e.getKeyChar();
111       int currentLength = 0;
112       int selectionStart = 0;
113       int selectionEnd = 0;
114       int endOfHead = 0;
115       int startOfTail = 0;
116       boolean backspace = false;
117       boolean delete = false;
118       boolean paste = false;
119       boolean insertionPoint = false;
120       boolean selectionAtStart = false;
121       boolean selectionAtEnd = false;
122 
123       backspace = (newChar == BACKSPACE);
124       delete = (newChar == DELETE);
125       paste = (newChar == PASTE);
126 
127       if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste)))  // A "key-typed" event
128       {
129          if (isValidCharacter(newChar))
130          {
131 
132             if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste))
133             {
134                // Analyze the current contents of the field
135                currentText = new String( getPassword() );
136                currentLength = currentText.length();
137 
138                selectionStart = getSelectionStart();
139                selectionEnd = getSelectionEnd();
140 
141                insertionPoint = (selectionStart == selectionEnd);
142                selectionAtStart = (selectionStart == 0);
143                selectionAtEnd = (selectionEnd >= currentLength);
144                if (selectionEnd > currentLength)
145                {
146                   setSelectionEnd(currentLength);
147                }
148 
149                // Generate new string
150                if (selectionStart > 0) // Create head of test string
151                {
152                   endOfHead = selectionStart;
153                   if (insertionPoint && backspace)
154                   {
155                      endOfHead -= 1;
156                   }
157                   testString += currentText.substring(0, endOfHead);
158                }
159 
160                if (!(backspace || delete || paste))  // Add the new character
161                {
162                   testString += newChar;
163                }
164 
165                if (paste)  // Add the string from the clipboard
166                {
167                   Transferable data = getToolkit().getSystemClipboard().getContents(this);
168                   if (data != null)
169                   {
170                      try
171                      {
172                         String clipString = (String)data.getTransferData(DataFlavor.stringFlavor);
173                         testString += clipString;
174                      }
175                      catch (java.io.IOException ioe)
176                      {
177                         // Do nothing
178                      }
179                      catch (UnsupportedFlavorException ufe)
180                      {
181                         // Do nothing
182                      }
183                   }
184                }
185 
186                if (selectionEnd < currentLength) // Add the tail of the string
187                {
188                   startOfTail = selectionEnd;
189                   if (insertionPoint && delete)
190                   {
191                      startOfTail += 1;
192                   }
193                   testString += currentText.substring(startOfTail);
194                }
195 
196             }
197 
198             if (testString.compareTo("") != 0)  // Null string is OK
199             {
200                if (!(isValidString(testString)))
201                {
202                   e.consume();
203                }
204             }
205          }
206          else
207          {
208             e.consume();
209          }
210       }
211       super.processKeyEvent(e);
212 
213       postProcessing();
214    }
215 
216 
217 /********************************
218 * PROTECTED METHODS
219 *******************************/
220 
221 /***
222 * Returns whether the inputted character is valid or not.  In this case all
223 * characters are valid input.
224 * @param aChar A character to perform the validity test with.
225 * @return True if the character is valid for this subclassed text field. <BR>
226 *         False is the character is not valid.
227 */
228    protected boolean isValidCharacter(char aChar)
229    {
230        return true;
231    }
232 
233 /***
234 * Returns whether a string is valid for this text field.  As the user types from
235 * the keyboard, this method is called to determine if the new string in the text
236 * field is valid based upon the restriction of this class.  The length of the
237 * new string is checked against the maximum password length.
238 * @param aString The string to perform the validity check with.
239 * @return True if the length of the string is less than or equal to the maximum length.
240 *         False if the character is not valud.
241 */
242    protected boolean isValidString(String aString)
243    {
244        if ( aString.length() > passwordLength )
245        {
246            return false;
247        }
248 
249        return true;
250    }
251 
252 /***
253 * This class does not need any post processing.
254 */
255    protected void postProcessing()
256    {
257        /* Do Nothing */
258    }
259 
260 
261 /********************************
262 * PRIVATE METHODS
263 *******************************/
264 
265    private boolean isPrintableCharacter(char inputChar)
266    {
267       if ((inputChar >= ' ') && (inputChar <= '~'))
268       {
269          return true;
270       }
271       return false;
272    }
273 
274 }