/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2000 Blacksmith, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see http://www.gnu.org
*/
package net.wotonomy.ui.swing.components;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.KeyEvent;
import javax.swing.JPasswordField;
/**
* SmartPasswordField is an extention of JPasswordField. It does everything a
* JPassword does, as well as limit the number of characters. The user of this
* class can specify that a password can only have a maximum of 10 characters
* for instance.
*
* @author rob@straylight.princeton.com
* @author $Author: cgruber $
* @version $Revision: 904 $
*/
public class SmartPasswordField extends JPasswordField {
/*******************************
* CONSTANTS
*******************************/
private static final int BACKSPACE = 8;
private static final int DELETE = 127;
private static final int SPACE = 32;
private static final int DASH = 45;
private static final int UNDERSCORE = 95;
private static final int PERIOD = 46;
private static final int PASTE = 22; // Ctl-V
private int passwordLength = Integer.MAX_VALUE;
/*******************************
* PUBLIC METHODS
*******************************/
/**
* Default constructor.
*/
public SmartPasswordField() {
super();
}
/**
* This constructor allows the user to set the maximum length of the password.
*
* @param aLength The maximum length of the password.
*/
public SmartPasswordField(int aLength) {
this();
setPasswordLength(aLength);
}
/**
* Sets the maximum lenght of the password. The value must be 0 or greater. If
* the length specified is less than 0, then no action occurs.
*
* @param aLength The maximum lenght of the password.
*/
public void setPasswordLength(int aLength) {
if (aLength >= 0) {
passwordLength = aLength;
}
}
/**
* Returns the current maximum length of the password.
*
* @return The current maximum length of the password.
*/
public int getPasswordLength() {
return passwordLength;
}
/**
* This method processes a key event. This event is generated by input from the
* keyboard when this text field has the focus. This method is called for every
* key that is pressed and released on the keyboard. This includes modifier keys
* like the shift and alt keys. This class looks at the key and determines if
* the key is valid input given the restrictions of this class.
*
*
* @param e A key event generated by a keyboard action.
*/
public void processKeyEvent(KeyEvent e) {
String currentText = "";
String testString = "";
char newChar = e.getKeyChar();
int currentLength = 0;
int selectionStart = 0;
int selectionEnd = 0;
int endOfHead = 0;
int startOfTail = 0;
boolean backspace = false;
boolean delete = false;
boolean paste = false;
boolean insertionPoint = false;
boolean selectionAtStart = false;
boolean selectionAtEnd = false;
backspace = (newChar == BACKSPACE);
delete = (newChar == DELETE);
paste = (newChar == PASTE);
if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event
{
if (isValidCharacter(newChar)) {
if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) {
// Analyze the current contents of the field
currentText = new String(getPassword());
currentLength = currentText.length();
selectionStart = getSelectionStart();
selectionEnd = getSelectionEnd();
insertionPoint = (selectionStart == selectionEnd);
selectionAtStart = (selectionStart == 0);
selectionAtEnd = (selectionEnd >= currentLength);
if (selectionEnd > currentLength) {
setSelectionEnd(currentLength);
}
// Generate new string
if (selectionStart > 0) // Create head of test string
{
endOfHead = selectionStart;
if (insertionPoint && backspace) {
endOfHead -= 1;
}
testString += currentText.substring(0, endOfHead);
}
if (!(backspace || delete || paste)) // Add the new character
{
testString += newChar;
}
if (paste) // Add the string from the clipboard
{
Transferable data = getToolkit().getSystemClipboard().getContents(this);
if (data != null) {
try {
String clipString = (String) data.getTransferData(DataFlavor.stringFlavor);
testString += clipString;
} catch (java.io.IOException ioe) {
// Do nothing
} catch (UnsupportedFlavorException ufe) {
// Do nothing
}
}
}
if (selectionEnd < currentLength) // Add the tail of the string
{
startOfTail = selectionEnd;
if (insertionPoint && delete) {
startOfTail += 1;
}
testString += currentText.substring(startOfTail);
}
}
if (testString.compareTo("") != 0) // Null string is OK
{
if (!(isValidString(testString))) {
e.consume();
}
}
} else {
e.consume();
}
}
super.processKeyEvent(e);
postProcessing();
}
/*******************************
* PROTECTED METHODS
*******************************/
/**
* Returns whether the inputted character is valid or not. In this case all
* characters are valid input.
*
* @param aChar A character to perform the validity test with.
* @return True if the character is valid for this subclassed text field.
* False is the character is not valid.
*/
protected boolean isValidCharacter(char aChar) {
return true;
}
/**
* Returns whether a string is valid for this text field. As the user types from
* the keyboard, this method is called to determine if the new string in the
* text field is valid based upon the restriction of this class. The length of
* the new string is checked against the maximum password length.
*
* @param aString The string to perform the validity check with.
* @return True if the length of the string is less than or equal to the maximum
* length. False if the character is not valud.
*/
protected boolean isValidString(String aString) {
if (aString.length() > passwordLength) {
return false;
}
return true;
}
/**
* This class does not need any post processing.
*/
protected void postProcessing() {
/* Do Nothing */
}
/*******************************
* PRIVATE METHODS
*******************************/
private boolean isPrintableCharacter(char inputChar) {
if ((inputChar >= ' ') && (inputChar <= '~')) {
return true;
}
return false;
}
}