FileDocCategorySizeDatePackage
JTextField.javaAPI DocJava SE 5 API31648Fri Aug 26 14:57:56 BST 2005javax.swing

JTextField.java

/*
 * @(#)JTextField.java	1.90 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package javax.swing;

import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.text.*;
import javax.swing.plaf.*;
import javax.swing.event.*;
import javax.accessibility.*;

import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.Serializable;

/**
 * <code>JTextField</code> is a lightweight component that allows the editing 
 * of a single line of text.  
 * For information on and examples of using text fields,
 * see 
 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/textfield.html">How to Use Text Fields</a>
 * in <em>The Java Tutorial.</em>
 *
 * <p>
 * <code>JTextField</code> is intended to be source-compatible
 * with <code>java.awt.TextField</code> where it is reasonable to do so.  This
 * component has capabilities not found in the <code>java.awt.TextField</code> 
 * class.  The superclass should be consulted for additional capabilities.
 * <p>
 * <code>JTextField</code> has a method to establish the string used as the
 * command string for the action event that gets fired.  The
 * <code>java.awt.TextField</code> used the text of the field as the command
 * string for the <code>ActionEvent</code>. 
 * <code>JTextField</code> will use the command
 * string set with the <code>setActionCommand</code> method if not <code>null</code>, 
 * otherwise it will use the text of the field as a compatibility with 
 * <code>java.awt.TextField</code>.
 * <p>
 * The method <code>setEchoChar</code> and <code>getEchoChar</code>
 * are not provided directly to avoid a new implementation of a
 * pluggable look-and-feel inadvertently exposing password characters.
 * To provide password-like services a separate class <code>JPasswordField</code>
 * extends <code>JTextField</code> to provide this service with an independently
 * pluggable look-and-feel.
 * <p>
 * The <code>java.awt.TextField</code> could be monitored for changes by adding
 * a <code>TextListener</code> for <code>TextEvent</code>'s. 
 * In the <code>JTextComponent</code> based
 * components, changes are broadcasted from the model via a
 * <code>DocumentEvent</code> to <code>DocumentListeners</code>.
 * The <code>DocumentEvent</code> gives 
 * the location of the change and the kind of change if desired.
 * The code fragment might look something like:
 * <pre><code>
 *     DocumentListener myListener = ??;
 *     JTextField myArea = ??;
 *     myArea.getDocument().addDocumentListener(myListener);
 * </code></pre>
 * <p>
 * The horizontal alignment of <code>JTextField</code> can be set to be left
 * justified, leading justified, centered, right justified or trailing justified.
 * Right/trailing justification is useful if the required size
 * of the field text is smaller than the size allocated to it.
 * This is determined by the <code>setHorizontalAlignment</code>
 * and <code>getHorizontalAlignment</code> methods.  The default
 * is to be leading justified.
 * <p>
 * How the text field consumes VK_ENTER events depends
 * on whether the text field has any action listeners.
 * If so, then VK_ENTER results in the listeners
 * getting an ActionEvent,
 * and the VK_ENTER event is consumed.
 * This is compatible with how AWT text fields handle VK_ENTER events.
 * If the text field has no action listeners, then as of v 1.3 the VK_ENTER
 * event is not consumed.  Instead, the bindings of ancestor components 
 * are processed, which enables the default button feature of
 * JFC/Swing to work.
 * <p>
 * Customized fields can easily be created by extending the model and
 * changing the default model provided.  For example, the following piece
 * of code will create a field that holds only upper case characters.  It
 * will work even if text is pasted into from the clipboard or it is altered via 
 * programmatic changes.
 * <pre><code>

 public class UpperCaseField extends JTextField {
 
     public UpperCaseField(int cols) {
         super(cols);
     }
 
     protected Document createDefaultModel() {
 	      return new UpperCaseDocument();
     }
 
     static class UpperCaseDocument extends PlainDocument {
 
         public void insertString(int offs, String str, AttributeSet a) 
 	          throws BadLocationException {
 
 	          if (str == null) {
 		      return;
 	          }
 	          char[] upper = str.toCharArray();
 	          for (int i = 0; i < upper.length; i++) {
 		      upper[i] = Character.toUpperCase(upper[i]);
 	          }
 	          super.insertString(offs, new String(upper), a);
 	      }
     }
 }

 * </code></pre>
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans<sup><font size="-2">TM</font></sup>
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @beaninfo
 *   attribute: isContainer false
 * description: A component which allows for the editing of a single line of text.
 *
 * @author  Timothy Prinzing
 * @version 1.90 12/19/03
 * @see #setActionCommand
 * @see JPasswordField
 * @see #addActionListener
 */
public class JTextField extends JTextComponent implements SwingConstants {

    /**
     * Constructs a new <code>TextField</code>.  A default model is created,
     * the initial string is <code>null</code>,
     * and the number of columns is set to 0.
     */
    public JTextField() {
        this(null, null, 0);
    }

    /**
     * Constructs a new <code>TextField</code> initialized with the
     * specified text. A default model is created and the number of
     * columns is 0.
     *
     * @param text the text to be displayed, or <code>null</code>
     */
    public JTextField(String text) {
        this(null, text, 0);
    }

    /**
     * Constructs a new empty <code>TextField</code> with the specified
     * number of columns.
     * A default model is created and the initial string is set to
     * <code>null</code>.
     *
     * @param columns  the number of columns to use to calculate 
     *   the preferred width; if columns is set to zero, the
     *   preferred width will be whatever naturally results from
     *   the component implementation
     */ 
    public JTextField(int columns) {
        this(null, null, columns);
    }

    /**
     * Constructs a new <code>TextField</code> initialized with the
     * specified text and columns.  A default model is created.
     *
     * @param text the text to be displayed, or <code>null</code>
     * @param columns  the number of columns to use to calculate 
     *   the preferred width; if columns is set to zero, the
     *   preferred width will be whatever naturally results from
     *   the component implementation
     */
    public JTextField(String text, int columns) {
        this(null, text, columns);
    }

    /**
     * Constructs a new <code>JTextField</code> that uses the given text
     * storage model and the given number of columns.
     * This is the constructor through which the other constructors feed.
     * If the document is <code>null</code>, a default model is created.
     *
     * @param doc  the text storage to use; if this is <code>null</code>,
     *		a default will be provided by calling the
     *		<code>createDefaultModel</code> method
     * @param text  the initial string to display, or <code>null</code>
     * @param columns  the number of columns to use to calculate 
     *   the preferred width >= 0; if <code>columns</code>
     *   is set to zero, the preferred width will be whatever
     *   naturally results from the component implementation
     * @exception IllegalArgumentException if <code>columns</code> < 0
     */
    public JTextField(Document doc, String text, int columns) {
        if (columns < 0) {
            throw new IllegalArgumentException("columns less than zero.");
        }
        visibility = new DefaultBoundedRangeModel();
        visibility.addChangeListener(new ScrollRepainter());
        this.columns = columns;
        if (doc == null) {
            doc = createDefaultModel();
        }
        setDocument(doc);
        if (text != null) {
            setText(text);
        }
    }

    /**
     * Gets the class ID for a UI.
     *
     * @return the string "TextFieldUI"
     * @see JComponent#getUIClassID
     * @see UIDefaults#getUI
     */
    public String getUIClassID() {
        return uiClassID;
    }

    
    /**
     * Associates the editor with a text document.
     * The currently registered factory is used to build a view for
     * the document, which gets displayed by the editor after revalidation.
     * A PropertyChange event ("document") is propagated to each listener.
     *
     * @param doc  the document to display/edit
     * @see #getDocument
     * @beaninfo
     *  description: the text document model
     *        bound: true
     *       expert: true
     */
    public void setDocument(Document doc) {
	if (doc != null) {
	    doc.putProperty("filterNewlines", Boolean.TRUE);
	}
	super.setDocument(doc);
    }

    /**
     * Calls to <code>revalidate</code> that come from within the
     * textfield itself will
     * be handled by validating the textfield, unless the textfield
     * is contained within a <code>JViewport</code>,
     * in which case this returns false.
     *
     * @return if the parent of this textfield is a <code>JViewPort</code>
     *		return false, otherwise return true
     * 
     * @see JComponent#revalidate
     * @see JComponent#isValidateRoot
     */
    public boolean isValidateRoot() {
	Component parent = getParent();
	if (parent instanceof JViewport) {
	    return false;
	}
        return true;
    }


    /**
     * Returns the horizontal alignment of the text.
     * Valid keys are:
     * <ul>
     * <li><code>JTextField.LEFT</code>
     * <li><code>JTextField.CENTER</code>
     * <li><code>JTextField.RIGHT</code>
     * <li><code>JTextField.LEADING</code>
     * <li><code>JTextField.TRAILING</code>
     * </ul>
     *
     * @return the horizontal alignment
     */
    public int getHorizontalAlignment() {
        return horizontalAlignment;
    }
    
    /**
     * Sets the horizontal alignment of the text.
     * Valid keys are:
     * <ul>
     * <li><code>JTextField.LEFT</code>
     * <li><code>JTextField.CENTER</code>
     * <li><code>JTextField.RIGHT</code>
     * <li><code>JTextField.LEADING</code>
     * <li><code>JTextField.TRAILING</code>
     * </ul>
     * <code>invalidate</code> and <code>repaint</code> are called when the
     * alignment is set, 
     * and a <code>PropertyChange</code> event ("horizontalAlignment") is fired.
     *
     * @param alignment the alignment
     * @exception IllegalArgumentException if <code>alignment</code>
     *  is not a valid key
     * @beaninfo
     *   preferred: true
     *       bound: true
     * description: Set the field alignment to LEFT, CENTER, RIGHT,
     *              LEADING (the default) or TRAILING
     *        enum: LEFT JTextField.LEFT CENTER JTextField.CENTER RIGHT JTextField.RIGHT
     *              LEADING JTextField.LEADING TRAILING JTextField.TRAILING
     */
     public void setHorizontalAlignment(int alignment) {
        if (alignment == horizontalAlignment) return;
        int oldValue = horizontalAlignment;
        if ((alignment == LEFT) || (alignment == CENTER) || 
	    (alignment == RIGHT)|| (alignment == LEADING) || 
	    (alignment == TRAILING)) {
            horizontalAlignment = alignment;
        } else {
            throw new IllegalArgumentException("horizontalAlignment");
        }
        firePropertyChange("horizontalAlignment", oldValue, horizontalAlignment);       
        invalidate();
        repaint();
    }

    /**
     * Creates the default implementation of the model
     * to be used at construction if one isn't explicitly 
     * given.  An instance of <code>PlainDocument</code> is returned.
     *
     * @return the default model implementation
     */
    protected Document createDefaultModel() {
        return new PlainDocument();
    }

    /**
     * Returns the number of columns in this <code>TextField</code>.
     *
     * @return the number of columns >= 0
     */
    public int getColumns() {
        return columns;
    }

    /**
     * Sets the number of columns in this <code>TextField</code>,
     * and then invalidate the layout.
     *
     * @param columns the number of columns >= 0
     * @exception IllegalArgumentException if <code>columns</code>
     *		is less than 0
     * @beaninfo
     * description: the number of columns preferred for display
     */
    public void setColumns(int columns) {
        int oldVal = this.columns;
        if (columns < 0) {
            throw new IllegalArgumentException("columns less than zero.");
        }
        if (columns != oldVal) {
            this.columns = columns;
            invalidate();
        }
    }

    /**
     * Returns the column width.
     * The meaning of what a column is can be considered a fairly weak
     * notion for some fonts.  This method is used to define the width
     * of a column.  By default this is defined to be the width of the
     * character <em>m</em> for the font used.  This method can be 
     * redefined to be some alternative amount
     *
     * @return the column width >= 1
     */
    protected int getColumnWidth() {
        if (columnWidth == 0) {
            FontMetrics metrics = getFontMetrics(getFont());
            columnWidth = metrics.charWidth('m');
        }
        return columnWidth;
    }

    /**
     * Returns the preferred size <code>Dimensions</code> needed for this 
     * <code>TextField</code>.  If a non-zero number of columns has been
     * set, the width is set to the columns multiplied by
     * the column width. 
     *
     * @return the dimension of this textfield
     */
    public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        if (columns != 0) {
            Insets insets = getInsets();
            size.width = columns * getColumnWidth() +
                insets.left + insets.right;
        }
        return size;
    }

    /**
     * Sets the current font.  This removes cached row height and column
     * width so the new font will be reflected. 
     * <code>revalidate</code> is called after setting the font.
     *
     * @param f the new font
     */
    public void setFont(Font f) {
        super.setFont(f);
        columnWidth = 0;
    }

    /**
     * Adds the specified action listener to receive 
     * action events from this textfield.
     *
     * @param l the action listener to be added
     */ 
    public synchronized void addActionListener(ActionListener l) {
        listenerList.add(ActionListener.class, l);
    }

    /**
     * Removes the specified action listener so that it no longer
     * receives action events from this textfield.
     *
     * @param l the action listener to be removed
     */ 
    public synchronized void removeActionListener(ActionListener l) {
	if ((l != null) && (getAction() == l)) {
	    setAction(null);
	} else {
	    listenerList.remove(ActionListener.class, l);
	}
    }

    /**
     * Returns an array of all the <code>ActionListener</code>s added
     * to this JTextField with addActionListener().
     *
     * @return all of the <code>ActionListener</code>s added or an empty
     *         array if no listeners have been added
     * @since 1.4
     */
    public synchronized ActionListener[] getActionListeners() {
        return (ActionListener[])listenerList.getListeners(
                ActionListener.class);
    }

    /**
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance 
     * is lazily created.
     * The listener list is processed in last to
     * first order.
     * @see EventListenerList
     */
    protected void fireActionPerformed() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        int modifiers = 0;
        AWTEvent currentEvent = EventQueue.getCurrentEvent();
        if (currentEvent instanceof InputEvent) {
            modifiers = ((InputEvent)currentEvent).getModifiers();
        } else if (currentEvent instanceof ActionEvent) {
            modifiers = ((ActionEvent)currentEvent).getModifiers();
        }
        ActionEvent e =
            new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
                            (command != null) ? command : getText(),
                            EventQueue.getMostRecentEventTime(), modifiers);
                            
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ActionListener.class) {
                ((ActionListener)listeners[i+1]).actionPerformed(e);
            }          
        }
    }

    /**
     * Sets the command string used for action events.
     *
     * @param command the command string
     */
    public void setActionCommand(String command) {
        this.command = command;
    }

    private Action action;
    private PropertyChangeListener actionPropertyChangeListener;

    /**
     * Sets the <code>Action</code> for the <code>ActionEvent</code> source.
     * The new <code>Action</code> replaces
     * any previously set <code>Action</code> but does not affect
     * <code>ActionListeners</code> independently
     * added with <code>addActionListener</code>.
     * If the <code>Action</code> is already a registered
     * <code>ActionListener</code>
     * for the <code>ActionEvent</code> source, it is not re-registered.
     *
     * A side-effect of setting the <code>Action</code> is that the
     * <code>ActionEvent</code> source's properties 
     * are immediately set from the values in the <code>Action</code>
     * (performed by the method <code>configurePropertiesFromAction</code>)
     * and subsequently updated as the <code>Action</code>'s
     * properties change (via a <code>PropertyChangeListener</code>
     * created by the method <code>createActionPropertyChangeListener</code>.
     *
     * @param a the <code>Action</code> for the <code>JTextField</code>,
     *		or <code>null</code>
     * @since 1.3
     * @see Action
     * @see #getAction
     * @see #configurePropertiesFromAction
     * @see #createActionPropertyChangeListener
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: the Action instance connected with this ActionEvent source
     */
    public void setAction(Action a) {
	Action oldValue = getAction();
	if (action==null || !action.equals(a)) {
	    action = a;
	    if (oldValue!=null) {
		removeActionListener(oldValue);
		oldValue.removePropertyChangeListener(actionPropertyChangeListener);
		actionPropertyChangeListener = null;
	    }
	    configurePropertiesFromAction(action);
	    if (action!=null) {		
		// Don't add if it is already a listener
		if (!isListener(ActionListener.class, action)) {
		    addActionListener(action);
		}
		// Reverse linkage:
		actionPropertyChangeListener = createActionPropertyChangeListener(action);
		action.addPropertyChangeListener(actionPropertyChangeListener);
	    }
	    firePropertyChange("action", oldValue, action);
	    revalidate();
	    repaint();
	}
    }

    private boolean isListener(Class c, ActionListener a) {
	boolean isListener = false;
	Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==c && listeners[i+1]==a) {
		    isListener=true;
	    }
	}
	return isListener;
    }

    /**
     * Returns the currently set <code>Action</code> for this
     * <code>ActionEvent</code> source, or <code>null</code>
     * if no <code>Action</code> is set.
     *
     * @return the <code>Action</code> for this <code>ActionEvent</code> source,
     *		or <code>null</code>
     * @since 1.3
     * @see Action
     * @see #setAction
     */
    public Action getAction() {
	return action;
    }

    /**
     * Factory method which sets the <code>ActionEvent</code>
     * source's properties according to values from the
     * <code>Action</code> instance.  The properties 
     * which are set may differ for subclasses.
     * By default, the properties which get set are 
     * <code>Enabled</code> and <code>ToolTipText</code>.
     *
     * @param a the <code>Action</code> from which to get the properties,
     *		or <code>null</code>
     * @since 1.3
     * @see Action
     * @see #setAction
     */
    protected void configurePropertiesFromAction(Action a) {
	setEnabled((a!=null?a.isEnabled():true));
 	setToolTipText((a!=null?(String)a.getValue(Action.SHORT_DESCRIPTION):null));	
    }

    /**
     * Factory method which creates the <code>PropertyChangeListener</code>
     * used to update the <code>ActionEvent</code> source as
     * properties change on its <code>Action</code> instance.
     * Subclasses may override this in order to provide their own
     * <code>PropertyChangeListener</code> if the set of
     * properties which should be kept up to date differs from the
     * default properties (Text, Enabled, ToolTipText).
     *
     * <p>
     * Note that <code>PropertyChangeListeners</code> should avoid holding
     * strong references to the <code>ActionEvent</code> source,
     * as this may hinder garbage collection of the
     * <code>ActionEvent</code> source and all components
     * in its containment hierarchy.  
     *
     * @param a the <code>Action</code> from which to get the properties,
     *		or <code>null</code>
     * @since 1.3
     * @see Action
     * @see #setAction
     */
    protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
        return new AbstractActionPropertyChangeListener(this, a) {
	    public void propertyChange(PropertyChangeEvent e) {	    
		String propertyName = e.getPropertyName();
		JTextField textField = (JTextField)getTarget();
		if (textField == null) {   //WeakRef GC'ed in 1.2
		    Action action = (Action)e.getSource();
		    action.removePropertyChangeListener(this);
		} else {
		    if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION)) {
			String text = (String) e.getNewValue();
			textField.setToolTipText(text);
		    } else if (propertyName.equals("enabled")) {
			Boolean enabledState = (Boolean) e.getNewValue();
			textField.setEnabled(enabledState.booleanValue());
			textField.repaint();
		    } 
		}
	    }
	};
    }

    /**
     * Fetches the command list for the editor.  This is
     * the list of commands supported by the plugged-in UI
     * augmented by the collection of commands that the
     * editor itself supports.  These are useful for binding
     * to events, such as in a keymap.
     *
     * @return the command list
     */
    public Action[] getActions() {
        return TextAction.augmentList(super.getActions(), defaultActions);
    }

    /** 
     * Processes action events occurring on this textfield by
     * dispatching them to any registered <code>ActionListener</code> objects.
     * This is normally called by the controller registered with
     * textfield.
     */  
    public void postActionEvent() {
        fireActionPerformed();
    }

    // --- Scrolling support -----------------------------------

    /**
     * Gets the visibility of the text field.  This can
     * be adjusted to change the location of the visible
     * area if the size of the field is greater than
     * the area that was allocated to the field.
     *
     * <p>
     * The fields look-and-feel implementation manages
     * the values of the minimum, maximum, and extent
     * properties on the <code>BoundedRangeModel</code>.
     * 
     * @return the visibility
     * @see BoundedRangeModel
     */
    public BoundedRangeModel getHorizontalVisibility() {
        return visibility;
    }

    /**
     * Gets the scroll offset, in pixels.
     *
     * @return the offset >= 0
     */
    public int getScrollOffset() {
        return visibility.getValue();
    }

    /**
     * Sets the scroll offset, in pixels.
     *
     * @param scrollOffset the offset >= 0
     */
    public void setScrollOffset(int scrollOffset) {
        visibility.setValue(scrollOffset);
    }
    
    /**
     * Scrolls the field left or right.
     *
     * @param r the region to scroll
     */
    public void scrollRectToVisible(Rectangle r) {
        // convert to coordinate system of the bounded range
	Insets i = getInsets();
        int x0 = r.x + visibility.getValue() - i.left;
        int x1 = x0 + r.width;
        if (x0 < visibility.getValue()) {
            // Scroll to the left
            visibility.setValue(x0);
        } else if(x1 > visibility.getValue() + visibility.getExtent()) {
            // Scroll to the right
            visibility.setValue(x1 - visibility.getExtent());
        }
    }

    /**
     * Returns true if the receiver has an <code>ActionListener</code>
     * installed.
     */
    boolean hasActionListener() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ActionListener.class) {
                return true;
            }          
        }
        return false;
    }

    // --- variables -------------------------------------------

    /**
     * Name of the action to send notification that the
     * contents of the field have been accepted.  Typically
     * this is bound to a carriage-return.
     */
    public static final String notifyAction = "notify-field-accept";

    private BoundedRangeModel visibility;
    private int horizontalAlignment = LEADING;
    private int columns;
    private int columnWidth;
    private String command;

    private static final Action[] defaultActions = {
        new NotifyAction()
    };

    /**
     * @see #getUIClassID
     * @see #readObject
     */
    private static final String uiClassID = "TextFieldUI";

    // --- Action implementations -----------------------------------

    // Note that JFormattedTextField.CommitAction extends this
    static class NotifyAction extends TextAction {

        NotifyAction() {
            super(notifyAction);
        }

        public void actionPerformed(ActionEvent e) {
            JTextComponent target = getFocusedComponent();
            if (target instanceof JTextField) {
                JTextField field = (JTextField) target;
                field.postActionEvent();
            }
        }

        public boolean isEnabled() {
            JTextComponent target = getFocusedComponent();
            if (target instanceof JTextField) {
                return ((JTextField)target).hasActionListener();
            }
            return false;
        }
    }

    class ScrollRepainter implements ChangeListener, Serializable {

        public void stateChanged(ChangeEvent e) {
            repaint();
        }

    }


    /** 
     * See <code>readObject</code> and <code>writeObject</code> in
     * <code>JComponent</code> for more 
     * information about serialization in Swing.
     */
    private void writeObject(ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        if (getUIClassID().equals(uiClassID)) {
            byte count = JComponent.getWriteObjCounter(this);
            JComponent.setWriteObjCounter(this, --count);
            if (count == 0 && ui != null) {
                ui.installUI(this);
            }
        }
    }


    /**
     * Returns a string representation of this <code>JTextField</code>.
     * This method is intended to be used only for debugging purposes,
     * and the content and format of the returned string may vary between      
     * implementations. The returned string may be empty but may not 
     * be <code>null</code>.
     * 
     * @return  a string representation of this <code>JTextField</code>
     */
    protected String paramString() {
        String horizontalAlignmentString;
        if (horizontalAlignment == LEFT) {
	    horizontalAlignmentString = "LEFT";
	} else if (horizontalAlignment == CENTER) {
	    horizontalAlignmentString = "CENTER";
	} else if (horizontalAlignment == RIGHT) {
	    horizontalAlignmentString = "RIGHT";
	} else if (horizontalAlignment == LEADING) {
	    horizontalAlignmentString = "LEADING";
	} else if (horizontalAlignment == TRAILING) {
	    horizontalAlignmentString = "TRAILING";
	} else horizontalAlignmentString = "";
        String commandString = (command != null ?
				command : "");

        return super.paramString() +
        ",columns=" + columns +
        ",columnWidth=" + columnWidth +
        ",command=" + commandString +
        ",horizontalAlignment=" + horizontalAlignmentString;
    }


/////////////////
// Accessibility support
////////////////


    /**
     * Gets the <code>AccessibleContext</code> associated with this 
     * <code>JTextField</code>. For <code>JTextFields</code>,
     * the <code>AccessibleContext</code> takes the form of an 
     * <code>AccessibleJTextField</code>. 
     * A new <code>AccessibleJTextField</code> instance is created
     * if necessary.
     *
     * @return an <code>AccessibleJTextField</code> that serves as the 
     *         <code>AccessibleContext</code> of this <code>JTextField</code>
     */
    public AccessibleContext getAccessibleContext() {
        if (accessibleContext == null) {
            accessibleContext = new AccessibleJTextField();
        }
        return accessibleContext;
    }

    /**
     * This class implements accessibility support for the 
     * <code>JTextField</code> class.  It provides an implementation of the 
     * Java Accessibility API appropriate to text field user-interface 
     * elements.
     * <p>
     * <strong>Warning:</strong>
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans<sup><font size="-2">TM</font></sup>
     * has been added to the <code>java.beans</code> package.
     * Please see {@link java.beans.XMLEncoder}.
     */
    protected class AccessibleJTextField extends AccessibleJTextComponent {

        /**
         * Gets the state set of this object.
         *
         * @return an instance of AccessibleStateSet describing the states 
         * of the object
         * @see AccessibleState
         */
        public AccessibleStateSet getAccessibleStateSet() {
            AccessibleStateSet states = super.getAccessibleStateSet();
            states.add(AccessibleState.SINGLE_LINE);
            return states;
        }
    }
}