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.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;
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)))
128 {
129 if (isValidCharacter(newChar))
130 {
131
132 if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste))
133 {
134
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
150 if (selectionStart > 0)
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))
161 {
162 testString += newChar;
163 }
164
165 if (paste)
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
178 }
179 catch (UnsupportedFlavorException ufe)
180 {
181
182 }
183 }
184 }
185
186 if (selectionEnd < currentLength)
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)
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
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 }