Source for javax.swing.JFormattedTextField

   1: /* JFormattedTextField.java --
   2:    Copyright (C) 2003, 2004 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: 
  39: package javax.swing;
  40: 
  41: import java.awt.event.FocusEvent;
  42: import java.io.Serializable;
  43: import java.text.Format;
  44: import java.text.ParseException;
  45: import java.util.Date;
  46: 
  47: import javax.swing.text.DateFormatter;
  48: import javax.swing.text.DefaultFormatter;
  49: import javax.swing.text.Document;
  50: import javax.swing.text.DocumentFilter;
  51: import javax.swing.text.NavigationFilter;
  52: 
  53: /**
  54:  * A text field that makes use of a formatter to display and edit a specific
  55:  * type of data. The value that is displayed can be an arbitrary object. The
  56:  * formatter is responsible for displaying the value in a textual form and
  57:  * it may allow editing of the value.
  58:  *
  59:  * Formatters are usually obtained using an instance of
  60:  * {@link AbstractFormatterFactory}. This factory is responsible for providing
  61:  * an instance of {@link AbstractFormatter} that is able to handle the
  62:  * formatting of the value of the JFormattedTextField.
  63:  *
  64:  * @author Michael Koch
  65:  *
  66:  * @since 1.4
  67:  */
  68: public class JFormattedTextField extends JTextField
  69: {
  70:   private static final long serialVersionUID = 5464657870110180632L;
  71: 
  72:   /**
  73:    * An abstract base implementation for a formatter that can be used by
  74:    * a JTextField. A formatter can display a specific type of object and
  75:    * may provide a way to edit this value.
  76:    */
  77:   public abstract static class AbstractFormatter implements Serializable
  78:   {
  79:     private static final long serialVersionUID = -5193212041738979680L;
  80:     
  81:     private JFormattedTextField textField;
  82:     
  83:     public AbstractFormatter ()
  84:     {
  85:       //Do nothing here.
  86:     }
  87: 
  88:     protected Object clone ()
  89:       throws CloneNotSupportedException
  90:     {
  91:       throw new InternalError ("not implemented");
  92:     }
  93: 
  94:     protected Action[] getActions ()
  95:     {
  96:       return textField.getActions();
  97:     }
  98: 
  99:     protected DocumentFilter getDocumentFilter ()
 100:     {
 101:       throw new InternalError ("not implemented");
 102:     }
 103: 
 104:     protected JFormattedTextField getFormattedTextField ()
 105:     {
 106:       return textField;
 107:     }
 108: 
 109:     protected NavigationFilter getNavigationFilter ()
 110:     {
 111:       return textField.getNavigationFilter();
 112:     }
 113: 
 114:     public void install(JFormattedTextField textField)
 115:     {
 116:       if (this.textField != null)
 117:     uninstall();
 118:       
 119:       this.textField = textField;
 120:     }
 121: 
 122:     public void uninstall ()
 123:     {
 124:       this.textField = null;
 125:     }
 126: 
 127:     protected void invalidEdit ()
 128:     {
 129:       textField.invalidEdit();
 130:     }
 131: 
 132:     protected void setEditValid (boolean valid)
 133:     {
 134:       textField.editValid = valid;
 135:     }
 136: 
 137:     public abstract Object stringToValue (String text)
 138:       throws ParseException;
 139: 
 140:     public abstract String valueToString (Object value)
 141:       throws ParseException;
 142:   }
 143: 
 144:   /**
 145:    * Delivers instances of an {@link AbstractFormatter} for
 146:    * a specific value type for a JFormattedTextField. 
 147:    */
 148:   public abstract static class AbstractFormatterFactory
 149:   {
 150:     public AbstractFormatterFactory ()
 151:     {
 152:       // Do nothing here.
 153:     }
 154: 
 155:     public abstract AbstractFormatter getFormatter (JFormattedTextField tf);
 156:   }
 157: 
 158:   static class FormatterFactoryWrapper extends AbstractFormatterFactory
 159:   {
 160:     AbstractFormatter formatter;
 161: 
 162:     public FormatterFactoryWrapper(AbstractFormatter formatter)
 163:     {
 164:       this.formatter = formatter;
 165:     }
 166: 
 167:     public AbstractFormatter getFormatter(JFormattedTextField tf)
 168:     {
 169:       return formatter;
 170:     }
 171:   }
 172: 
 173:   public static final int COMMIT = 0;
 174:   public static final int COMMIT_OR_REVERT = 1;
 175:   public static final int REVERT = 2;
 176:   public static final int PERSIST = 3;
 177: 
 178:   private Object value;
 179:   private int focusLostBehavior = COMMIT_OR_REVERT;
 180:   private AbstractFormatterFactory formatterFactory;
 181:   // Package-private to avoid an accessor method.
 182:   boolean editValid = true;
 183:   
 184:   public JFormattedTextField ()
 185:   {
 186:     this((AbstractFormatterFactory) null, null);
 187:   }
 188: 
 189:   public JFormattedTextField (Format format)
 190:   {
 191:     throw new InternalError ("not implemented");
 192:   }
 193: 
 194:   public JFormattedTextField (AbstractFormatter formatter)
 195:   {
 196:     this(new FormatterFactoryWrapper(formatter), null);
 197:   }
 198: 
 199:   public JFormattedTextField (AbstractFormatterFactory factory)
 200:   {
 201:     this(factory, null);
 202:   }
 203: 
 204:   public JFormattedTextField (AbstractFormatterFactory factory, Object value)
 205:   {
 206:     this.formatterFactory = factory;
 207:     this.value = value;
 208:   }
 209: 
 210:   public JFormattedTextField (Object value)
 211:   {
 212:     this.value = value;
 213:   }
 214: 
 215:   public void commitEdit ()
 216:     throws ParseException
 217:   {
 218:     throw new InternalError ("not implemented");
 219:   }
 220: 
 221:   public Action[] getActions ()
 222:   {
 223:     // FIXME: Add JFormattedTextField specific actions
 224:     return super.getActions();
 225:   }
 226: 
 227:   public int getFocusLostBehavior()
 228:   {
 229:     return focusLostBehavior;
 230:   }
 231: 
 232:   public AbstractFormatter getFormatter ()
 233:   {
 234:     if (formatterFactory == null)
 235:       return null;
 236:     
 237:     return formatterFactory.getFormatter(this);
 238:   }
 239: 
 240:   public AbstractFormatterFactory getFormatterFactory ()
 241:   {
 242:     return formatterFactory;
 243:   }
 244: 
 245:   public String getUIClassID ()
 246:   {
 247:     return "FormattedTextFieldUI";
 248:   }
 249: 
 250:   public Object getValue ()
 251:   {
 252:     return value;
 253:   }
 254: 
 255:   protected void invalidEdit ()
 256:   {
 257:     UIManager.getLookAndFeel().provideErrorFeedback(this);
 258:   }
 259: 
 260:   public boolean isEditValid ()
 261:   {
 262:     return editValid;
 263:   }
 264: 
 265:   protected void processFocusEvent (FocusEvent evt)
 266:   {
 267:     // it's safe to simply call super for now, until it gets clear
 268:     // what this method is supposed to do
 269:     // throw new InternalError ("not implemented");
 270:     super.processFocusEvent(evt);
 271:   }
 272: 
 273:   public void setDocument(Document newDocument)
 274:   {
 275:     Document oldDocument = getDocument();
 276: 
 277:     if (oldDocument == newDocument)
 278:       return;
 279:     
 280:     super.setDocument(newDocument);
 281:   }
 282: 
 283:   public void setFocusLostBehavior(int behavior)
 284:   {
 285:     if (behavior != COMMIT
 286:     && behavior != COMMIT_OR_REVERT
 287:     && behavior != PERSIST
 288:     && behavior != REVERT)
 289:       throw new IllegalArgumentException("invalid behavior");
 290: 
 291:     this.focusLostBehavior = behavior;
 292:   }
 293: 
 294:   protected void setFormatter (AbstractFormatter formatter)
 295:   {
 296:     AbstractFormatter oldFormatter = null;
 297:     
 298:     if (formatterFactory != null)
 299:       oldFormatter = formatterFactory.getFormatter(this);
 300: 
 301:     if (oldFormatter == formatter)
 302:       return;
 303: 
 304:     setFormatterFactory(new FormatterFactoryWrapper(formatter));
 305:     firePropertyChange("formatter", oldFormatter, formatter);
 306:   }
 307: 
 308:   public void setFormatterFactory (AbstractFormatterFactory factory)
 309:   {
 310:     if (formatterFactory == factory)
 311:       return;
 312:     
 313:     AbstractFormatterFactory oldFactory = formatterFactory;
 314:     formatterFactory = factory;
 315:     firePropertyChange("formatterFactory", oldFactory, factory);
 316:   }
 317: 
 318:   public void setValue (Object newValue)
 319:   {
 320:     if (value == newValue)
 321:       return;
 322: 
 323:     // format value
 324:     AbstractFormatter formatter = createFormatter(newValue);
 325:     try
 326:       {
 327:         setText(formatter.valueToString(newValue));
 328:       }
 329:     catch (ParseException ex)
 330:       {
 331:         // TODO: what should we do with this?
 332:       }
 333: 
 334:     Object oldValue = value;
 335:     value = newValue;
 336:     firePropertyChange("value", oldValue, newValue);
 337:   }
 338: 
 339:   /**
 340:    * A helper method that attempts to create a formatter that is suitable
 341:    * to format objects of the type like <code>value</code>.
 342:    *
 343:    * If <code>formatterFactory</code> is not null and the returned formatter
 344:    * is also not <code>null</code> then this formatter is used. Otherwise we
 345:    * try to create one based on the type of <code>value</code>.
 346:    *
 347:    * @param value an object which should be formatted by the formatter
 348:    *
 349:    * @return a formatter able to format objects of the class of
 350:    *     <code>value</code>
 351:    */
 352:   AbstractFormatter createFormatter(Object value)
 353:   {
 354:     AbstractFormatter formatter = null;
 355:     if (formatterFactory != null
 356:         && formatterFactory.getFormatter(this) != null)
 357:      formatter = formatterFactory.getFormatter(this);
 358:    else
 359:      {
 360:        if (value instanceof Date)
 361:          formatter = new DateFormatter();
 362:        else
 363:          formatter = new DefaultFormatter();
 364:      }
 365:     return formatter;
 366:   }
 367: }