OSDN Git Service

bf7c02a004df3c271404b77a1fe79f494cfef04b
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / DefaultFormatter.java
1 /* DefaultFormatter.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 package javax.swing.text;
39
40 import java.io.Serializable;
41 import java.lang.reflect.Constructor;
42 import java.text.ParseException;
43
44 import javax.swing.JFormattedTextField;
45
46 /**
47  * The <code>DefaultFormatter</code> is a concrete formatter for use in
48  * {@link JFormattedTextField}s.
49  *
50  * It can format arbitrary values by invoking
51  * their {@link Object#toString} method.
52  *
53  * In order to convert a String back to
54  * a value, the value class must provide a single argument constructor that
55  * takes a String object as argument value. If no such constructor is found,
56  * the String itself is passed back by #stringToValue.
57  *  
58  * @author Roman Kennke (roman@kennke.org)
59  */
60 public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
61   implements Cloneable, Serializable
62 {
63
64   /**
65    * A {@link DocumentFilter} that intercepts modification of the
66    * JFormattedTextField's Document and commits the value depending
67    * on the value of the <code>commitsOnValidEdit</code> property.
68    *
69    */
70   // FIXME: Handle allowsInvalid and overwriteMode properties
71   private class FormatterDocumentFilter
72     extends DocumentFilter
73   {
74     /**
75      * Invoked when text is removed from a text component.
76      *
77      * @param bypass the FilterBypass to use to mutate the document
78      * @param offset the start position of the modification
79      * @param length the length of the removed text
80      *
81      * @throws BadLocationException if offset or lenght are invalid in
82      *     the Document
83      */
84     public void remove(DocumentFilter.FilterBypass bypass, int offset,
85                         int length)
86       throws BadLocationException
87     {
88       super.remove(bypass, offset, length);
89       checkValidInput();
90       commitIfAllowed();
91     }
92     
93     /**
94      * Invoked when text is inserted into a text component.
95      *
96      * @param bypass the FilterBypass to use to mutate the document
97      * @param offset the start position of the modification
98      * @param text the inserted text
99      * @param attributes the attributes of the inserted text
100      *
101      * @throws BadLocationException if offset or lenght are invalid in
102      *     the Document
103      */
104     public void insertString(DocumentFilter.FilterBypass bypass, int offset,
105                               String text, AttributeSet attributes)
106       throws BadLocationException
107     {
108       if (overwriteMode == true)
109         replace(bypass, offset, text.length(), text, attributes);
110       else
111         super.insertString(bypass, offset, text, attributes);
112       checkValidInput();
113       commitIfAllowed();
114     }
115
116     /**
117      * Invoked when text is replaced in a text component.
118      * 
119      * @param bypass the FilterBypass to use to mutate the document
120      * @param offset the start position of the modification
121      * @param length the length of the removed text
122      * @param text the inserted text
123      * @param attributes the attributes of the inserted text
124      *
125      * @throws BadLocationException if offset or lenght are invalid in
126      *     the Document
127      */
128     public void replace(DocumentFilter.FilterBypass bypass, int offset,
129                          int length, String text, AttributeSet attributes)
130       throws BadLocationException
131     {
132       super.replace(bypass, offset, length, text, attributes);
133       checkValidInput();
134       commitIfAllowed();
135     }
136
137     /**
138      * Commits the value to the JTextTextField if the property
139      * <code>commitsOnValidEdit</code> is set to <code>true</code>.
140      */
141     private void commitIfAllowed()
142     {
143       if (commitsOnValidEdit == true)
144         try
145           {
146             getFormattedTextField().commitEdit();
147           }
148         catch (ParseException ex)
149           {
150             // ignore invalid edits
151           }
152     }
153
154     /**
155      * Checks if the value in the input field is valid. If the
156      * property allowsInvalid is set to <code>false</code>, then
157      * the string in the input field is not allowed to be entered.
158      */
159     private void checkValidInput()
160     {
161       JFormattedTextField ftf = getFormattedTextField();
162       try
163         {
164           Object newval = stringToValue(ftf.getText());
165         }
166       catch (ParseException ex)
167         {
168           if (!allowsInvalid)
169             {
170               // roll back the input if invalid edits are not allowed
171               try
172                 {
173                   ftf.setText(valueToString(ftf.getValue()));
174                 }
175               catch (ParseException pe)
176                 {
177                   // if that happens, something serious must be wrong
178                   AssertionError ae;
179                   ae = new AssertionError("values must be parseable");
180                   ae.initCause(pe);
181                   throw ae;
182                 }
183             }
184         }
185     }
186   }
187
188   /** The serialization UID (compatible with JDK1.5). */
189   private static final long serialVersionUID = -355018354457785329L;
190
191   /**
192    * Indicates if the value should be committed after every
193    * valid modification of the Document.
194    */
195   boolean commitsOnValidEdit;
196
197   /**
198    * If <code>true</code> newly inserted characters overwrite existing
199    * values, otherwise insertion is done the normal way.
200    */
201   boolean overwriteMode;
202
203   /**
204    * If <code>true</code> invalid edits are allowed for a limited
205    * time.
206    */
207   boolean allowsInvalid;
208
209   /**
210    * The class that is used for values.
211    */
212   Class valueClass;
213
214   /**
215    * Creates a new instance of <code>DefaultFormatter</code>.
216    */
217   public DefaultFormatter()
218   {
219     commitsOnValidEdit = false;
220     overwriteMode = true;
221     allowsInvalid = true;
222   }
223
224   /**
225    * Installs the formatter on the specified {@link JFormattedTextField}.
226    *
227    * This method does the following things:
228    * <ul>
229    * <li>Display the value of #valueToString in the
230    *  <code>JFormattedTextField</code></li>
231    * <li>Install the Actions from #getActions on the <code>JTextField</code>
232    * </li>
233    * <li>Install the DocumentFilter returned by #getDocumentFilter</li>
234    * <li>Install the NavigationFilter returned by #getNavigationFilter</li>
235    * </ul>
236    *
237    * This method is typically not overridden by subclasses. Instead override
238    * one of the mentioned methods in order to customize behaviour.
239    *
240    * @param ftf the {@link JFormattedTextField} in which this formatter
241    *     is installed 
242    */
243   public void install(JFormattedTextField ftf)
244   {
245     super.install(ftf);
246   }
247
248   /**
249    * Returns <code>true</code> if the value should be committed after
250    * each valid modification of the input field, <code>false</code> if
251    * it should never be committed by this formatter.
252    *
253    * @return the state of the <code>commitsOnValidEdit</code> property
254    *
255    * @see #setCommitsOnValidEdit
256    */
257   public boolean getCommitsOnValidEdit()
258   {
259     return commitsOnValidEdit;
260   }
261
262   /**
263    * Sets the value of the <code>commitsOnValidEdit</code> property.
264    *
265    * @param commitsOnValidEdit the new state of the
266    *     <code>commitsOnValidEdit</code> property
267    *
268    * @see #getCommitsOnValidEdit
269    */
270   public void setCommitsOnValidEdit(boolean commitsOnValidEdit)
271   {
272     this.commitsOnValidEdit = commitsOnValidEdit;
273   }
274
275   /**
276    * Returns the value of the <code>overwriteMode</code> property.
277    * If that is set to <code>true</code> then newly inserted characters
278    * overwrite existing values, otherwise the characters are inserted like
279    * normal. The default is <code>true</code>.
280    *
281    * @return the value of the <code>overwriteMode</code> property
282    */
283   public boolean getOverwriteMode()
284   {
285     return overwriteMode;
286   }
287
288   /**
289    * Sets the value of the <code>overwriteMode</code> property.
290    * 
291    * If that is set to <code>true</code> then newly inserted characters
292    * overwrite existing values, otherwise the characters are inserted like
293    * normal. The default is <code>true</code>.
294    *
295    * @param overwriteMode the new value for the <code>overwriteMode</code>
296    *     property
297    */
298   public void setOverwriteMode(boolean overwriteMode)
299   {
300     this.overwriteMode = overwriteMode;
301   }
302
303   /**
304    * Returns whether or not invalid edits are allowed or not. If invalid
305    * edits are allowed, the JFormattedTextField may temporarily contain invalid
306    * characters.
307    *
308    * @return the value of the allowsInvalid property
309    */
310   public boolean getAllowsInvalid()
311   {
312     return allowsInvalid;
313   }
314
315   /**
316    * Sets the value of the <code>allowsInvalid</code> property.
317    *
318    * @param allowsInvalid the new value for the property
319    *
320    * @see #getAllowsInvalid()
321    */
322   public void setAllowsInvalid(boolean allowsInvalid)
323   {
324     this.allowsInvalid = allowsInvalid;
325   }
326
327   /**
328    * Returns the class that is used for values. When Strings are converted
329    * back to values, this class is used to create new value objects.
330    *
331    * @return the class that is used for values
332    */
333   public Class<?> getValueClass()
334   {
335     return valueClass;
336   }
337
338   /**
339    * Sets the class that is used for values.
340    *
341    * @param valueClass the class that is used for values
342    *
343    * @see #getValueClass()
344    */
345   public void setValueClass(Class<?> valueClass)
346   {
347     this.valueClass = valueClass;
348   }
349
350   /**
351    * Converts a String (from the JFormattedTextField input) to a value.
352    * In order to achieve this, the formatter tries to instantiate an object
353    * of the class returned by #getValueClass() using a single argument
354    * constructor that takes a String argument. If such a constructor cannot
355    * be found, the String itself is returned.
356    *
357    * @param string the string to convert
358    *
359    * @return the value for the string
360    *
361    * @throws ParseException if the string cannot be converted into
362    *     a value object (e.g. invalid input)
363    */
364   public Object stringToValue(String string)
365     throws ParseException
366   {
367     Object value = string;
368     Class valueClass = getValueClass();
369     if (valueClass == null)
370       {
371         JFormattedTextField jft = getFormattedTextField();
372         if (jft != null)
373           valueClass = jft.getValue().getClass();
374       }
375     if (valueClass != null)
376       try
377         {
378           Constructor constr = valueClass.getConstructor
379                                              (new Class[]{String.class});
380           value = constr.newInstance(new Object[]{ string });
381         }
382       catch (NoSuchMethodException ex)
383         {
384           // leave value as string
385         }
386       catch (Exception ex)
387         {
388           throw new ParseException(string, 0);
389         }
390     return value;
391   }
392
393   /**
394    * Converts a value object into a String. This is done by invoking the
395    * {@link Object#toString()} method on the value.
396    *
397    * @param value the value to be converted
398    *
399    * @return the string representation of the value
400    *
401    * @throws ParseException if the value cannot be converted
402    */
403   public String valueToString(Object value)
404     throws ParseException
405   {
406     if (value == null)
407       return "";
408     return value.toString();
409   }
410
411   /**
412    * Creates and returns a clone of this DefaultFormatter.
413    *
414    * @return a clone of this object
415    *
416    * @throws CloneNotSupportedException not thrown here
417    */
418   public Object clone()
419     throws CloneNotSupportedException
420   {
421     return super.clone();
422   }
423
424   /**
425    * Returns the DocumentFilter that is used to restrict input.
426    *
427    * @return the DocumentFilter that is used to restrict input
428    */
429   protected DocumentFilter getDocumentFilter()
430   {
431     return new FormatterDocumentFilter();
432   }
433 }