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