OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / MaskFormatter.java
1 /* MaskFormatter.java -- 
2    Copyright (C) 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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)
9 any later version.
10
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.
15
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
19 02110-1301 USA.
20
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
24 combination.
25
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. */
37
38
39 package javax.swing.text;
40
41 import java.text.ParseException;
42
43 import javax.swing.JFormattedTextField;
44
45 /**
46  * @author Anthony Balkissoon abalkiss at redhat dot com
47  *
48  */
49 public class MaskFormatter extends DefaultFormatter
50 {
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';
60   
61   /** The mask for this MaskFormatter **/
62   private String mask;
63   
64   /** 
65    * A String made up of the characters that are not valid for input for 
66    * this MaskFormatter. 
67    */
68   private String invalidChars;
69   
70   /** 
71    * A String made up of the characters that are valid for input for 
72    * this MaskFormatter. 
73    */
74   private String validChars;
75   
76   /** A String used in place of missing chracters if the value does not 
77    * completely fill in the spaces in the mask.
78    */
79   private String placeHolder;
80   
81   /** A character used in place of missing characters if the value does 
82    * not completely fill in the spaces in the mask.
83    */
84   private char placeHolderChar = ' ';
85   
86   /**
87    * Whether or not stringToValue should return literal characters in the mask.
88    */
89   private boolean valueContainsLiteralCharacters = true;
90   
91   /** A String used for easy access to valid HEX characters **/
92   private static String hexString = "0123456789abcdefABCDEF";
93   
94   /** An int to hold the length of the mask, accounting for escaped characters **/
95   int maskLength = 0;
96   
97   public MaskFormatter ()
98   {
99     // Override super's default behaviour, in MaskFormatter the default
100     // is not to allow invalid values
101     setAllowsInvalid(false);
102   }
103   
104   /**
105    * Creates a MaskFormatter with the specified mask.
106    * @specnote doesn't actually throw a ParseException although it 
107    * is declared to do so
108    * @param mask
109    * @throws java.text.ParseException
110    */
111   public MaskFormatter (String mask) throws java.text.ParseException
112   {
113     this();
114     setMask (mask);
115   }
116   
117   /**
118    * Returns the mask used in this MaskFormatter.
119    * @return the mask used in this MaskFormatter.
120    */
121   public String getMask()
122   {
123     return mask;
124   }
125   
126   /**
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.
130    */
131   public String getInvalidCharacters()
132   {
133     return invalidChars;
134   }
135   
136   /**
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.
140    * 
141    * @param invalidCharacters the String specifying invalid characters.
142    */
143   public void setInvalidCharacters (String invalidCharacters)
144   {
145     this.invalidChars = invalidCharacters;
146   }
147   
148   /**
149    * Returns a String containing the characters that are valid for input
150    * for this MaskFormatter.
151    * @return a String containing the valid characters.
152    */
153   public String getValidCharacters()
154   {
155     return validChars;
156   }
157   
158   /**
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.
162    * 
163    * @param validCharacters the String specifying valid characters.
164    */
165   public void setValidCharacters (String validCharacters)
166   {
167     this.validChars = validCharacters;
168   }
169
170   /**
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
173    * in the mask.
174    * @return the place holder String.
175    */
176   public String getPlaceholder()
177   {
178     return placeHolder;
179   }
180   
181   /**
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 
185    * fill in the mask.
186    */
187   public void setPlaceholder (String placeholder)
188   {
189     this.placeHolder = placeholder;
190   }
191   
192   /**
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
196    */
197   public char getPlaceholderCharacter()
198   {
199     return placeHolderChar;
200   }
201   
202   /**
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 
207    * fill in the mask.
208    */
209   public void setPlaceholderCharacter (char placeholder)
210   {
211     this.placeHolderChar = placeholder;
212   }
213   
214   /**
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
219    */
220   public boolean getValueContainsLiteralCharacters()
221   {
222     return valueContainsLiteralCharacters;
223   }
224   
225   /**
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.
229    */
230   public void setValueContainsLiteralCharacters (boolean containsLiteralChars)
231   {
232     this.valueContainsLiteralCharacters = containsLiteralChars;
233   }
234   
235   /**
236    * Sets the mask for this MaskFormatter.  
237    * @specnote doesn't actually throw a ParseException even though it is
238    * declared to do so
239    * @param mask the new mask for this MaskFormatter
240    * @throws ParseException if <code>mask</code> is not valid.
241    */
242   public void setMask (String mask) throws ParseException
243   {
244     this.mask = mask;
245
246     // Update the cached maskLength.
247     int end = mask.length() - 1;
248     maskLength = 0;    
249     for (int i = 0; i <= end; i++)
250       {
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) == '\'')
255           {            
256             // Escape characters at the end of the mask don't do anything.
257             if (i != end)
258               maskLength++;
259             i++;
260           }
261         else
262           maskLength++;
263       }
264   }
265   
266   /**
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.
272    * 
273    * If valueToString throws a ParseException, this method sets the text
274    * to an empty String and marks the JFormattedTextField as invalid.
275    */
276   public void install (JFormattedTextField ftf)
277   {
278     super.install(ftf);
279     if (ftf != null)
280       {
281         try
282         {
283           valueToString(ftf.getValue());
284         }
285         catch (ParseException pe)
286         {
287           // Set the text to an empty String and mark the JFormattedTextField
288           // as invalid.
289           ftf.setText("");
290           setEditValid(false);
291         }
292       }
293   }
294   
295   /**
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.
301    * 
302    * @param value the String to parse
303    * @throws ParseException if value doesn't match the mask and valid/invalid
304    * character sets
305    */
306   public Object stringToValue (String value) throws ParseException
307   {
308     return super.stringToValue(convertStringToValue(value));
309   }
310   
311   private String convertStringToValue(String value)
312     throws ParseException
313   {
314     StringBuffer result = new StringBuffer();
315     char valueChar;
316     boolean isPlaceHolder;
317
318     int length = mask.length();
319     for (int i = 0, j = 0; j < length; j++)
320       {
321         char maskChar = mask.charAt(j);
322
323         if (i < value.length())
324           {
325             isPlaceHolder = false;
326             valueChar = value.charAt(i);
327             if (maskChar != ESCAPE_CHAR && maskChar != valueChar)
328               {
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);
335               }
336           }
337         else if (placeHolder != null && i < placeHolder.length())
338           {
339             isPlaceHolder = true;
340             valueChar = placeHolder.charAt(i);
341           }
342         else
343           {
344             isPlaceHolder = true;
345             valueChar = placeHolderChar;
346           }
347
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.
351         switch (maskChar)
352           {
353           case NUM_CHAR:
354             if (! Character.isDigit(valueChar))
355               throw new ParseException("Number expected: " + valueChar, i);
356             result.append(valueChar);
357             i++;
358             break;
359           case UPPERCASE_CHAR:
360             if (! Character.isLetter(valueChar))
361               throw new ParseException("Letter expected", i);
362             result.append(Character.toUpperCase(valueChar));
363             i++;
364             break;
365           case LOWERCASE_CHAR:
366             if (! Character.isLetter(valueChar))
367               throw new ParseException("Letter expected", i);
368             result.append(Character.toLowerCase(valueChar));
369             i++;
370             break;
371           case ALPHANUM_CHAR:
372             if (! Character.isLetterOrDigit(valueChar))
373               throw new ParseException("Letter or number expected", i);
374             result.append(valueChar);
375             i++;
376             break;
377           case LETTER_CHAR:
378             if (! Character.isLetter(valueChar))
379               throw new ParseException("Letter expected", i);
380             result.append(valueChar);
381             i++;
382             break;
383           case HEX_CHAR:
384             if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
385               throw new ParseException("Hexadecimal character expected", i);
386             result.append(valueChar);
387             i++;
388             break;
389           case ANYTHING_CHAR:
390             result.append(valueChar);
391             i++;
392             break;
393           case ESCAPE_CHAR:
394             // Escape character, check the next character to make sure that 
395             // the literals match
396             j++;
397             if (j < length)
398               {
399                 maskChar = mask.charAt(j);
400                 if (! isPlaceHolder && getValueContainsLiteralCharacters()
401                     && valueChar != maskChar)
402                   throw new ParseException ("Invalid character: "+ valueChar, i);
403                 if (getValueContainsLiteralCharacters())
404                   {
405                     result.append(maskChar);
406                   }
407                 i++;
408               }
409             else if (! isPlaceHolder)
410               throw new ParseException("Bad match at trailing escape: ", i);
411             break;
412           default:
413             if (! isPlaceHolder && getValueContainsLiteralCharacters()
414                 && valueChar != maskChar)
415               throw new ParseException ("Invalid character: "+ valueChar, i);
416             if (getValueContainsLiteralCharacters())
417               {
418                 result.append(maskChar);
419               }
420             i++;
421           }
422       }
423     return result.toString();
424   }
425
426   /**
427    * Returns a String representation of the Object value based on the mask.
428    * 
429    * @param value the value to convert
430    * @throws ParseException if value is invalid for this mask and valid/invalid
431    * character sets
432    */
433   public String valueToString(Object value) throws ParseException
434   {
435     String string = value != null ? value.toString() : "";
436     return convertValueToString(string);
437   }
438   
439   /**
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
446    */
447   private String convertValueToString(String value)
448     throws ParseException
449   {
450     StringBuffer result = new StringBuffer();
451     char valueChar;
452     boolean isPlaceHolder;
453
454     int length = mask.length();
455     for (int i = 0, j = 0; j < length; j++)
456       {
457         char maskChar = mask.charAt(j);
458         if (i < value.length())
459           {
460             isPlaceHolder = false;
461             valueChar = value.charAt(i);
462             if (maskChar != ESCAPE_CHAR && valueChar != maskChar)
463               {
464                 if (invalidChars != null
465                     && invalidChars.indexOf(valueChar) != -1)
466                   throw new ParseException("Invalid character: " + valueChar,
467                                            i);
468                 if (validChars != null && validChars.indexOf(valueChar) == -1)
469                   throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar,
470                                            i);
471               }
472           }
473         else if (placeHolder != null && i < placeHolder.length())
474           {
475             isPlaceHolder = true;
476             valueChar = placeHolder.charAt(i);
477           }
478         else
479           {
480             isPlaceHolder = true;
481             valueChar = placeHolderChar;
482           }
483
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.
487         switch (maskChar)
488           {
489           case NUM_CHAR:
490             if ( ! isPlaceHolder && ! Character.isDigit(valueChar))
491               throw new ParseException("Number expected: " + valueChar, i);
492             result.append(valueChar);
493             i++;
494             break;
495           case UPPERCASE_CHAR:
496             if (! Character.isLetter(valueChar))
497               throw new ParseException("Letter expected", i);
498             result.append(Character.toUpperCase(valueChar));
499             i++;
500             break;
501           case LOWERCASE_CHAR:
502             if (! Character.isLetter(valueChar))
503               throw new ParseException("Letter expected", i);
504             result.append(Character.toLowerCase(valueChar));
505             i++;
506             break;
507           case ALPHANUM_CHAR:
508             if (! Character.isLetterOrDigit(valueChar))
509               throw new ParseException("Letter or number expected", i);
510             result.append(valueChar);
511             i++;
512             break;
513           case LETTER_CHAR:
514             if (! Character.isLetter(valueChar))
515               throw new ParseException("Letter expected", i);
516             result.append(valueChar);
517             i++;
518             break;
519           case HEX_CHAR:
520             if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
521               throw new ParseException("Hexadecimal character expected", i);
522             result.append(valueChar);
523             i++;
524             break;
525           case ANYTHING_CHAR:
526             result.append(valueChar);
527             i++;
528             break;
529           case ESCAPE_CHAR:
530             // Escape character, check the next character to make sure that 
531             // the literals match
532             j++;
533             if (j < length)
534               {
535                 maskChar = mask.charAt(j);
536                 if (! isPlaceHolder && getValueContainsLiteralCharacters()
537                     && valueChar != maskChar)
538                   throw new ParseException ("Invalid character: "+ valueChar, i);
539                 if (getValueContainsLiteralCharacters())
540                   i++;
541                 result.append(maskChar);
542               }
543             break;
544           default:
545             if (! isPlaceHolder && getValueContainsLiteralCharacters()
546                 && valueChar != maskChar)
547               throw new ParseException ("Invalid character: "+ valueChar, i);
548             if (getValueContainsLiteralCharacters())
549               i++;
550             result.append(maskChar);
551           }
552       }
553     return result.toString();
554   }
555
556 }