Source Code Cross Referenced for NumberFormat.java in  » Ajax » GWT » com » google » gwt » i18n » client » Java Source Code / Java DocumentationJava Source Code and Java Documentation

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 geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Ajax » GWT » com.google.gwt.i18n.client 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2007 Google Inc.
0003:         * 
0004:         * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005:         * use this file except in compliance with the License. You may obtain a copy of
0006:         * the License at
0007:         * 
0008:         * http://www.apache.org/licenses/LICENSE-2.0
0009:         * 
0010:         * Unless required by applicable law or agreed to in writing, software
0011:         * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012:         * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013:         * License for the specific language governing permissions and limitations under
0014:         * the License.
0015:         */
0016:        package com.google.gwt.i18n.client;
0017:
0018:        import com.google.gwt.core.client.GWT;
0019:        import com.google.gwt.i18n.client.constants.CurrencyCodeMapConstants;
0020:        import com.google.gwt.i18n.client.constants.NumberConstants;
0021:
0022:        import java.util.Map;
0023:
0024:        /**
0025:         * Formats and parses numbers using locale-sensitive patterns.
0026:         * 
0027:         * This class provides comprehensive and flexible support for a wide variety of
0028:         * localized formats, including
0029:         * <ul>
0030:         * <li><b>Locale-specific symbols</b> such as decimal point, group separator,
0031:         * digit representation, currency symbol, percent, and permill</li>
0032:         * <li><b>Numeric variations</b> including integers ("123"), fixed-point
0033:         * numbers ("123.4"), scientific notation ("1.23E4"), percentages ("12%"), and
0034:         * currency amounts ("$123")</li>
0035:         * <li><b>Predefined standard patterns</b> that can be used both for parsing
0036:         * and formatting, including {@link #getDecimalFormat() decimal},
0037:         * {@link #getCurrencyFormat() currency},
0038:         * {@link #getPercentFormat() percentages}, and
0039:         * {@link #getScientificFormat() scientific}</li>
0040:         * <li><b>Custom patterns</b> and supporting features designed to make it
0041:         * possible to parse and format numbers in any locale, including support for
0042:         * Western, Arabic, and Indic digits</li>
0043:         * </ul>
0044:         * 
0045:         * <h3>Patterns</h3>
0046:         * <p>
0047:         * Formatting and parsing are based on customizable patterns that can include a
0048:         * combination of literal characters and special characters that act as
0049:         * placeholders and are replaced by their localized counterparts. Many
0050:         * characters in a pattern are taken literally; they are matched during parsing
0051:         * and output unchanged during formatting. Special characters, on the other
0052:         * hand, stand for other characters, strings, or classes of characters. For
0053:         * example, the '<code>#</code>' character is replaced by a localized digit.
0054:         * </p>
0055:         * 
0056:         * <p>
0057:         * Often the replacement character is the same as the pattern character. In the
0058:         * U.S. locale, for example, the '<code>,</code>' grouping character is
0059:         * replaced by the same character '<code>,</code>'. However, the replacement
0060:         * is still actually happening, and in a different locale, the grouping
0061:         * character may change to a different character, such as '<code>.</code>'.
0062:         * Some special characters affect the behavior of the formatter by their
0063:         * presence. For example, if the percent character is seen, then the value is
0064:         * multiplied by 100 before being displayed.
0065:         * </p>
0066:         * 
0067:         * <p>
0068:         * The characters listed below are used in patterns. Localized symbols use the
0069:         * corresponding characters taken from corresponding locale symbol collection,
0070:         * which can be found in the properties files residing in the
0071:         * <code><nobr>com.google.gwt.i18n.client.constants</nobr></code>. To insert
0072:         * a special character in a pattern as a literal (that is, without any special
0073:         * meaning) the character must be quoted. There are some exceptions to this
0074:         * which are noted below.
0075:         * </p>
0076:         * 
0077:         * <table>
0078:         * <tr>
0079:         * <th>Symbol</th>
0080:         * <th>Location</th>
0081:         * <th>Localized?</th>
0082:         * <th>Meaning</th>
0083:         * </tr>
0084:         * 
0085:         * <tr>
0086:         * <td><code>0</code></td>
0087:         * <td>Number</td>
0088:         * <td>Yes</td>
0089:         * <td>Digit</td>
0090:         * </tr>
0091:         * 
0092:         * <tr>
0093:         * <td><code>#</code></td>
0094:         * <td>Number</td>
0095:         * <td>Yes</td>
0096:         * <td>Digit, zero shows as absent</td>
0097:         * </tr>
0098:         * 
0099:         * <tr>
0100:         * <td><code>.</code></td>
0101:         * <td>Number</td>
0102:         * <td>Yes</td>
0103:         * <td>Decimal separator or monetary decimal separator</td>
0104:         * </tr>
0105:         * 
0106:         * <tr>
0107:         * <td><code>-</code></td>
0108:         * <td>Number</td>
0109:         * <td>Yes</td>
0110:         * <td>Minus sign</td>
0111:         * </tr>
0112:         * 
0113:         * <tr>
0114:         * <td><code>,</code></td>
0115:         * <td>Number</td>
0116:         * <td>Yes</td>
0117:         * <td>Grouping separator</td>
0118:         * </tr>
0119:         * 
0120:         * <tr>
0121:         * <td><code>E</code></td>
0122:         * <td>Number</td>
0123:         * <td>Yes</td>
0124:         * <td>Separates mantissa and exponent in scientific notation; need not be
0125:         * quoted in prefix or suffix</td>
0126:         * </tr>
0127:         * 
0128:         * <tr>
0129:         * <td><code>E</code></td>
0130:         * <td>Subpattern boundary</td>
0131:         * <td>Yes</td>
0132:         * <td>Separates positive and negative subpatterns</td>
0133:         * </tr>
0134:         * 
0135:         * <tr>
0136:         * <td><code>%</code></td>
0137:         * <td>Prefix or suffix</td>
0138:         * <td>Yes</td>
0139:         * <td>Multiply by 100 and show as percentage</td>
0140:         * </tr>
0141:         * 
0142:         * <tr>
0143:         * <td><nobr><code>\u2030</code> (\u005Cu2030)</nobr></td>
0144:         * <td>Prefix or suffix</td>
0145:         * <td>Yes</td>
0146:         * <td>Multiply by 1000 and show as per mille</td>
0147:         * </tr>
0148:         * 
0149:         * <tr>
0150:         * <td><nobr><code>\u00A4</code> (\u005Cu00A4)</nobr></td>
0151:         * <td>Prefix or suffix</td>
0152:         * <td>No</td>
0153:         * <td>Currency sign, replaced by currency symbol; if doubled, replaced by
0154:         * international currency symbol; if present in a pattern, the monetary decimal
0155:         * separator is used instead of the decimal separator</td>
0156:         * </tr>
0157:         * 
0158:         * <tr>
0159:         * <td><code>'</code></td>
0160:         * <td>Prefix or suffix</td>
0161:         * <td>No</td>
0162:         * <td>Used to quote special characters in a prefix or suffix; for example,
0163:         * <code>"'#'#"</code> formats <code>123</code> to <code>"#123"</code>;
0164:         * to create a single quote itself, use two in succession, such as
0165:         * <code>"# o''clock"</code></td>
0166:         * </tr>
0167:         * 
0168:         * </table>
0169:         * 
0170:         * <p>
0171:         * A <code>NumberFormat</code> pattern contains a postive and negative
0172:         * subpattern separated by a semicolon, such as
0173:         * <code>"#,##0.00;(#,##0.00)"</code>. Each subpattern has a prefix, a
0174:         * numeric part, and a suffix. If there is no explicit negative subpattern, the
0175:         * negative subpattern is the localized minus sign prefixed to the positive
0176:         * subpattern. That is, <code>"0.00"</code> alone is equivalent to
0177:         * <code>"0.00;-0.00"</code>. If there is an explicit negative subpattern, it
0178:         * serves only to specify the negative prefix and suffix; the number of digits,
0179:         * minimal digits, and other characteristics are ignored in the negative
0180:         * subpattern. That means that <code>"#,##0.0#;(#)"</code> has precisely the
0181:         * same result as <code>"#,##0.0#;(#,##0.0#)"</code>.
0182:         * </p>
0183:         * 
0184:         * <p>
0185:         * The prefixes, suffixes, and various symbols used for infinity, digits,
0186:         * thousands separators, decimal separators, etc. may be set to arbitrary
0187:         * values, and they will appear properly during formatting. However, care must
0188:         * be taken that the symbols and strings do not conflict, or parsing will be
0189:         * unreliable. For example, the decimal separator and thousands separator should
0190:         * be distinct characters, or parsing will be impossible.
0191:         * </p>
0192:         * 
0193:         * <p>
0194:         * The grouping separator is a character that separates clusters of integer
0195:         * digits to make large numbers more legible. It commonly used for thousands,
0196:         * but in some locales it separates ten-thousands. The grouping size is the
0197:         * number of digits between the grouping separators, such as 3 for "100,000,000"
0198:         * or 4 for "1 0000 0000".
0199:         * </p>
0200:         * 
0201:         * <h3>Pattern Grammar (BNF)</h3>
0202:         * <p>
0203:         * The pattern itself uses the following grammar:
0204:         * </p>
0205:         * 
0206:         * <table>
0207:         * <tr>
0208:         * <td>pattern</td>
0209:         * <td>:=</td>
0210:         * <td style="white-space: nowrap">subpattern ('<code>;</code>'
0211:         * subpattern)?</td>
0212:         * </tr>
0213:         * <tr>
0214:         * <td>subpattern</td>
0215:         * <td>:=</td>
0216:         * <td>prefix? number exponent? suffix?</td>
0217:         * </tr>
0218:         * <tr>
0219:         * <td>number</td>
0220:         * <td>:=</td>
0221:         * <td style="white-space: nowrap">(integer ('<code>.</code>' fraction)?) |
0222:         * sigDigits</td>
0223:         * </tr>
0224:         * <tr>
0225:         * <td>prefix</td>
0226:         * <td>:=</td>
0227:         * <td style="white-space: nowrap">'<code>\u005Cu0000</code>'..'<code>\u005CuFFFD</code>' -
0228:         * specialCharacters</td>
0229:         * </tr>
0230:         * <tr>
0231:         * <td>suffix</td>
0232:         * <td>:=</td>
0233:         * <td style="white-space: nowrap">'<code>\u005Cu0000</code>'..'<code>\u005CuFFFD</code>' -
0234:         * specialCharacters</td>
0235:         * </tr>
0236:         * <tr>
0237:         * <td>integer</td>
0238:         * <td>:=</td>
0239:         * <td style="white-space: nowrap">'<code>#</code>'* '<code>0</code>'*'<code>0</code>'</td>
0240:         * </tr>
0241:         * <tr>
0242:         * <td>fraction</td>
0243:         * <td>:=</td>
0244:         * <td style="white-space: nowrap">'<code>0</code>'* '<code>#</code>'*</td>
0245:         * </tr>
0246:         * <tr>
0247:         * <td>sigDigits</td>
0248:         * <td>:=</td>
0249:         * <td style="white-space: nowrap">'<code>#</code>'* '<code>@</code>''<code>@</code>'* '<code>#</code>'*</td>
0250:         * </tr>
0251:         * <tr>
0252:         * <td>exponent</td>
0253:         * <td>:=</td>
0254:         * <td style="white-space: nowrap">'<code>E</code>' '<code>+</code>'? '<code>0</code>'* '<code>0</code>'</td>
0255:         * </tr>
0256:         * <tr>
0257:         * <td>padSpec</td>
0258:         * <td>:=</td>
0259:         * <td style="white-space: nowrap">'<code>*</code>' padChar</td>
0260:         * </tr>
0261:         * <tr>
0262:         * <td>padChar</td>
0263:         * <td>:=</td>
0264:         * <td>'<code>\u005Cu0000</code>'..'<code>\u005CuFFFD</code>' - quote</td>
0265:         * </tr>
0266:         * </table>
0267:         * 
0268:         * <p>
0269:         * Notation:
0270:         * </p>
0271:         * 
0272:         * <table>
0273:         * <tr>
0274:         * <td>X*</td>
0275:         * <td style="white-space: nowrap">0 or more instances of X</td>
0276:         * </tr>
0277:         * 
0278:         * <tr>
0279:         * <td>X?</td>
0280:         * <td style="white-space: nowrap">0 or 1 instances of X</td>
0281:         * </tr>
0282:         * 
0283:         * <tr>
0284:         * <td>X|Y</td>
0285:         * <td style="white-space: nowrap">either X or Y</td>
0286:         * </tr>
0287:         * 
0288:         * <tr>
0289:         * <td>C..D</td>
0290:         * <td style="white-space: nowrap">any character from C up to D, inclusive</td>
0291:         * </tr>
0292:         * 
0293:         * <tr>
0294:         * <td>S-T</td>
0295:         * <td style="white-space: nowrap">characters in S, except those in T</td>
0296:         * </tr>
0297:         * </table>
0298:         * 
0299:         * <p>
0300:         * The first subpattern is for positive numbers. The second (optional)
0301:         * subpattern is for negative numbers.
0302:         * </p>
0303:         */
0304:        public class NumberFormat {
0305:
0306:            // Sets of constants as defined for the default locale.
0307:            private static final NumberConstants defaultNumberConstants = (NumberConstants) GWT
0308:                    .create(NumberConstants.class);
0309:            private static final CurrencyCodeMapConstants defaultCurrencyCodeMapConstants = (CurrencyCodeMapConstants) GWT
0310:                    .create(CurrencyCodeMapConstants.class);
0311:
0312:            // Constants for characters used in programmatic (unlocalized) patterns.
0313:            private static final char PATTERN_ZERO_DIGIT = '0';
0314:            private static final char PATTERN_GROUPING_SEPARATOR = ',';
0315:            private static final char PATTERN_DECIMAL_SEPARATOR = '.';
0316:            private static final char PATTERN_PER_MILLE = '\u2030';
0317:            private static final char PATTERN_PERCENT = '%';
0318:            private static final char PATTERN_DIGIT = '#';
0319:            private static final char PATTERN_SEPARATOR = ';';
0320:            private static final char PATTERN_EXPONENT = 'E';
0321:            private static final char PATTERN_MINUS = '-';
0322:            private static final char CURRENCY_SIGN = '\u00A4';
0323:            private static final char QUOTE = '\'';
0324:
0325:            // Cached instances of standard formatters.
0326:            private static NumberFormat cachedDecimalFormat;
0327:            private static NumberFormat cachedScientificFormat;
0328:            private static NumberFormat cachedPercentFormat;
0329:            private static NumberFormat cachedCurrencyFormat;
0330:
0331:            /**
0332:             * Provides the standard currency format for the default locale.
0333:             * 
0334:             * @return a <code>NumberFormat</code> capable of producing and consuming
0335:             *         currency format for the default locale
0336:             */
0337:            public static NumberFormat getCurrencyFormat() {
0338:                if (cachedCurrencyFormat == null) {
0339:                    cachedCurrencyFormat = new NumberFormat(
0340:                            defaultNumberConstants.currencyPattern(),
0341:                            defaultNumberConstants.defCurrencyCode());
0342:                }
0343:                return cachedCurrencyFormat;
0344:            }
0345:
0346:            /**
0347:             * Provides the standard decimal format for the default locale.
0348:             * 
0349:             * @return a <code>NumberFormat</code> capable of producing and consuming
0350:             *         decimal format for the default locale
0351:             */
0352:            public static NumberFormat getDecimalFormat() {
0353:                if (cachedDecimalFormat == null) {
0354:                    cachedDecimalFormat = new NumberFormat(
0355:                            defaultNumberConstants.decimalPattern(),
0356:                            defaultNumberConstants.defCurrencyCode());
0357:                }
0358:                return cachedDecimalFormat;
0359:            }
0360:
0361:            /**
0362:             * Gets a <code>NumberFormat</code> instance for the default locale using
0363:             * the specified pattern and the default currencyCode.
0364:             * 
0365:             * @param pattern pattern for this formatter
0366:             * @return a NumberFormat instance
0367:             * @throws IllegalArgumentException if the specified pattern is invalid
0368:             */
0369:            public static NumberFormat getFormat(String pattern) {
0370:                return new NumberFormat(pattern, defaultNumberConstants
0371:                        .defCurrencyCode());
0372:            }
0373:
0374:            /**
0375:             * Gets a custom <code>NumberFormat</code> instance for the default locale
0376:             * using the specified pattern and currency code.
0377:             * 
0378:             * @param pattern pattern for this formatter
0379:             * @param currencyCode international currency code
0380:             * @return a NumberFormat instance
0381:             * @throws IllegalArgumentException if the specified pattern is invalid
0382:             */
0383:            public static NumberFormat getFormat(String pattern,
0384:                    String currencyCode) {
0385:                return new NumberFormat(pattern, currencyCode);
0386:            }
0387:
0388:            /**
0389:             * Provides the standard percent format for the default locale.
0390:             * 
0391:             * @return a <code>NumberFormat</code> capable of producing and consuming
0392:             *         percent format for the default locale
0393:             */
0394:            public static NumberFormat getPercentFormat() {
0395:                if (cachedPercentFormat == null) {
0396:                    cachedPercentFormat = new NumberFormat(
0397:                            defaultNumberConstants.percentPattern(),
0398:                            defaultNumberConstants.defCurrencyCode());
0399:                }
0400:                return cachedPercentFormat;
0401:            }
0402:
0403:            /**
0404:             * Provides the standard scientific format for the default locale.
0405:             * 
0406:             * @return a <code>NumberFormat</code> capable of producing and consuming
0407:             *         scientific format for the default locale
0408:             */
0409:            public static NumberFormat getScientificFormat() {
0410:                if (cachedScientificFormat == null) {
0411:                    cachedScientificFormat = new NumberFormat(
0412:                            defaultNumberConstants.scientificPattern(),
0413:                            defaultNumberConstants.defCurrencyCode());
0414:                }
0415:                return cachedScientificFormat;
0416:            }
0417:
0418:            // Locale specific symbol collection.
0419:            private final NumberConstants numberConstants;
0420:
0421:            private int maximumIntegerDigits = 40;
0422:            private int minimumIntegerDigits = 1;
0423:            private int maximumFractionDigits = 3; // invariant, >= minFractionDigits.
0424:            private int minimumFractionDigits = 0;
0425:            private int minExponentDigits;
0426:
0427:            private String positivePrefix = "";
0428:            private String positiveSuffix = "";
0429:            private String negativePrefix = "-";
0430:            private String negativeSuffix = "";
0431:
0432:            // The multiplier for use in percent, per mille, etc.
0433:            private int multiplier = 1;
0434:
0435:            // The number of digits between grouping separators in the integer
0436:            // portion of a number.
0437:            private int groupingSize = 3;
0438:
0439:            // Forces the decimal separator to always appear in a formatted number.
0440:            private boolean decimalSeparatorAlwaysShown = false;
0441:
0442:            private boolean isCurrencyFormat = false;
0443:
0444:            // True to force the use of exponential (i.e. scientific) notation.
0445:            private boolean useExponentialNotation = false;
0446:
0447:            // Currency setting.
0448:            private final String currencySymbol;
0449:
0450:            // The currency code.
0451:            private final String currencyCode;
0452:
0453:            // The pattern to use for formatting and parsing.
0454:            private final String pattern;
0455:
0456:            /**
0457:             * Constructs a format object based on the specified settings.
0458:             * 
0459:             * @param numberConstants the locale-specific number constants to use for this
0460:             *          format
0461:             * @param currencyCodeMapConstants the locale-specific currency code map to
0462:             *          use for this format
0463:             * @param pattern pattern that specify how number should be formatted
0464:             * @param currencyCode currency that should be used
0465:             * @skip
0466:             */
0467:            protected NumberFormat(NumberConstants numberConstants,
0468:                    CurrencyCodeMapConstants currencyCodeMapConstants,
0469:                    String pattern, String currencyCode) {
0470:                this .numberConstants = numberConstants;
0471:                this .pattern = pattern;
0472:                this .currencyCode = currencyCode;
0473:
0474:                Map<String, String> currencyMap = currencyCodeMapConstants
0475:                        .currencyMap();
0476:                currencySymbol = currencyMap.get(currencyCode);
0477:
0478:                parsePattern(this .pattern);
0479:            }
0480:
0481:            /**
0482:             * Constructs a format object for the default locale based on the specified
0483:             * settings.
0484:             * 
0485:             * @param pattern pattern that specify how number should be formatted
0486:             * @param currencyCode currency that should be used
0487:             * @skip
0488:             */
0489:            protected NumberFormat(String pattern, String currencyCode) {
0490:                this (defaultNumberConstants, defaultCurrencyCodeMapConstants,
0491:                        pattern, currencyCode);
0492:            }
0493:
0494:            /**
0495:             * This method formats a double to produce a string.
0496:             * 
0497:             * @param number The double to format
0498:             * @return the formatted number string
0499:             */
0500:            public String format(double number) {
0501:                StringBuffer result = new StringBuffer();
0502:
0503:                if (Double.isNaN(number)) {
0504:                    result.append(numberConstants.notANumber());
0505:                    return result.toString();
0506:                }
0507:
0508:                boolean isNegative = ((number < 0.0) || (number == 0.0 && 1 / number < 0.0));
0509:
0510:                result.append(isNegative ? negativePrefix : positivePrefix);
0511:                if (Double.isInfinite(number)) {
0512:                    result.append(numberConstants.infinity());
0513:                } else {
0514:                    if (isNegative) {
0515:                        number = -number;
0516:                    }
0517:                    number *= multiplier;
0518:                    if (useExponentialNotation) {
0519:                        subformatExponential(number, result);
0520:                    } else {
0521:                        subformatFixed(number, result, minimumIntegerDigits);
0522:                    }
0523:                }
0524:
0525:                result.append(isNegative ? negativeSuffix : positiveSuffix);
0526:
0527:                return result.toString();
0528:            }
0529:
0530:            /**
0531:             * Returns the pattern used by this number format.
0532:             */
0533:            public String getPattern() {
0534:                return pattern;
0535:            }
0536:
0537:            /**
0538:             * Parses text to produce a numeric value. A {@link NumberFormatException} is
0539:             * thrown if either the text is empty or if the parse does not consume all
0540:             * characters of the text.
0541:             * 
0542:             * @param text the string being parsed
0543:             * @return a parsed number value
0544:             * @throws NumberFormatException if the entire text could not be converted
0545:             *           into a number
0546:             */
0547:            public double parse(String text) {
0548:                int[] pos = { 0 };
0549:                double result = parse(text, pos);
0550:                if (pos[0] == 0 || pos[0] != text.length()) {
0551:                    throw new NumberFormatException(text);
0552:                }
0553:                return result;
0554:            }
0555:
0556:            /**
0557:             * Parses text to produce a numeric value.
0558:             * 
0559:             * <p>
0560:             * The method attempts to parse text starting at the index given by pos. If
0561:             * parsing succeeds, then the index of <code>pos</code> is updated to the
0562:             * index after the last character used (parsing does not necessarily use all
0563:             * characters up to the end of the string), and the parsed number is returned.
0564:             * The updated <code>pos</code> can be used to indicate the starting point
0565:             * for the next call to this method. If an error occurs, then the index of
0566:             * <code>pos</code> is not changed.
0567:             * </p>
0568:             * 
0569:             * @param text the string to be parsed
0570:             * @param inOutPos position to pass in and get back
0571:             * @return a double value representing the parsed number, or <code>0.0</code>
0572:             *         if the parse fails
0573:             */
0574:            public double parse(String text, int[] inOutPos) {
0575:                int start = inOutPos[0];
0576:                boolean gotPositive, gotNegative;
0577:                double ret = 0.0;
0578:
0579:                gotPositive = (text.indexOf(positivePrefix, inOutPos[0]) == inOutPos[0]);
0580:                gotNegative = (text.indexOf(negativePrefix, inOutPos[0]) == inOutPos[0]);
0581:
0582:                if (gotPositive && gotNegative) {
0583:                    if (positivePrefix.length() > negativePrefix.length()) {
0584:                        gotNegative = false;
0585:                    } else if (positivePrefix.length() < negativePrefix
0586:                            .length()) {
0587:                        gotPositive = false;
0588:                    }
0589:                }
0590:
0591:                if (gotPositive) {
0592:                    inOutPos[0] += positivePrefix.length();
0593:                } else if (gotNegative) {
0594:                    inOutPos[0] += negativePrefix.length();
0595:                }
0596:
0597:                // Process digits or Inf, and find decimal position.
0598:                if (text.indexOf(numberConstants.infinity(), inOutPos[0]) == inOutPos[0]) {
0599:                    inOutPos[0] += numberConstants.infinity().length();
0600:                    ret = Double.POSITIVE_INFINITY;
0601:                } else if (text.indexOf(numberConstants.notANumber(),
0602:                        inOutPos[0]) == inOutPos[0]) {
0603:                    inOutPos[0] += numberConstants.notANumber().length();
0604:                    ret = Double.NaN;
0605:                } else {
0606:                    ret = parseNumber(text, inOutPos);
0607:                }
0608:
0609:                // Check for suffix.
0610:                if (gotPositive) {
0611:                    if (!(text.indexOf(positiveSuffix, inOutPos[0]) == inOutPos[0])) {
0612:                        inOutPos[0] = start;
0613:                        return 0.0;
0614:                    }
0615:                    inOutPos[0] += positiveSuffix.length();
0616:                } else if (gotNegative) {
0617:                    if (!(text.indexOf(negativeSuffix, inOutPos[0]) == inOutPos[0])) {
0618:                        inOutPos[0] = start;
0619:                        return 0.0;
0620:                    }
0621:                    inOutPos[0] += negativeSuffix.length();
0622:                }
0623:
0624:                if (gotNegative) {
0625:                    ret = -ret;
0626:                }
0627:
0628:                return ret;
0629:            }
0630:
0631:            /**
0632:             * This method formats the exponent part of a double.
0633:             * 
0634:             * @param exponent exponential value
0635:             * @param result formatted exponential part will be append to it
0636:             */
0637:            private void addExponentPart(int exponent, StringBuffer result) {
0638:                result.append(numberConstants.exponentialSymbol());
0639:
0640:                if (exponent < 0) {
0641:                    exponent = -exponent;
0642:                    result.append(numberConstants.minusSign());
0643:                }
0644:
0645:                String exponentDigits = String.valueOf(exponent);
0646:                for (int i = exponentDigits.length(); i < minExponentDigits; ++i) {
0647:                    result.append(numberConstants.zeroDigit());
0648:                }
0649:                result.append(exponentDigits);
0650:            }
0651:
0652:            /**
0653:             * This method return the digit that represented by current character, it
0654:             * could be either '0' to '9', or a locale specific digit.
0655:             * 
0656:             * @param ch character that represents a digit
0657:             * @return the digit value
0658:             */
0659:            private int getDigit(char ch) {
0660:                if ('0' <= ch && ch <= '0' + 9) {
0661:                    return (ch - '0');
0662:                } else {
0663:                    char zeroChar = numberConstants.zeroDigit().charAt(0);
0664:                    return ((zeroChar <= ch && ch <= zeroChar + 9) ? (ch - zeroChar)
0665:                            : -1);
0666:                }
0667:            }
0668:
0669:            /**
0670:             * This method parses affix part of pattern.
0671:             * 
0672:             * @param pattern pattern string that need to be parsed
0673:             * @param start start position to parse
0674:             * @param affix store the parsed result
0675:             * @return how many characters parsed
0676:             */
0677:            private int parseAffix(String pattern, int start, StringBuffer affix) {
0678:                affix.delete(0, affix.length());
0679:                boolean inQuote = false;
0680:                int len = pattern.length();
0681:
0682:                for (int pos = start; pos < len; ++pos) {
0683:                    char ch = pattern.charAt(pos);
0684:                    if (ch == QUOTE) {
0685:                        if ((pos + 1) < len && pattern.charAt(pos + 1) == QUOTE) {
0686:                            ++pos;
0687:                            affix.append("'"); // 'don''t'
0688:                        } else {
0689:                            inQuote = !inQuote;
0690:                        }
0691:                        continue;
0692:                    }
0693:
0694:                    if (inQuote) {
0695:                        affix.append(ch);
0696:                    } else {
0697:                        switch (ch) {
0698:                        case PATTERN_DIGIT:
0699:                        case PATTERN_ZERO_DIGIT:
0700:                        case PATTERN_GROUPING_SEPARATOR:
0701:                        case PATTERN_DECIMAL_SEPARATOR:
0702:                        case PATTERN_SEPARATOR:
0703:                            return pos - start;
0704:                        case CURRENCY_SIGN:
0705:                            isCurrencyFormat = true;
0706:                            if ((pos + 1) < len
0707:                                    && pattern.charAt(pos + 1) == CURRENCY_SIGN) {
0708:                                ++pos;
0709:                                affix.append(currencyCode);
0710:                            } else {
0711:                                affix.append(currencySymbol);
0712:                            }
0713:                            break;
0714:                        case PATTERN_PERCENT:
0715:                            if (multiplier != 1) {
0716:                                throw new IllegalArgumentException(
0717:                                        "Too many percent/per mille characters in pattern \""
0718:                                                + pattern + '"');
0719:                            }
0720:                            multiplier = 100;
0721:                            affix.append(numberConstants.percent());
0722:                            break;
0723:                        case PATTERN_PER_MILLE:
0724:                            if (multiplier != 1) {
0725:                                throw new IllegalArgumentException(
0726:                                        "Too many percent/per mille characters in pattern \""
0727:                                                + pattern + '"');
0728:                            }
0729:                            multiplier = 1000;
0730:                            affix.append(numberConstants.perMill());
0731:                            break;
0732:                        case PATTERN_MINUS:
0733:                            affix.append("-");
0734:                            break;
0735:                        default:
0736:                            affix.append(ch);
0737:                        }
0738:                    }
0739:                }
0740:                return len - start;
0741:            }
0742:
0743:            /**
0744:             * This function parses a "localized" text into a <code>double</code>. It
0745:             * needs to handle locale specific decimal, grouping, exponent and digit.
0746:             * 
0747:             * @param text the text that need to be parsed
0748:             * @param pos in/out parsing position. in case of failure, this shouldn't be
0749:             *          changed
0750:             * @return double value, could be 0.0 if nothing can be parsed
0751:             */
0752:            private double parseNumber(String text, int[] pos) {
0753:                double ret;
0754:                boolean sawDecimal = false;
0755:                boolean sawExponent = false;
0756:                boolean sawDigit = false;
0757:                int scale = 1;
0758:                String decimal = isCurrencyFormat ? numberConstants
0759:                        .monetarySeparator() : numberConstants
0760:                        .decimalSeparator();
0761:                String grouping = isCurrencyFormat ? numberConstants
0762:                        .monetaryGroupingSeparator() : numberConstants
0763:                        .groupingSeparator();
0764:                String exponentChar = numberConstants.exponentialSymbol();
0765:
0766:                StringBuffer normalizedText = new StringBuffer();
0767:                for (; pos[0] < text.length(); ++pos[0]) {
0768:                    char ch = text.charAt(pos[0]);
0769:                    int digit = getDigit(ch);
0770:                    if (digit >= 0 && digit <= 9) {
0771:                        normalizedText.append((char) (digit + '0'));
0772:                        sawDigit = true;
0773:                    } else if (ch == decimal.charAt(0)) {
0774:                        if (sawDecimal || sawExponent) {
0775:                            break;
0776:                        }
0777:                        normalizedText.append('.');
0778:                        sawDecimal = true;
0779:                    } else if (ch == grouping.charAt(0)) {
0780:                        if (sawDecimal || sawExponent) {
0781:                            break;
0782:                        }
0783:                        continue;
0784:                    } else if (ch == exponentChar.charAt(0)) {
0785:                        if (sawExponent) {
0786:                            break;
0787:                        }
0788:                        normalizedText.append('E');
0789:                        sawExponent = true;
0790:                    } else if (ch == '+' || ch == '-') {
0791:                        normalizedText.append(ch);
0792:                    } else if (ch == numberConstants.percent().charAt(0)) {
0793:                        if (scale != 1) {
0794:                            break;
0795:                        }
0796:                        scale = 100;
0797:                        if (sawDigit) {
0798:                            ++pos[0];
0799:                            break;
0800:                        }
0801:                    } else if (ch == numberConstants.perMill().charAt(0)) {
0802:                        if (scale != 1) {
0803:                            break;
0804:                        }
0805:                        scale = 1000;
0806:                        if (sawDigit) {
0807:                            ++pos[0];
0808:                            break;
0809:                        }
0810:                    } else {
0811:                        break;
0812:                    }
0813:                }
0814:
0815:                try {
0816:                    ret = Double.parseDouble(normalizedText.toString());
0817:                    ret = ret / scale;
0818:                    return ret;
0819:                } catch (NumberFormatException e) {
0820:                    return 0.0;
0821:                }
0822:            }
0823:
0824:            /**
0825:             * Method parses provided pattern, result is stored in member variables.
0826:             * 
0827:             * @param pattern
0828:             */
0829:            private void parsePattern(String pattern) {
0830:                int pos = 0;
0831:                StringBuffer affix = new StringBuffer();
0832:
0833:                pos += parseAffix(pattern, pos, affix);
0834:                positivePrefix = affix.toString();
0835:                int posPartLen = parseTrunk(pattern, pos);
0836:                pos += posPartLen;
0837:                pos += parseAffix(pattern, pos, affix);
0838:                positiveSuffix = affix.toString();
0839:
0840:                if (pos < pattern.length()
0841:                        && pattern.charAt(pos) == PATTERN_SEPARATOR) {
0842:                    ++pos;
0843:                    pos += parseAffix(pattern, pos, affix);
0844:                    negativePrefix = affix.toString();
0845:                    // The assumption made here is that negative part is identical to
0846:                    // positive part. User must make sure pattern is correctly constructed.
0847:                    pos += posPartLen;
0848:                    pos += parseAffix(pattern, pos, affix);
0849:                    negativeSuffix = affix.toString();
0850:                }
0851:            }
0852:
0853:            /**
0854:             * This method parses the trunk part of a pattern.
0855:             * 
0856:             * @param pattern pattern string that need to be parsed
0857:             * @param start where parse started
0858:             * @return how many characters parsed
0859:             */
0860:            private int parseTrunk(String pattern, int start) {
0861:                int decimalPos = -1;
0862:                int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0;
0863:                byte groupingCount = -1;
0864:
0865:                int len = pattern.length();
0866:                int pos = start;
0867:                boolean loop = true;
0868:                for (; (pos < len) && loop; ++pos) {
0869:                    char ch = pattern.charAt(pos);
0870:                    switch (ch) {
0871:                    case PATTERN_DIGIT:
0872:                        if (zeroDigitCount > 0) {
0873:                            ++digitRightCount;
0874:                        } else {
0875:                            ++digitLeftCount;
0876:                        }
0877:                        if (groupingCount >= 0 && decimalPos < 0) {
0878:                            ++groupingCount;
0879:                        }
0880:                        break;
0881:                    case PATTERN_ZERO_DIGIT:
0882:                        if (digitRightCount > 0) {
0883:                            throw new IllegalArgumentException(
0884:                                    "Unexpected '0' in pattern \"" + pattern
0885:                                            + '"');
0886:                        }
0887:                        ++zeroDigitCount;
0888:                        if (groupingCount >= 0 && decimalPos < 0) {
0889:                            ++groupingCount;
0890:                        }
0891:                        break;
0892:                    case PATTERN_GROUPING_SEPARATOR:
0893:                        groupingCount = 0;
0894:                        break;
0895:                    case PATTERN_DECIMAL_SEPARATOR:
0896:                        if (decimalPos >= 0) {
0897:                            throw new IllegalArgumentException(
0898:                                    "Multiple decimal separators in pattern \""
0899:                                            + pattern + '"');
0900:                        }
0901:                        decimalPos = digitLeftCount + zeroDigitCount
0902:                                + digitRightCount;
0903:                        break;
0904:                    case PATTERN_EXPONENT:
0905:                        if (useExponentialNotation) {
0906:                            throw new IllegalArgumentException(
0907:                                    "Multiple exponential "
0908:                                            + "symbols in pattern \"" + pattern
0909:                                            + '"');
0910:                        }
0911:                        useExponentialNotation = true;
0912:                        minExponentDigits = 0;
0913:
0914:                        // Use lookahead to parse out the exponential part
0915:                        // of the pattern, then jump into phase 2.
0916:                        while ((pos + 1) < len
0917:                                && pattern.charAt(pos + 1) == numberConstants
0918:                                        .zeroDigit().charAt(0)) {
0919:                            ++pos;
0920:                            ++minExponentDigits;
0921:                        }
0922:
0923:                        if ((digitLeftCount + zeroDigitCount) < 1
0924:                                || minExponentDigits < 1) {
0925:                            throw new IllegalArgumentException(
0926:                                    "Malformed exponential " + "pattern \""
0927:                                            + pattern + '"');
0928:                        }
0929:                        loop = false;
0930:                        break;
0931:                    default:
0932:                        --pos;
0933:                        loop = false;
0934:                        break;
0935:                    }
0936:                }
0937:
0938:                if (zeroDigitCount == 0 && digitLeftCount > 0
0939:                        && decimalPos >= 0) {
0940:                    // Handle "###.###" and "###." and ".###".
0941:                    int n = decimalPos;
0942:                    if (n == 0) { // Handle ".###"
0943:                        ++n;
0944:                    }
0945:                    digitRightCount = digitLeftCount - n;
0946:                    digitLeftCount = n - 1;
0947:                    zeroDigitCount = 1;
0948:                }
0949:
0950:                // Do syntax checking on the digits.
0951:                if ((decimalPos < 0 && digitRightCount > 0)
0952:                        || (decimalPos >= 0 && (decimalPos < digitLeftCount || decimalPos > (digitLeftCount + zeroDigitCount)))
0953:                        || groupingCount == 0) {
0954:                    throw new IllegalArgumentException("Malformed pattern \""
0955:                            + pattern + '"');
0956:                }
0957:                int totalDigits = digitLeftCount + zeroDigitCount
0958:                        + digitRightCount;
0959:
0960:                maximumFractionDigits = (decimalPos >= 0 ? (totalDigits - decimalPos)
0961:                        : 0);
0962:                if (decimalPos >= 0) {
0963:                    minimumFractionDigits = digitLeftCount + zeroDigitCount
0964:                            - decimalPos;
0965:                    if (minimumFractionDigits < 0) {
0966:                        minimumFractionDigits = 0;
0967:                    }
0968:                }
0969:
0970:                /*
0971:                 * The effectiveDecimalPos is the position the decimal is at or would be at
0972:                 * if there is no decimal. Note that if decimalPos<0, then digitTotalCount ==
0973:                 * digitLeftCount + zeroDigitCount.
0974:                 */
0975:                int effectiveDecimalPos = decimalPos >= 0 ? decimalPos
0976:                        : totalDigits;
0977:                minimumIntegerDigits = effectiveDecimalPos - digitLeftCount;
0978:                if (useExponentialNotation) {
0979:                    maximumIntegerDigits = digitLeftCount
0980:                            + minimumIntegerDigits;
0981:
0982:                    // In exponential display, integer part can't be empty.
0983:                    if (maximumFractionDigits == 0 && minimumIntegerDigits == 0) {
0984:                        minimumIntegerDigits = 1;
0985:                    }
0986:                }
0987:
0988:                this .groupingSize = (groupingCount > 0) ? groupingCount : 0;
0989:                decimalSeparatorAlwaysShown = (decimalPos == 0 || decimalPos == totalDigits);
0990:
0991:                return pos - start;
0992:            }
0993:
0994:            /**
0995:             * This method formats a <code>double</code> in exponential format.
0996:             * 
0997:             * @param number value need to be formated
0998:             * @param result where the formatted string goes
0999:             */
1000:            private void subformatExponential(double number, StringBuffer result) {
1001:                if (number == 0.0) {
1002:                    subformatFixed(number, result, minimumIntegerDigits);
1003:                    addExponentPart(0, result);
1004:                    return;
1005:                }
1006:
1007:                int exponent = (int) Math
1008:                        .floor(Math.log(number) / Math.log(10));
1009:                number /= Math.pow(10, exponent);
1010:
1011:                int minIntDigits = minimumIntegerDigits;
1012:                if (maximumIntegerDigits > 1
1013:                        && maximumIntegerDigits > minimumIntegerDigits) {
1014:                    // A repeating range is defined; adjust to it as follows.
1015:                    // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
1016:                    // -3,-4,-5=>-6, etc. This takes into account that the
1017:                    // exponent we have here is off by one from what we expect;
1018:                    // it is for the format 0.MMMMMx10^n.
1019:                    while ((exponent % maximumIntegerDigits) != 0) {
1020:                        number *= 10;
1021:                        exponent--;
1022:                    }
1023:                    minIntDigits = 1;
1024:                } else {
1025:                    // No repeating range is defined; use minimum integer digits.
1026:                    if (minimumIntegerDigits < 1) {
1027:                        exponent++;
1028:                        number /= 10;
1029:                    } else {
1030:                        for (int i = 1; i < minimumIntegerDigits; i++) {
1031:                            exponent--;
1032:                            number *= 10;
1033:                        }
1034:                    }
1035:                }
1036:
1037:                subformatFixed(number, result, minIntDigits);
1038:                addExponentPart(exponent, result);
1039:            }
1040:
1041:            /**
1042:             * This method formats a <code>double</code> into a fractional
1043:             * representation.
1044:             * 
1045:             * @param number value need to be formated
1046:             * @param result result will be written here
1047:             * @param minIntDigits minimum integer digits
1048:             */
1049:            private void subformatFixed(double number, StringBuffer result,
1050:                    int minIntDigits) {
1051:                // Round the number.
1052:                double power = Math.pow(10, maximumFractionDigits);
1053:                number = Math.round(number * power);
1054:                long intValue = (long) Math.floor(number / power);
1055:                long fracValue = (long) Math.floor(number - intValue * power);
1056:
1057:                boolean fractionPresent = (minimumFractionDigits > 0)
1058:                        || (fracValue > 0);
1059:
1060:                String intPart = String.valueOf(intValue);
1061:                String grouping = isCurrencyFormat ? numberConstants
1062:                        .monetaryGroupingSeparator() : numberConstants
1063:                        .groupingSeparator();
1064:                String decimal = isCurrencyFormat ? numberConstants
1065:                        .monetarySeparator() : numberConstants
1066:                        .decimalSeparator();
1067:
1068:                int zeroDelta = numberConstants.zeroDigit().charAt(0) - '0';
1069:                int digitLen = intPart.length();
1070:
1071:                if (intValue > 0 || minIntDigits > 0) {
1072:                    for (int i = digitLen; i < minIntDigits; i++) {
1073:                        result.append(numberConstants.zeroDigit());
1074:                    }
1075:
1076:                    for (int i = 0; i < digitLen; i++) {
1077:                        result.append((char) (intPart.charAt(i) + zeroDelta));
1078:
1079:                        if (digitLen - i > 1 && groupingSize > 0
1080:                                && ((digitLen - i) % groupingSize == 1)) {
1081:                            result.append(grouping);
1082:                        }
1083:                    }
1084:                } else if (!fractionPresent) {
1085:                    // If there is no fraction present, and we haven't printed any
1086:                    // integer digits, then print a zero.
1087:                    result.append(numberConstants.zeroDigit());
1088:                }
1089:
1090:                // Output the decimal separator if we always do so.
1091:                if (decimalSeparatorAlwaysShown || fractionPresent) {
1092:                    result.append(decimal);
1093:                }
1094:
1095:                // To make sure it lead zero will be kept.
1096:                String fracPart = String.valueOf(fracValue + (long) power);
1097:                int fracLen = fracPart.length();
1098:                while (fracPart.charAt(fracLen - 1) == '0'
1099:                        && fracLen > minimumFractionDigits + 1) {
1100:                    fracLen--;
1101:                }
1102:
1103:                for (int i = 1; i < fracLen; i++) {
1104:                    result.append((char) (fracPart.charAt(i) + zeroDelta));
1105:                }
1106:            }
1107:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.