GNU Classpath (0.20) | |
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 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 = true; 220: overwriteMode = true; 221: allowsInvalid = true; 222: valueClass = Object.class; 223: } 224: 225: /** 226: * Installs the formatter on the specified {@link JFormattedTextField}. 227: * 228: * This method does the following things: 229: * <ul> 230: * <li>Display the value of #valueToString in the 231: * <code>JFormattedTextField</code></li> 232: * <li>Install the Actions from #getActions on the <code>JTextField</code> 233: * </li> 234: * <li>Install the DocumentFilter returned by #getDocumentFilter</li> 235: * <li>Install the NavigationFilter returned by #getNavigationFilter</li> 236: * </ul> 237: * 238: * This method is typically not overridden by subclasses. Instead override 239: * one of the mentioned methods in order to customize behaviour. 240: * 241: * @param ftf the {@link JFormattedTextField} in which this formatter 242: * is installed 243: */ 244: public void install(JFormattedTextField ftf) 245: { 246: super.install(ftf); 247: } 248: 249: /** 250: * Returns <code>true</code> if the value should be committed after 251: * each valid modification of the input field, <code>false</code> if 252: * it should never be committed by this formatter. 253: * 254: * @return the state of the <code>commitsOnValidEdit</code> property 255: * 256: * @see #setCommitsOnValidEdit 257: */ 258: public boolean getCommitsOnValidEdit() 259: { 260: return commitsOnValidEdit; 261: } 262: 263: /** 264: * Sets the value of the <code>commitsOnValidEdit</code> property. 265: * 266: * @param commitsOnValidEdit the new state of the 267: * <code>commitsOnValidEdit</code> property 268: * 269: * @see #getCommitsOnValidEdit 270: */ 271: public void setCommitsOnValidEdit(boolean commitsOnValidEdit) 272: { 273: this.commitsOnValidEdit = commitsOnValidEdit; 274: } 275: 276: /** 277: * Returns the value of the <code>overwriteMode</code> property. 278: * If that is set to <code>true</code> then newly inserted characters 279: * overwrite existing values, otherwise the characters are inserted like 280: * normal. The default is <code>true</code>. 281: * 282: * @return the value of the <code>overwriteMode</code> property 283: */ 284: public boolean getOverwriteMode() 285: { 286: return overwriteMode; 287: } 288: 289: /** 290: * Sets the value of the <code>overwriteMode</code> property. 291: * 292: * If that is set to <code>true</code> then newly inserted characters 293: * overwrite existing values, otherwise the characters are inserted like 294: * normal. The default is <code>true</code>. 295: * 296: * @param overwriteMode the new value for the <code>overwriteMode</code> 297: * property 298: */ 299: public void setOverwriteMode(boolean overwriteMode) 300: { 301: this.overwriteMode = overwriteMode; 302: } 303: 304: /** 305: * Returns whether or not invalid edits are allowed or not. If invalid 306: * edits are allowed, the JFormattedTextField may temporarily contain invalid 307: * characters. 308: * 309: * @return the value of the allowsInvalid property 310: */ 311: public boolean getAllowsInvalid() 312: { 313: return allowsInvalid; 314: } 315: 316: /** 317: * Sets the value of the <code>allowsInvalid</code> property. 318: * 319: * @param allowsInvalid the new value for the property 320: * 321: * @see #getAllowsInvalid() 322: */ 323: public void setAllowsInvalid(boolean allowsInvalid) 324: { 325: this.allowsInvalid = allowsInvalid; 326: } 327: 328: /** 329: * Returns the class that is used for values. When Strings are converted 330: * back to values, this class is used to create new value objects. 331: * 332: * @return the class that is used for values 333: */ 334: public Class getValueClass() 335: { 336: return valueClass; 337: } 338: 339: /** 340: * Sets the class that is used for values. 341: * 342: * @param valueClass the class that is used for values 343: * 344: * @see #getValueClass() 345: */ 346: public void setValueClass(Class valueClass) 347: { 348: this.valueClass = valueClass; 349: } 350: 351: /** 352: * Converts a String (from the JFormattedTextField input) to a value. 353: * In order to achieve this, the formatter tries to instantiate an object 354: * of the class returned by #getValueClass() using a single argument 355: * constructor that takes a String argument. If such a constructor cannot 356: * be found, the String itself is returned. 357: * 358: * @param string the string to convert 359: * 360: * @return the value for the string 361: * 362: * @throws ParseException if the string cannot be converted into 363: * a value object (e.g. invalid input) 364: */ 365: public Object stringToValue(String string) 366: throws ParseException 367: { 368: Object value = string; 369: Class valueClass = getValueClass(); 370: if (valueClass == null) 371: valueClass = getFormattedTextField().getValue().getClass(); 372: if (valueClass != null) 373: try 374: { 375: Constructor constr = valueClass.getConstructor 376: (new Class[]{String.class}); 377: value = constr.newInstance(new Object[]{ string }); 378: } 379: catch (NoSuchMethodException ex) 380: { 381: // leave value as string 382: } 383: catch (Exception ex) 384: { 385: throw new ParseException(string, 0); 386: } 387: return value; 388: } 389: 390: /** 391: * Converts a value object into a String. This is done by invoking the 392: * {@link Object#toString()} method on the value. 393: * 394: * @param value the value to be converted 395: * 396: * @return the string representation of the value 397: * 398: * @throws ParseException if the value cannot be converted 399: */ 400: public String valueToString(Object value) 401: throws ParseException 402: { 403: if (value == null) 404: return ""; 405: return value.toString(); 406: } 407: 408: /** 409: * Creates and returns a clone of this DefaultFormatter. 410: * 411: * @return a clone of this object 412: * 413: * @throws CloneNotSupportedException not thrown here 414: */ 415: public Object clone() 416: throws CloneNotSupportedException 417: { 418: return super.clone(); 419: } 420: 421: /** 422: * Returns the DocumentFilter that is used to restrict input. 423: * 424: * @return the DocumentFilter that is used to restrict input 425: */ 426: protected DocumentFilter getDocumentFilter() 427: { 428: return new FormatterDocumentFilter(); 429: } 430: }
GNU Classpath (0.20) |