Source Code Cross Referenced for NumberFormatter.java in  » 6.0-JDK-Core » swing » javax » swing » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001        /*
002         * Copyright 2000-2003 Sun Microsystems, Inc.  All Rights Reserved.
003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004         *
005         * This code is free software; you can redistribute it and/or modify it
006         * under the terms of the GNU General Public License version 2 only, as
007         * published by the Free Software Foundation.  Sun designates this
008         * particular file as subject to the "Classpath" exception as provided
009         * by Sun in the LICENSE file that accompanied this code.
010         *
011         * This code is distributed in the hope that it will be useful, but WITHOUT
012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014         * version 2 for more details (a copy is included in the LICENSE file that
015         * accompanied this code).
016         *
017         * You should have received a copy of the GNU General Public License version
018         * 2 along with this work; if not, write to the Free Software Foundation,
019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020         *
021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022         * CA 95054 USA or visit www.sun.com if you need additional information or
023         * have any questions.
024         */
025        package javax.swing.text;
026
027        import java.lang.reflect.*;
028        import java.text.*;
029        import java.util.*;
030        import javax.swing.text.*;
031
032        /**
033         * <code>NumberFormatter</code> subclasses <code>InternationalFormatter</code>
034         * adding special behavior for numbers. Among the specializations are
035         * (these are only used if the <code>NumberFormatter</code> does not display
036         * invalid nubers, eg <code>setAllowsInvalid(false)</code>):
037         * <ul>
038         *   <li>Pressing +/- (- is determined from the
039         *       <code>DecimalFormatSymbols</code> associated with the
040         *       <code>DecimalFormat</code>) in any field but the exponent
041         *       field will attempt to change the sign of the number to
042         *       positive/negative.
043         *   <li>Pressing +/- (- is determined from the
044         *       <code>DecimalFormatSymbols</code> associated with the
045         *       <code>DecimalFormat</code>) in the exponent field will
046         *       attemp to change the sign of the exponent to positive/negative.
047         * </ul>
048         * <p>
049         * If you are displaying scientific numbers, you may wish to turn on
050         * overwrite mode, <code>setOverwriteMode(true)</code>. For example:
051         * <pre>
052         * DecimalFormat decimalFormat = new DecimalFormat("0.000E0");
053         * NumberFormatter textFormatter = new NumberFormatter(decimalFormat);
054         * textFormatter.setOverwriteMode(true);
055         * textFormatter.setAllowsInvalid(false);
056         * </pre>
057         * <p>
058         * If you are going to allow the user to enter decimal
059         * values, you should either force the DecimalFormat to contain at least
060         * one decimal (<code>#.0###</code>), or allow the value to be invalid 
061         * <code>setAllowsInvalid(true)</code>. Otherwise users may not be able to
062         * input decimal values.
063         * <p>
064         * <code>NumberFormatter</code> provides slightly different behavior to
065         * <code>stringToValue</code> than that of its superclass. If you have
066         * specified a Class for values, {@link #setValueClass}, that is one of
067         * of <code>Integer</code>, <code>Long</code>, <code>Float</code>,
068         * <code>Double</code>, <code>Byte</code> or <code>Short</code> and
069         * the Format's <code>parseObject</code> returns an instance of
070         * <code>Number</code>, the corresponding instance of the value class
071         * will be created using the constructor appropriate for the primitive
072         * type the value class represents. For example:
073         * <code>setValueClass(Integer.class)</code> will cause the resulting
074         * value to be created via
075         * <code>new Integer(((Number)formatter.parseObject(string)).intValue())</code>.
076         * This is typically useful if you
077         * wish to set a min/max value as the various <code>Number</code>
078         * implementations are generally not comparable to each other. This is also
079         * useful if for some reason you need a specific <code>Number</code>
080         * implementation for your values.
081         * <p>
082         * <strong>Warning:</strong>
083         * Serialized objects of this class will not be compatible with
084         * future Swing releases. The current serialization support is
085         * appropriate for short term storage or RMI between applications running
086         * the same version of Swing.  As of 1.4, support for long term storage
087         * of all JavaBeans<sup><font size="-2">TM</font></sup>
088         * has been added to the <code>java.beans</code> package.
089         * Please see {@link java.beans.XMLEncoder}.
090         *
091         * @version 1.4 03/05/01
092         * @since 1.4
093         */
094        public class NumberFormatter extends InternationalFormatter {
095            /** The special characters from the Format instance. */
096            private String specialChars;
097
098            /**
099             * Creates a <code>NumberFormatter</code> with the a default
100             * <code>NumberFormat</code> instance obtained from
101             * <code>NumberFormat.getNumberInstance()</code>.
102             */
103            public NumberFormatter() {
104                this (NumberFormat.getNumberInstance());
105            }
106
107            /**
108             * Creates a NumberFormatter with the specified Format instance.
109             *
110             * @param format Format used to dictate legal values
111             */
112            public NumberFormatter(NumberFormat format) {
113                super (format);
114                setFormat(format);
115                setAllowsInvalid(true);
116                setCommitsOnValidEdit(false);
117                setOverwriteMode(false);
118            }
119
120            /**
121             * Sets the format that dictates the legal values that can be edited
122             * and displayed.
123             * <p>
124             * If you have used the nullary constructor the value of this property
125             * will be determined for the current locale by way of the
126             * <code>NumberFormat.getNumberInstance()</code> method.
127             *
128             * @param format NumberFormat instance used to dictate legal values
129             */
130            public void setFormat(Format format) {
131                super .setFormat(format);
132
133                DecimalFormatSymbols dfs = getDecimalFormatSymbols();
134
135                if (dfs != null) {
136                    StringBuffer sb = new StringBuffer();
137
138                    sb.append(dfs.getCurrencySymbol());
139                    sb.append(dfs.getDecimalSeparator());
140                    sb.append(dfs.getGroupingSeparator());
141                    sb.append(dfs.getInfinity());
142                    sb.append(dfs.getInternationalCurrencySymbol());
143                    sb.append(dfs.getMinusSign());
144                    sb.append(dfs.getMonetaryDecimalSeparator());
145                    sb.append(dfs.getNaN());
146                    sb.append(dfs.getPercent());
147                    sb.append('+');
148                    specialChars = sb.toString();
149                } else {
150                    specialChars = "";
151                }
152            }
153
154            /**
155             * Invokes <code>parseObject</code> on <code>f</code>, returning
156             * its value.
157             */
158            Object stringToValue(String text, Format f) throws ParseException {
159                if (f == null) {
160                    return text;
161                }
162                Object value = f.parseObject(text);
163
164                return convertValueToValueClass(value, getValueClass());
165            }
166
167            /**
168             * Converts the passed in value to the passed in class. This only
169             * works if <code>valueClass</code> is one of <code>Integer</code>,
170             * <code>Long</code>, <code>Float</code>, <code>Double</code>,
171             * <code>Byte</code> or <code>Short</code> and <code>value</code>
172             * is an instanceof <code>Number</code>.
173             */
174            private Object convertValueToValueClass(Object value,
175                    Class valueClass) {
176                if (valueClass != null && (value instanceof  Number)) {
177                    if (valueClass == Integer.class) {
178                        return new Integer(((Number) value).intValue());
179                    } else if (valueClass == Long.class) {
180                        return new Long(((Number) value).longValue());
181                    } else if (valueClass == Float.class) {
182                        return new Float(((Number) value).floatValue());
183                    } else if (valueClass == Double.class) {
184                        return new Double(((Number) value).doubleValue());
185                    } else if (valueClass == Byte.class) {
186                        return new Byte(((Number) value).byteValue());
187                    } else if (valueClass == Short.class) {
188                        return new Short(((Number) value).shortValue());
189                    }
190                }
191                return value;
192            }
193
194            /**
195             * Returns the character that is used to toggle to positive values.
196             */
197            private char getPositiveSign() {
198                return '+';
199            }
200
201            /**
202             * Returns the character that is used to toggle to negative values.
203             */
204            private char getMinusSign() {
205                DecimalFormatSymbols dfs = getDecimalFormatSymbols();
206
207                if (dfs != null) {
208                    return dfs.getMinusSign();
209                }
210                return '-';
211            }
212
213            /**
214             * Returns the character that is used to toggle to negative values.
215             */
216            private char getDecimalSeparator() {
217                DecimalFormatSymbols dfs = getDecimalFormatSymbols();
218
219                if (dfs != null) {
220                    return dfs.getDecimalSeparator();
221                }
222                return '.';
223            }
224
225            /**
226             * Returns the DecimalFormatSymbols from the Format instance.
227             */
228            private DecimalFormatSymbols getDecimalFormatSymbols() {
229                Format f = getFormat();
230
231                if (f instanceof  DecimalFormat) {
232                    return ((DecimalFormat) f).getDecimalFormatSymbols();
233                }
234                return null;
235            }
236
237            /**
238             */
239            private boolean isValidInsertionCharacter(char aChar) {
240                return (Character.isDigit(aChar) || specialChars.indexOf(aChar) != -1);
241            }
242
243            /**
244             * Subclassed to return false if <code>text</code> contains in an invalid
245             * character to insert, that is, it is not a digit
246             * (<code>Character.isDigit()</code>) and
247             * not one of the characters defined by the DecimalFormatSymbols.
248             */
249            boolean isLegalInsertText(String text) {
250                if (getAllowsInvalid()) {
251                    return true;
252                }
253                for (int counter = text.length() - 1; counter >= 0; counter--) {
254                    char aChar = text.charAt(counter);
255
256                    if (!Character.isDigit(aChar)
257                            && specialChars.indexOf(aChar) == -1) {
258                        return false;
259                    }
260                }
261                return true;
262            }
263
264            /**
265             * Subclassed to treat the decimal separator, grouping separator,
266             * exponent symbol, percent, permille, currency and sign as literals.
267             */
268            boolean isLiteral(Map attrs) {
269                if (!super .isLiteral(attrs)) {
270                    if (attrs == null) {
271                        return false;
272                    }
273                    int size = attrs.size();
274
275                    if (attrs.get(NumberFormat.Field.GROUPING_SEPARATOR) != null) {
276                        size--;
277                        if (attrs.get(NumberFormat.Field.INTEGER) != null) {
278                            size--;
279                        }
280                    }
281                    if (attrs.get(NumberFormat.Field.EXPONENT_SYMBOL) != null) {
282                        size--;
283                    }
284                    if (attrs.get(NumberFormat.Field.PERCENT) != null) {
285                        size--;
286                    }
287                    if (attrs.get(NumberFormat.Field.PERMILLE) != null) {
288                        size--;
289                    }
290                    if (attrs.get(NumberFormat.Field.CURRENCY) != null) {
291                        size--;
292                    }
293                    if (attrs.get(NumberFormat.Field.SIGN) != null) {
294                        size--;
295                    }
296                    if (size == 0) {
297                        return true;
298                    }
299                    return false;
300                }
301                return true;
302            }
303
304            /**
305             * Subclassed to make the decimal separator navigatable, as well
306             * as making the character between the integer field and the next
307             * field navigatable.
308             */
309            boolean isNavigatable(int index) {
310                if (!super .isNavigatable(index)) {
311                    // Don't skip the decimal, it causes wierd behavior
312                    if (getBufferedChar(index) == getDecimalSeparator()) {
313                        return true;
314                    }
315                    return false;
316                }
317                return true;
318            }
319
320            /**
321             * Returns the first <code>NumberFormat.Field</code> starting
322             * <code>index</code> incrementing by <code>direction</code>.
323             */
324            private NumberFormat.Field getFieldFrom(int index, int direction) {
325                if (isValidMask()) {
326                    int max = getFormattedTextField().getDocument().getLength();
327                    AttributedCharacterIterator iterator = getIterator();
328
329                    if (index >= max) {
330                        index += direction;
331                    }
332                    while (index >= 0 && index < max) {
333                        iterator.setIndex(index);
334
335                        Map attrs = iterator.getAttributes();
336
337                        if (attrs != null && attrs.size() > 0) {
338                            Iterator keys = attrs.keySet().iterator();
339
340                            while (keys.hasNext()) {
341                                Object key = keys.next();
342
343                                if (key instanceof  NumberFormat.Field) {
344                                    return (NumberFormat.Field) key;
345                                }
346                            }
347                        }
348                        index += direction;
349                    }
350                }
351                return null;
352            }
353
354            /**
355             * Overriden to toggle the value if the positive/minus sign
356             * is inserted.
357             */
358            void replace(DocumentFilter.FilterBypass fb, int offset,
359                    int length, String string, AttributeSet attr)
360                    throws BadLocationException {
361                if (!getAllowsInvalid() && length == 0 && string != null
362                        && string.length() == 1
363                        && toggleSignIfNecessary(fb, offset, string.charAt(0))) {
364                    return;
365                }
366                super .replace(fb, offset, length, string, attr);
367            }
368
369            /**
370             * Will change the sign of the integer or exponent field if
371             * <code>aChar</code> is the positive or minus sign. Returns
372             * true if a sign change was attempted.
373             */
374            private boolean toggleSignIfNecessary(
375                    DocumentFilter.FilterBypass fb, int offset, char aChar)
376                    throws BadLocationException {
377                if (aChar == getMinusSign() || aChar == getPositiveSign()) {
378                    NumberFormat.Field field = getFieldFrom(offset, -1);
379                    Object newValue;
380
381                    try {
382                        if (field == null
383                                || (field != NumberFormat.Field.EXPONENT
384                                        && field != NumberFormat.Field.EXPONENT_SYMBOL && field != NumberFormat.Field.EXPONENT_SIGN)) {
385                            newValue = toggleSign((aChar == getPositiveSign()));
386                        } else {
387                            // exponent
388                            newValue = toggleExponentSign(offset, aChar);
389                        }
390                        if (newValue != null && isValidValue(newValue, false)) {
391                            int lc = getLiteralCountTo(offset);
392                            String string = valueToString(newValue);
393
394                            fb.remove(0, fb.getDocument().getLength());
395                            fb.insertString(0, string, null);
396                            updateValue(newValue);
397                            repositionCursor(getLiteralCountTo(offset) - lc
398                                    + offset, 1);
399                            return true;
400                        }
401                    } catch (ParseException pe) {
402                        invalidEdit();
403                    }
404                }
405                return false;
406            }
407
408            /**
409             * Returns true if the range offset to length identifies the only
410             * integer field.
411             */
412            private boolean isOnlyIntegerField(int offset, int length) {
413                if (isValidMask()) {
414                    int start = getAttributeStart(NumberFormat.Field.INTEGER);
415
416                    if (start != -1) {
417                        AttributedCharacterIterator iterator = getIterator();
418
419                        iterator.setIndex(start);
420                        if (offset > start
421                                || iterator
422                                        .getRunLimit(NumberFormat.Field.INTEGER) > (offset + length)) {
423                            return false;
424                        }
425                        return true;
426                    }
427                }
428                return false;
429            }
430
431            /**
432             * Invoked to toggle the sign. For this to work the value class
433             * must have a single arg constructor that takes a String.
434             */
435            private Object toggleSign(boolean positive) throws ParseException {
436                Object value = stringToValue(getFormattedTextField().getText());
437
438                if (value != null) {
439                    // toString isn't localized, so that using +/- should work
440                    // correctly.
441                    String string = value.toString();
442
443                    if (string != null && string.length() > 0) {
444                        if (positive) {
445                            if (string.charAt(0) == '-') {
446                                string = string.substring(1);
447                            }
448                        } else {
449                            if (string.charAt(0) == '+') {
450                                string = string.substring(1);
451                            }
452                            if (string.length() > 0 && string.charAt(0) != '-') {
453                                string = "-" + string;
454                            }
455                        }
456                        if (string != null) {
457                            Class valueClass = getValueClass();
458
459                            if (valueClass == null) {
460                                valueClass = value.getClass();
461                            }
462                            try {
463                                Constructor cons = valueClass
464                                        .getConstructor(new Class[] { String.class });
465
466                                if (cons != null) {
467                                    return cons
468                                            .newInstance(new Object[] { string });
469                                }
470                            } catch (Throwable ex) {
471                            }
472                        }
473                    }
474                }
475                return null;
476            }
477
478            /**
479             * Invoked to toggle the sign of the exponent (for scientific
480             * numbers).
481             */
482            private Object toggleExponentSign(int offset, char aChar)
483                    throws BadLocationException, ParseException {
484                String string = getFormattedTextField().getText();
485                int replaceLength = 0;
486                int loc = getAttributeStart(NumberFormat.Field.EXPONENT_SIGN);
487
488                if (loc >= 0) {
489                    replaceLength = 1;
490                    offset = loc;
491                }
492                if (aChar == getPositiveSign()) {
493                    string = getReplaceString(offset, replaceLength, null);
494                } else {
495                    string = getReplaceString(offset, replaceLength,
496                            new String(new char[] { aChar }));
497                }
498                return stringToValue(string);
499            }
500        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.