/* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.awt.event.ActionEvent;import java.io.*;import java.text.*;import java.text.AttributedCharacterIterator.Attribute;import java.util.*;import javax.swing.*; /** * <code>InternationalFormatter</code> extends <code>DefaultFormatter</code>, * using an instance of <code>java.text.Format</code> to handle the * conversion to a String, and the conversion from a String. * <p> * If <code>getAllowsInvalid()</code> is false, this will ask the * <code>Format</code> to format the current text on every edit. * <p> * You can specify a minimum and maximum value by way of the * <code>setMinimum</code> and <code>setMaximum</code> methods. In order * for this to work the values returned from <code>stringToValue</code> must be * comparable to the min/max values by way of the <code>Comparable</code> * interface. * <p> * Be careful how you configure the <code>Format</code> and the * <code>InternationalFormatter</code>, as it is possible to create a * situation where certain values can not be input. Consider the date * format 'M/d/yy', an <code>InternationalFormatter</code> that is always * valid (<code>setAllowsInvalid(false)</code>), is in overwrite mode * (<code>setOverwriteMode(true)</code>) and the date 7/1/99. In this * case the user will not be able to enter a two digit month or day of * month. To avoid this, the format should be 'MM/dd/yy'. * <p> * If <code>InternationalFormatter</code> is configured to only allow valid * values (<code>setAllowsInvalid(false)</code>), every valid edit will result * in the text of the <code>JFormattedTextField</code> being completely reset * from the <code>Format</code>. * The cursor position will also be adjusted as literal characters are * added/removed from the resulting String. * <p> * <code>InternationalFormatter</code>'s behavior of * <code>stringToValue</code> is slightly different than that of * <code>DefaultTextFormatter</code>, it does the following: * <ol> * <li><code>parseObject</code> is invoked on the <code>Format</code> * specified by <code>setFormat</code> * <li>If a Class has been set for the values (<code>setValueClass</code>), * supers implementation is invoked to convert the value returned * from <code>parseObject</code> to the appropriate class. * <li>If a <code>ParseException</code> has not been thrown, and the value * is outside the min/max a <code>ParseException</code> is thrown. * <li>The value is returned. * </ol> * <code>InternationalFormatter</code> implements <code>stringToValue</code> * in this manner so that you can specify an alternate Class than * <code>Format</code> may return. * <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}. * * @see java.text.Format * @see java.lang.Comparable * * @since 1.4 */public class InternationalFormatter extends DefaultFormatter { /** * Used by <code>getFields</code>. */ private static final Format.Field[] EMPTY_FIELD_ARRAY =new Format.Field[0]; /** * Object used to handle the conversion. */ private Format format; /** * Can be used to impose a maximum value. */ private Comparable max; /** * Can be used to impose a minimum value. */ private Comparable min; /** * <code>InternationalFormatter</code>'s behavior is dicatated by a * <code>AttributedCharacterIterator</code> that is obtained from * the <code>Format</code>. On every edit, assuming * allows invalid is false, the <code>Format</code> instance is invoked * with <code>formatToCharacterIterator</code>. A <code>BitSet</code> is * also kept upto date with the non-literal characters, that is * for every index in the <code>AttributedCharacterIterator</code> an * entry in the bit set is updated based on the return value from * <code>isLiteral(Map)</code>. <code>isLiteral(int)</code> then uses * this cached information. * <p> * If allowsInvalid is false, every edit results in resetting the complete * text of the JTextComponent. * <p> * InternationalFormatterFilter can also provide two actions suitable for * incrementing and decrementing. To enable this a subclass must * override <code>getSupportsIncrement</code> to return true, and * override <code>adjustValue</code> to handle the changing of the * value. If you want to support changing the value outside of * the valid FieldPositions, you will need to override * <code>canIncrement</code>. */ /** * A bit is set for every index identified in the * AttributedCharacterIterator that is not considered decoration. * This should only be used if validMask is true. */ private transient BitSet literalMask; /** * Used to iterate over characters. */ private transient AttributedCharacterIterator iterator; /** * True if the Format was able to convert the value to a String and * back. */ private transient boolean validMask; /** * Current value being displayed. */ private transient String string; /** * If true, DocumentFilter methods are unconditionally allowed, * and no checking is done on their values. This is used when * incrementing/decrementing via the actions. */ private transient boolean ignoreDocumentMutate; /** * Creates an <code>InternationalFormatter</code> with no * <code>Format</code> specified. */ public InternationalFormatter() { setOverwriteMode(false); } /** * Creates an <code>InternationalFormatter</code> with the specified * <code>Format</code> instance. * * @param format Format instance used for converting from/to Strings */ public InternationalFormatter(Format format) { this(); setFormat(format); } /** * Sets the format that dictates the legal values that can be edited * and displayed. * * @param format <code>Format</code> instance used for converting * from/to Strings */ public void setFormat(Format format) { this.format = format; } /** * Returns the format that dictates the legal values that can be edited * and displayed. * * @return Format instance used for converting from/to Strings */ public Format getFormat() { return format; } /** * Sets the minimum permissible value. If the <code>valueClass</code> has * not been specified, and <code>minimum</code> is non null, the * <code>valueClass</code> will be set to that of the class of * <code>minimum</code>. * * @param minimum Minimum legal value that can be input * @see #setValueClass */ public void setMinimum(Comparable minimum) { if (getValueClass() == null && minimum != null) { setValueClass(minimum.getClass()); } min = minimum; } /** * Returns the minimum permissible value. * * @return Minimum legal value that can be input */ public Comparable getMinimum() { return min; } /** * Sets the maximum permissible value. If the <code>valueClass</code> has * not been specified, and <code>max</code> is non null, the * <code>valueClass</code> will be set to that of the class of * <code>max</code>. * * @param max Maximum legal value that can be input * @see #setValueClass */ public void setMaximum(Comparable max) { if (getValueClass() == null && max != null) { setValueClass(max.getClass()); } this.max = max; } /** * Returns the maximum permissible value. * * @return Maximum legal value that can be input */ public Comparable getMaximum() { return max; } /** * Installs the <code>DefaultFormatter</code> onto a particular * <code>JFormattedTextField</code>. * This will invoke <code>valueToString</code> to convert the * current value from the <code>JFormattedTextField</code> to * a String. This will then install the <code>Action</code>s from * <code>getActions</code>, the <code>DocumentFilter</code> * returned from <code>getDocumentFilter</code> and the * <code>NavigationFilter</code> returned from * <code>getNavigationFilter</code> onto the * <code>JFormattedTextField</code>. * <p> * Subclasses will typically only need to override this if they * wish to install additional listeners on the * <code>JFormattedTextField</code>. * <p> * If there is a <code>ParseException</code> in converting the * current value to a String, this will set the text to an empty * String, and mark the <code>JFormattedTextField</code> as being * in an invalid state. * <p> * While this is a public method, this is typically only useful * for subclassers of <code>JFormattedTextField</code>. * <code>JFormattedTextField</code> will invoke this method at * the appropriate times when the value changes, or its internal * state changes. * * @param ftf JFormattedTextField to format for, may be null indicating * uninstall from current JFormattedTextField. */ public void install(JFormattedTextField ftf) { super.install(ftf); updateMaskIfNecessary(); // invoked again as the mask should now be valid. positionCursorAtInitialLocation(); } /** * Returns a String representation of the Object <code>value</code>. * This invokes <code>format</code> on the current <code>Format</code>. * * @throws ParseException if there is an error in the conversion * @param value Value to convert * @return String representation of value */ public String valueToString(Object value) throws ParseException { if (value == null) { return ""; } Format f = getFormat(); if (f == null) { return value.toString(); } return f.format(value); } /** * Returns the <code>Object</code> representation of the * <code>String</code> <code>text</code>. * * @param text <code>String</code> to convert * @return <code>Object</code> representation of text * @throws ParseException if there is an error in the conversion */ public Object stringToValue(String text) throws ParseException { Object value = stringToValue(text, getFormat()); // Convert to the value class if the Value returned from the // Format does not match. if (value != null && getValueClass() != null && !getValueClass().isInstance(value)) { value = super.stringToValue(value.toString()); } try { if (!isValidValue(value, true)) { throw new ParseException("Value not within min/max range", 0); } } catch (ClassCastException cce) { throw new ParseException("Class cast exception comparing values: " + cce, 0); } return value; } /** * Returns the <code>Format.Field</code> constants associated with * the text at <code>offset</code>. If <code>offset</code> is not * a valid location into the current text, this will return an * empty array. * * @param offset offset into text to be examined * @return Format.Field constants associated with the text at the * given position. */ public Format.Field[] getFields(int offset) { if (getAllowsInvalid()) { // This will work if the currently edited value is valid. updateMask(); } Map<Attribute, Object> attrs = getAttributes(offset); if (attrs != null && attrs.size() > 0) { ArrayList<Attribute> al = new ArrayList<Attribute>(); al.addAll(attrs.keySet()); return al.toArray(EMPTY_FIELD_ARRAY); } return EMPTY_FIELD_ARRAY; } /** * Creates a copy of the DefaultFormatter. * * @return copy of the DefaultFormatter */ public Object clone() throws CloneNotSupportedException { InternationalFormatter formatter = (InternationalFormatter)super. clone(); formatter.literalMask = null; formatter.iterator = null; formatter.validMask = false; formatter.string = null; return formatter; } /** * If <code>getSupportsIncrement</code> returns true, this returns * two Actions suitable for incrementing/decrementing the value. */ protected Action[] getActions() { if (getSupportsIncrement()) { return new Action[] { new IncrementAction("increment", 1), new IncrementAction("decrement", -1) }; } return null; } /** * Invokes <code>parseObject</code> on <code>f</code>, returning * its value. */ Object stringToValue(String text, Format f) throws ParseException { if (f == null) { return text; } return f.parseObject(text); } /** * Returns true if <code>value</code> is between the min/max. * * @param wantsCCE If false, and a ClassCastException is thrown in * comparing the values, the exception is consumed and * false is returned. */ boolean isValidValue(Object value, boolean wantsCCE) { Comparable min = getMinimum(); try { if (min != null && min.compareTo(value) > 0) { return false; } } catch (ClassCastException cce) { if (wantsCCE) { throw cce; } return false; } Comparable max = getMaximum(); try { if (max != null && max.compareTo(value) < 0) { return false; } } catch (ClassCastException cce) { if (wantsCCE) { throw cce; } return false; } return true; } /** * Returns a Set of the attribute identifiers at <code>index</code>. */ Map<Attribute, Object> getAttributes(int index) { if (isValidMask()) { AttributedCharacterIterator iterator = getIterator(); if (index >= 0 && index <= iterator.getEndIndex()) { iterator.setIndex(index); return iterator.getAttributes(); } } return null; } /** * Returns the start of the first run that contains the attribute * <code>id</code>. This will return <code>-1</code> if the attribute * can not be found. */ int getAttributeStart(AttributedCharacterIterator.Attribute id) { if (isValidMask()) { AttributedCharacterIterator iterator = getIterator(); iterator.first(); while (iterator.current() != CharacterIterator.DONE) { if (iterator.getAttribute(id) != null) { return iterator.getIndex(); } iterator.next(); } } return -1; } /** * Returns the <code>AttributedCharacterIterator</code> used to * format the last value. */ AttributedCharacterIterator getIterator() { return iterator; } /** * Updates the AttributedCharacterIterator and bitset, if necessary. */ void updateMaskIfNecessary() { if (!getAllowsInvalid() && (getFormat() != null)) { if (!isValidMask()) { updateMask(); } else { String newString = getFormattedTextField().getText(); if (!newString.equals(string)) { updateMask(); } } } }Lines 1–500 of 1058. Load more
/* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.text; import java.awt.event.ActionEvent; import java.io.*; import java.text.*; import java.text.AttributedCharacterIterator.Attribute; import java.util.*; import javax.swing.*; /** * <code>InternationalFormatter</code> extends <code>DefaultFormatter</code>, * using an instance of <code>java.text.Format</code> to handle the * conversion to a String, and the conversion from a String. * <p> * If <code>getAllowsInvalid()</code> is false, this will ask the * <code>Format</code> to format the current text on every edit. * <p> * You can specify a minimum and maximum value by way of the * <code>setMinimum</code> and <code>setMaximum</code> methods. In order * for this to work the values returned from <code>stringToValue</code> must be * comparable to the min/max values by way of the <code>Comparable</code> * interface. * <p> * Be careful how you configure the <code>Format</code> and the * <code>InternationalFormatter</code>, as it is possible to create a * situation where certain values can not be input. Consider the date * format 'M/d/yy', an <code>InternationalFormatter</code> that is always * valid (<code>setAllowsInvalid(false)</code>), is in overwrite mode * (<code>setOverwriteMode(true)</code>) and the date 7/1/99. In this * case the user will not be able to enter a two digit month or day of * month. To avoid this, the format should be 'MM/dd/yy'. * <p> * If <code>InternationalFormatter</code> is configured to only allow valid * values (<code>setAllowsInvalid(false)</code>), every valid edit will result * in the text of the <code>JFormattedTextField</code> being completely reset * from the <code>Format</code>. * The cursor position will also be adjusted as literal characters are * added/removed from the resulting String. * <p> * <code>InternationalFormatter</code>'s behavior of * <code>stringToValue</code> is slightly different than that of * <code>DefaultTextFormatter</code>, it does the following: * <ol> * <li><code>parseObject</code> is invoked on the <code>Format</code> * specified by <code>setFormat</code> * <li>If a Class has been set for the values (<code>setValueClass</code>), * supers implementation is invoked to convert the value returned * from <code>parseObject</code> to the appropriate class. * <li>If a <code>ParseException</code> has not been thrown, and the value * is outside the min/max a <code>ParseException</code> is thrown. * <li>The value is returned. * </ol> * <code>InternationalFormatter</code> implements <code>stringToValue</code> * in this manner so that you can specify an alternate Class than * <code>Format</code> may return. * <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}. * * @see java.text.Format * @see java.lang.Comparable * * @since 1.4 */ public class InternationalFormatter extends DefaultFormatter { /** * Used by <code>getFields</code>. */ private static final Format.Field[] EMPTY_FIELD_ARRAY =new Format.Field[0]; /** * Object used to handle the conversion. */ private Format format; /** * Can be used to impose a maximum value. */ private Comparable max; /** * Can be used to impose a minimum value. */ private Comparable min; /** * <code>InternationalFormatter</code>'s behavior is dicatated by a * <code>AttributedCharacterIterator</code> that is obtained from * the <code>Format</code>. On every edit, assuming * allows invalid is false, the <code>Format</code> instance is invoked * with <code>formatToCharacterIterator</code>. A <code>BitSet</code> is * also kept upto date with the non-literal characters, that is * for every index in the <code>AttributedCharacterIterator</code> an * entry in the bit set is updated based on the return value from * <code>isLiteral(Map)</code>. <code>isLiteral(int)</code> then uses * this cached information. * <p> * If allowsInvalid is false, every edit results in resetting the complete * text of the JTextComponent. * <p> * InternationalFormatterFilter can also provide two actions suitable for * incrementing and decrementing. To enable this a subclass must * override <code>getSupportsIncrement</code> to return true, and * override <code>adjustValue</code> to handle the changing of the * value. If you want to support changing the value outside of * the valid FieldPositions, you will need to override * <code>canIncrement</code>. */ /** * A bit is set for every index identified in the * AttributedCharacterIterator that is not considered decoration. * This should only be used if validMask is true. */ private transient BitSet literalMask; /** * Used to iterate over characters. */ private transient AttributedCharacterIterator iterator; /** * True if the Format was able to convert the value to a String and * back. */ private transient boolean validMask; /** * Current value being displayed. */ private transient String string; /** * If true, DocumentFilter methods are unconditionally allowed, * and no checking is done on their values. This is used when * incrementing/decrementing via the actions. */ private transient boolean ignoreDocumentMutate; /** * Creates an <code>InternationalFormatter</code> with no * <code>Format</code> specified. */ public InternationalFormatter() { setOverwriteMode(false); } /** * Creates an <code>InternationalFormatter</code> with the specified * <code>Format</code> instance. * * @param format Format instance used for converting from/to Strings */ public InternationalFormatter(Format format) { this(); setFormat(format); } /** * Sets the format that dictates the legal values that can be edited * and displayed. * * @param format <code>Format</code> instance used for converting * from/to Strings */ public void setFormat(Format format) { this.format = format; } /** * Returns the format that dictates the legal values that can be edited * and displayed. * * @return Format instance used for converting from/to Strings */ public Format getFormat() { return format; } /** * Sets the minimum permissible value. If the <code>valueClass</code> has * not been specified, and <code>minimum</code> is non null, the * <code>valueClass</code> will be set to that of the class of * <code>minimum</code>. * * @param minimum Minimum legal value that can be input * @see #setValueClass */ public void setMinimum(Comparable minimum) { if (getValueClass() == null && minimum != null) { setValueClass(minimum.getClass()); } min = minimum; } /** * Returns the minimum permissible value. * * @return Minimum legal value that can be input */ public Comparable getMinimum() { return min; } /** * Sets the maximum permissible value. If the <code>valueClass</code> has * not been specified, and <code>max</code> is non null, the * <code>valueClass</code> will be set to that of the class of * <code>max</code>. * * @param max Maximum legal value that can be input * @see #setValueClass */ public void setMaximum(Comparable max) { if (getValueClass() == null && max != null) { setValueClass(max.getClass()); } this.max = max; } /** * Returns the maximum permissible value. * * @return Maximum legal value that can be input */ public Comparable getMaximum() { return max; } /** * Installs the <code>DefaultFormatter</code> onto a particular * <code>JFormattedTextField</code>. * This will invoke <code>valueToString</code> to convert the * current value from the <code>JFormattedTextField</code> to * a String. This will then install the <code>Action</code>s from * <code>getActions</code>, the <code>DocumentFilter</code> * returned from <code>getDocumentFilter</code> and the * <code>NavigationFilter</code> returned from * <code>getNavigationFilter</code> onto the * <code>JFormattedTextField</code>. * <p> * Subclasses will typically only need to override this if they * wish to install additional listeners on the * <code>JFormattedTextField</code>. * <p> * If there is a <code>ParseException</code> in converting the * current value to a String, this will set the text to an empty * String, and mark the <code>JFormattedTextField</code> as being * in an invalid state. * <p> * While this is a public method, this is typically only useful * for subclassers of <code>JFormattedTextField</code>. * <code>JFormattedTextField</code> will invoke this method at * the appropriate times when the value changes, or its internal * state changes. * * @param ftf JFormattedTextField to format for, may be null indicating * uninstall from current JFormattedTextField. */ public void install(JFormattedTextField ftf) { super.install(ftf); updateMaskIfNecessary(); // invoked again as the mask should now be valid. positionCursorAtInitialLocation(); } /** * Returns a String representation of the Object <code>value</code>. * This invokes <code>format</code> on the current <code>Format</code>. * * @throws ParseException if there is an error in the conversion * @param value Value to convert * @return String representation of value */ public String valueToString(Object value) throws ParseException { if (value == null) { return ""; } Format f = getFormat(); if (f == null) { return value.toString(); } return f.format(value); } /** * Returns the <code>Object</code> representation of the * <code>String</code> <code>text</code>. * * @param text <code>String</code> to convert * @return <code>Object</code> representation of text * @throws ParseException if there is an error in the conversion */ public Object stringToValue(String text) throws ParseException { Object value = stringToValue(text, getFormat()); // Convert to the value class if the Value returned from the // Format does not match. if (value != null && getValueClass() != null && !getValueClass().isInstance(value)) { value = super.stringToValue(value.toString()); } try { if (!isValidValue(value, true)) { throw new ParseException("Value not within min/max range", 0); } } catch (ClassCastException cce) { throw new ParseException("Class cast exception comparing values: " + cce, 0); } return value; } /** * Returns the <code>Format.Field</code> constants associated with * the text at <code>offset</code>. If <code>offset</code> is not * a valid location into the current text, this will return an * empty array. * * @param offset offset into text to be examined * @return Format.Field constants associated with the text at the * given position. */ public Format.Field[] getFields(int offset) { if (getAllowsInvalid()) { // This will work if the currently edited value is valid. updateMask(); } Map<Attribute, Object> attrs = getAttributes(offset); if (attrs != null && attrs.size() > 0) { ArrayList<Attribute> al = new ArrayList<Attribute>(); al.addAll(attrs.keySet()); return al.toArray(EMPTY_FIELD_ARRAY); } return EMPTY_FIELD_ARRAY; } /** * Creates a copy of the DefaultFormatter. * * @return copy of the DefaultFormatter */ public Object clone() throws CloneNotSupportedException { InternationalFormatter formatter = (InternationalFormatter)super. clone(); formatter.literalMask = null; formatter.iterator = null; formatter.validMask = false; formatter.string = null; return formatter; } /** * If <code>getSupportsIncrement</code> returns true, this returns * two Actions suitable for incrementing/decrementing the value. */ protected Action[] getActions() { if (getSupportsIncrement()) { return new Action[] { new IncrementAction("increment", 1), new IncrementAction("decrement", -1) }; } return null; } /** * Invokes <code>parseObject</code> on <code>f</code>, returning * its value. */ Object stringToValue(String text, Format f) throws ParseException { if (f == null) { return text; } return f.parseObject(text); } /** * Returns true if <code>value</code> is between the min/max. * * @param wantsCCE If false, and a ClassCastException is thrown in * comparing the values, the exception is consumed and * false is returned. */ boolean isValidValue(Object value, boolean wantsCCE) { Comparable min = getMinimum(); try { if (min != null && min.compareTo(value) > 0) { return false; } } catch (ClassCastException cce) { if (wantsCCE) { throw cce; } return false; } Comparable max = getMaximum(); try { if (max != null && max.compareTo(value) < 0) { return false; } } catch (ClassCastException cce) { if (wantsCCE) { throw cce; } return false; } return true; } /** * Returns a Set of the attribute identifiers at <code>index</code>. */ Map<Attribute, Object> getAttributes(int index) { if (isValidMask()) { AttributedCharacterIterator iterator = getIterator(); if (index >= 0 && index <= iterator.getEndIndex()) { iterator.setIndex(index); return iterator.getAttributes(); } } return null; } /** * Returns the start of the first run that contains the attribute * <code>id</code>. This will return <code>-1</code> if the attribute * can not be found. */ int getAttributeStart(AttributedCharacterIterator.Attribute id) { if (isValidMask()) { AttributedCharacterIterator iterator = getIterator(); iterator.first(); while (iterator.current() != CharacterIterator.DONE) { if (iterator.getAttribute(id) != null) { return iterator.getIndex(); } iterator.next(); } } return -1; } /** * Returns the <code>AttributedCharacterIterator</code> used to * format the last value. */ AttributedCharacterIterator getIterator() { return iterator; } /** * Updates the AttributedCharacterIterator and bitset, if necessary. */ void updateMaskIfNecessary() { if (!getAllowsInvalid() && (getFormat() != null)) { if (!isValidMask()) { updateMask(); } else { String newString = getFormattedTextField().getText(); if (!newString.equals(string)) { updateMask(); } } } }