summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/javax/swing/text/MaskFormatter.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/text/MaskFormatter.java')
-rw-r--r--libjava/classpath/javax/swing/text/MaskFormatter.java583
1 files changed, 583 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/text/MaskFormatter.java b/libjava/classpath/javax/swing/text/MaskFormatter.java
new file mode 100644
index 00000000000..d12b9ea29d7
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/MaskFormatter.java
@@ -0,0 +1,583 @@
+/* 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 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
+ {
+ // Override super's default behaviour, in MaskFormatter the default
+ // is not to allow invalid values
+ setAllowsInvalid(false);
+ 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
+ {
+ int vLength = value.length();
+
+ // For value to be a valid it must be the same length as the mask
+ // note this doesn't take into account symbols that occupy more than
+ // one character, this is something we may possibly need to fix.
+ if (maskLength != vLength)
+ throw new ParseException ("stringToValue passed invalid value", vLength);
+
+ // Check if the value is valid according to the mask and valid/invalid
+ // sets.
+ try
+ {
+ convertValue(value, false);
+ }
+ catch (ParseException pe)
+ {
+ throw new ParseException("stringToValue passed invalid value",
+ pe.getErrorOffset());
+ }
+
+ if (!getValueContainsLiteralCharacters())
+ value = stripLiterals(value);
+ return super.stringToValue(value);
+ }
+
+ /**
+ * Strips the literal characters from the given String.
+ * @param value the String to strip
+ * @return the stripped String
+ */
+ String stripLiterals(String value)
+ {
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < value.length(); i++)
+ {
+ // Only append the characters that don't correspond to literal
+ // characters in the mask.
+ switch (mask.charAt(i))
+ {
+ case NUM_CHAR:
+ case UPPERCASE_CHAR:
+ case LOWERCASE_CHAR:
+ case ALPHANUM_CHAR:
+ case LETTER_CHAR:
+ case HEX_CHAR:
+ case ANYTHING_CHAR:
+ result.append(value.charAt(i));
+ break;
+ default:
+ }
+ }
+ 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 result = super.valueToString(value);
+ int rLength = result.length();
+
+ // If value is longer than the mask, truncate it. Note we may need to
+ // account for symbols that are more than one character long.
+ if (rLength > maskLength)
+ result = result.substring(0, maskLength);
+
+ // Verify the validity and convert to upper/lowercase as needed.
+ result = convertValue(result, true);
+ if (rLength < maskLength)
+ return pad(result, rLength);
+ return result;
+ }
+
+ /**
+ * 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
+ * @param convert true if we should convert letters to upper/lowercase
+ * @return the converted String
+ * @throws ParseException if the given String isn't valid for the mask
+ */
+ String convertValue(String value, boolean convert) throws ParseException
+ {
+ StringBuffer result = new StringBuffer(value);
+ char markChar;
+ char resultChar;
+ boolean literal;
+
+ // this boolean is specifically to avoid calling the isCharValid method
+ // when neither invalidChars or validChars has been set
+ boolean checkCharSets = (invalidChars != null || validChars != null);
+
+ for (int i = 0, j = 0; i < value.length(); i++, j++)
+ {
+ literal = false;
+ resultChar = result.charAt(i);
+ // 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 (mask.charAt(j))
+ {
+ case NUM_CHAR:
+ if (!Character.isDigit(resultChar))
+ throw new ParseException("Number expected", i);
+ break;
+ case UPPERCASE_CHAR:
+ if (!Character.isLetter(resultChar))
+ throw new ParseException("Letter expected", i);
+ if (convert)
+ result.setCharAt(i, Character.toUpperCase(resultChar));
+ break;
+ case LOWERCASE_CHAR:
+ if (!Character.isLetter(resultChar))
+ throw new ParseException("Letter expected", i);
+ if (convert)
+ result.setCharAt(i, Character.toLowerCase(resultChar));
+ break;
+ case ALPHANUM_CHAR:
+ if (!Character.isLetterOrDigit(resultChar))
+ throw new ParseException("Letter or number expected", i);
+ break;
+ case LETTER_CHAR:
+ if (!Character.isLetter(resultChar))
+ throw new ParseException("Letter expected", i);
+ break;
+ case HEX_CHAR:
+ if (hexString.indexOf(resultChar) == -1)
+ throw new ParseException("Hexadecimal character expected", i);
+ break;
+ case ANYTHING_CHAR:
+ break;
+ case ESCAPE_CHAR:
+ // Escape character, check the next character to make sure that
+ // the literals match
+ j++;
+ literal = true;
+ if (resultChar != mask.charAt(j))
+ throw new ParseException ("Invalid character: "+resultChar, i);
+ break;
+ default:
+ literal = true;
+ if (!getValueContainsLiteralCharacters() && convert)
+ throw new ParseException ("Invalid character: "+resultChar, i);
+ else if (resultChar != mask.charAt(j))
+ throw new ParseException ("Invalid character: "+resultChar, i);
+ }
+ // If necessary, check if the character is valid.
+ if (!literal && checkCharSets && !isCharValid(resultChar))
+ throw new ParseException("invalid character: "+resultChar, i);
+
+ }
+ return result.toString();
+ }
+
+ /**
+ * Convenience method used by many other methods to check if a character is
+ * valid according to the mask, the validChars, and the invalidChars. To
+ * be valid a character must:
+ * 1. be allowed by the mask
+ * 2. be present in any non-null validChars String
+ * 3. not be present in any non-null invalidChars String
+ * @param testChar the character to test
+ * @return true if the character is valid
+ */
+ boolean isCharValid(char testChar)
+ {
+ char lower = Character.toLowerCase(testChar);
+ char upper = Character.toUpperCase(testChar);
+ // If validChars isn't null, the character must appear in it.
+ if (validChars != null)
+ if (validChars.indexOf(lower) == -1 && validChars.indexOf(upper) == -1)
+ return false;
+ // If invalidChars isn't null, the character must not appear in it.
+ if (invalidChars != null)
+ if (invalidChars.indexOf(lower) != -1
+ || invalidChars.indexOf(upper) != -1)
+ return false;
+ return true;
+ }
+
+ /**
+ * Pads the value with literals, the placeholder String and/or placeholder
+ * character as appropriate.
+ * @param value the value to pad
+ * @param currLength the current length of the value
+ * @return the padded String
+ */
+ String pad (String value, int currLength)
+ {
+ StringBuffer result = new StringBuffer(value);
+ int index = currLength;
+ while (result.length() < maskLength)
+ {
+ // The character used to pad may be a literal, a character from the
+ // place holder string, or the place holder character. getPadCharAt
+ // will find the proper one for us.
+ result.append (getPadCharAt(index));
+ index++;
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns the character with which to pad the value at the given index
+ * position. If the mask has a literal at this position, this is returned
+ * otherwise if the place holder string is initialized and is longer than
+ * <code>i</code> characters then the character at position <code>i</code>
+ * from this String is returned. Else, the place holder character is
+ * returned.
+ * @param i the index at which we want to pad the value
+ * @return the character with which we should pad the value
+ */
+ char getPadCharAt(int i)
+ {
+ boolean escaped = false;
+ int target = i;
+ char maskChar;
+ int holderLength = placeHolder == null ? -1 : placeHolder.length();
+ // We must iterate through the mask from the beginning, because the given
+ // index doesn't account for escaped characters. For example, with the
+ // mask "1A'A''A1" index 2 refers to the literalized A, not to the
+ // single quotation.
+ for (int n = 0; n < mask.length(); n++)
+ {
+ maskChar = mask.charAt(n);
+ if (maskChar == ESCAPE_CHAR && !escaped)
+ {
+ target++;
+ escaped = true;
+ }
+ else if (escaped == true)
+ {
+ // Check if target == n which means we've come to the character
+ // we want to return and since it is a literal (because escaped
+ // is true), we return it.
+ if (target == n)
+ return maskChar;
+ escaped = false;
+ }
+ if (target == n)
+ {
+ // We've come to the character we want to return. It wasn't
+ // escaped so if it isn't a literal we should return either
+ // the character from place holder string or the place holder
+ // character, depending on whether or not the place holder
+ // string is long enough.
+ switch (maskChar)
+ {
+ case NUM_CHAR:
+ case UPPERCASE_CHAR:
+ case LOWERCASE_CHAR:
+ case ALPHANUM_CHAR:
+ case LETTER_CHAR:
+ case HEX_CHAR:
+ case ANYTHING_CHAR:
+ if (holderLength > i)
+ return placeHolder.charAt(i);
+ else
+ return placeHolderChar;
+ default:
+ return maskChar;
+ }
+ }
+ }
+ // This shouldn't happen
+ throw new AssertionError("MaskFormatter.getMaskCharAt failed");
+ }
+}
OpenPOWER on IntegriCloud