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