1 /* MaskFormatter.java --
2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text;
41 import java.text.ParseException;
43 import javax.swing.JFormattedTextField;
46 * @author Anthony Balkissoon abalkiss at redhat dot com
49 public class MaskFormatter extends DefaultFormatter
51 // The declaration of the valid mask characters
52 private static final char NUM_CHAR = '#';
53 private static final char ESCAPE_CHAR = '\'';
54 private static final char UPPERCASE_CHAR = 'U';
55 private static final char LOWERCASE_CHAR = 'L';
56 private static final char ALPHANUM_CHAR = 'A';
57 private static final char LETTER_CHAR = '?';
58 private static final char ANYTHING_CHAR = '*';
59 private static final char HEX_CHAR = 'H';
61 /** The mask for this MaskFormatter **/
65 * A String made up of the characters that are not valid for input for
68 private String invalidChars;
71 * A String made up of the characters that are valid for input for
74 private String validChars;
76 /** A String used in place of missing chracters if the value does not
77 * completely fill in the spaces in the mask.
79 private String placeHolder;
81 /** A character used in place of missing characters if the value does
82 * not completely fill in the spaces in the mask.
84 private char placeHolderChar = ' ';
87 * Whether or not stringToValue should return literal characters in the mask.
89 private boolean valueContainsLiteralCharacters = true;
91 /** A String used for easy access to valid HEX characters **/
92 private static String hexString = "0123456789abcdefABCDEF";
94 /** An int to hold the length of the mask, accounting for escaped characters **/
97 public MaskFormatter ()
99 // Override super's default behaviour, in MaskFormatter the default
100 // is not to allow invalid values
101 setAllowsInvalid(false);
105 * Creates a MaskFormatter with the specified mask.
106 * @specnote doesn't actually throw a ParseException although it
107 * is declared to do so
109 * @throws java.text.ParseException
111 public MaskFormatter (String mask) throws java.text.ParseException
118 * Returns the mask used in this MaskFormatter.
119 * @return the mask used in this MaskFormatter.
121 public String getMask()
127 * Returns a String containing the characters that are not valid for input
128 * for this MaskFormatter.
129 * @return a String containing the invalid characters.
131 public String getInvalidCharacters()
137 * Sets characters that are not valid for input. If
138 * <code>invalidCharacters</code> is non-null then no characters contained
139 * in it will be allowed to be input.
141 * @param invalidCharacters the String specifying invalid characters.
143 public void setInvalidCharacters (String invalidCharacters)
145 this.invalidChars = invalidCharacters;
149 * Returns a String containing the characters that are valid for input
150 * for this MaskFormatter.
151 * @return a String containing the valid characters.
153 public String getValidCharacters()
159 * Sets characters that are valid for input. If
160 * <code>validCharacters</code> is non-null then no characters that are
161 * not contained in it will be allowed to be input.
163 * @param validCharacters the String specifying valid characters.
165 public void setValidCharacters (String validCharacters)
167 this.validChars = validCharacters;
171 * Returns the place holder String that is used in place of missing
172 * characters when the value doesn't completely fill in the spaces
174 * @return the place holder String.
176 public String getPlaceholder()
182 * Sets the string to use if the value does not completely fill in the mask.
183 * If this is null, the place holder character will be used instead.
184 * @param placeholder the String to use if the value doesn't completely
187 public void setPlaceholder (String placeholder)
189 this.placeHolder = placeholder;
193 * Returns the character used in place of missing characters when the
194 * value doesn't completely fill the mask.
195 * @return the place holder character
197 public char getPlaceholderCharacter()
199 return placeHolderChar;
203 * Sets the char to use if the value does not completely fill in the mask.
204 * This is only used if the place holder String has not been set or does
205 * not completely fill in the mask.
206 * @param placeholder the char to use if the value doesn't completely
209 public void setPlaceholderCharacter (char placeholder)
211 this.placeHolderChar = placeholder;
215 * Returns true if stringToValue should return the literal
216 * characters in the mask.
217 * @return true if stringToValue should return the literal
218 * characters in the mask
220 public boolean getValueContainsLiteralCharacters()
222 return valueContainsLiteralCharacters;
226 * Determines whether stringToValue will return literal characters or not.
227 * @param containsLiteralChars if true, stringToValue will return the
228 * literal characters in the mask, otherwise it will not.
230 public void setValueContainsLiteralCharacters (boolean containsLiteralChars)
232 this.valueContainsLiteralCharacters = containsLiteralChars;
236 * Sets the mask for this MaskFormatter.
237 * @specnote doesn't actually throw a ParseException even though it is
239 * @param mask the new mask for this MaskFormatter
240 * @throws ParseException if <code>mask</code> is not valid.
242 public void setMask (String mask) throws ParseException
246 // Update the cached maskLength.
247 int end = mask.length() - 1;
249 for (int i = 0; i <= end; i++)
251 // Handle escape characters properly - they don't add to the maskLength
252 // but 2 escape characters in a row is really one escape character and
253 // one literal single quote, so that does add 1 to the maskLength.
254 if (mask.charAt(i) == '\'')
256 // Escape characters at the end of the mask don't do anything.
267 * Installs this MaskFormatter on the JFormattedTextField.
268 * Invokes valueToString to convert the current value from the
269 * JFormattedTextField to a String, then installs the Actions from
270 * getActions, the DocumentFilter from getDocumentFilter, and the
271 * NavigationFilter from getNavigationFilter.
273 * If valueToString throws a ParseException, this method sets the text
274 * to an empty String and marks the JFormattedTextField as invalid.
276 public void install (JFormattedTextField ftf)
283 valueToString(ftf.getValue());
285 catch (ParseException pe)
287 // Set the text to an empty String and mark the JFormattedTextField
296 * Parses the text using the mask, valid characters, and invalid characters
297 * to determine the appropriate Object to return. This strips the literal
298 * characters if necessary and invokes super.stringToValue. If the paramter
299 * is invalid for the current mask and valid/invalid character sets this
300 * method will throw a ParseException.
302 * @param value the String to parse
303 * @throws ParseException if value doesn't match the mask and valid/invalid
306 public Object stringToValue (String value) throws ParseException
308 return super.stringToValue(convertStringToValue(value));
311 private String convertStringToValue(String value)
312 throws ParseException
314 StringBuffer result = new StringBuffer();
316 boolean isPlaceHolder;
318 int length = mask.length();
319 for (int i = 0, j = 0; j < length; j++)
321 char maskChar = mask.charAt(j);
323 if (i < value.length())
325 isPlaceHolder = false;
326 valueChar = value.charAt(i);
327 if (maskChar != ESCAPE_CHAR && maskChar != valueChar)
329 if (invalidChars != null
330 && invalidChars.indexOf(valueChar) != -1)
331 throw new ParseException("Invalid character: " + valueChar, i);
332 if (validChars != null
333 && validChars.indexOf(valueChar) == -1)
334 throw new ParseException("Invalid character: " + valueChar, i);
337 else if (placeHolder != null && i < placeHolder.length())
339 isPlaceHolder = true;
340 valueChar = placeHolder.charAt(i);
344 isPlaceHolder = true;
345 valueChar = placeHolderChar;
348 // This switch block on the mask character checks that the character
349 // within <code>value</code> at that point is valid according to the
350 // mask and also converts to upper/lowercase as needed.
354 if (! Character.isDigit(valueChar))
355 throw new ParseException("Number expected: " + valueChar, i);
356 result.append(valueChar);
360 if (! Character.isLetter(valueChar))
361 throw new ParseException("Letter expected", i);
362 result.append(Character.toUpperCase(valueChar));
366 if (! Character.isLetter(valueChar))
367 throw new ParseException("Letter expected", i);
368 result.append(Character.toLowerCase(valueChar));
372 if (! Character.isLetterOrDigit(valueChar))
373 throw new ParseException("Letter or number expected", i);
374 result.append(valueChar);
378 if (! Character.isLetter(valueChar))
379 throw new ParseException("Letter expected", i);
380 result.append(valueChar);
384 if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
385 throw new ParseException("Hexadecimal character expected", i);
386 result.append(valueChar);
390 result.append(valueChar);
394 // Escape character, check the next character to make sure that
395 // the literals match
399 maskChar = mask.charAt(j);
400 if (! isPlaceHolder && getValueContainsLiteralCharacters()
401 && valueChar != maskChar)
402 throw new ParseException ("Invalid character: "+ valueChar, i);
403 if (getValueContainsLiteralCharacters())
405 result.append(maskChar);
409 else if (! isPlaceHolder)
410 throw new ParseException("Bad match at trailing escape: ", i);
413 if (! isPlaceHolder && getValueContainsLiteralCharacters()
414 && valueChar != maskChar)
415 throw new ParseException ("Invalid character: "+ valueChar, i);
416 if (getValueContainsLiteralCharacters())
418 result.append(maskChar);
423 return result.toString();
427 * Returns a String representation of the Object value based on the mask.
429 * @param value the value to convert
430 * @throws ParseException if value is invalid for this mask and valid/invalid
433 public String valueToString(Object value) throws ParseException
435 String string = value != null ? value.toString() : "";
436 return convertValueToString(string);
440 * This method takes in a String and runs it through the mask to make
441 * sure that it is valid. If <code>convert</code> is true, it also
442 * converts letters to upper/lowercase as required by the mask.
443 * @param value the String to convert
444 * @return the converted String
445 * @throws ParseException if the given String isn't valid for the mask
447 private String convertValueToString(String value)
448 throws ParseException
450 StringBuffer result = new StringBuffer();
452 boolean isPlaceHolder;
454 int length = mask.length();
455 for (int i = 0, j = 0; j < length; j++)
457 char maskChar = mask.charAt(j);
458 if (i < value.length())
460 isPlaceHolder = false;
461 valueChar = value.charAt(i);
462 if (maskChar != ESCAPE_CHAR && valueChar != maskChar)
464 if (invalidChars != null
465 && invalidChars.indexOf(valueChar) != -1)
466 throw new ParseException("Invalid character: " + valueChar,
468 if (validChars != null && validChars.indexOf(valueChar) == -1)
469 throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar,
473 else if (placeHolder != null && i < placeHolder.length())
475 isPlaceHolder = true;
476 valueChar = placeHolder.charAt(i);
480 isPlaceHolder = true;
481 valueChar = placeHolderChar;
484 // This switch block on the mask character checks that the character
485 // within <code>value</code> at that point is valid according to the
486 // mask and also converts to upper/lowercase as needed.
490 if ( ! isPlaceHolder && ! Character.isDigit(valueChar))
491 throw new ParseException("Number expected: " + valueChar, i);
492 result.append(valueChar);
496 if (! Character.isLetter(valueChar))
497 throw new ParseException("Letter expected", i);
498 result.append(Character.toUpperCase(valueChar));
502 if (! Character.isLetter(valueChar))
503 throw new ParseException("Letter expected", i);
504 result.append(Character.toLowerCase(valueChar));
508 if (! Character.isLetterOrDigit(valueChar))
509 throw new ParseException("Letter or number expected", i);
510 result.append(valueChar);
514 if (! Character.isLetter(valueChar))
515 throw new ParseException("Letter expected", i);
516 result.append(valueChar);
520 if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
521 throw new ParseException("Hexadecimal character expected", i);
522 result.append(valueChar);
526 result.append(valueChar);
530 // Escape character, check the next character to make sure that
531 // the literals match
535 maskChar = mask.charAt(j);
536 if (! isPlaceHolder && getValueContainsLiteralCharacters()
537 && valueChar != maskChar)
538 throw new ParseException ("Invalid character: "+ valueChar, i);
539 if (getValueContainsLiteralCharacters())
541 result.append(maskChar);
545 if (! isPlaceHolder && getValueContainsLiteralCharacters()
546 && valueChar != maskChar)
547 throw new ParseException ("Invalid character: "+ valueChar, i);
548 if (getValueContainsLiteralCharacters())
550 result.append(maskChar);
553 return result.toString();