1 16 package com.google.gwt.i18n.client; 17 18 import com.google.gwt.core.client.GWT; 19 import com.google.gwt.i18n.client.constants.CurrencyCodeMapConstants; 20 import com.google.gwt.i18n.client.constants.NumberConstants; 21 22 import java.util.Map ; 23 24 304 public class NumberFormat { 305 306 private static final NumberConstants defaultNumberConstants = (NumberConstants) GWT.create(NumberConstants.class); 308 private static final CurrencyCodeMapConstants defaultCurrencyCodeMapConstants = (CurrencyCodeMapConstants) GWT.create(CurrencyCodeMapConstants.class); 309 310 private static final char PATTERN_ZERO_DIGIT = '0'; 312 private static final char PATTERN_GROUPING_SEPARATOR = ','; 313 private static final char PATTERN_DECIMAL_SEPARATOR = '.'; 314 private static final char PATTERN_PER_MILLE = '\u2030'; 315 private static final char PATTERN_PERCENT = '%'; 316 private static final char PATTERN_DIGIT = '#'; 317 private static final char PATTERN_SEPARATOR = ';'; 318 private static final char PATTERN_EXPONENT = 'E'; 319 private static final char PATTERN_MINUS = '-'; 320 private static final char CURRENCY_SIGN = '\u00A4'; 321 private static final char QUOTE = '\''; 322 323 private static NumberFormat cachedDecimalFormat; 325 private static NumberFormat cachedScientificFormat; 326 private static NumberFormat cachedPercentFormat; 327 private static NumberFormat cachedCurrencyFormat; 328 329 335 public static NumberFormat getCurrencyFormat() { 336 if (cachedCurrencyFormat == null) { 337 cachedCurrencyFormat = new NumberFormat( 338 defaultNumberConstants.currencyPattern(), 339 defaultNumberConstants.defCurrencyCode()); 340 } 341 return cachedCurrencyFormat; 342 } 343 344 350 public static NumberFormat getDecimalFormat() { 351 if (cachedDecimalFormat == null) { 352 cachedDecimalFormat = new NumberFormat( 353 defaultNumberConstants.decimalPattern(), 354 defaultNumberConstants.defCurrencyCode()); 355 } 356 return cachedDecimalFormat; 357 } 358 359 367 public static NumberFormat getFormat(String pattern) { 368 return new NumberFormat(pattern, defaultNumberConstants.defCurrencyCode()); 369 } 370 371 380 public static NumberFormat getFormat(String pattern, String currencyCode) { 381 return new NumberFormat(pattern, currencyCode); 382 } 383 384 390 public static NumberFormat getPercentFormat() { 391 if (cachedPercentFormat == null) { 392 cachedPercentFormat = new NumberFormat( 393 defaultNumberConstants.percentPattern(), 394 defaultNumberConstants.defCurrencyCode()); 395 } 396 return cachedPercentFormat; 397 } 398 399 405 public static NumberFormat getScientificFormat() { 406 if (cachedScientificFormat == null) { 407 cachedScientificFormat = new NumberFormat( 408 defaultNumberConstants.scientificPattern(), 409 defaultNumberConstants.defCurrencyCode()); 410 } 411 return cachedScientificFormat; 412 } 413 414 private final NumberConstants numberConstants; 416 417 private int maximumIntegerDigits = 40; 418 private int minimumIntegerDigits = 1; 419 private int maximumFractionDigits = 3; private int minimumFractionDigits = 0; 421 private int minExponentDigits; 422 423 private String positivePrefix = ""; 424 private String positiveSuffix = ""; 425 private String negativePrefix = "-"; 426 private String negativeSuffix = ""; 427 428 private int multiplier = 1; 430 431 private int groupingSize = 3; 434 435 private boolean decimalSeparatorAlwaysShown = false; 437 438 private boolean isCurrencyFormat = false; 439 440 private boolean useExponentialNotation = false; 442 443 private final String currencySymbol; 445 446 private final String currencyCode; 448 449 private final String pattern; 451 452 463 protected NumberFormat(NumberConstants numberConstants, 464 CurrencyCodeMapConstants currencyCodeMapConstants, String pattern, 465 String currencyCode) { 466 this.numberConstants = numberConstants; 467 this.pattern = pattern; 468 this.currencyCode = currencyCode; 469 470 Map currencyMap = currencyCodeMapConstants.currencyMap(); 471 currencySymbol = (String ) currencyMap.get(currencyCode); 472 473 parsePattern(this.pattern); 474 } 475 476 484 protected NumberFormat(String pattern, String currencyCode) { 485 this(defaultNumberConstants, defaultCurrencyCodeMapConstants, pattern, 486 currencyCode); 487 } 488 489 495 public String format(double number) { 496 StringBuffer result = new StringBuffer (); 497 498 if (Double.isNaN(number)) { 499 result.append(numberConstants.notANumber()); 500 return result.toString(); 501 } 502 503 boolean isNegative = ((number < 0.0) || (number == 0.0 && 1 / number < 0.0)); 504 505 result.append(isNegative ? negativePrefix : positivePrefix); 506 if (Double.isInfinite(number)) { 507 result.append(numberConstants.infinity()); 508 } else { 509 if (isNegative) { 510 number = -number; 511 } 512 number *= multiplier; 513 if (useExponentialNotation) { 514 subformatExponential(number, result); 515 } else { 516 subformatFixed(number, result, minimumIntegerDigits); 517 } 518 } 519 520 result.append(isNegative ? negativeSuffix : positiveSuffix); 521 522 return result.toString(); 523 } 524 525 528 public String getPattern() { 529 return pattern; 530 } 531 532 542 public double parse(String text) { 543 int[] pos = {0}; 544 double result = parse(text, pos); 545 if (pos[0] == 0 || pos[0] != text.length()) { 546 throw new NumberFormatException (text); 547 } 548 return result; 549 } 550 551 569 public double parse(String text, int[] inOutPos) { 570 int start = inOutPos[0]; 571 boolean gotPositive, gotNegative; 572 double ret = 0.0; 573 574 gotPositive = (text.indexOf(positivePrefix, inOutPos[0]) == inOutPos[0]); 575 gotNegative = (text.indexOf(negativePrefix, inOutPos[0]) == inOutPos[0]); 576 577 if (gotPositive && gotNegative) { 578 if (positivePrefix.length() > negativePrefix.length()) { 579 gotNegative = false; 580 } else if (positivePrefix.length() < negativePrefix.length()) { 581 gotPositive = false; 582 } 583 } 584 585 if (gotPositive) { 586 inOutPos[0] += positivePrefix.length(); 587 } else if (gotNegative) { 588 inOutPos[0] += negativePrefix.length(); 589 } 590 591 if (text.indexOf(numberConstants.infinity(), inOutPos[0]) == inOutPos[0]) { 593 inOutPos[0] += numberConstants.infinity().length(); 594 ret = Double.POSITIVE_INFINITY; 595 } else if (text.indexOf(numberConstants.notANumber(), inOutPos[0]) == inOutPos[0]) { 596 inOutPos[0] += numberConstants.notANumber().length(); 597 ret = Double.NaN; 598 } else { 599 ret = parseNumber(text, inOutPos); 600 } 601 602 if (gotPositive) { 604 if (!(text.indexOf(positiveSuffix, inOutPos[0]) == inOutPos[0])) { 605 inOutPos[0] = start; 606 return 0.0; 607 } 608 inOutPos[0] += positiveSuffix.length(); 609 } else if (gotNegative) { 610 if (!(text.indexOf(negativeSuffix, inOutPos[0]) == inOutPos[0])) { 611 inOutPos[0] = start; 612 return 0.0; 613 } 614 inOutPos[0] += negativeSuffix.length(); 615 } 616 617 if (gotNegative) { 618 ret = -ret; 619 } 620 621 return ret; 622 } 623 624 630 private void addExponentPart(int exponent, StringBuffer result) { 631 result.append(numberConstants.exponentialSymbol()); 632 633 if (exponent < 0) { 634 exponent = -exponent; 635 result.append(numberConstants.minusSign()); 636 } 637 638 String exponentDigits = String.valueOf(exponent); 639 for (int i = exponentDigits.length(); i < minExponentDigits; ++i) { 640 result.append(numberConstants.zeroDigit()); 641 } 642 result.append(exponentDigits); 643 } 644 645 652 private int getDigit(char ch) { 653 if ('0' <= ch && ch <= '0' + 9) { 654 return (ch - '0'); 655 } else { 656 char zeroChar = numberConstants.zeroDigit().charAt(0); 657 return ((zeroChar <= ch && ch <= zeroChar + 9) ? (ch - zeroChar) : -1); 658 } 659 } 660 661 669 private int parseAffix(String pattern, int start, StringBuffer affix) { 670 affix.delete(0, affix.length()); 671 boolean inQuote = false; 672 int len = pattern.length(); 673 674 for (int pos = start; pos < len; ++pos) { 675 char ch = pattern.charAt(pos); 676 if (ch == QUOTE) { 677 if ((pos + 1) < len && pattern.charAt(pos + 1) == QUOTE) { 678 ++pos; 679 affix.append("'"); } else { 681 inQuote = !inQuote; 682 } 683 continue; 684 } 685 686 if (inQuote) { 687 affix.append(ch); 688 } else { 689 switch (ch) { 690 case PATTERN_DIGIT: 691 case PATTERN_ZERO_DIGIT: 692 case PATTERN_GROUPING_SEPARATOR: 693 case PATTERN_DECIMAL_SEPARATOR: 694 case PATTERN_SEPARATOR: 695 return pos - start; 696 case CURRENCY_SIGN: 697 isCurrencyFormat = true; 698 if ((pos + 1) < len && pattern.charAt(pos + 1) == CURRENCY_SIGN) { 699 ++pos; 700 affix.append(currencyCode); 701 } else { 702 affix.append(currencySymbol); 703 } 704 break; 705 case PATTERN_PERCENT: 706 if (multiplier != 1) { 707 throw new IllegalArgumentException ( 708 "Too many percent/per mille characters in pattern \"" 709 + pattern + '"'); 710 } 711 multiplier = 100; 712 affix.append(numberConstants.percent()); 713 break; 714 case PATTERN_PER_MILLE: 715 if (multiplier != 1) { 716 throw new IllegalArgumentException ( 717 "Too many percent/per mille characters in pattern \"" 718 + pattern + '"'); 719 } 720 multiplier = 1000; 721 affix.append(numberConstants.perMill()); 722 break; 723 case PATTERN_MINUS: 724 affix.append("-"); 725 break; 726 default: 727 affix.append(ch); 728 } 729 } 730 } 731 return len - start; 732 } 733 734 743 private double parseNumber(String text, int[] pos) { 744 double ret; 745 boolean sawDecimal = false; 746 boolean sawExponent = false; 747 boolean sawDigit = false; 748 int scale = 1; 749 String decimal = isCurrencyFormat ? numberConstants.monetarySeparator() 750 : numberConstants.decimalSeparator(); 751 String grouping = isCurrencyFormat 752 ? numberConstants.monetaryGroupingSeparator() 753 : numberConstants.groupingSeparator(); 754 String exponentChar = numberConstants.exponentialSymbol(); 755 756 StringBuffer normalizedText = new StringBuffer (); 757 for (; pos[0] < text.length(); ++pos[0]) { 758 char ch = text.charAt(pos[0]); 759 int digit = getDigit(ch); 760 if (digit >= 0 && digit <= 9) { 761 normalizedText.append((char) (digit + '0')); 762 sawDigit = true; 763 } else if (ch == decimal.charAt(0)) { 764 if (sawDecimal || sawExponent) { 765 break; 766 } 767 normalizedText.append('.'); 768 sawDecimal = true; 769 } else if (ch == grouping.charAt(0)) { 770 if (sawDecimal || sawExponent) { 771 break; 772 } 773 continue; 774 } else if (ch == exponentChar.charAt(0)) { 775 if (sawExponent) { 776 break; 777 } 778 normalizedText.append('E'); 779 sawExponent = true; 780 } else if (ch == '+' || ch == '-') { 781 normalizedText.append(ch); 782 } else if (ch == numberConstants.percent().charAt(0)) { 783 if (scale != 1) { 784 break; 785 } 786 scale = 100; 787 if (sawDigit) { 788 ++pos[0]; 789 break; 790 } 791 } else if (ch == numberConstants.perMill().charAt(0)) { 792 if (scale != 1) { 793 break; 794 } 795 scale = 1000; 796 if (sawDigit) { 797 ++pos[0]; 798 break; 799 } 800 } else { 801 break; 802 } 803 } 804 805 try { 806 ret = Double.parseDouble(normalizedText.toString()); 807 ret = ret / scale; 808 return ret; 809 } catch (NumberFormatException e) { 810 return 0.0; 811 } 812 } 813 814 819 private void parsePattern(String pattern) { 820 int pos = 0; 821 StringBuffer affix = new StringBuffer (); 822 823 pos += parseAffix(pattern, pos, affix); 824 positivePrefix = affix.toString(); 825 int posPartLen = parseTrunk(pattern, pos); 826 pos += posPartLen; 827 pos += parseAffix(pattern, pos, affix); 828 positiveSuffix = affix.toString(); 829 830 if (pos < pattern.length() && pattern.charAt(pos) == PATTERN_SEPARATOR) { 831 ++pos; 832 pos += parseAffix(pattern, pos, affix); 833 negativePrefix = affix.toString(); 834 pos += posPartLen; 837 pos += parseAffix(pattern, pos, affix); 838 negativeSuffix = affix.toString(); 839 } 840 } 841 842 849 private int parseTrunk(String pattern, int start) { 850 int decimalPos = -1; 851 int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0; 852 byte groupingCount = -1; 853 854 int len = pattern.length(); 855 int pos = start; 856 boolean loop = true; 857 for (; (pos < len) && loop; ++pos) { 858 char ch = pattern.charAt(pos); 859 switch (ch) { 860 case PATTERN_DIGIT: 861 if (zeroDigitCount > 0) { 862 ++digitRightCount; 863 } else { 864 ++digitLeftCount; 865 } 866 if (groupingCount >= 0 && decimalPos < 0) { 867 ++groupingCount; 868 } 869 break; 870 case PATTERN_ZERO_DIGIT: 871 if (digitRightCount > 0) { 872 throw new IllegalArgumentException ("Unexpected '0' in pattern \"" 873 + pattern + '"'); 874 } 875 ++zeroDigitCount; 876 if (groupingCount >= 0 && decimalPos < 0) { 877 ++groupingCount; 878 } 879 break; 880 case PATTERN_GROUPING_SEPARATOR: 881 groupingCount = 0; 882 break; 883 case PATTERN_DECIMAL_SEPARATOR: 884 if (decimalPos >= 0) { 885 throw new IllegalArgumentException ( 886 "Multiple decimal separators in pattern \"" + pattern + '"'); 887 } 888 decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; 889 break; 890 case PATTERN_EXPONENT: 891 if (useExponentialNotation) { 892 throw new IllegalArgumentException ("Multiple exponential " 893 + "symbols in pattern \"" + pattern + '"'); 894 } 895 useExponentialNotation = true; 896 minExponentDigits = 0; 897 898 while ((pos + 1) < len 901 && pattern.charAt(pos + 1) == numberConstants.zeroDigit().charAt( 902 0)) { 903 ++pos; 904 ++minExponentDigits; 905 } 906 907 if ((digitLeftCount + zeroDigitCount) < 1 || minExponentDigits < 1) { 908 throw new IllegalArgumentException ("Malformed exponential " 909 + "pattern \"" + pattern + '"'); 910 } 911 loop = false; 912 break; 913 default: 914 --pos; 915 loop = false; 916 break; 917 } 918 } 919 920 if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { 921 int n = decimalPos; 923 if (n == 0) { ++n; 925 } 926 digitRightCount = digitLeftCount - n; 927 digitLeftCount = n - 1; 928 zeroDigitCount = 1; 929 } 930 931 if ((decimalPos < 0 && digitRightCount > 0) 933 || (decimalPos >= 0 && (decimalPos < digitLeftCount || decimalPos > (digitLeftCount + zeroDigitCount))) 934 || groupingCount == 0) { 935 throw new IllegalArgumentException ("Malformed pattern \"" + pattern + '"'); 936 } 937 int totalDigits = digitLeftCount + zeroDigitCount + digitRightCount; 938 939 maximumFractionDigits = (decimalPos >= 0 ? (totalDigits - decimalPos) : 0); 940 if (decimalPos >= 0) { 941 minimumFractionDigits = digitLeftCount + zeroDigitCount - decimalPos; 942 if (minimumFractionDigits < 0) { 943 minimumFractionDigits = 0; 944 } 945 } 946 947 952 int effectiveDecimalPos = decimalPos >= 0 ? decimalPos : totalDigits; 953 minimumIntegerDigits = effectiveDecimalPos - digitLeftCount; 954 if (useExponentialNotation) { 955 maximumIntegerDigits = digitLeftCount + minimumIntegerDigits; 956 957 if (maximumFractionDigits == 0 && minimumIntegerDigits == 0) { 959 minimumIntegerDigits = 1; 960 } 961 } 962 963 this.groupingSize = (groupingCount > 0) ? groupingCount : 0; 964 decimalSeparatorAlwaysShown = (decimalPos == 0 || decimalPos == totalDigits); 965 966 return pos - start; 967 } 968 969 975 private void subformatExponential(double number, StringBuffer result) { 976 if (number == 0.0) { 977 subformatFixed(number, result, minimumIntegerDigits); 978 addExponentPart(0, result); 979 return; 980 } 981 982 int exponent = (int) Math.floor(Math.log(number) / Math.log(10)); 983 number /= Math.pow(10, exponent); 984 985 int minIntDigits = minimumIntegerDigits; 986 if (maximumIntegerDigits > 1 && maximumIntegerDigits > minimumIntegerDigits) { 987 while ((exponent % maximumIntegerDigits) != 0) { 993 number *= 10; 994 exponent--; 995 } 996 minIntDigits = 1; 997 } else { 998 if (minimumIntegerDigits < 1) { 1000 exponent++; 1001 number /= 10; 1002 } else { 1003 for (int i = 1; i < minimumIntegerDigits; i++) { 1004 exponent--; 1005 number *= 10; 1006 } 1007 } 1008 } 1009 1010 subformatFixed(number, result, minIntDigits); 1011 addExponentPart(exponent, result); 1012 } 1013 1014 1022 private void subformatFixed(double number, StringBuffer result, 1023 int minIntDigits) { 1024 double power = Math.pow(10, maximumFractionDigits); 1026 number = Math.round(number * power); 1027 long intValue = (long) Math.floor(number / power); 1028 long fracValue = (long) Math.floor(number - intValue * power); 1029 1030 boolean fractionPresent = (minimumFractionDigits > 0) || (fracValue > 0); 1031 1032 String intPart = String.valueOf(intValue); 1033 String grouping = isCurrencyFormat 1034 ? numberConstants.monetaryGroupingSeparator() 1035 : numberConstants.groupingSeparator(); 1036 String decimal = isCurrencyFormat ? numberConstants.monetarySeparator() 1037 : numberConstants.decimalSeparator(); 1038 1039 int zeroDelta = numberConstants.zeroDigit().charAt(0) - '0'; 1040 int digitLen = intPart.length(); 1041 1042 if (intValue > 0 || minIntDigits > 0) { 1043 for (int i = digitLen; i < minIntDigits; i++) { 1044 result.append(numberConstants.zeroDigit()); 1045 } 1046 1047 for (int i = 0; i < digitLen; i++) { 1048 result.append((char) (intPart.charAt(i) + zeroDelta)); 1049 1050 if (digitLen - i > 1 && groupingSize > 0 1051 && ((digitLen - i) % groupingSize == 1)) { 1052 result.append(grouping); 1053 } 1054 } 1055 } else if (!fractionPresent) { 1056 result.append(numberConstants.zeroDigit()); 1059 } 1060 1061 if (decimalSeparatorAlwaysShown || fractionPresent) { 1063 result.append(decimal); 1064 } 1065 1066 String fracPart = String.valueOf(fracValue + (long) power); 1068 int fracLen = fracPart.length(); 1069 while (fracPart.charAt(fracLen - 1) == '0' 1070 && fracLen > minimumFractionDigits + 1) { 1071 fracLen--; 1072 } 1073 1074 for (int i = 1; i < fracLen; i++) { 1075 result.append((char) (fracPart.charAt(i) + zeroDelta)); 1076 } 1077 } 1078} 1079 | Popular Tags |