| /* MaskFormatter.java -- | |
| Copyright (C) 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package javax.swing.text; | |
| import gnu.java.lang.CPStringBuilder; | |
| import java.text.ParseException; | |
| import javax.swing.JFormattedTextField; | |
| /** | |
| * @author Anthony Balkissoon abalkiss at redhat dot com | |
| * | |
| */ | |
| public class MaskFormatter extends DefaultFormatter | |
| { | |
| // The declaration of the valid mask characters | |
| private static final char NUM_CHAR = '#'; | |
| private static final char ESCAPE_CHAR = '\''; | |
| private static final char UPPERCASE_CHAR = 'U'; | |
| private static final char LOWERCASE_CHAR = 'L'; | |
| private static final char ALPHANUM_CHAR = 'A'; | |
| private static final char LETTER_CHAR = '?'; | |
| private static final char ANYTHING_CHAR = '*'; | |
| private static final char HEX_CHAR = 'H'; | |
| /** The mask for this MaskFormatter **/ | |
| private String mask; | |
| /** | |
| * A String made up of the characters that are not valid for input for | |
| * this MaskFormatter. | |
| */ | |
| private String invalidChars; | |
| /** | |
| * A String made up of the characters that are valid for input for | |
| * this MaskFormatter. | |
| */ | |
| private String validChars; | |
| /** A String used in place of missing chracters if the value does not | |
| * completely fill in the spaces in the mask. | |
| */ | |
| private String placeHolder; | |
| /** A character used in place of missing characters if the value does | |
| * not completely fill in the spaces in the mask. | |
| */ | |
| private char placeHolderChar = ' '; | |
| /** | |
| * Whether or not stringToValue should return literal characters in the mask. | |
| */ | |
| private boolean valueContainsLiteralCharacters = true; | |
| /** A String used for easy access to valid HEX characters **/ | |
| private static String hexString = "0123456789abcdefABCDEF"; | |
| /** An int to hold the length of the mask, accounting for escaped characters **/ | |
| int maskLength = 0; | |
| public MaskFormatter () | |
| { | |
| // Override super's default behaviour, in MaskFormatter the default | |
| // is not to allow invalid values | |
| setAllowsInvalid(false); | |
| } | |
| /** | |
| * Creates a MaskFormatter with the specified mask. | |
| * @specnote doesn't actually throw a ParseException although it | |
| * is declared to do so | |
| * @param mask | |
| * @throws java.text.ParseException | |
| */ | |
| public MaskFormatter (String mask) throws java.text.ParseException | |
| { | |
| this(); | |
| setMask (mask); | |
| } | |
| /** | |
| * Returns the mask used in this MaskFormatter. | |
| * @return the mask used in this MaskFormatter. | |
| */ | |
| public String getMask() | |
| { | |
| return mask; | |
| } | |
| /** | |
| * Returns a String containing the characters that are not valid for input | |
| * for this MaskFormatter. | |
| * @return a String containing the invalid characters. | |
| */ | |
| public String getInvalidCharacters() | |
| { | |
| return invalidChars; | |
| } | |
| /** | |
| * Sets characters that are not valid for input. If | |
| * <code>invalidCharacters</code> is non-null then no characters contained | |
| * in it will be allowed to be input. | |
| * | |
| * @param invalidCharacters the String specifying invalid characters. | |
| */ | |
| public void setInvalidCharacters (String invalidCharacters) | |
| { | |
| this.invalidChars = invalidCharacters; | |
| } | |
| /** | |
| * Returns a String containing the characters that are valid for input | |
| * for this MaskFormatter. | |
| * @return a String containing the valid characters. | |
| */ | |
| public String getValidCharacters() | |
| { | |
| return validChars; | |
| } | |
| /** | |
| * Sets characters that are valid for input. If | |
| * <code>validCharacters</code> is non-null then no characters that are | |
| * not contained in it will be allowed to be input. | |
| * | |
| * @param validCharacters the String specifying valid characters. | |
| */ | |
| public void setValidCharacters (String validCharacters) | |
| { | |
| this.validChars = validCharacters; | |
| } | |
| /** | |
| * Returns the place holder String that is used in place of missing | |
| * characters when the value doesn't completely fill in the spaces | |
| * in the mask. | |
| * @return the place holder String. | |
| */ | |
| public String getPlaceholder() | |
| { | |
| return placeHolder; | |
| } | |
| /** | |
| * Sets the string to use if the value does not completely fill in the mask. | |
| * If this is null, the place holder character will be used instead. | |
| * @param placeholder the String to use if the value doesn't completely | |
| * fill in the mask. | |
| */ | |
| public void setPlaceholder (String placeholder) | |
| { | |
| this.placeHolder = placeholder; | |
| } | |
| /** | |
| * Returns the character used in place of missing characters when the | |
| * value doesn't completely fill the mask. | |
| * @return the place holder character | |
| */ | |
| public char getPlaceholderCharacter() | |
| { | |
| return placeHolderChar; | |
| } | |
| /** | |
| * Sets the char to use if the value does not completely fill in the mask. | |
| * This is only used if the place holder String has not been set or does | |
| * not completely fill in the mask. | |
| * @param placeholder the char to use if the value doesn't completely | |
| * fill in the mask. | |
| */ | |
| public void setPlaceholderCharacter (char placeholder) | |
| { | |
| this.placeHolderChar = placeholder; | |
| } | |
| /** | |
| * Returns true if stringToValue should return the literal | |
| * characters in the mask. | |
| * @return true if stringToValue should return the literal | |
| * characters in the mask | |
| */ | |
| public boolean getValueContainsLiteralCharacters() | |
| { | |
| return valueContainsLiteralCharacters; | |
| } | |
| /** | |
| * Determines whether stringToValue will return literal characters or not. | |
| * @param containsLiteralChars if true, stringToValue will return the | |
| * literal characters in the mask, otherwise it will not. | |
| */ | |
| public void setValueContainsLiteralCharacters (boolean containsLiteralChars) | |
| { | |
| this.valueContainsLiteralCharacters = containsLiteralChars; | |
| } | |
| /** | |
| * Sets the mask for this MaskFormatter. | |
| * @specnote doesn't actually throw a ParseException even though it is | |
| * declared to do so | |
| * @param mask the new mask for this MaskFormatter | |
| * @throws ParseException if <code>mask</code> is not valid. | |
| */ | |
| public void setMask (String mask) throws ParseException | |
| { | |
| this.mask = mask; | |
| // Update the cached maskLength. | |
| int end = mask.length() - 1; | |
| maskLength = 0; | |
| for (int i = 0; i <= end; i++) | |
| { | |
| // Handle escape characters properly - they don't add to the maskLength | |
| // but 2 escape characters in a row is really one escape character and | |
| // one literal single quote, so that does add 1 to the maskLength. | |
| if (mask.charAt(i) == '\'') | |
| { | |
| // Escape characters at the end of the mask don't do anything. | |
| if (i != end) | |
| maskLength++; | |
| i++; | |
| } | |
| else | |
| maskLength++; | |
| } | |
| } | |
| /** | |
| * Installs this MaskFormatter on the JFormattedTextField. | |
| * Invokes valueToString to convert the current value from the | |
| * JFormattedTextField to a String, then installs the Actions from | |
| * getActions, the DocumentFilter from getDocumentFilter, and the | |
| * NavigationFilter from getNavigationFilter. | |
| * | |
| * If valueToString throws a ParseException, this method sets the text | |
| * to an empty String and marks the JFormattedTextField as invalid. | |
| */ | |
| public void install (JFormattedTextField ftf) | |
| { | |
| super.install(ftf); | |
| if (ftf != null) | |
| { | |
| try | |
| { | |
| valueToString(ftf.getValue()); | |
| } | |
| catch (ParseException pe) | |
| { | |
| // Set the text to an empty String and mark the JFormattedTextField | |
| // as invalid. | |
| ftf.setText(""); | |
| setEditValid(false); | |
| } | |
| } | |
| } | |
| /** | |
| * Parses the text using the mask, valid characters, and invalid characters | |
| * to determine the appropriate Object to return. This strips the literal | |
| * characters if necessary and invokes super.stringToValue. If the paramter | |
| * is invalid for the current mask and valid/invalid character sets this | |
| * method will throw a ParseException. | |
| * | |
| * @param value the String to parse | |
| * @throws ParseException if value doesn't match the mask and valid/invalid | |
| * character sets | |
| */ | |
| public Object stringToValue (String value) throws ParseException | |
| { | |
| return super.stringToValue(convertStringToValue(value)); | |
| } | |
| private String convertStringToValue(String value) | |
| throws ParseException | |
| { | |
| CPStringBuilder result = new CPStringBuilder(); | |
| char valueChar; | |
| boolean isPlaceHolder; | |
| int length = mask.length(); | |
| for (int i = 0, j = 0; j < length; j++) | |
| { | |
| char maskChar = mask.charAt(j); | |
| if (i < value.length()) | |
| { | |
| isPlaceHolder = false; | |
| valueChar = value.charAt(i); | |
| if (maskChar != ESCAPE_CHAR && maskChar != valueChar) | |
| { | |
| if (invalidChars != null | |
| && invalidChars.indexOf(valueChar) != -1) | |
| throw new ParseException("Invalid character: " + valueChar, i); | |
| if (validChars != null | |
| && validChars.indexOf(valueChar) == -1) | |
| throw new ParseException("Invalid character: " + valueChar, i); | |
| } | |
| } | |
| else if (placeHolder != null && i < placeHolder.length()) | |
| { | |
| isPlaceHolder = true; | |
| valueChar = placeHolder.charAt(i); | |
| } | |
| else | |
| { | |
| isPlaceHolder = true; | |
| valueChar = placeHolderChar; | |
| } | |
| // This switch block on the mask character checks that the character | |
| // within <code>value</code> at that point is valid according to the | |
| // mask and also converts to upper/lowercase as needed. | |
| switch (maskChar) | |
| { | |
| case NUM_CHAR: | |
| if (! Character.isDigit(valueChar)) | |
| throw new ParseException("Number expected: " + valueChar, i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case UPPERCASE_CHAR: | |
| if (! Character.isLetter(valueChar)) | |
| throw new ParseException("Letter expected", i); | |
| result.append(Character.toUpperCase(valueChar)); | |
| i++; | |
| break; | |
| case LOWERCASE_CHAR: | |
| if (! Character.isLetter(valueChar)) | |
| throw new ParseException("Letter expected", i); | |
| result.append(Character.toLowerCase(valueChar)); | |
| i++; | |
| break; | |
| case ALPHANUM_CHAR: | |
| if (! Character.isLetterOrDigit(valueChar)) | |
| throw new ParseException("Letter or number expected", i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case LETTER_CHAR: | |
| if (! Character.isLetter(valueChar)) | |
| throw new ParseException("Letter expected", i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case HEX_CHAR: | |
| if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder) | |
| throw new ParseException("Hexadecimal character expected", i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case ANYTHING_CHAR: | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case ESCAPE_CHAR: | |
| // Escape character, check the next character to make sure that | |
| // the literals match | |
| j++; | |
| if (j < length) | |
| { | |
| maskChar = mask.charAt(j); | |
| if (! isPlaceHolder && getValueContainsLiteralCharacters() | |
| && valueChar != maskChar) | |
| throw new ParseException ("Invalid character: "+ valueChar, i); | |
| if (getValueContainsLiteralCharacters()) | |
| { | |
| result.append(maskChar); | |
| } | |
| i++; | |
| } | |
| else if (! isPlaceHolder) | |
| throw new ParseException("Bad match at trailing escape: ", i); | |
| break; | |
| default: | |
| if (! isPlaceHolder && getValueContainsLiteralCharacters() | |
| && valueChar != maskChar) | |
| throw new ParseException ("Invalid character: "+ valueChar, i); | |
| if (getValueContainsLiteralCharacters()) | |
| { | |
| result.append(maskChar); | |
| } | |
| i++; | |
| } | |
| } | |
| return result.toString(); | |
| } | |
| /** | |
| * Returns a String representation of the Object value based on the mask. | |
| * | |
| * @param value the value to convert | |
| * @throws ParseException if value is invalid for this mask and valid/invalid | |
| * character sets | |
| */ | |
| public String valueToString(Object value) throws ParseException | |
| { | |
| String string = value != null ? value.toString() : ""; | |
| return convertValueToString(string); | |
| } | |
| /** | |
| * This method takes in a String and runs it through the mask to make | |
| * sure that it is valid. If <code>convert</code> is true, it also | |
| * converts letters to upper/lowercase as required by the mask. | |
| * @param value the String to convert | |
| * @return the converted String | |
| * @throws ParseException if the given String isn't valid for the mask | |
| */ | |
| private String convertValueToString(String value) | |
| throws ParseException | |
| { | |
| CPStringBuilder result = new CPStringBuilder(); | |
| char valueChar; | |
| boolean isPlaceHolder; | |
| int length = mask.length(); | |
| for (int i = 0, j = 0; j < length; j++) | |
| { | |
| char maskChar = mask.charAt(j); | |
| if (i < value.length()) | |
| { | |
| isPlaceHolder = false; | |
| valueChar = value.charAt(i); | |
| if (maskChar != ESCAPE_CHAR && valueChar != maskChar) | |
| { | |
| if (invalidChars != null | |
| && invalidChars.indexOf(valueChar) != -1) | |
| throw new ParseException("Invalid character: " + valueChar, | |
| i); | |
| if (validChars != null && validChars.indexOf(valueChar) == -1) | |
| throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar, | |
| i); | |
| } | |
| } | |
| else if (placeHolder != null && i < placeHolder.length()) | |
| { | |
| isPlaceHolder = true; | |
| valueChar = placeHolder.charAt(i); | |
| } | |
| else | |
| { | |
| isPlaceHolder = true; | |
| valueChar = placeHolderChar; | |
| } | |
| // This switch block on the mask character checks that the character | |
| // within <code>value</code> at that point is valid according to the | |
| // mask and also converts to upper/lowercase as needed. | |
| switch (maskChar) | |
| { | |
| case NUM_CHAR: | |
| if ( ! isPlaceHolder && ! Character.isDigit(valueChar)) | |
| throw new ParseException("Number expected: " + valueChar, i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case UPPERCASE_CHAR: | |
| if (! Character.isLetter(valueChar)) | |
| throw new ParseException("Letter expected", i); | |
| result.append(Character.toUpperCase(valueChar)); | |
| i++; | |
| break; | |
| case LOWERCASE_CHAR: | |
| if (! Character.isLetter(valueChar)) | |
| throw new ParseException("Letter expected", i); | |
| result.append(Character.toLowerCase(valueChar)); | |
| i++; | |
| break; | |
| case ALPHANUM_CHAR: | |
| if (! Character.isLetterOrDigit(valueChar)) | |
| throw new ParseException("Letter or number expected", i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case LETTER_CHAR: | |
| if (! Character.isLetter(valueChar)) | |
| throw new ParseException("Letter expected", i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case HEX_CHAR: | |
| if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder) | |
| throw new ParseException("Hexadecimal character expected", i); | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case ANYTHING_CHAR: | |
| result.append(valueChar); | |
| i++; | |
| break; | |
| case ESCAPE_CHAR: | |
| // Escape character, check the next character to make sure that | |
| // the literals match | |
| j++; | |
| if (j < length) | |
| { | |
| maskChar = mask.charAt(j); | |
| if (! isPlaceHolder && getValueContainsLiteralCharacters() | |
| && valueChar != maskChar) | |
| throw new ParseException ("Invalid character: "+ valueChar, i); | |
| if (getValueContainsLiteralCharacters()) | |
| i++; | |
| result.append(maskChar); | |
| } | |
| break; | |
| default: | |
| if (! isPlaceHolder && getValueContainsLiteralCharacters() | |
| && valueChar != maskChar) | |
| throw new ParseException ("Invalid character: "+ valueChar, i); | |
| if (getValueContainsLiteralCharacters()) | |
| i++; | |
| result.append(maskChar); | |
| } | |
| } | |
| return result.toString(); | |
| } | |
| } |