< prev index next >

src/java.desktop/share/classes/javax/swing/text/MaskFormatter.java

Print this page




  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.text;
  27 
  28 import java.io.*;
  29 import java.text.*;
  30 import java.util.*;
  31 import javax.swing.*;
  32 
  33 /**
  34  * <code>MaskFormatter</code> is used to format and edit strings. The behavior
  35  * of a <code>MaskFormatter</code> is controlled by way of a String mask
  36  * that specifies the valid characters that can be contained at a particular
  37  * location in the <code>Document</code> model. The following characters can
  38  * be specified:
  39  *
  40  * <table border=1 summary="Valid characters and their descriptions">
  41  * <tr>
  42  *    <th>Character&nbsp;</th>
  43  *    <th><p style="text-align:left">Description</p></th>
  44  * </tr>
  45  * <tr>
  46  *    <td>#</td>
  47  *    <td>Any valid number, uses <code>Character.isDigit</code>.</td>
  48  * </tr>
  49  * <tr>
  50  *    <td>'</td>
  51  *    <td>Escape character, used to escape any of the
  52  *       special formatting characters.</td>
  53  * </tr>
  54  * <tr>
  55  *    <td>U</td><td>Any character (<code>Character.isLetter</code>). All
  56  *        lowercase letters are mapped to upper case.</td>
  57  * </tr>
  58  * <tr><td>L</td><td>Any character (<code>Character.isLetter</code>). All
  59  *        upper case letters are mapped to lower case.</td>
  60  * </tr>
  61  * <tr><td>A</td><td>Any character or number (<code>Character.isLetter</code>
  62  *       or <code>Character.isDigit</code>)</td>
  63  * </tr>
  64  * <tr><td>?</td><td>Any character
  65  *        (<code>Character.isLetter</code>).</td>
  66  * </tr>
  67  * <tr><td>*</td><td>Anything.</td></tr>
  68  * <tr><td>H</td><td>Any hex character (0-9, a-f or A-F).</td></tr>
  69  * </table>
  70  *
  71  * <p>
  72  * Typically characters correspond to one char, but in certain languages this
  73  * is not the case. The mask is on a per character basis, and will thus
  74  * adjust to fit as many chars as are needed.
  75  * <p>
  76  * You can further restrict the characters that can be input by the
  77  * <code>setInvalidCharacters</code> and <code>setValidCharacters</code>
  78  * methods. <code>setInvalidCharacters</code> allows you to specify
  79  * which characters are not legal. <code>setValidCharacters</code> allows
  80  * you to specify which characters are valid. For example, the following
  81  * code block is equivalent to a mask of '0xHHH' with no invalid/valid
  82  * characters:
  83  * <pre>
  84  * MaskFormatter formatter = new MaskFormatter("0x***");
  85  * formatter.setValidCharacters("0123456789abcdefABCDEF");
  86  * </pre>
  87  * <p>
  88  * When initially formatting a value if the length of the string is
  89  * less than the length of the mask, two things can happen. Either
  90  * the placeholder string will be used, or the placeholder character will
  91  * be used. Precedence is given to the placeholder string. For example:
  92  * <pre>
  93  *   MaskFormatter formatter = new MaskFormatter("###-####");
  94  *   formatter.setPlaceholderCharacter('_');
  95  *   formatter.getDisplayValue(tf, "123");
  96  * </pre>
  97  * <p>
  98  * Would result in the string '123-____'. If
  99  * <code>setPlaceholder("555-1212")</code> was invoked '123-1212' would
 100  * result. The placeholder String is only used on the initial format,
 101  * on subsequent formats only the placeholder character will be used.
 102  * <p>
 103  * If a <code>MaskFormatter</code> is configured to only allow valid characters
 104  * (<code>setAllowsInvalid(false)</code>) literal characters will be skipped as
 105  * necessary when editing. Consider a <code>MaskFormatter</code> with
 106  * the mask "###-####" and current value "555-1212". Using the right
 107  * arrow key to navigate through the field will result in (| indicates the
 108  * position of the caret):
 109  * <pre>
 110  *   |555-1212
 111  *   5|55-1212
 112  *   55|5-1212
 113  *   555-|1212
 114  *   555-1|212
 115  * </pre>
 116  * The '-' is a literal (non-editable) character, and is skipped.
 117  * <p>
 118  * Similar behavior will result when editing. Consider inserting the string
 119  * '123-45' and '12345' into the <code>MaskFormatter</code> in the
 120  * previous example. Both inserts will result in the same String,
 121  * '123-45__'. When <code>MaskFormatter</code>
 122  * is processing the insert at character position 3 (the '-'), two things can
 123  * happen:
 124  * <ol>
 125  *   <li>If the inserted character is '-', it is accepted.
 126  *   <li>If the inserted character matches the mask for the next non-literal
 127  *       character, it is accepted at the new location.
 128  *   <li>Anything else results in an invalid edit
 129  * </ol>
 130  * <p>
 131  * By default <code>MaskFormatter</code> will not allow invalid edits, you can
 132  * change this with the <code>setAllowsInvalid</code> method, and will
 133  * commit edits on valid edits (use the <code>setCommitsOnValidEdit</code> to
 134  * change this).
 135  * <p>
 136  * By default, <code>MaskFormatter</code> is in overwrite mode. That is as
 137  * characters are typed a new character is not inserted, rather the character
 138  * at the current location is replaced with the newly typed character. You
 139  * can change this behavior by way of the method <code>setOverwriteMode</code>.
 140  * <p>
 141  * <strong>Warning:</strong>
 142  * Serialized objects of this class will not be compatible with
 143  * future Swing releases. The current serialization support is
 144  * appropriate for short term storage or RMI between applications running
 145  * the same version of Swing.  As of 1.4, support for long term storage
 146  * of all JavaBeans&trade;
 147  * has been added to the <code>java.beans</code> package.
 148  * Please see {@link java.beans.XMLEncoder}.
 149  *
 150  * @since 1.4
 151  */
 152 @SuppressWarnings("serial") // Same-version serialization only
 153 public class MaskFormatter extends DefaultFormatter {
 154     // Potential values in mask.
 155     private static final char DIGIT_KEY = '#';
 156     private static final char LITERAL_KEY = '\'';
 157     private static final char UPPERCASE_KEY = 'U';
 158     private static final char LOWERCASE_KEY = 'L';
 159     private static final char ALPHA_NUMERIC_KEY = 'A';
 160     private static final char CHARACTER_KEY = '?';
 161     private static final char ANYTHING_KEY = '*';
 162     private static final char HEX_KEY = 'H';
 163 
 164     private static final MaskCharacter[] EmptyMaskChars = new MaskCharacter[0];
 165 
 166     /** The user specified mask. */
 167     private String mask;


 179     private String placeholderString;
 180 
 181     /** String used to represent characters not present. */
 182     private char placeholder;
 183 
 184     /** Indicates if the value contains the literal characters. */
 185     private boolean containsLiteralChars;
 186 
 187 
 188     /**
 189      * Creates a MaskFormatter with no mask.
 190      */
 191     public MaskFormatter() {
 192         setAllowsInvalid(false);
 193         containsLiteralChars = true;
 194         maskChars = EmptyMaskChars;
 195         placeholder = ' ';
 196     }
 197 
 198     /**
 199      * Creates a <code>MaskFormatter</code> with the specified mask.
 200      * A <code>ParseException</code>
 201      * will be thrown if <code>mask</code> is an invalid mask.
 202      * @param mask the mask
 203      * @throws ParseException if mask does not contain valid mask characters
 204      */
 205     public MaskFormatter(String mask) throws ParseException {
 206         this();
 207         setMask(mask);
 208     }
 209 
 210     /**
 211      * Sets the mask dictating the legal characters.
 212      * This will throw a <code>ParseException</code> if <code>mask</code> is
 213      * not valid.
 214      * @param mask the mask
 215      *
 216      * @throws ParseException if mask does not contain valid mask characters
 217      */
 218     public void setMask(String mask) throws ParseException {
 219         this.mask = mask;
 220         updateInternalMask();
 221     }
 222 
 223     /**
 224      * Returns the formatting mask.
 225      *
 226      * @return Mask dictating legal character values.
 227      */
 228     public String getMask() {
 229         return mask;
 230     }
 231 
 232     /**
 233      * Allows for further restricting of the characters that can be input.
 234      * Only characters specified in the mask, not in the
 235      * <code>invalidCharacters</code>, and in
 236      * <code>validCharacters</code> will be allowed to be input. Passing
 237      * in null (the default) implies the valid characters are only bound
 238      * by the mask and the invalid characters.
 239      *
 240      * @param validCharacters If non-null, specifies legal characters.
 241      */
 242     public void setValidCharacters(String validCharacters) {
 243         this.validCharacters = validCharacters;
 244     }
 245 
 246     /**
 247      * Returns the valid characters that can be input.
 248      *
 249      * @return Legal characters
 250      */
 251     public String getValidCharacters() {
 252         return validCharacters;
 253     }
 254 
 255     /**
 256      * Allows for further restricting of the characters that can be input.
 257      * Only characters specified in the mask, not in the
 258      * <code>invalidCharacters</code>, and in
 259      * <code>validCharacters</code> will be allowed to be input. Passing
 260      * in null (the default) implies the valid characters are only bound
 261      * by the mask and the valid characters.
 262      *
 263      * @param invalidCharacters If non-null, specifies illegal characters.
 264      */
 265     public void setInvalidCharacters(String invalidCharacters) {
 266         this.invalidCharacters = invalidCharacters;
 267     }
 268 
 269     /**
 270      * Returns the characters that are not valid for input.
 271      *
 272      * @return illegal characters.
 273      */
 274     public String getInvalidCharacters() {
 275         return invalidCharacters;
 276     }
 277 
 278     /**
 279      * Sets the string to use if the value does not completely fill in


 310      */
 311     public void setPlaceholderCharacter(char placeholder) {
 312         this.placeholder = placeholder;
 313     }
 314 
 315     /**
 316      * Returns the character to use in place of characters that are not present
 317      * in the value, ie the user must fill them in.
 318      *
 319      * @return Character used when formatting if the value does not
 320      *        completely fill the mask
 321      */
 322     public char getPlaceholderCharacter() {
 323         return placeholder;
 324     }
 325 
 326     /**
 327      * If true, the returned value and set value will also contain the literal
 328      * characters in mask.
 329      * <p>
 330      * For example, if the mask is <code>'(###) ###-####'</code>, the
 331      * current value is <code>'(415) 555-1212'</code>, and
 332      * <code>valueContainsLiteralCharacters</code> is
 333      * true <code>stringToValue</code> will return
 334      * <code>'(415) 555-1212'</code>. On the other hand, if
 335      * <code>valueContainsLiteralCharacters</code> is false,
 336      * <code>stringToValue</code> will return <code>'4155551212'</code>.
 337      *
 338      * @param containsLiteralChars Used to indicate if literal characters in
 339      *        mask should be returned in stringToValue
 340      */
 341     public void setValueContainsLiteralCharacters(
 342                         boolean containsLiteralChars) {
 343         this.containsLiteralChars = containsLiteralChars;
 344     }
 345 
 346     /**
 347      * Returns true if <code>stringToValue</code> should return literal
 348      * characters in the mask.
 349      *
 350      * @return True if literal characters in mask should be returned in
 351      *         stringToValue
 352      */
 353     public boolean getValueContainsLiteralCharacters() {
 354         return containsLiteralChars;
 355     }
 356 
 357     /**
 358      * Parses the text, returning the appropriate Object representation of
 359      * the String <code>value</code>. This strips the literal characters as
 360      * necessary and invokes supers <code>stringToValue</code>, so that if
 361      * you have specified a value class (<code>setValueClass</code>) an
 362      * instance of it will be created. This will throw a
 363      * <code>ParseException</code> if the value does not match the current
 364      * mask.  Refer to {@link #setValueContainsLiteralCharacters} for details
 365      * on how literals are treated.
 366      *
 367      * @throws ParseException if there is an error in the conversion
 368      * @param value String to convert
 369      * @see #setValueContainsLiteralCharacters
 370      * @return Object representation of text
 371      */
 372     public Object stringToValue(String value) throws ParseException {
 373         return stringToValue(value, true);
 374     }
 375 
 376     /**
 377      * Returns a String representation of the Object <code>value</code>
 378      * based on the mask.  Refer to
 379      * {@link #setValueContainsLiteralCharacters} for details
 380      * on how literals are treated.
 381      *
 382      * @throws ParseException if there is an error in the conversion
 383      * @param value Value to convert
 384      * @see #setValueContainsLiteralCharacters
 385      * @return String representation of value
 386      */
 387     public String valueToString(Object value) throws ParseException {
 388         String sValue = (value == null) ? "" : value.toString();
 389         StringBuilder result = new StringBuilder();
 390         String placeholder = getPlaceholder();
 391         int[] valueCounter = { 0 };
 392 
 393         append(result, sValue, valueCounter, placeholder, maskChars);
 394         return result.toString();
 395     }
 396 
 397     /**
 398      * Installs the <code>DefaultFormatter</code> onto a particular
 399      * <code>JFormattedTextField</code>.
 400      * This will invoke <code>valueToString</code> to convert the
 401      * current value from the <code>JFormattedTextField</code> to
 402      * a String. This will then install the <code>Action</code>s from
 403      * <code>getActions</code>, the <code>DocumentFilter</code>
 404      * returned from <code>getDocumentFilter</code> and the
 405      * <code>NavigationFilter</code> returned from
 406      * <code>getNavigationFilter</code> onto the
 407      * <code>JFormattedTextField</code>.
 408      * <p>
 409      * Subclasses will typically only need to override this if they
 410      * wish to install additional listeners on the
 411      * <code>JFormattedTextField</code>.
 412      * <p>
 413      * If there is a <code>ParseException</code> in converting the
 414      * current value to a String, this will set the text to an empty
 415      * String, and mark the <code>JFormattedTextField</code> as being
 416      * in an invalid state.
 417      * <p>
 418      * While this is a public method, this is typically only useful
 419      * for subclassers of <code>JFormattedTextField</code>.
 420      * <code>JFormattedTextField</code> will invoke this method at
 421      * the appropriate times when the value changes, or its internal
 422      * state changes.
 423      *
 424      * @param ftf JFormattedTextField to format for, may be null indicating
 425      *            uninstall from current JFormattedTextField.
 426      */
 427     public void install(JFormattedTextField ftf) {
 428         super.install(ftf);
 429         // valueToString doesn't throw, but stringToValue does, need to
 430         // update the editValid state appropriately
 431         if (ftf != null) {
 432             Object value = ftf.getValue();
 433 
 434             try {
 435                 stringToValue(valueToString(value));
 436             } catch (ParseException pe) {
 437                 setEditValid(false);
 438             }
 439         }
 440     }
 441 
 442     /**
 443      * Actual <code>stringToValue</code> implementation.
 444      * If <code>completeMatch</code> is true, the value must exactly match
 445      * the mask, on the other hand if <code>completeMatch</code> is false
 446      * the string must match the mask or the placeholder string.
 447      */
 448     private Object stringToValue(String value, boolean completeMatch) throws
 449                          ParseException {
 450         int errorOffset;
 451 
 452         if ((errorOffset = getInvalidOffset(value, completeMatch)) == -1) {
 453             if (!getValueContainsLiteralCharacters()) {
 454                 value = stripLiteralChars(value);
 455             }
 456             return super.stringToValue(value);
 457         }
 458         throw new ParseException("stringToValue passed invalid value",
 459                                  errorOffset);
 460     }
 461 
 462     /**
 463      * Returns -1 if the passed in string is valid, otherwise the index of
 464      * the first bogus character is returned.
 465      */
 466     private int getInvalidOffset(String string, boolean completeMatch) {
 467         int iLength = string.length();
 468 
 469         if (iLength != getMaxLength()) {
 470             // trivially false
 471             return iLength;
 472         }
 473         for (int counter = 0, max = string.length(); counter < max; counter++){
 474             char aChar = string.charAt(counter);
 475 
 476             if (!isValidCharacter(counter, aChar) &&
 477                 (completeMatch || !isPlaceholder(counter, aChar))) {
 478                 return counter;
 479             }
 480         }
 481         return -1;
 482     }
 483 
 484     /**
 485      * Invokes <code>append</code> on the mask characters in
 486      * <code>mask</code>.
 487      */
 488     private void append(StringBuilder result, String value, int[] index,
 489                         String placeholder, MaskCharacter[] mask)
 490                           throws ParseException {
 491         for (int counter = 0, maxCounter = mask.length;
 492              counter < maxCounter; counter++) {
 493             mask[counter].append(result, value, index, placeholder);
 494         }
 495     }
 496 
 497     /**
 498      * Updates the internal representation of the mask.
 499      */
 500     private void updateInternalMask() throws ParseException {
 501         String mask = getMask();
 502         ArrayList<MaskCharacter> fixed = new ArrayList<MaskCharacter>();
 503         ArrayList<MaskCharacter> temp = fixed;
 504 
 505         if (mask != null) {
 506             for (int counter = 0, maxCounter = mask.length();


 652         throws IOException, ClassNotFoundException {
 653         ObjectInputStream.GetField f = s.readFields();
 654 
 655         validCharacters = (String) f.get("validCharacters", null);
 656         invalidCharacters = (String) f.get("invalidCharacters", null);
 657         placeholderString = (String) f.get("placeholderString", null);
 658         placeholder = f.get("placeholder", '\0');
 659         containsLiteralChars = f.get("containsLiteralChars", false);
 660         mask = (String) f.get("mask", null);
 661 
 662         try {
 663             updateInternalMask();
 664         } catch (ParseException pe) {
 665             // assert();
 666         }
 667     }
 668 
 669     /**
 670      * Returns true if the MaskFormatter allows invalid, or
 671      * the offset is less than the max length and the character at
 672      * <code>offset</code> is a literal.
 673      */
 674     boolean isNavigatable(int offset) {
 675         if (!getAllowsInvalid()) {
 676             return (offset < getMaxLength() && !isLiteral(offset));
 677         }
 678         return true;
 679     }
 680 
 681     /*
 682      * Returns true if the operation described by <code>rh</code> will
 683      * result in a legal edit.  This may set the <code>value</code>
 684      * field of <code>rh</code>.
 685      * <p>
 686      * This is overriden to return true for a partial match.
 687      */
 688     boolean isValidEdit(ReplaceHolder rh) {
 689         if (!getAllowsInvalid()) {
 690             String newString = getReplaceString(rh.offset, rh.length, rh.text);
 691 
 692             try {
 693                 rh.value = stringToValue(newString, false);
 694 
 695                 return true;
 696             } catch (ParseException pe) {
 697                 return false;
 698             }
 699         }
 700         return true;
 701     }
 702 
 703     /**
 704      * This method does the following (assuming !getAllowsInvalid()):


 815             }
 816         }
 817         return super.canReplace(rh);
 818     }
 819 
 820 
 821     //
 822     // Interal classes used to represent the mask.
 823     //
 824     private class MaskCharacter {
 825         /**
 826          * Subclasses should override this returning true if the instance
 827          * represents a literal character. The default implementation
 828          * returns false.
 829          */
 830         public boolean isLiteral() {
 831             return false;
 832         }
 833 
 834         /**
 835          * Returns true if <code>aChar</code> is a valid reprensentation of
 836          * the receiver. The default implementation returns true if the
 837          * receiver represents a literal character and <code>getChar</code>
 838          * == aChar. Otherwise, this will return true is <code>aChar</code>
 839          * is contained in the valid characters and not contained
 840          * in the invalid characters.
 841          */
 842         public boolean isValidCharacter(char aChar) {
 843             if (isLiteral()) {
 844                 return (getChar(aChar) == aChar);
 845             }
 846 
 847             aChar = getChar(aChar);
 848 
 849             String filter = getValidCharacters();
 850 
 851             if (filter != null && filter.indexOf(aChar) == -1) {
 852                 return false;
 853             }
 854             filter = getInvalidCharacters();
 855             if (filter != null && filter.indexOf(aChar) != -1) {
 856                 return false;
 857             }
 858             return true;
 859         }
 860 
 861         /**
 862          * Returns the character to insert for <code>aChar</code>. The
 863          * default implementation returns <code>aChar</code>. Subclasses
 864          * that wish to do some sort of mapping, perhaps lower case to upper
 865          * case should override this and do the necessary mapping.
 866          */
 867         public char getChar(char aChar) {
 868             return aChar;
 869         }
 870 
 871         /**
 872          * Appends the necessary character in <code>formatting</code> at
 873          * <code>index</code> to <code>buff</code>.
 874          */
 875         public void append(StringBuilder buff, String formatting, int[] index,
 876                            String placeholder)
 877                           throws ParseException {
 878             boolean inString = index[0] < formatting.length();
 879             char aChar = inString ? formatting.charAt(index[0]) : 0;
 880 
 881             if (isLiteral()) {
 882                 buff.append(getChar(aChar));
 883                 if (getValueContainsLiteralCharacters()) {
 884                     if (inString && aChar != getChar(aChar)) {
 885                         throw new ParseException("Invalid character: " +
 886                                                  aChar, index[0]);
 887                     }
 888                     index[0] = index[0] + 1;
 889                 }
 890             }
 891             else if (index[0] >= formatting.length()) {
 892                 if (placeholder != null && index[0] < placeholder.length()) {
 893                     buff.append(placeholder.charAt(index[0]));


 913      * Used to represent a fixed character in the mask.
 914      */
 915     private class LiteralCharacter extends MaskCharacter {
 916         private char fixedChar;
 917 
 918         public LiteralCharacter(char fixedChar) {
 919             this.fixedChar = fixedChar;
 920         }
 921 
 922         public boolean isLiteral() {
 923             return true;
 924         }
 925 
 926         public char getChar(char aChar) {
 927             return fixedChar;
 928         }
 929     }
 930 
 931 
 932     /**
 933      * Represents a number, uses <code>Character.isDigit</code>.
 934      */
 935     private class DigitMaskCharacter extends MaskCharacter {
 936         public boolean isValidCharacter(char aChar) {
 937             return (Character.isDigit(aChar) &&
 938                     super.isValidCharacter(aChar));
 939         }
 940     }
 941 
 942 
 943     /**
 944      * Represents a character, lower case letters are mapped to upper case
 945      * using <code>Character.toUpperCase</code>.
 946      */
 947     private class UpperCaseCharacter extends MaskCharacter {
 948         public boolean isValidCharacter(char aChar) {
 949             return (Character.isLetter(aChar) &&
 950                      super.isValidCharacter(aChar));
 951         }
 952 
 953         public char getChar(char aChar) {
 954             return Character.toUpperCase(aChar);
 955         }
 956     }
 957 
 958 
 959     /**
 960      * Represents a character, upper case letters are mapped to lower case
 961      * using <code>Character.toLowerCase</code>.
 962      */
 963     private class LowerCaseCharacter extends MaskCharacter {
 964         public boolean isValidCharacter(char aChar) {
 965             return (Character.isLetter(aChar) &&
 966                      super.isValidCharacter(aChar));
 967         }
 968 
 969         public char getChar(char aChar) {
 970             return Character.toLowerCase(aChar);
 971         }
 972     }
 973 
 974 
 975     /**
 976      * Represents either a character or digit, uses
 977      * <code>Character.isLetterOrDigit</code>.
 978      */
 979     private class AlphaNumericCharacter extends MaskCharacter {
 980         public boolean isValidCharacter(char aChar) {
 981             return (Character.isLetterOrDigit(aChar) &&
 982                      super.isValidCharacter(aChar));
 983         }
 984     }
 985 
 986 
 987     /**
 988      * Represents a letter, uses <code>Character.isLetter</code>.
 989      */
 990     private class CharCharacter extends MaskCharacter {
 991         public boolean isValidCharacter(char aChar) {
 992             return (Character.isLetter(aChar) &&
 993                      super.isValidCharacter(aChar));
 994         }
 995     }
 996 
 997 
 998     /**
 999      * Represents a hex character, 0-9a-fA-F. a-f is mapped to A-F
1000      */
1001     private class HexCharacter extends MaskCharacter {
1002         public boolean isValidCharacter(char aChar) {
1003             return ((aChar == '0' || aChar == '1' ||
1004                      aChar == '2' || aChar == '3' ||
1005                      aChar == '4' || aChar == '5' ||
1006                      aChar == '6' || aChar == '7' ||
1007                      aChar == '8' || aChar == '9' ||
1008                      aChar == 'a' || aChar == 'A' ||


  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.text;
  27 
  28 import java.io.*;
  29 import java.text.*;
  30 import java.util.*;
  31 import javax.swing.*;
  32 
  33 /**
  34  * {@code MaskFormatter} is used to format and edit strings. The behavior
  35  * of a {@code MaskFormatter} is controlled by way of a String mask
  36  * that specifies the valid characters that can be contained at a particular
  37  * location in the {@code Document} model. The following characters can
  38  * be specified:
  39  *
  40  * <table border=1 summary="Valid characters and their descriptions">
  41  * <tr>
  42  *    <th>Character&nbsp;</th>
  43  *    <th><p style="text-align:left">Description</p></th>
  44  * </tr>
  45  * <tr>
  46  *    <td>#</td>
  47  *    <td>Any valid number, uses {@code Character.isDigit}.</td>
  48  * </tr>
  49  * <tr>
  50  *    <td>'</td>
  51  *    <td>Escape character, used to escape any of the
  52  *       special formatting characters.</td>
  53  * </tr>
  54  * <tr>
  55  *    <td>U</td><td>Any character ({@code Character.isLetter}). All
  56  *        lowercase letters are mapped to upper case.</td>
  57  * </tr>
  58  * <tr><td>L</td><td>Any character ({@code Character.isLetter}). All
  59  *        upper case letters are mapped to lower case.</td>
  60  * </tr>
  61  * <tr><td>A</td><td>Any character or number ({@code Character.isLetter}
  62  *       or {@code Character.isDigit})</td>
  63  * </tr>
  64  * <tr><td>?</td><td>Any character
  65  *        ({@code Character.isLetter}).</td>
  66  * </tr>
  67  * <tr><td>*</td><td>Anything.</td></tr>
  68  * <tr><td>H</td><td>Any hex character (0-9, a-f or A-F).</td></tr>
  69  * </table>
  70  *
  71  * <p>
  72  * Typically characters correspond to one char, but in certain languages this
  73  * is not the case. The mask is on a per character basis, and will thus
  74  * adjust to fit as many chars as are needed.
  75  * <p>
  76  * You can further restrict the characters that can be input by the
  77  * {@code setInvalidCharacters} and {@code setValidCharacters}
  78  * methods. {@code setInvalidCharacters} allows you to specify
  79  * which characters are not legal. {@code setValidCharacters} allows
  80  * you to specify which characters are valid. For example, the following
  81  * code block is equivalent to a mask of '0xHHH' with no invalid/valid
  82  * characters:
  83  * <pre>
  84  * MaskFormatter formatter = new MaskFormatter("0x***");
  85  * formatter.setValidCharacters("0123456789abcdefABCDEF");
  86  * </pre>
  87  * <p>
  88  * When initially formatting a value if the length of the string is
  89  * less than the length of the mask, two things can happen. Either
  90  * the placeholder string will be used, or the placeholder character will
  91  * be used. Precedence is given to the placeholder string. For example:
  92  * <pre>
  93  *   MaskFormatter formatter = new MaskFormatter("###-####");
  94  *   formatter.setPlaceholderCharacter('_');
  95  *   formatter.getDisplayValue(tf, "123");
  96  * </pre>
  97  * <p>
  98  * Would result in the string '123-____'. If
  99  * {@code setPlaceholder("555-1212")} was invoked '123-1212' would
 100  * result. The placeholder String is only used on the initial format,
 101  * on subsequent formats only the placeholder character will be used.
 102  * <p>
 103  * If a {@code MaskFormatter} is configured to only allow valid characters
 104  * ({@code setAllowsInvalid(false)}) literal characters will be skipped as
 105  * necessary when editing. Consider a {@code MaskFormatter} with
 106  * the mask "###-####" and current value "555-1212". Using the right
 107  * arrow key to navigate through the field will result in (| indicates the
 108  * position of the caret):
 109  * <pre>
 110  *   |555-1212
 111  *   5|55-1212
 112  *   55|5-1212
 113  *   555-|1212
 114  *   555-1|212
 115  * </pre>
 116  * The '-' is a literal (non-editable) character, and is skipped.
 117  * <p>
 118  * Similar behavior will result when editing. Consider inserting the string
 119  * '123-45' and '12345' into the {@code MaskFormatter} in the
 120  * previous example. Both inserts will result in the same String,
 121  * '123-45__'. When {@code MaskFormatter}
 122  * is processing the insert at character position 3 (the '-'), two things can
 123  * happen:
 124  * <ol>
 125  *   <li>If the inserted character is '-', it is accepted.
 126  *   <li>If the inserted character matches the mask for the next non-literal
 127  *       character, it is accepted at the new location.
 128  *   <li>Anything else results in an invalid edit
 129  * </ol>
 130  * <p>
 131  * By default {@code MaskFormatter} will not allow invalid edits, you can
 132  * change this with the {@code setAllowsInvalid} method, and will
 133  * commit edits on valid edits (use the {@code setCommitsOnValidEdit} to
 134  * change this).
 135  * <p>
 136  * By default, {@code MaskFormatter} is in overwrite mode. That is as
 137  * characters are typed a new character is not inserted, rather the character
 138  * at the current location is replaced with the newly typed character. You
 139  * can change this behavior by way of the method {@code setOverwriteMode}.
 140  * <p>
 141  * <strong>Warning:</strong>
 142  * Serialized objects of this class will not be compatible with
 143  * future Swing releases. The current serialization support is
 144  * appropriate for short term storage or RMI between applications running
 145  * the same version of Swing.  As of 1.4, support for long term storage
 146  * of all JavaBeans&trade;
 147  * has been added to the {@code java.beans} package.
 148  * Please see {@link java.beans.XMLEncoder}.
 149  *
 150  * @since 1.4
 151  */
 152 @SuppressWarnings("serial") // Same-version serialization only
 153 public class MaskFormatter extends DefaultFormatter {
 154     // Potential values in mask.
 155     private static final char DIGIT_KEY = '#';
 156     private static final char LITERAL_KEY = '\'';
 157     private static final char UPPERCASE_KEY = 'U';
 158     private static final char LOWERCASE_KEY = 'L';
 159     private static final char ALPHA_NUMERIC_KEY = 'A';
 160     private static final char CHARACTER_KEY = '?';
 161     private static final char ANYTHING_KEY = '*';
 162     private static final char HEX_KEY = 'H';
 163 
 164     private static final MaskCharacter[] EmptyMaskChars = new MaskCharacter[0];
 165 
 166     /** The user specified mask. */
 167     private String mask;


 179     private String placeholderString;
 180 
 181     /** String used to represent characters not present. */
 182     private char placeholder;
 183 
 184     /** Indicates if the value contains the literal characters. */
 185     private boolean containsLiteralChars;
 186 
 187 
 188     /**
 189      * Creates a MaskFormatter with no mask.
 190      */
 191     public MaskFormatter() {
 192         setAllowsInvalid(false);
 193         containsLiteralChars = true;
 194         maskChars = EmptyMaskChars;
 195         placeholder = ' ';
 196     }
 197 
 198     /**
 199      * Creates a {@code MaskFormatter} with the specified mask.
 200      * A {@code ParseException}
 201      * will be thrown if {@code mask} is an invalid mask.
 202      * @param mask the mask
 203      * @throws ParseException if mask does not contain valid mask characters
 204      */
 205     public MaskFormatter(String mask) throws ParseException {
 206         this();
 207         setMask(mask);
 208     }
 209 
 210     /**
 211      * Sets the mask dictating the legal characters.
 212      * This will throw a {@code ParseException} if {@code mask} is
 213      * not valid.
 214      * @param mask the mask
 215      *
 216      * @throws ParseException if mask does not contain valid mask characters
 217      */
 218     public void setMask(String mask) throws ParseException {
 219         this.mask = mask;
 220         updateInternalMask();
 221     }
 222 
 223     /**
 224      * Returns the formatting mask.
 225      *
 226      * @return Mask dictating legal character values.
 227      */
 228     public String getMask() {
 229         return mask;
 230     }
 231 
 232     /**
 233      * Allows for further restricting of the characters that can be input.
 234      * Only characters specified in the mask, not in the
 235      * {@code invalidCharacters}, and in
 236      * {@code validCharacters} will be allowed to be input. Passing
 237      * in null (the default) implies the valid characters are only bound
 238      * by the mask and the invalid characters.
 239      *
 240      * @param validCharacters If non-null, specifies legal characters.
 241      */
 242     public void setValidCharacters(String validCharacters) {
 243         this.validCharacters = validCharacters;
 244     }
 245 
 246     /**
 247      * Returns the valid characters that can be input.
 248      *
 249      * @return Legal characters
 250      */
 251     public String getValidCharacters() {
 252         return validCharacters;
 253     }
 254 
 255     /**
 256      * Allows for further restricting of the characters that can be input.
 257      * Only characters specified in the mask, not in the
 258      * {@code invalidCharacters}, and in
 259      * {@code validCharacters} will be allowed to be input. Passing
 260      * in null (the default) implies the valid characters are only bound
 261      * by the mask and the valid characters.
 262      *
 263      * @param invalidCharacters If non-null, specifies illegal characters.
 264      */
 265     public void setInvalidCharacters(String invalidCharacters) {
 266         this.invalidCharacters = invalidCharacters;
 267     }
 268 
 269     /**
 270      * Returns the characters that are not valid for input.
 271      *
 272      * @return illegal characters.
 273      */
 274     public String getInvalidCharacters() {
 275         return invalidCharacters;
 276     }
 277 
 278     /**
 279      * Sets the string to use if the value does not completely fill in


 310      */
 311     public void setPlaceholderCharacter(char placeholder) {
 312         this.placeholder = placeholder;
 313     }
 314 
 315     /**
 316      * Returns the character to use in place of characters that are not present
 317      * in the value, ie the user must fill them in.
 318      *
 319      * @return Character used when formatting if the value does not
 320      *        completely fill the mask
 321      */
 322     public char getPlaceholderCharacter() {
 323         return placeholder;
 324     }
 325 
 326     /**
 327      * If true, the returned value and set value will also contain the literal
 328      * characters in mask.
 329      * <p>
 330      * For example, if the mask is {@code '(###) ###-####'}, the
 331      * current value is {@code '(415) 555-1212'}, and
 332      * {@code valueContainsLiteralCharacters} is
 333      * true {@code stringToValue} will return
 334      * {@code '(415) 555-1212'}. On the other hand, if
 335      * {@code valueContainsLiteralCharacters} is false,
 336      * {@code stringToValue} will return {@code '4155551212'}.
 337      *
 338      * @param containsLiteralChars Used to indicate if literal characters in
 339      *        mask should be returned in stringToValue
 340      */
 341     public void setValueContainsLiteralCharacters(
 342                         boolean containsLiteralChars) {
 343         this.containsLiteralChars = containsLiteralChars;
 344     }
 345 
 346     /**
 347      * Returns true if {@code stringToValue} should return literal
 348      * characters in the mask.
 349      *
 350      * @return True if literal characters in mask should be returned in
 351      *         stringToValue
 352      */
 353     public boolean getValueContainsLiteralCharacters() {
 354         return containsLiteralChars;
 355     }
 356 
 357     /**
 358      * Parses the text, returning the appropriate Object representation of
 359      * the String {@code value}. This strips the literal characters as
 360      * necessary and invokes supers {@code stringToValue}, so that if
 361      * you have specified a value class ({@code setValueClass}) an
 362      * instance of it will be created. This will throw a
 363      * {@code ParseException} if the value does not match the current
 364      * mask.  Refer to {@link #setValueContainsLiteralCharacters} for details
 365      * on how literals are treated.
 366      *
 367      * @throws ParseException if there is an error in the conversion
 368      * @param value String to convert
 369      * @see #setValueContainsLiteralCharacters
 370      * @return Object representation of text
 371      */
 372     public Object stringToValue(String value) throws ParseException {
 373         return stringToValue(value, true);
 374     }
 375 
 376     /**
 377      * Returns a String representation of the Object {@code value}
 378      * based on the mask.  Refer to
 379      * {@link #setValueContainsLiteralCharacters} for details
 380      * on how literals are treated.
 381      *
 382      * @throws ParseException if there is an error in the conversion
 383      * @param value Value to convert
 384      * @see #setValueContainsLiteralCharacters
 385      * @return String representation of value
 386      */
 387     public String valueToString(Object value) throws ParseException {
 388         String sValue = (value == null) ? "" : value.toString();
 389         StringBuilder result = new StringBuilder();
 390         String placeholder = getPlaceholder();
 391         int[] valueCounter = { 0 };
 392 
 393         append(result, sValue, valueCounter, placeholder, maskChars);
 394         return result.toString();
 395     }
 396 
 397     /**
 398      * Installs the {@code DefaultFormatter} onto a particular
 399      * {@code JFormattedTextField}.
 400      * This will invoke {@code valueToString} to convert the
 401      * current value from the {@code JFormattedTextField} to
 402      * a String. This will then install the {@code Action}s from
 403      * {@code getActions}, the {@code DocumentFilter}
 404      * returned from {@code getDocumentFilter} and the
 405      * {@code NavigationFilter} returned from
 406      * {@code getNavigationFilter} onto the
 407      * {@code JFormattedTextField}.
 408      * <p>
 409      * Subclasses will typically only need to override this if they
 410      * wish to install additional listeners on the
 411      * {@code JFormattedTextField}.
 412      * <p>
 413      * If there is a {@code ParseException} in converting the
 414      * current value to a String, this will set the text to an empty
 415      * String, and mark the {@code JFormattedTextField} as being
 416      * in an invalid state.
 417      * <p>
 418      * While this is a public method, this is typically only useful
 419      * for subclassers of {@code JFormattedTextField}.
 420      * {@code JFormattedTextField} will invoke this method at
 421      * the appropriate times when the value changes, or its internal
 422      * state changes.
 423      *
 424      * @param ftf JFormattedTextField to format for, may be null indicating
 425      *            uninstall from current JFormattedTextField.
 426      */
 427     public void install(JFormattedTextField ftf) {
 428         super.install(ftf);
 429         // valueToString doesn't throw, but stringToValue does, need to
 430         // update the editValid state appropriately
 431         if (ftf != null) {
 432             Object value = ftf.getValue();
 433 
 434             try {
 435                 stringToValue(valueToString(value));
 436             } catch (ParseException pe) {
 437                 setEditValid(false);
 438             }
 439         }
 440     }
 441 
 442     /**
 443      * Actual {@code stringToValue} implementation.
 444      * If {@code completeMatch} is true, the value must exactly match
 445      * the mask, on the other hand if {@code completeMatch} is false
 446      * the string must match the mask or the placeholder string.
 447      */
 448     private Object stringToValue(String value, boolean completeMatch) throws
 449                          ParseException {
 450         int errorOffset;
 451 
 452         if ((errorOffset = getInvalidOffset(value, completeMatch)) == -1) {
 453             if (!getValueContainsLiteralCharacters()) {
 454                 value = stripLiteralChars(value);
 455             }
 456             return super.stringToValue(value);
 457         }
 458         throw new ParseException("stringToValue passed invalid value",
 459                                  errorOffset);
 460     }
 461 
 462     /**
 463      * Returns -1 if the passed in string is valid, otherwise the index of
 464      * the first bogus character is returned.
 465      */
 466     private int getInvalidOffset(String string, boolean completeMatch) {
 467         int iLength = string.length();
 468 
 469         if (iLength != getMaxLength()) {
 470             // trivially false
 471             return iLength;
 472         }
 473         for (int counter = 0, max = string.length(); counter < max; counter++){
 474             char aChar = string.charAt(counter);
 475 
 476             if (!isValidCharacter(counter, aChar) &&
 477                 (completeMatch || !isPlaceholder(counter, aChar))) {
 478                 return counter;
 479             }
 480         }
 481         return -1;
 482     }
 483 
 484     /**
 485      * Invokes {@code append} on the mask characters in
 486      * {@code mask}.
 487      */
 488     private void append(StringBuilder result, String value, int[] index,
 489                         String placeholder, MaskCharacter[] mask)
 490                           throws ParseException {
 491         for (int counter = 0, maxCounter = mask.length;
 492              counter < maxCounter; counter++) {
 493             mask[counter].append(result, value, index, placeholder);
 494         }
 495     }
 496 
 497     /**
 498      * Updates the internal representation of the mask.
 499      */
 500     private void updateInternalMask() throws ParseException {
 501         String mask = getMask();
 502         ArrayList<MaskCharacter> fixed = new ArrayList<MaskCharacter>();
 503         ArrayList<MaskCharacter> temp = fixed;
 504 
 505         if (mask != null) {
 506             for (int counter = 0, maxCounter = mask.length();


 652         throws IOException, ClassNotFoundException {
 653         ObjectInputStream.GetField f = s.readFields();
 654 
 655         validCharacters = (String) f.get("validCharacters", null);
 656         invalidCharacters = (String) f.get("invalidCharacters", null);
 657         placeholderString = (String) f.get("placeholderString", null);
 658         placeholder = f.get("placeholder", '\0');
 659         containsLiteralChars = f.get("containsLiteralChars", false);
 660         mask = (String) f.get("mask", null);
 661 
 662         try {
 663             updateInternalMask();
 664         } catch (ParseException pe) {
 665             // assert();
 666         }
 667     }
 668 
 669     /**
 670      * Returns true if the MaskFormatter allows invalid, or
 671      * the offset is less than the max length and the character at
 672      * {@code offset} is a literal.
 673      */
 674     boolean isNavigatable(int offset) {
 675         if (!getAllowsInvalid()) {
 676             return (offset < getMaxLength() && !isLiteral(offset));
 677         }
 678         return true;
 679     }
 680 
 681     /*
 682      * Returns true if the operation described by {@code rh} will
 683      * result in a legal edit.  This may set the {@code value}
 684      * field of {@code rh}.
 685      * <p>
 686      * This is overriden to return true for a partial match.
 687      */
 688     boolean isValidEdit(ReplaceHolder rh) {
 689         if (!getAllowsInvalid()) {
 690             String newString = getReplaceString(rh.offset, rh.length, rh.text);
 691 
 692             try {
 693                 rh.value = stringToValue(newString, false);
 694 
 695                 return true;
 696             } catch (ParseException pe) {
 697                 return false;
 698             }
 699         }
 700         return true;
 701     }
 702 
 703     /**
 704      * This method does the following (assuming !getAllowsInvalid()):


 815             }
 816         }
 817         return super.canReplace(rh);
 818     }
 819 
 820 
 821     //
 822     // Interal classes used to represent the mask.
 823     //
 824     private class MaskCharacter {
 825         /**
 826          * Subclasses should override this returning true if the instance
 827          * represents a literal character. The default implementation
 828          * returns false.
 829          */
 830         public boolean isLiteral() {
 831             return false;
 832         }
 833 
 834         /**
 835          * Returns true if {@code aChar} is a valid reprensentation of
 836          * the receiver. The default implementation returns true if the
 837          * receiver represents a literal character and {@code getChar}
 838          * == aChar. Otherwise, this will return true is {@code aChar}
 839          * is contained in the valid characters and not contained
 840          * in the invalid characters.
 841          */
 842         public boolean isValidCharacter(char aChar) {
 843             if (isLiteral()) {
 844                 return (getChar(aChar) == aChar);
 845             }
 846 
 847             aChar = getChar(aChar);
 848 
 849             String filter = getValidCharacters();
 850 
 851             if (filter != null && filter.indexOf(aChar) == -1) {
 852                 return false;
 853             }
 854             filter = getInvalidCharacters();
 855             if (filter != null && filter.indexOf(aChar) != -1) {
 856                 return false;
 857             }
 858             return true;
 859         }
 860 
 861         /**
 862          * Returns the character to insert for {@code aChar}. The
 863          * default implementation returns {@code aChar}. Subclasses
 864          * that wish to do some sort of mapping, perhaps lower case to upper
 865          * case should override this and do the necessary mapping.
 866          */
 867         public char getChar(char aChar) {
 868             return aChar;
 869         }
 870 
 871         /**
 872          * Appends the necessary character in {@code formatting} at
 873          * {@code index} to {@code buff}.
 874          */
 875         public void append(StringBuilder buff, String formatting, int[] index,
 876                            String placeholder)
 877                           throws ParseException {
 878             boolean inString = index[0] < formatting.length();
 879             char aChar = inString ? formatting.charAt(index[0]) : 0;
 880 
 881             if (isLiteral()) {
 882                 buff.append(getChar(aChar));
 883                 if (getValueContainsLiteralCharacters()) {
 884                     if (inString && aChar != getChar(aChar)) {
 885                         throw new ParseException("Invalid character: " +
 886                                                  aChar, index[0]);
 887                     }
 888                     index[0] = index[0] + 1;
 889                 }
 890             }
 891             else if (index[0] >= formatting.length()) {
 892                 if (placeholder != null && index[0] < placeholder.length()) {
 893                     buff.append(placeholder.charAt(index[0]));


 913      * Used to represent a fixed character in the mask.
 914      */
 915     private class LiteralCharacter extends MaskCharacter {
 916         private char fixedChar;
 917 
 918         public LiteralCharacter(char fixedChar) {
 919             this.fixedChar = fixedChar;
 920         }
 921 
 922         public boolean isLiteral() {
 923             return true;
 924         }
 925 
 926         public char getChar(char aChar) {
 927             return fixedChar;
 928         }
 929     }
 930 
 931 
 932     /**
 933      * Represents a number, uses {@code Character.isDigit}.
 934      */
 935     private class DigitMaskCharacter extends MaskCharacter {
 936         public boolean isValidCharacter(char aChar) {
 937             return (Character.isDigit(aChar) &&
 938                     super.isValidCharacter(aChar));
 939         }
 940     }
 941 
 942 
 943     /**
 944      * Represents a character, lower case letters are mapped to upper case
 945      * using {@code Character.toUpperCase}.
 946      */
 947     private class UpperCaseCharacter extends MaskCharacter {
 948         public boolean isValidCharacter(char aChar) {
 949             return (Character.isLetter(aChar) &&
 950                      super.isValidCharacter(aChar));
 951         }
 952 
 953         public char getChar(char aChar) {
 954             return Character.toUpperCase(aChar);
 955         }
 956     }
 957 
 958 
 959     /**
 960      * Represents a character, upper case letters are mapped to lower case
 961      * using {@code Character.toLowerCase}.
 962      */
 963     private class LowerCaseCharacter extends MaskCharacter {
 964         public boolean isValidCharacter(char aChar) {
 965             return (Character.isLetter(aChar) &&
 966                      super.isValidCharacter(aChar));
 967         }
 968 
 969         public char getChar(char aChar) {
 970             return Character.toLowerCase(aChar);
 971         }
 972     }
 973 
 974 
 975     /**
 976      * Represents either a character or digit, uses
 977      * {@code Character.isLetterOrDigit}.
 978      */
 979     private class AlphaNumericCharacter extends MaskCharacter {
 980         public boolean isValidCharacter(char aChar) {
 981             return (Character.isLetterOrDigit(aChar) &&
 982                      super.isValidCharacter(aChar));
 983         }
 984     }
 985 
 986 
 987     /**
 988      * Represents a letter, uses {@code Character.isLetter}.
 989      */
 990     private class CharCharacter extends MaskCharacter {
 991         public boolean isValidCharacter(char aChar) {
 992             return (Character.isLetter(aChar) &&
 993                      super.isValidCharacter(aChar));
 994         }
 995     }
 996 
 997 
 998     /**
 999      * Represents a hex character, 0-9a-fA-F. a-f is mapped to A-F
1000      */
1001     private class HexCharacter extends MaskCharacter {
1002         public boolean isValidCharacter(char aChar) {
1003             return ((aChar == '0' || aChar == '1' ||
1004                      aChar == '2' || aChar == '3' ||
1005                      aChar == '4' || aChar == '5' ||
1006                      aChar == '6' || aChar == '7' ||
1007                      aChar == '8' || aChar == '9' ||
1008                      aChar == 'a' || aChar == 'A' ||
< prev index next >