Frames | No Frames |
1: /* InternationalFormatter.java -- 2: Copyright (C) 2005, 2006 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.text.AttributedCharacterIterator; 41: import java.text.Format; 42: import java.text.ParseException; 43: import java.util.Iterator; 44: import java.util.Map; 45: import java.util.Set; 46: 47: import javax.swing.Action; 48: import javax.swing.JFormattedTextField; 49: 50: /** 51: * This extends {@link DefaultFormatter} so that the value to string 52: * conversion is done via a {@link Format} object. This allows 53: * various additional formats to be handled by JFormattedField. 54: * 55: * @author Roman Kennke (roman@kennke.org) 56: */ 57: public class InternationalFormatter 58: extends DefaultFormatter 59: { 60: /** The serialization UID (compatible with JDK1.5). */ 61: private static final long serialVersionUID = 2436068675711756856L; 62: 63: /** The format that handles value to string conversion. */ 64: Format format; 65: 66: /** The minimal permissable value. */ 67: Comparable minimum; 68: 69: /** The maximal permissable value. */ 70: Comparable maximum; 71: 72: /** 73: * Creates a new InternationalFormatter with no Format specified. 74: */ 75: public InternationalFormatter() 76: { 77: super(); 78: minimum = null; 79: maximum = null; 80: format = null; 81: setCommitsOnValidEdit(false); 82: setOverwriteMode(false); 83: } 84: 85: /** 86: * Creates a new InternationalFormatter that uses the specified 87: * Format object for value to string conversion. 88: * 89: * @param format the Format object to use for value to string conversion 90: */ 91: public InternationalFormatter(Format format) 92: { 93: this(); 94: setFormat(format); 95: } 96: 97: /** 98: * Sets the Format object that is used to convert values to strings. 99: * 100: * @param format the Format to use for value to string conversion 101: * 102: * @see Format 103: */ 104: public void setFormat(Format format) 105: { 106: this.format = format; 107: } 108: 109: /** 110: * Returns the currently used Format object that is used to format 111: * the JFormattedField. 112: * 113: * @return the current Format 114: */ 115: public Format getFormat() 116: { 117: return format; 118: } 119: 120: /** 121: * Sets the minimum value that is allowed by this Formatter. The minimum 122: * value is given as an object that implements the {@link Comparable} 123: * interface. 124: * 125: * If <code>minValue</code> is null, then the Formatter has no restrictions 126: * at the lower end. 127: * 128: * If value class is not yet specified and <code>minValue</code> is not 129: * null, then <code>valueClass</code> is set to the class of the minimum 130: * value. 131: * 132: * @param minValue the minimum permissable value 133: * 134: * @see Comparable 135: */ 136: public void setMinimum(Comparable minValue) 137: { 138: minimum = minValue; 139: if (valueClass == null && minValue != null) 140: valueClass = minValue.getClass(); 141: } 142: 143: /** 144: * Returns the minimal value that is allowed by this Formatter. 145: * 146: * A <code>null</code> value means that there is no restriction. 147: * 148: * @return the minimal value that is allowed by this Formatter or 149: * <code>null</code> if there is no restriction 150: */ 151: public Comparable getMinimum() 152: { 153: return minimum; 154: } 155: 156: /** 157: * Sets the maximum value that is allowed by this Formatter. The maximum 158: * value is given as an object that implements the {@link Comparable} 159: * interface. 160: * 161: * If <code>maxValue</code> is null, then the Formatter has no restrictions 162: * at the upper end. 163: * 164: * If value class is not yet specified and <code>maxValue</code> is not 165: * null, then <code>valueClass</code> is set to the class of the maximum 166: * value. 167: * 168: * @param maxValue the maximum permissable value 169: * 170: * @see Comparable 171: */ 172: public void setMaximum(Comparable maxValue) 173: { 174: maximum = maxValue; 175: if (valueClass == null && maxValue != null) 176: valueClass = maxValue.getClass(); 177: } 178: 179: /** 180: * Returns the maximal value that is allowed by this Formatter. 181: * 182: * A <code>null</code> value means that there is no restriction. 183: * 184: * @return the maximal value that is allowed by this Formatter or 185: * <code>null</code> if there is no restriction 186: */ 187: public Comparable getMaximum() 188: { 189: return maximum; 190: } 191: 192: /** 193: * Installs the formatter on the specified {@link JFormattedTextField}. 194: * 195: * This method does the following things: 196: * <ul> 197: * <li>Display the value of #valueToString in the 198: * <code>JFormattedTextField</code></li> 199: * <li>Install the Actions from #getActions on the <code>JTextField</code> 200: * </li> 201: * <li>Install the DocumentFilter returned by #getDocumentFilter</li> 202: * <li>Install the NavigationFilter returned by #getNavigationFilter</li> 203: * </ul> 204: * 205: * This method is typically not overridden by subclasses. Instead override 206: * one of the mentioned methods in order to customize behaviour. 207: * 208: * @param ftf the {@link JFormattedTextField} in which this formatter 209: * is installed 210: */ 211: public void install(JFormattedTextField ftf) 212: { 213: super.install(ftf); 214: } 215: 216: /** 217: * Converts a value object into a String. This is done by invoking 218: * {@link Format#format(Object)} on the specified <code>Format</code> object. 219: * If no format is set, then {@link DefaultFormatter#valueToString(Object)} 220: * is called as a fallback. 221: * 222: * @param value the value to be converted 223: * 224: * @return the string representation of the value 225: * 226: * @throws ParseException if the value cannot be converted 227: */ 228: public String valueToString(Object value) 229: throws ParseException 230: { 231: if (value == null) 232: return ""; 233: if (format != null) 234: return format.format(value); 235: else 236: return super.valueToString(value); 237: } 238: 239: /** 240: * Converts a String (from the JFormattedTextField input) to a value. 241: * This is achieved by invoking {@link Format#parseObject(String)} on 242: * the specified <code>Format</code> object. 243: * 244: * This implementation differs slightly from {@link DefaultFormatter}, 245: * it does: 246: * <ol> 247: * <li>Convert the string to an <code>Object</code> using the 248: * <code>Formatter</code>.</li> 249: * <li>If a <code>valueClass</code> has been set, this object is passed to 250: * {@link DefaultFormatter#stringToValue(String)} so that the value 251: * has the correct type. This may or may not work correctly, depending on 252: * the implementation of toString() in the value class and if the class 253: * implements a constructor that takes one String as argument.</li> 254: * <li>If no {@link ParseException} has been thrown so far, we check if the 255: * value exceeds either <code>minimum</code> or <code>maximum</code> if 256: * one of those has been specified and throw a <code>ParseException</code> 257: * if it does.</li> 258: * <li>Return the value.</li> 259: * </ol> 260: * 261: * If no format has been specified, then 262: * {@link DefaultFormatter#stringToValue(String)} is invoked as fallback. 263: * 264: * @param string the string to convert 265: * 266: * @return the value for the string 267: * 268: * @throws ParseException if the string cannot be converted into 269: * a value object (e.g. invalid input) 270: */ 271: public Object stringToValue(String string) 272: throws ParseException 273: { 274: if (format != null) 275: { 276: Object o = format.parseObject(string); 277: 278: // If a value class has been set, call super in order to get 279: // the class right. That is what the JDK API docs suggest, so we do 280: // it that way. 281: if (valueClass != null) 282: o = super.stringToValue(o.toString()); 283: 284: // Check for minimum and maximum bounds 285: if (minimum != null && minimum.compareTo(o) > 0) 286: throw new ParseException("The value may not be less than the" 287: + " specified minimum", 0); 288: if (maximum != null && maximum.compareTo(o) < 0) 289: throw new ParseException("The value may not be greater than the" 290: + " specified maximum", 0); 291: return o; 292: } 293: else 294: return super.stringToValue(string); 295: } 296: 297: /** 298: * Returns the {@link Format.Field} constants that are associated with 299: * the specified position in the text. 300: * 301: * If <code>offset</code> is not a valid location in the input field, 302: * an empty array of fields is returned. 303: * 304: * @param offset the position in the text from which we want to fetch 305: * the fields constants 306: * 307: * @return the field values associated with the specified position in 308: * the text 309: */ 310: public Format.Field[] getFields(int offset) 311: { 312: // TODO: don't know if this is correct 313: AttributedCharacterIterator aci = format.formatToCharacterIterator 314: (getFormattedTextField().getValue()); 315: aci.setIndex(offset); 316: Map atts = aci.getAttributes(); 317: Set keys = atts.keySet(); 318: Format.Field[] fields = new Format.Field[keys.size()]; 319: int index = 0; 320: for (Iterator i = keys.iterator(); i.hasNext(); index++) 321: fields[index] = (Format.Field) i.next(); 322: return fields; 323: } 324: 325: /** 326: * This creates and returns a clone of this Formatter. 327: * 328: * @return a clone of this formatter 329: * 330: * @throws CloneNotSupportedException not thrown here, since cloning is 331: * supported 332: */ 333: public Object clone() 334: throws CloneNotSupportedException 335: { 336: // TODO: it has to be considered, if we should return a deep or shallow 337: // clone here. for now we return a shallow clone 338: Object clone = super.clone(); 339: return clone; 340: } 341: 342: /** 343: * Returns the Actions that are supported by this Formatter. 344: * 345: * @specnote the JDK API docs say here: <cite>If 346: * <code>getSupportsIncrement</code> returns true, this returns two 347: * Actions suitable for incrementing/decrementing the value.</cite> 348: * The questsion is, which method <code>getSupportsIncrement</code>? 349: * There is no such method in the whole API! So we just call 350: * super.getActions here. 351: */ 352: protected Action[] getActions() 353: { 354: return super.getActions(); 355: } 356: }