001 /* 002 // $Id: //open/mondrian/src/main/mondrian/util/Format.java#30 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2000-2002 Kana Software, Inc. 007 // Copyright (C) 2001-2008 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 // 011 // jhyde, 2 November, 2000 012 */ 013 014 package mondrian.util; 015 import mondrian.olap.Util; 016 017 import java.io.PrintWriter; 018 import java.math.BigDecimal; 019 import java.math.BigInteger; 020 import java.text.*; 021 import java.util.*; 022 023 /** 024 * <code>Format</code> formats numbers, strings and dates according to the 025 * same specification as Visual Basic's 026 * <code>format()</code> function. This function is described in more detail 027 * <a href="http://www.apostate.com/programming/vb-format.html">here</a>. We 028 * have made the following enhancements to this specification:<ul> 029 * 030 * <li>if the international currency symbol (¤) occurs in a format 031 * string, it is translated to the locale's currency symbol.</li> 032 * 033 * <li>the format string "Currency" is translated to the locale's currency 034 * format string. Negative currency values appear in parentheses.</li> 035 * 036 * <li>the string "USD" (abbreviation for U.S. Dollars) may occur in a format 037 * string.</li> 038 * 039 * </ul> 040 * 041 * <p>One format object can be used to format multiple values, thereby 042 * amortizing the time required to parse the format string. Example:</p> 043 * 044 * <pre><code> 045 * double[] values; 046 * Format format = new Format("##,##0.###;(##,##0.###);;Nil"); 047 * for (int i = 0; i < values.length; i++) { 048 * System.out.println("Value #" + i + " is " + format.format(values[i])); 049 * } 050 * </code></pre> 051 * 052 * <p>Still to be implemented:<ul> 053 * 054 * <li>String formatting (upper-case, lower-case, fill from left/right)</li> 055 * 056 * <li>Use client's timezone for printing times.</li> 057 * 058 * </ul> 059 * 060 * @author jhyde 061 * @version $Id: //open/mondrian/src/main/mondrian/util/Format.java#30 $ 062 */ 063 public class Format { 064 private String formatString; 065 private BasicFormat format; 066 private FormatLocale locale; 067 private static final FieldPosition dummyFieldPos = createDummyFieldPos(); 068 069 /** 070 * Maximum number of entries in the format cache used by 071 * {@link #get(String, java.util.Locale)}. 072 */ 073 public static final int CacheLimit = 1000; 074 075 /** 076 * Gets the dummy implementation of {@link FieldPosition} which the JDK 077 * uses when you don't care about the status of a call to 078 * {@link Format#format}. 079 */ 080 private static FieldPosition createDummyFieldPos() { 081 final DummyDecimalFormat format1 = new DummyDecimalFormat(); 082 format1.format(0.0); 083 return format1.pos; 084 } 085 086 /** 087 * Maps (formatString, locale) pairs to {@link Format} objects. 088 * 089 * <p>If the number of entries in the cache exceeds 1000, 090 */ 091 private static Map<String, Format> cache = 092 new LinkedHashMap<String, Format>() { 093 public boolean removeEldestEntry(Map.Entry<String, Format> entry) { 094 return size() > CacheLimit; 095 } 096 }; 097 098 static final char[] digits = { 099 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 100 }; 101 102 static final char thousandSeparator_en = ','; 103 static final char decimalPlaceholder_en = '.'; 104 static final String dateSeparator_en = "/"; 105 static final String timeSeparator_en = ":"; 106 static final String currencySymbol_en = "$"; 107 static final String currencyFormat_en = "$#,##0.00"; 108 static final String[] daysOfWeekShort_en = { 109 "", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 110 }; 111 static final String[] daysOfWeekLong_en = { 112 "", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", 113 "Saturday" 114 }; 115 static final String[] monthsShort_en = { 116 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 117 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "", 118 }; 119 static final String[] monthsLong_en = { 120 "January", "February", "March", "April", "May", "June", 121 "July", "August", "September", "October", "November", "December", "", 122 }; 123 static final char intlCurrencySymbol = '\u08a4'; 124 125 /** 126 * Maps strings representing locales (for example, "en_US_Boston", "en_US", 127 * "en", or "" for the default) to a {@link Format.FormatLocale}. 128 */ 129 private static final Map<String, FormatLocale> mapLocaleToFormatLocale = 130 new HashMap<String, FormatLocale>(); 131 132 /** 133 * Locale for US English, also the default for English and for all 134 * locales. 135 */ 136 static final FormatLocale locale_US = createLocale( 137 '\0', '\0', null, null, null, null, null, null, null, null, 138 Locale.US); 139 140 /** 141 * Formats an object using a format string, according to a given locale. 142 * 143 * <p>If you need to format many objects using the same format string, 144 * create a formatter object using 145 * {@link mondrian.util.Format#Format(String, java.util.Locale)}. 146 */ 147 static String format(Object o, String formatString, Locale locale) 148 { 149 Format format = new Format(formatString, locale); 150 return format.format(o); 151 } 152 153 static class Token { 154 int code; 155 int flags; 156 String token; 157 158 Token(int code, int flags, String token) 159 { 160 this.code = code; 161 this.flags = flags; 162 this.token = token; 163 } 164 165 boolean isSpecial() 166 { 167 return (flags & SPECIAL) == SPECIAL; 168 } 169 170 boolean isNumeric() 171 { 172 return (flags & NUMERIC) == NUMERIC; 173 } 174 175 boolean isDate() 176 { 177 return (flags & DATE) == DATE; 178 } 179 180 boolean isString() 181 { 182 return (flags & STRING) == STRING; 183 } 184 185 BasicFormat makeFormat(FormatLocale locale) 186 { 187 if (isDate()) { 188 return new DateFormat(code, token, locale, false); 189 } else if (isNumeric()) { 190 return new LiteralFormat(code, token); 191 } else if (isString()) { 192 throw new Error(); 193 } else { 194 return new LiteralFormat(token); 195 } 196 } 197 } 198 199 /** 200 * BasicFormat is the interface implemented by the classes which do all 201 * the work. Whereas {@link Format} has only one method for formatting, 202 * {@link Format#format(Object)}, this class provides methods for several 203 * primitive types. To make it easy to combine formatting objects, all 204 * methods write to a {@link PrintWriter}. 205 * 206 * The base implementation of most of these methods throws an error, there 207 * is no requirement that a derived class implements all of these methods. 208 * It is up to {@link Format#parseFormatString} to ensure that, for example, 209 * the {@link #format(double,StringBuilder)} method is never called for 210 * {@link DateFormat}. 211 */ 212 static class BasicFormat { 213 int code; 214 215 BasicFormat() { 216 this(0); 217 } 218 219 BasicFormat(int code) { 220 this.code = code; 221 } 222 223 boolean isNumeric() { 224 return false; 225 } 226 227 boolean isDate() { 228 return false; 229 } 230 231 boolean isString() { 232 return false; 233 } 234 235 void formatNull(StringBuilder buf) { 236 } 237 238 void format(double d, StringBuilder buf) { 239 throw new Error(); 240 } 241 242 void format(long n, StringBuilder buf) { 243 throw new Error(); 244 } 245 246 void format(String s, StringBuilder buf) { 247 throw new Error(); 248 } 249 250 void format(Date date, StringBuilder buf) { 251 Calendar calendar = Calendar.getInstance(); // todo: use locale 252 calendar.setTime(date); 253 format(calendar, buf); 254 } 255 256 void format(Calendar calendar, StringBuilder buf) { 257 throw new Error(); 258 } 259 260 /** 261 * Returns whether this format can handle a given value. 262 * 263 * <p>Usually returns true; 264 * one notable exception is a format for negative numbers which 265 * causes the number to be underflow to zero and therefore be 266 * ineligible for the negative format. 267 * 268 * @param n value 269 * @return Whether this format is applicable for a given value 270 */ 271 boolean isApplicableTo(double n) { 272 return true; 273 } 274 275 /** 276 * Returns whether this format can handle a given value. 277 * 278 * <p>Usually returns true; 279 * one notable exception is a format for negative numbers which 280 * causes the number to be underflow to zero and therefore be 281 * ineligible for the negative format. 282 * 283 * @param n value 284 * @return Whether this format is applicable for a given value 285 */ 286 boolean isApplicableTo(long n) { 287 return true; 288 } 289 } 290 291 /** 292 * AlternateFormat is an implementation of {@link Format.BasicFormat} which 293 * allows a different format to be used for different kinds of values. If 294 * there are 4 formats, purposes are as follows:<ol> 295 * <li>positive numbers</li> 296 * <li>negative numbers</li> 297 * <li>zero</li> 298 * <li>null values</li> 299 * </ol> 300 * 301 * If there are fewer than 4 formats, the first is used as a fall-back. 302 * See the <a href="http://www.apostate.com/programming/vb-format.html">the 303 * visual basic format specification</a> for more details. 304 */ 305 static class AlternateFormat extends BasicFormat { 306 final BasicFormat[] formats; 307 308 AlternateFormat(BasicFormat[] formats) 309 { 310 this.formats = formats; 311 assert formats.length >= 2; 312 } 313 314 void formatNull(StringBuilder buf) { 315 if (formats.length >= 4) { 316 formats[3].format(0, buf); 317 } else { 318 super.formatNull(buf); 319 } 320 } 321 322 void format(double n, StringBuilder buf) { 323 if (formats.length == 0) { 324 buf.append(n); 325 } else { 326 int i; 327 if (n == 0 && 328 formats.length >= 3 && 329 formats[2] != null) { 330 i = 2; 331 } else if (n < 0 && 332 formats.length >= 2 && 333 formats[1] != null) { 334 if (formats[1].isApplicableTo(n)) { 335 n = -n; 336 i = 1; 337 } else { 338 // Does not fit into the negative mask, so use the nil 339 // mask. For example, "#.0;(#.0);Nil" formats -0.0001 340 // as "Nil". 341 i = 2; 342 } 343 } else { 344 i = 0; 345 } 346 formats[i].format(n, buf); 347 } 348 } 349 350 void format(long n, StringBuilder buf) { 351 if (formats.length == 0) { 352 buf.append(n); 353 } else { 354 int i; 355 if (n == 0 && 356 formats.length >= 3 && 357 formats[2] != null) { 358 i = 2; 359 } else if (n < 0 && 360 formats.length >= 2 && 361 formats[1] != null && 362 formats[1].isApplicableTo(n)) { 363 n = -n; 364 i = 1; 365 } else { 366 i = 0; 367 } 368 formats[i].format(n, buf); 369 } 370 } 371 372 void format(String s, StringBuilder buf) { 373 formats[0].format(s, buf); 374 } 375 376 void format(Date date, StringBuilder buf) { 377 formats[0].format(date, buf); 378 } 379 380 void format(Calendar calendar, StringBuilder buf) { 381 formats[0].format(calendar, buf); 382 } 383 } 384 385 /** 386 * LiteralFormat is an implementation of {@link Format.BasicFormat} which 387 * prints a constant value, regardless of the value to be formatted. 388 * 389 * @see CompoundFormat 390 */ 391 static class LiteralFormat extends BasicFormat 392 { 393 String s; 394 395 LiteralFormat(String s) 396 { 397 this(FORMAT_LITERAL, s); 398 } 399 400 LiteralFormat(int code, String s) 401 { 402 super(code); 403 this.s = s; 404 } 405 406 void format(double d, StringBuilder buf) { 407 buf.append(s); 408 } 409 410 void format(long n, StringBuilder buf) { 411 buf.append(s); 412 } 413 414 void format(String s, StringBuilder buf) { 415 buf.append(s); 416 } 417 418 void format(Date date, StringBuilder buf) { 419 buf.append(s); 420 } 421 422 void format(Calendar calendar, StringBuilder buf) { 423 buf.append(s); 424 } 425 } 426 427 /** 428 * CompoundFormat is an implementation of {@link Format.BasicFormat} where 429 * each value is formatted by applying a sequence of format elements. Each 430 * format element is itself a format. 431 * 432 * @see AlternateFormat 433 */ 434 static class CompoundFormat extends BasicFormat 435 { 436 final BasicFormat[] formats; 437 CompoundFormat(BasicFormat[] formats) 438 { 439 this.formats = formats; 440 assert formats.length >= 2; 441 } 442 443 void formatNull(StringBuilder buf) { 444 for (int i = 0; i < formats.length; i++) { 445 formats[i].formatNull(buf); 446 } 447 } 448 449 void format(double v, StringBuilder buf) { 450 for (int i = 0; i < formats.length; i++) { 451 formats[i].format(v, buf); 452 } 453 } 454 455 void format(long v, StringBuilder buf) { 456 for (int i = 0; i < formats.length; i++) { 457 formats[i].format(v, buf); 458 } 459 } 460 461 void format(String v, StringBuilder buf) { 462 for (int i = 0; i < formats.length; i++) { 463 formats[i].format(v, buf); 464 } 465 } 466 467 void format(Date v, StringBuilder buf) { 468 for (int i = 0; i < formats.length; i++) { 469 formats[i].format(v, buf); 470 } 471 } 472 473 void format(Calendar v, StringBuilder buf) { 474 for (int i = 0; i < formats.length; i++) { 475 formats[i].format(v, buf); 476 } 477 } 478 479 boolean isApplicableTo(double n) { 480 for (int i = 0; i < formats.length; i++) { 481 if (!formats[i].isApplicableTo(n)) { 482 return false; 483 } 484 } 485 return true; 486 } 487 488 } 489 490 /** 491 * JavaFormat is an implementation of {@link Format.BasicFormat} which 492 * prints values using Java's default formatting for their type. 493 * <code>null</code> values appear as an empty string. 494 */ 495 static class JavaFormat extends BasicFormat 496 { 497 private final NumberFormat numberFormat; 498 private final java.text.DateFormat dateFormat; 499 500 JavaFormat(Locale locale) 501 { 502 this.numberFormat = NumberFormat.getNumberInstance(locale); 503 this.dateFormat = java.text.DateFormat.getDateInstance( 504 java.text.DateFormat.SHORT, locale); 505 } 506 507 // No need to override format(Object,PrintWriter) or 508 // format(Date,PrintWriter). 509 510 void format(double d, StringBuilder buf) { 511 // NOTE (jhyde, 2006/12/1): We'd use 512 // NumberFormat(double,StringBuilder,FieldPosition) if it existed. 513 buf.append(numberFormat.format(d)); 514 } 515 516 void format(long n, StringBuilder buf) { 517 // NOTE (jhyde, 2006/12/1): We'd use 518 // NumberFormat(long,StringBuilder,FieldPosition) if it existed. 519 buf.append(numberFormat.format(n)); 520 } 521 522 void format(String s, StringBuilder buf) { 523 buf.append(s); 524 } 525 526 void format(Calendar calendar, StringBuilder buf) { 527 // NOTE (jhyde, 2006/12/1): We'd use 528 // NumberFormat(Date,StringBuilder,FieldPosition) if it existed. 529 buf.append(dateFormat.format(calendar.getTime())); 530 } 531 } 532 533 /** 534 * FallbackFormat catches un-handled datatypes and prints the original 535 * format string. Better than giving an error. Abstract base class for 536 * NumericFormat and DateFormat. 537 */ 538 static abstract class FallbackFormat extends BasicFormat 539 { 540 String token; 541 542 FallbackFormat(int code, String token) 543 { 544 super(code); 545 this.token = token; 546 } 547 548 void format(double d, StringBuilder buf) { 549 buf.append(token); 550 } 551 552 void format(long n, StringBuilder buf) { 553 buf.append(token); 554 } 555 556 void format(String s, StringBuilder buf) { 557 buf.append(token); 558 } 559 560 void format(Calendar calendar, StringBuilder buf) { 561 buf.append(token); 562 } 563 } 564 565 /** 566 * NumericFormat is an implementation of {@link Format.BasicFormat} which 567 * prints numbers with a given number of decimal places, leading zeroes, in 568 * exponential notation, etc. 569 * 570 * <p>It is implemented using {@link FloatingDecimal}, which is a 571 * barely-modified version of <code>java.lang.FloatingDecimal</code>. 572 */ 573 static class NumericFormat extends FallbackFormat 574 { 575 FormatLocale locale; 576 int digitsLeftOfPoint; 577 int zeroesLeftOfPoint; 578 int digitsRightOfPoint; 579 int zeroesRightOfPoint; 580 int digitsRightOfExp; 581 int zeroesRightOfExp; 582 583 /** 584 * Number of decimal places to shift the number left before 585 * formatting it: 2 means multiply by 100; -3 means divide by 586 * 1000. 587 */ 588 int decimalShift; 589 char expChar; 590 boolean expSign; 591 boolean useDecimal; // not used 592 boolean useThouSep; 593 594 NumericFormat( 595 String token, FormatLocale locale, 596 int expFormat, 597 int digitsLeftOfPoint, int zeroesLeftOfPoint, 598 int digitsRightOfPoint, int zeroesRightOfPoint, 599 int digitsRightOfExp, int zeroesRightOfExp, 600 boolean useDecimal, boolean useThouSep) 601 { 602 super(FORMAT_NULL, token); 603 this.locale = locale; 604 switch (expFormat) { 605 case FORMAT_E_MINUS_UPPER: 606 this.expChar = 'E'; 607 this.expSign = false; 608 break; 609 case FORMAT_E_PLUS_UPPER: 610 this.expChar = 'E'; 611 this.expSign = true; 612 break; 613 case FORMAT_E_MINUS_LOWER: 614 this.expChar = 'e'; 615 this.expSign = false; 616 break; 617 case FORMAT_E_PLUS_LOWER: 618 this.expChar = 'e'; 619 this.expSign = true; 620 break; 621 default: 622 this.expChar = 0; 623 this.expSign = false; 624 } 625 this.digitsLeftOfPoint = digitsLeftOfPoint; 626 this.zeroesLeftOfPoint = zeroesLeftOfPoint; 627 this.digitsRightOfPoint = digitsRightOfPoint; 628 this.zeroesRightOfPoint = zeroesRightOfPoint; 629 this.digitsRightOfExp = digitsRightOfExp; 630 this.zeroesRightOfExp = zeroesRightOfExp; 631 this.useDecimal = useDecimal; 632 this.useThouSep = useThouSep; 633 this.decimalShift = 0; // set later 634 } 635 636 void format(double n, StringBuilder buf) 637 { 638 FloatingDecimal fd = new FloatingDecimal(n); 639 fd.shift(decimalShift); 640 final int formatDigitsRightOfPoint = 641 zeroesRightOfPoint + digitsRightOfPoint; 642 if (n == 0.0 || (n < 0 && !shows(fd, formatDigitsRightOfPoint))) { 643 // Underflow of negative number. Make it zero, so there is no 644 // '-' sign. 645 n = 0; 646 fd = new FloatingDecimal(0); 647 } 648 String s = fd.toJavaFormatString( 649 zeroesLeftOfPoint, 650 locale.decimalPlaceholder, 651 zeroesRightOfPoint, 652 formatDigitsRightOfPoint, 653 expChar, 654 expSign, 655 zeroesRightOfExp, 656 useThouSep ? locale.thousandSeparator : '\0'); 657 buf.append(s); 658 } 659 660 boolean isApplicableTo(double n) { 661 if (n >= 0) { 662 return true; 663 } 664 FloatingDecimal fd = new FloatingDecimal(n); 665 fd.shift(decimalShift); 666 final int formatDigitsRightOfPoint = 667 zeroesRightOfPoint + digitsRightOfPoint; 668 return shows(fd, formatDigitsRightOfPoint); 669 } 670 671 private static boolean shows( 672 FloatingDecimal fd, int formatDigitsRightOfPoint) { 673 final int i0 = - fd.decExponent - formatDigitsRightOfPoint; 674 if (i0 < 0) { 675 return true; 676 } 677 if (i0 > 0) { 678 return false; 679 } 680 if (fd.digits[0] >= '5') { 681 return true; 682 } 683 return false; 684 } 685 686 void format(long n, StringBuilder buf) 687 { 688 mondrian.util.Format.FloatingDecimal fd 689 = new mondrian.util.Format.FloatingDecimal(n); 690 fd.shift(decimalShift); 691 String s = fd.toJavaFormatString( 692 zeroesLeftOfPoint, 693 locale.decimalPlaceholder, 694 zeroesRightOfPoint, 695 zeroesRightOfPoint + digitsRightOfPoint, 696 expChar, 697 expSign, 698 zeroesRightOfExp, 699 useThouSep ? locale.thousandSeparator : '\0'); 700 buf.append(s); 701 } 702 } 703 704 /** 705 * DateFormat is an element of a {@link Format.CompoundFormat} which has a 706 * value when applied to a {@link Calendar} object. (Values of type {@link 707 * Date} are automatically converted into {@link Calendar}s when you call 708 * {@link Format.BasicFormat#format(java.util.Date,StringBuilder)} calls to format 709 * other kinds of values give a runtime error.) 710 * 711 * <p>In a typical use of this class, a format string such as "m/d/yy" is 712 * parsed into DateFormat objects for "m", "d", and "yy", and {@link 713 * LiteralFormat} objects for "/". A {@link Format.CompoundFormat} object 714 * is created to bind them together. 715 */ 716 static class DateFormat extends FallbackFormat 717 { 718 FormatLocale locale; 719 boolean twelveHourClock; 720 721 DateFormat(int code, String s, FormatLocale locale, boolean twelveHourClock) 722 { 723 super(code, s); 724 this.locale = locale; 725 this.twelveHourClock = twelveHourClock; 726 } 727 728 void setTwelveHourClock(boolean twelveHourClock) 729 { 730 this.twelveHourClock = twelveHourClock; 731 } 732 733 void format(Calendar calendar, StringBuilder buf) 734 { 735 format(code, calendar, buf); 736 } 737 738 private void format(int code, Calendar calendar, StringBuilder buf) 739 { 740 switch (code) { 741 case FORMAT_C: 742 { 743 boolean dateSet = !( 744 calendar.get(Calendar.DAY_OF_YEAR) == 0 && 745 calendar.get(Calendar.YEAR) == 0); 746 boolean timeSet = !( 747 calendar.get(Calendar.SECOND) == 0 && 748 calendar.get(Calendar.MINUTE) == 0 && 749 calendar.get(Calendar.HOUR) == 0); 750 if (dateSet) { 751 format(FORMAT_DDDDD, calendar, buf); 752 } 753 if (dateSet && timeSet) { 754 buf.append(' '); 755 } 756 if (timeSet) { 757 format(FORMAT_TTTTT, calendar, buf); 758 } 759 break; 760 } 761 case FORMAT_D: 762 { 763 int d = calendar.get(Calendar.DAY_OF_MONTH); 764 buf.append(d); 765 break; 766 } 767 case FORMAT_DD: 768 { 769 int d = calendar.get(Calendar.DAY_OF_MONTH); 770 if (d < 10) { 771 buf.append('0'); 772 } 773 buf.append(d); 774 break; 775 } 776 case FORMAT_DDD: 777 { 778 int dow = calendar.get(Calendar.DAY_OF_WEEK); 779 buf.append(locale.daysOfWeekShort[dow]); // e.g. Sun 780 break; 781 } 782 case FORMAT_DDDD: 783 { 784 int dow = calendar.get(Calendar.DAY_OF_WEEK); 785 buf.append(locale.daysOfWeekLong[dow]); // e.g. Sunday 786 break; 787 } 788 case FORMAT_DDDDD: 789 { 790 // Officially, we should use the system's short date 791 // format. But for now, we always print using the default 792 // format, m/d/yy. 793 format(FORMAT_M, calendar,buf); 794 buf.append(locale.dateSeparator); 795 format(FORMAT_D, calendar,buf); 796 buf.append(locale.dateSeparator); 797 format(FORMAT_YY, calendar,buf); 798 break; 799 } 800 case FORMAT_DDDDDD: 801 { 802 format(FORMAT_MMMM_UPPER, calendar, buf); 803 buf.append(" "); 804 format(FORMAT_DD, calendar, buf); 805 buf.append(", "); 806 format(FORMAT_YYYY, calendar, buf); 807 break; 808 } 809 case FORMAT_W: 810 { 811 int dow = calendar.get(Calendar.DAY_OF_WEEK); 812 buf.append(dow); 813 break; 814 } 815 case FORMAT_WW: 816 { 817 int woy = calendar.get(Calendar.WEEK_OF_YEAR); 818 buf.append(woy); 819 break; 820 } 821 case FORMAT_M: 822 { 823 int m = calendar.get(Calendar.MONTH) + 1; // 0-based 824 buf.append(m); 825 break; 826 } 827 case FORMAT_MM: 828 { 829 int mm = calendar.get(Calendar.MONTH) + 1; // 0-based 830 if (mm < 10) { 831 buf.append('0'); 832 } 833 buf.append(mm); 834 break; 835 } 836 case FORMAT_MMM_LOWER: 837 case FORMAT_MMM_UPPER: 838 { 839 int m = calendar.get(Calendar.MONTH); // 0-based 840 buf.append(locale.monthsShort[m]); // e.g. Jan 841 break; 842 } 843 case FORMAT_MMMM_LOWER: 844 case FORMAT_MMMM_UPPER: 845 { 846 int m = calendar.get(Calendar.MONTH); // 0-based 847 buf.append(locale.monthsLong[m]); // e.g. January 848 break; 849 } 850 case FORMAT_Q: 851 { 852 int m = calendar.get(Calendar.MONTH); 853 // 0(Jan) -> q1, 1(Feb) -> q1, 2(Mar) -> q1, 3(Apr) -> q2 854 int q = m / 3 + 1; 855 buf.append(q); 856 break; 857 } 858 case FORMAT_Y: 859 { 860 int doy = calendar.get(Calendar.DAY_OF_YEAR); 861 buf.append(doy); 862 break; 863 } 864 case FORMAT_YY: 865 { 866 int y = calendar.get(Calendar.YEAR) % 100; 867 if (y < 10) { 868 buf.append('0'); 869 } 870 buf.append(y); 871 break; 872 } 873 case FORMAT_YYYY: 874 { 875 int y = calendar.get(Calendar.YEAR); 876 buf.append(y); 877 break; 878 } 879 case FORMAT_H: 880 { 881 int h = calendar.get( 882 twelveHourClock ? Calendar.HOUR : Calendar.HOUR_OF_DAY); 883 buf.append(h); 884 break; 885 } 886 case FORMAT_HH: 887 { 888 int h = calendar.get( 889 twelveHourClock ? Calendar.HOUR : Calendar.HOUR_OF_DAY); 890 if (h < 10) { 891 buf.append('0'); 892 } 893 buf.append(h); 894 break; 895 } 896 case FORMAT_N: 897 { 898 int n = calendar.get(Calendar.MINUTE); 899 buf.append(n); 900 break; 901 } 902 case FORMAT_NN: 903 { 904 int n = calendar.get(Calendar.MINUTE); 905 if (n < 10) { 906 buf.append('0'); 907 } 908 buf.append(n); 909 break; 910 } 911 case FORMAT_S: 912 { 913 int s = calendar.get(Calendar.SECOND); 914 buf.append(s); 915 break; 916 } 917 case FORMAT_SS: 918 { 919 int s = calendar.get(Calendar.SECOND); 920 if (s < 10) { 921 buf.append('0'); 922 } 923 buf.append(s); 924 break; 925 } 926 case FORMAT_TTTTT: 927 { 928 // Officially, we should use the system's time format. But 929 // for now, we always print using the default format, h:mm:ss. 930 format(FORMAT_H, calendar,buf); 931 buf.append(locale.timeSeparator); 932 format(FORMAT_NN, calendar,buf); 933 buf.append(locale.timeSeparator); 934 format(FORMAT_SS, calendar,buf); 935 break; 936 } 937 case FORMAT_AMPM: 938 case FORMAT_UPPER_AM_SOLIDUS_PM: 939 { 940 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM; 941 buf.append(isAm ? "AM" : "PM"); 942 break; 943 } 944 case FORMAT_LOWER_AM_SOLIDUS_PM: 945 { 946 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM; 947 buf.append(isAm ? "am" : "pm"); 948 break; 949 } 950 case FORMAT_UPPER_A_SOLIDUS_P: 951 { 952 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM; 953 buf.append(isAm ? "A" : "P"); 954 break; 955 } 956 case FORMAT_LOWER_A_SOLIDUS_P: 957 { 958 boolean isAm = calendar.get(Calendar.AM_PM) == Calendar.AM; 959 buf.append(isAm ? "a" : "p"); 960 break; 961 } 962 default: 963 throw new Error(); 964 } 965 } 966 } 967 968 /** 969 * A FormatLocale contains all information necessary to format objects 970 * based upon the locale of the end-user. Use {@link Format#createLocale} 971 * to make one. 972 */ 973 public static class FormatLocale 974 { 975 char thousandSeparator; 976 char decimalPlaceholder; 977 String dateSeparator; 978 String timeSeparator; 979 String currencySymbol; 980 String currencyFormat; 981 String[] daysOfWeekShort; 982 String[] daysOfWeekLong; 983 String[] monthsShort; 984 String[] monthsLong; 985 private final Locale locale; 986 987 private FormatLocale( 988 char thousandSeparator, 989 char decimalPlaceholder, 990 String dateSeparator, 991 String timeSeparator, 992 String currencySymbol, 993 String currencyFormat, 994 String[] daysOfWeekShort, 995 String[] daysOfWeekLong, 996 String[] monthsShort, 997 String[] monthsLong, 998 Locale locale) 999 { 1000 this.locale = locale; 1001 if (thousandSeparator == '\0') { 1002 thousandSeparator = thousandSeparator_en; 1003 } 1004 this.thousandSeparator = thousandSeparator; 1005 if (decimalPlaceholder == '\0') { 1006 decimalPlaceholder = decimalPlaceholder_en; 1007 } 1008 this.decimalPlaceholder = decimalPlaceholder; 1009 if (dateSeparator == null) { 1010 dateSeparator = dateSeparator_en; 1011 } 1012 this.dateSeparator = dateSeparator; 1013 if (timeSeparator == null) { 1014 timeSeparator = timeSeparator_en; 1015 } 1016 this.timeSeparator = timeSeparator; 1017 if (currencySymbol == null) { 1018 currencySymbol = currencySymbol_en; 1019 } 1020 this.currencySymbol = currencySymbol; 1021 if (currencyFormat == null) { 1022 currencyFormat = currencyFormat_en; 1023 } 1024 this.currencyFormat = currencyFormat; 1025 if (daysOfWeekShort == null) { 1026 daysOfWeekShort = daysOfWeekShort_en; 1027 } 1028 this.daysOfWeekShort = daysOfWeekShort; 1029 if (daysOfWeekLong == null) { 1030 daysOfWeekLong = daysOfWeekLong_en; 1031 } 1032 this.daysOfWeekLong = daysOfWeekLong; 1033 if (monthsShort == null) { 1034 monthsShort = monthsShort_en; 1035 } 1036 this.monthsShort = monthsShort; 1037 if (monthsLong == null) { 1038 monthsLong = monthsLong_en; 1039 } 1040 this.monthsLong = monthsLong; 1041 if (daysOfWeekShort.length != 8 || 1042 daysOfWeekLong.length != 8 || 1043 monthsShort.length != 13 || 1044 monthsLong.length != 13) { 1045 throw new IllegalArgumentException( 1046 "Format: day or month array has incorrect length"); 1047 } 1048 } 1049 1050 // /** 1051 // * Get the localized string for day of week, given 1052 // * an <CODE>int</CODE> day value, with 0 = SUNDAY. 1053 // */ 1054 // public static String getDayOfWeek(int day) 1055 // { 1056 // LocaleResource localeResource = FormatLocale.getResource(); 1057 // switch (day) 1058 // { 1059 // case 0: return localeResource.getsunday(); 1060 // case 1: return localeResource.getmonday(); 1061 // case 2: return localeResource.gettuesday(); 1062 // case 3: return localeResource.getwednesday(); 1063 // case 4: return localeResource.getthursday(); 1064 // case 5: return localeResource.getfriday(); 1065 // case 6: return localeResource.getsaturday(); 1066 // default: throw new IllegalArgumentException(); 1067 // } 1068 // } 1069 1070 // /** 1071 // * Get the localized string for month of year, given 1072 // * an <CODE>int</CODE> month value, with 0 = JANUARY. 1073 // */ 1074 // public static String getMonthOfYear(int month) 1075 // { 1076 // LocaleResource localeResource = FormatLocale.getResource(); 1077 // switch (month) 1078 // { 1079 // case 0: return localeResource.getjanuary(); 1080 // case 1: return localeResource.getfebruary(); 1081 // case 2: return localeResource.getmarch(); 1082 // case 3: return localeResource.getapril(); 1083 // case 4: return localeResource.getmay(); 1084 // case 5: return localeResource.getjune(); 1085 // case 6: return localeResource.getjuly(); 1086 // case 7: return localeResource.getaugust(); 1087 // case 8: return localeResource.getseptember(); 1088 // case 9: return localeResource.getoctober(); 1089 // case 10: return localeResource.getnovember(); 1090 // case 11: return localeResource.getdecember(); 1091 // default: throw new IllegalArgumentException(); 1092 // } 1093 // } 1094 1095 // /** 1096 // * Get the string representation of the calendar 1097 // * quarter for a given quarter and year. Subclasses 1098 // * should override this method. 1099 // */ 1100 // public static String getCalendarQuarter(int quarterIn, int yearIn) 1101 // { 1102 // Integer year = new Integer (yearIn % 100); 1103 // Integer quarter = new Integer(quarterIn); 1104 1105 // String strYear = (year.intValue() < 10) 1106 // ? "0" + year.toString() : year.toString(); 1107 // LocaleResource localeResource = FormatLocale.getResource(); 1108 // String ret = localeResource.getcalendarQuarter(quarter.toString(), strYear); 1109 1110 // return ret; 1111 // } 1112 1113 // /** 1114 // * Get the string representation of the fiscal 1115 // * quarter for a given quarter and year. Subclasses 1116 // * should override this method. 1117 // */ 1118 // public static String getFiscalQuarter(int quarterIn, int yearIn) 1119 // { 1120 // Integer year = new Integer (yearIn % 100); 1121 // Integer quarter = new Integer(quarterIn); 1122 1123 // String strYear = (year.intValue() < 10) 1124 // ? "0" + year.toString() : year.toString(); 1125 1126 // LocaleResource localeResource = FormatLocale.getResource(); 1127 // String ret = localeResource.getfiscalQuarter(quarter.toString(), 1128 // strYear); 1129 // return ret; 1130 // } 1131 1132 } 1133 1134 private static class StringFormat extends BasicFormat 1135 { 1136 int stringCase; 1137 1138 StringFormat(int stringCase) { 1139 this.stringCase = stringCase; 1140 } 1141 } 1142 1143 /** Values for {@link StringFormat#stringCase}. */ 1144 private static final int CASE_ASIS = 0; 1145 private static final int CASE_UPPER = 1; 1146 private static final int CASE_LOWER = 2; 1147 1148 /** Types of Format. */ 1149 private static final int GENERAL = 0; 1150 private static final int DATE = 3; 1151 private static final int NUMERIC = 4; 1152 private static final int STRING = 5; 1153 /** A Format is flagged SPECIAL if it needs special processing 1154 * during parsing. */ 1155 private static final int SPECIAL = 8; 1156 1157 /** Values for {@link Format.BasicFormat#code}. */ 1158 private static final int FORMAT_NULL = 0; 1159 private static final int FORMAT_C = 3; 1160 private static final int FORMAT_D = 4; 1161 private static final int FORMAT_DD = 5; 1162 private static final int FORMAT_DDD = 6; 1163 private static final int FORMAT_DDDD = 7; 1164 private static final int FORMAT_DDDDD = 8; 1165 private static final int FORMAT_DDDDDD = 9; 1166 private static final int FORMAT_W = 10; 1167 private static final int FORMAT_WW = 11; 1168 private static final int FORMAT_M = 12; 1169 private static final int FORMAT_MM = 13; 1170 private static final int FORMAT_MMM_UPPER = 14; 1171 private static final int FORMAT_MMMM_UPPER = 15; 1172 private static final int FORMAT_Q = 16; 1173 private static final int FORMAT_Y = 17; 1174 private static final int FORMAT_YY = 18; 1175 private static final int FORMAT_YYYY = 19; 1176 private static final int FORMAT_H = 20; 1177 private static final int FORMAT_HH = 21; 1178 private static final int FORMAT_N = 22; 1179 private static final int FORMAT_NN = 23; 1180 private static final int FORMAT_S = 24; 1181 private static final int FORMAT_SS = 25; 1182 private static final int FORMAT_TTTTT = 26; 1183 private static final int FORMAT_UPPER_AM_SOLIDUS_PM = 27; 1184 private static final int FORMAT_LOWER_AM_SOLIDUS_PM = 28; 1185 private static final int FORMAT_UPPER_A_SOLIDUS_P = 29; 1186 private static final int FORMAT_LOWER_A_SOLIDUS_P = 30; 1187 private static final int FORMAT_AMPM = 31; 1188 private static final int FORMAT_0 = 32; 1189 private static final int FORMAT_POUND = 33; 1190 private static final int FORMAT_DECIMAL = 34; 1191 private static final int FORMAT_PERCENT = 35; 1192 private static final int FORMAT_THOUSEP = 36; 1193 private static final int FORMAT_TIMESEP = 37; 1194 private static final int FORMAT_DATESEP = 38; 1195 private static final int FORMAT_E_MINUS_UPPER = 39; 1196 private static final int FORMAT_E_PLUS_UPPER = 40; 1197 private static final int FORMAT_E_MINUS_LOWER = 41; 1198 private static final int FORMAT_E_PLUS_LOWER = 42; 1199 private static final int FORMAT_LITERAL = 43; 1200 private static final int FORMAT_BACKSLASH = 44; 1201 private static final int FORMAT_QUOTE = 45; 1202 private static final int FORMAT_CHARACTER_OR_SPACE = 46; 1203 private static final int FORMAT_CHARACTER_OR_NOTHING = 47; 1204 private static final int FORMAT_LOWER = 48; 1205 private static final int FORMAT_UPPER = 49; 1206 private static final int FORMAT_FILL_FROM_LEFT = 50; 1207 private static final int FORMAT_SEMI = 51; 1208 private static final int FORMAT_GENERAL_NUMBER = 52; 1209 private static final int FORMAT_GENERAL_DATE = 53; 1210 private static final int FORMAT_INTL_CURRENCY = 54; 1211 private static final int FORMAT_MMM_LOWER = 55; 1212 private static final int FORMAT_MMMM_LOWER = 56; 1213 private static final int FORMAT_USD = 57; 1214 1215 private static final Token nfe( 1216 int code, int flags, String token, String purpose, String description) 1217 { 1218 Util.discard(purpose); 1219 Util.discard(description); 1220 return new Token(code,flags,token); 1221 } 1222 1223 public static final List<Token> getTokenList() 1224 { 1225 return Collections.unmodifiableList(Arrays.asList(tokens)); 1226 } 1227 1228 static final Token[] tokens = { 1229 nfe(FORMAT_NULL , NUMERIC, null, "No formatting", "Display the number with no formatting."), 1230 nfe(FORMAT_C , DATE, "C", null, "Display the date as ddddd and display the time as t t t t t, in that order. Display only date information if there is no fractional part to the date serial number; display only time information if there is no integer portion."), 1231 nfe(FORMAT_D , DATE, "d", null, "Display the day as a number without a leading zero (1 - 31)."), 1232 nfe(FORMAT_DD , DATE, "dd", null, "Display the day as a number with a leading zero (01 - 31)."), 1233 nfe(FORMAT_DDD , DATE, "Ddd", null, "Display the day as an abbreviation (Sun - Sat)."), 1234 nfe(FORMAT_DDDD , DATE, "dddd", null, "Display the day as a full name (Sunday - Saturday)."), 1235 nfe(FORMAT_DDDDD , DATE, "ddddd", null, "Display the date as a complete date (including day, month, and year), formatted according to your system's short date format setting. The default short date format is m/d/yy."), 1236 nfe(FORMAT_DDDDDD , DATE, "dddddd", null, "Display a date serial number as a complete date (including day, month, and year) formatted according to the long date setting recognized by your system. The default long date format is mmmm dd, yyyy."), 1237 nfe(FORMAT_W , DATE, "w", null, "Display the day of the week as a number (1 for Sunday through 7 for Saturday)."), 1238 nfe(FORMAT_WW , DATE, "ww", null, "Display the week of the year as a number (1 - 53)."), 1239 nfe(FORMAT_M , DATE | SPECIAL, "m", null, "Display the month as a number without a leading zero (1 - 12). If m immediately follows h or hh, the minute rather than the month is displayed."), 1240 nfe(FORMAT_MM , DATE | SPECIAL, "mm", null, "Display the month as a number with a leading zero (01 - 12). If m immediately follows h or hh, the minute rather than the month is displayed."), 1241 nfe(FORMAT_MMM_LOWER , DATE, "mmm", null, "Display the month as an abbreviation (Jan - Dec)."), 1242 nfe(FORMAT_MMMM_LOWER , DATE, "mmmm", null, "Display the month as a full month name (January - December)."), 1243 nfe(FORMAT_MMM_UPPER , DATE, "mmm", null, "Display the month as an abbreviation (Jan - Dec)."), 1244 nfe(FORMAT_MMMM_UPPER , DATE, "mmmm", null, "Display the month as a full month name (January - December)."), 1245 nfe(FORMAT_Q , DATE, "q", null, "Display the quarter of the year as a number (1 - 4)."), 1246 nfe(FORMAT_Y , DATE, "y", null, "Display the day of the year as a number (1 - 366)."), 1247 nfe(FORMAT_YY , DATE, "yy", null, "Display the year as a 2-digit number (00 - 99)."), 1248 nfe(FORMAT_YYYY , DATE, "yyyy", null, "Display the year as a 4-digit number (100 - 9999)."), 1249 nfe(FORMAT_H , DATE, "h", null, "Display the hour as a number without leading zeros (0 - 23)."), 1250 nfe(FORMAT_HH , DATE, "hh", null, "Display the hour as a number with leading zeros (00 - 23)."), 1251 nfe(FORMAT_N , DATE, "n", null, "Display the minute as a number without leading zeros (0 - 59)."), 1252 nfe(FORMAT_NN , DATE, "nn", null, "Display the minute as a number with leading zeros (00 - 59)."), 1253 nfe(FORMAT_S , DATE, "s", null, "Display the second as a number without leading zeros (0 - 59)."), 1254 nfe(FORMAT_SS , DATE, "ss", null, "Display the second as a number with leading zeros (00 - 59)."), 1255 nfe(FORMAT_TTTTT , DATE, "ttttt", null, "Display a time as a complete time (including hour, minute, and second), formatted using the time separator defined by the time format recognized by your system. A leading zero is displayed if the leading zero option is selected and the time is before 10:00 A.M. or P.M. The default time format is h:mm:ss."), 1256 nfe(FORMAT_UPPER_AM_SOLIDUS_PM , DATE, "AM/PM", null, "Use the 12-hour clock and display an uppercase AM with any hour before noon; display an uppercase PM with any hour between noon and 11:59 P.M."), 1257 nfe(FORMAT_LOWER_AM_SOLIDUS_PM , DATE, "am/pm", null, "Use the 12-hour clock and display a lowercase AM with any hour before noon; display a lowercase PM with any hour between noon and 11:59 P.M."), 1258 nfe(FORMAT_UPPER_A_SOLIDUS_P , DATE, "A/P", null, "Use the 12-hour clock and display an uppercase A with any hour before noon; display an uppercase P with any hour between noon and 11:59 P.M."), 1259 nfe(FORMAT_LOWER_A_SOLIDUS_P , DATE, "a/p", null, "Use the 12-hour clock and display a lowercase A with any hour before noon; display a lowercase P with any hour between noon and 11:59 P.M."), 1260 nfe(FORMAT_AMPM , DATE, "AMPM", null, "Use the 12-hour clock and display the AM string literal as defined by your system with any hour before noon; display the PM string literal as defined by your system with any hour between noon and 11:59 P.M. AMPM can be either uppercase or lowercase, but the case of the string displayed matches the string as defined by your system settings. The default format is AM/PM."), 1261 nfe(FORMAT_0 , NUMERIC | SPECIAL, "0", "Digit placeholder", "Display a digit or a zero. If the expression has a digit in the position where the 0 appears in the format string, display it; otherwise, display a zero in that position. If the number has fewer digits than there are zeros (on either side of the decimal) in the format expression, display leading or trailing zeros. If the number has more digits to the right of the decimal separator than there are zeros to the right of the decimal separator in the format expression, round the number to as many decimal places as there are zeros. If the number has more digits to the left of the decimal separator than there are zeros to the left of the decimal separator in the format expression, display the extra digits without modification."), 1262 nfe(FORMAT_POUND , NUMERIC | SPECIAL, "#", "Digit placeholder", "Display a digit or nothing. If the expression has a digit in the position where the # appears in the format string, display it; otherwise, display nothing in that position. This symbol works like the 0 digit placeholder, except that leading and trailing zeros aren't displayed if the number has the same or fewer digits than there are # characters on either side of the decimal separator in the format expression."), 1263 nfe(FORMAT_DECIMAL , NUMERIC | SPECIAL, ".", "Decimal placeholder", "In some locales, a comma is used as the decimal separator. The decimal placeholder determines how many digits are displayed to the left and right of the decimal separator. If the format expression contains only number signs to the left of this symbol, numbers smaller than 1 begin with a decimal separator. If you always want a leading zero displayed with fractional numbers, use 0 as the first digit placeholder to the left of the decimal separator instead. The actual character used as a decimal placeholder in the formatted output depends on the Number Format recognized by your system."), 1264 nfe(FORMAT_PERCENT , NUMERIC, "%", "Percent placeholder", "The expression is multiplied by 100. The percent character (%) is inserted in the position where it appears in the format string."), 1265 nfe(FORMAT_THOUSEP , NUMERIC | SPECIAL, ",", "Thousand separator", "In some locales, a period is used as a thousand separator. The thousand separator separates thousands from hundreds within a number that has four or more places to the left of the decimal separator. Standard use of the thousand separator is specified if the format contains a thousand separator surrounded by digit placeholders (0 or #). Two adjacent thousand separators or a thousand separator immediately to the left of the decimal separator (whether or not a decimal is specified) means \"scale the number by dividing it by 1000, rounding as needed.\" You can scale large numbers using this technique. For example, you can use the format string \"##0,,\" to represent 100 million as 100. Numbers smaller than 1 million are displayed as 0. Two adjacent thousand separators in any position other than immediately to the left of the decimal separator are treated simply as specifying the use of a thousand separator. The actual character used as the thousand separator in the formatted output depends on the Number Format recognized by your system."), 1266 nfe(FORMAT_TIMESEP , DATE | SPECIAL, ":", "Time separator", "In some locales, other characters may be used to represent the time separator. The time separator separates hours, minutes, and seconds when time values are formatted. The actual character used as the time separator in formatted output is determined by your system settings."), 1267 nfe(FORMAT_DATESEP , DATE | SPECIAL, "/", "Date separator", "In some locales, other characters may be used to represent the date separator. The date separator separates the day, month, and year when date values are formatted. The actual character used as the date separator in formatted output is determined by your system settings."), 1268 nfe(FORMAT_E_MINUS_UPPER , NUMERIC | SPECIAL, "E-", "Scientific format", "If the format expression contains at least one digit placeholder (0 or #) to the right of E-, E+, e-, or e+, the number is displayed in scientific format and E or e is inserted between the number and its exponent. The number of digit placeholders to the right determines the number of digits in the exponent. Use E- or e- to place a minus sign next to negative exponents. Use E+ or e+ to place a minus sign next to negative exponents and a plus sign next to positive exponents."), 1269 nfe(FORMAT_E_PLUS_UPPER , NUMERIC | SPECIAL, "E+", "Scientific format", "See E-."), 1270 nfe(FORMAT_E_MINUS_LOWER , NUMERIC | SPECIAL, "e-", "Scientific format", "See E-."), 1271 nfe(FORMAT_E_PLUS_LOWER , NUMERIC | SPECIAL, "e+", "Scientific format", "See E-."), 1272 nfe(FORMAT_LITERAL , GENERAL, "-", "Display a literal character", "To display a character other than one of those listed, precede it with a backslash (\\) or enclose it in double quotation marks (\" \")."), 1273 nfe(FORMAT_LITERAL , GENERAL, "+", "Display a literal character", "See -."), 1274 nfe(FORMAT_LITERAL , GENERAL, "$", "Display a literal character", "See -."), 1275 nfe(FORMAT_LITERAL , GENERAL, "(", "Display a literal character", "See -."), 1276 nfe(FORMAT_LITERAL , GENERAL, ")", "Display a literal character", "See -."), 1277 nfe(FORMAT_LITERAL , GENERAL, " ", "Display a literal character", "See -."), 1278 nfe(FORMAT_BACKSLASH , GENERAL | SPECIAL, "\\", "Display the next character in the format string", "Many characters in the format expression have a special meaning and can't be displayed as literal characters unless they are preceded by a backslash. The backslash itself isn't displayed. Using a backslash is the same as enclosing the next character in double quotation marks. To display a backslash, use two backslashes (\\). Examples of characters that can't be displayed as literal characters are the date- and time-formatting characters (a, c, d, h, m, n, p, q, s, t, w, y, and /:), the numeric-formatting characters (#, 0, %, E, e, comma, and period), and the string-formatting characters (@, &, <, >, and !)."), 1279 nfe(FORMAT_QUOTE , GENERAL | SPECIAL, "\"", "Display the string inside the double quotation marks", "To include a string in format from within code, you must use Chr(34) to enclose the text (34 is the character code for a double quotation mark)."), 1280 nfe(FORMAT_CHARACTER_OR_SPACE , STRING, "@", "Character placeholder", "Display a character or a space. If the string has a character in the position where the @ appears in the format string, display it; otherwise, display a space in that position. Placeholders are filled from right to left unless there is an ! character in the format string. See below."), 1281 nfe(FORMAT_CHARACTER_OR_NOTHING, STRING, "&", "Character placeholder", "Display a character or nothing. If the string has a character in the position where the & appears, display it; otherwise, display nothing. Placeholders are filled from right to left unless there is an ! character in the format string. See below."), 1282 nfe(FORMAT_LOWER , STRING | SPECIAL, "<", "Force lowercase", "Display all characters in lowercase format."), 1283 nfe(FORMAT_UPPER , STRING | SPECIAL, ">", "Force uppercase", "Display all characters in uppercase format."), 1284 nfe(FORMAT_FILL_FROM_LEFT , STRING | SPECIAL, "!", "Force left to right fill of placeholders", "The default is to fill from right to left."), 1285 nfe(FORMAT_SEMI , GENERAL | SPECIAL, ";", "Separates format strings for different kinds of values", "If there is one section, the format expression applies to all values. If there are two sections, the first section applies to positive values and zeros, the second to negative values. If there are three sections, the first section applies to positive values, the second to negative values, and the third to zeros. If there are four sections, the first section applies to positive values, the second to negative values, the third to zeros, and the fourth to Null values."), 1286 nfe(FORMAT_INTL_CURRENCY , NUMERIC | SPECIAL, intlCurrencySymbol + "", null, "Display the locale's currency symbol."), 1287 nfe(FORMAT_USD , GENERAL, "USD", null, "Display USD (U.S. Dollars)."), 1288 nfe(FORMAT_GENERAL_NUMBER , NUMERIC | SPECIAL, "General Number", null, "Shows numbers as entered."), 1289 nfe(FORMAT_GENERAL_DATE , DATE | SPECIAL, "General Date", null, "Shows date and time if expression contains both. If expression is only a date or a time, the missing information is not displayed."), 1290 }; 1291 1292 static class MacroToken { 1293 String name; 1294 String translation; 1295 String description; 1296 1297 MacroToken(String name, String translation, String description) 1298 { 1299 this.name = name; 1300 this.translation = translation; 1301 this.description = description; 1302 } 1303 } 1304 1305 // Named formats. todo: Supply the translation strings. 1306 private static final MacroToken[] macroTokens = { 1307 new MacroToken( 1308 "Currency", null, "Shows currency values according to the locale's CurrencyFormat. Negative numbers are inside parentheses."), 1309 new MacroToken( 1310 "Fixed", "0", "Shows at least one digit."), 1311 new MacroToken( 1312 "Standard", "#,##0", "Uses a thousands separator."), 1313 new MacroToken( 1314 "Percent", "0.00%", "Multiplies the value by 100 with a percent sign at the end."), 1315 new MacroToken( 1316 "Scientific", "0.00e+00", "Uses standard scientific notation."), 1317 new MacroToken( 1318 "Long Date", "dddd, mmmm dd, yyyy", "Uses the Long Date format specified in the Regional Settings dialog box of the Microsoft Windows Control Panel."), 1319 new MacroToken( 1320 "Medium Date", "dd-mmm-yy", "Uses the dd-mmm-yy format (for example, 03-Apr-93)"), 1321 new MacroToken( 1322 "Short Date", "m/d/yy", "Uses the Short Date format specified in the Regional Settings dialog box of the Windows Control Panel."), 1323 new MacroToken( 1324 "Long Time", "h:mm:ss AM/PM", "Shows the hour, minute, second, and \"AM\" or \"PM\" using the h:mm:ss format."), 1325 new MacroToken( 1326 "Medium Time", "h:mm AM/PM", "Shows the hour, minute, and \"AM\" or \"PM\" using the \"hh:mm AM/PM\" format."), 1327 new MacroToken( 1328 "Short Time", "hh:mm", "Shows the hour and minute using the hh:mm format."), 1329 new MacroToken( 1330 "Yes/No", "\\Y\\e\\s;\\Y\\e\\s;\\N\\o;\\N\\o", "Any nonzero numeric value (usually - 1) is Yes. Zero is No."), 1331 new MacroToken( 1332 "True/False", "\\T\\r\\u\\e;\\T\\r\\u\\e;\\F\\a\\l\\s\\e;\\F\\a\\l\\s\\e", "Any nonzero numeric value (usually - 1) is True. Zero is False."), 1333 new MacroToken( 1334 "On/Off", "\\O\\n;\\O\\n;\\O\\f\\f;\\O\\f\\f", "Any nonzero numeric value (usually - 1) is On. Zero is Off."), 1335 1336 }; 1337 1338 /** 1339 * Constructs a <code>Format</code> in a specific locale. 1340 * 1341 * @param formatString the format string; see 1342 * <a href="http://www.apostate.com/programming/vb-format.html">this 1343 * description</a> for more details 1344 * @param locale The locale 1345 */ 1346 public Format(String formatString, Locale locale) 1347 { 1348 this(formatString, getBestFormatLocale(locale)); 1349 } 1350 1351 /** 1352 * Constructs a <code>Format</code> in a specific locale. 1353 * 1354 * @see FormatLocale 1355 * @see #createLocale 1356 */ 1357 public Format(String formatString, FormatLocale locale) 1358 { 1359 if (formatString == null) { 1360 formatString = ""; 1361 } 1362 this.formatString = formatString; 1363 if (locale == null) { 1364 locale = locale_US; 1365 } 1366 this.locale = locale; 1367 1368 List<BasicFormat> alternateFormatList = new ArrayList<BasicFormat>(); 1369 while (formatString.length() > 0) { 1370 formatString = parseFormatString( 1371 formatString, alternateFormatList); 1372 } 1373 1374 // If the format string is empty, use a Java format. 1375 // Later entries in the formats list default to the first (e.g. 1376 // "#.00;;Nil"), but the first entry must be set. 1377 if (alternateFormatList.size() == 0 || alternateFormatList.get(0) == null) { 1378 format = new JavaFormat(locale.locale); 1379 } else if (alternateFormatList.size() == 1) { 1380 format = (BasicFormat) alternateFormatList.get(0); 1381 } else { 1382 BasicFormat[] alternateFormats = 1383 (BasicFormat[]) alternateFormatList.toArray( 1384 new BasicFormat[alternateFormatList.size()]); 1385 format = new AlternateFormat(alternateFormats); 1386 } 1387 } 1388 1389 /** 1390 * Constructs a <code>Format</code> in a specific locale, or retrieves 1391 * one from the cache if one already exists. 1392 * 1393 * <p>If the number of entries in the cache exceeds {@link #CacheLimit}, 1394 * replaces the eldest entry in the cache. 1395 * 1396 * @param formatString the format string; see 1397 * <a href="http://www.apostate.com/programming/vb-format.html">this 1398 * description</a> for more details 1399 */ 1400 public static Format get(String formatString, Locale locale) { 1401 String key = formatString + "@@@" + locale; 1402 Format format = (Format) cache.get(key); 1403 if (format == null) { 1404 synchronized (cache) { 1405 format = (Format) cache.get(key); 1406 if (format == null) { 1407 format = new Format(formatString, locale); 1408 cache.put(key, format); 1409 } 1410 } 1411 } 1412 return format; 1413 } 1414 1415 /** 1416 * Create a {@link FormatLocale} object characterized by the given 1417 * properties. 1418 * 1419 * @param thousandSeparator the character used to separate thousands in 1420 * numbers, or ',' by default. For example, 12345 is '12,345 in English, 1421 * '12.345 in French. 1422 * @param decimalPlaceholder the character placed between the integer and 1423 * the fractional part of decimal numbers, or '.' by default. For 1424 * example, 12.34 is '12.34' in English, '12,34' in French. 1425 * @param dateSeparator the character placed between the year, month and 1426 * day of a date such as '12/07/2001', or '/' by default. 1427 * @param timeSeparator the character placed between the hour, minute and 1428 * second value of a time such as '1:23:45 AM', or ':' by default. 1429 * @param daysOfWeekShort Short forms of the days of the week. 1430 * The array is 1-based, because position 1431 * {@link Calendar#SUNDAY} (= 1) must hold Sunday, etc. 1432 * The array must have 8 elements. 1433 * For example {"", "Sun", "Mon", ..., "Sat"}. 1434 * @param daysOfWeekLong Long forms of the days of the week. 1435 * The array is 1-based, because position 1436 * {@link Calendar#SUNDAY} must hold Sunday, etc. 1437 * The array must have 8 elements. 1438 * For example {"", "Sunday", ..., "Saturday"}. 1439 * @param monthsShort Short forms of the months of the year. 1440 * The array is 0-based, because position 1441 * {@link Calendar#JANUARY} (= 0) holds January, etc. 1442 * For example {"Jan", ..., "Dec", ""}. 1443 * @param monthsLong Long forms of the months of the year. 1444 * The array is 0-based, because position 1445 * {@link Calendar#JANUARY} (= 0) holds January, etc. 1446 * For example {"January", ..., "December", ""}. 1447 * @param locale if this is not null, register that the constructed 1448 * <code>FormatLocale</code> is the default for <code>locale</code> 1449 */ 1450 public static FormatLocale createLocale( 1451 char thousandSeparator, 1452 char decimalPlaceholder, 1453 String dateSeparator, 1454 String timeSeparator, 1455 String currencySymbol, 1456 String currencyFormat, 1457 String[] daysOfWeekShort, 1458 String[] daysOfWeekLong, 1459 String[] monthsShort, 1460 String[] monthsLong, 1461 Locale locale) 1462 { 1463 FormatLocale formatLocale = new FormatLocale( 1464 thousandSeparator, decimalPlaceholder, dateSeparator, 1465 timeSeparator, currencySymbol, currencyFormat, daysOfWeekShort, 1466 daysOfWeekLong, monthsShort, monthsLong, locale); 1467 if (locale != null) { 1468 registerFormatLocale(formatLocale, locale); 1469 } 1470 return formatLocale; 1471 } 1472 1473 public static FormatLocale createLocale(Locale locale) 1474 { 1475 final DecimalFormatSymbols decimalSymbols = 1476 new DecimalFormatSymbols(locale); 1477 final DateFormatSymbols dateSymbols = new DateFormatSymbols(locale); 1478 1479 Calendar calendar = Calendar.getInstance(locale); 1480 calendar.set(1969, 11, 31, 0, 0, 0); 1481 final Date date = calendar.getTime(); 1482 1483 final java.text.DateFormat dateFormat = 1484 java.text.DateFormat.getDateInstance( 1485 java.text.DateFormat.SHORT, locale); 1486 final String dateValue = dateFormat.format(date); // "12/31/69" 1487 String dateSeparator = dateValue.substring(2, 3); // "/" 1488 1489 final java.text.DateFormat timeFormat = 1490 java.text.DateFormat.getTimeInstance( 1491 java.text.DateFormat.SHORT, locale); 1492 final String timeValue = timeFormat.format(date); // "12:00:00" 1493 String timeSeparator = timeValue.substring(2, 3); // ":" 1494 1495 // Deduce the locale's currency format. 1496 // For example, US is "$#,###.00"; France is "#,###-00FF". 1497 final NumberFormat currencyFormat = 1498 NumberFormat.getCurrencyInstance(locale); 1499 final String currencyValue = currencyFormat.format(123456.78); 1500 String currencyLeft = 1501 currencyValue.substring(0, currencyValue.indexOf("1")); 1502 String currencyRight = 1503 currencyValue.substring(currencyValue.indexOf("8") + 1); 1504 StringBuilder buf = new StringBuilder(); 1505 buf.append(currencyLeft); 1506 int minimumIntegerDigits = currencyFormat.getMinimumIntegerDigits(); 1507 for (int i = Math.max(minimumIntegerDigits, 4) - 1; i >= 0; --i) { 1508 buf.append(i < minimumIntegerDigits ? '0' : '#'); 1509 if (i % 3 == 0 && i > 0) { 1510 buf.append(','); 1511 } 1512 } 1513 if (currencyFormat.getMaximumFractionDigits() > 0) { 1514 buf.append('.'); 1515 appendTimes(buf, '0', currencyFormat.getMinimumFractionDigits()); 1516 appendTimes(buf, '#', 1517 currencyFormat.getMaximumFractionDigits() - 1518 currencyFormat.getMinimumFractionDigits()); 1519 } 1520 buf.append(currencyRight); 1521 String currencyFormatString = buf.toString(); 1522 1523 return createLocale( 1524 decimalSymbols.getGroupingSeparator(), 1525 decimalSymbols.getDecimalSeparator(), 1526 dateSeparator, 1527 timeSeparator, 1528 decimalSymbols.getCurrencySymbol(), 1529 currencyFormatString, 1530 dateSymbols.getShortWeekdays(), 1531 dateSymbols.getWeekdays(), 1532 dateSymbols.getShortMonths(), 1533 dateSymbols.getMonths(), 1534 locale); 1535 } 1536 1537 private static void appendTimes(StringBuilder buf, char c, int i) { 1538 while (i-- > 0) { 1539 buf.append(c); 1540 } 1541 } 1542 1543 /** 1544 * Returns the {@link FormatLocale} which precisely matches {@link Locale}, 1545 * if any, or null if there is none. 1546 */ 1547 public static FormatLocale getFormatLocale(Locale locale) 1548 { 1549 if (locale == null) { 1550 locale = Locale.US; 1551 } 1552 String key = locale.toString(); 1553 return mapLocaleToFormatLocale.get(key); 1554 } 1555 1556 /** 1557 * Returns the best {@link FormatLocale} for a given {@link Locale}. 1558 * Never returns null, even if <code>locale</code> is null. 1559 */ 1560 public static synchronized FormatLocale getBestFormatLocale(Locale locale) 1561 { 1562 FormatLocale formatLocale; 1563 if (locale == null) { 1564 return locale_US; 1565 } 1566 String key = locale.toString(); 1567 // Look in the cache first. 1568 formatLocale = mapLocaleToFormatLocale.get(key); 1569 if (formatLocale == null) { 1570 // Not in the cache, so ask the factory. 1571 formatLocale = getFormatLocaleUsingFactory(locale); 1572 if (formatLocale == null) { 1573 formatLocale = locale_US; 1574 } 1575 // Add to cache. 1576 mapLocaleToFormatLocale.put(key, formatLocale); 1577 } 1578 return formatLocale; 1579 } 1580 1581 private static FormatLocale getFormatLocaleUsingFactory(Locale locale) 1582 { 1583 FormatLocale formatLocale; 1584 // Lookup full locale, e.g. "en-US-Boston" 1585 if (!locale.getVariant().equals("")) { 1586 formatLocale = createLocale(locale); 1587 if (formatLocale != null) { 1588 return formatLocale; 1589 } 1590 locale = new Locale(locale.getLanguage(), locale.getCountry()); 1591 } 1592 // Lookup language and country, e.g. "en-US" 1593 if (!locale.getCountry().equals("")) { 1594 formatLocale = createLocale(locale); 1595 if (formatLocale != null) { 1596 return formatLocale; 1597 } 1598 locale = new Locale(locale.getLanguage()); 1599 } 1600 // Lookup language, e.g. "en" 1601 formatLocale = createLocale(locale); 1602 if (formatLocale != null) { 1603 return formatLocale; 1604 } 1605 return null; 1606 } 1607 1608 /** 1609 * Registers a {@link FormatLocale} to a given {@link Locale}. Returns the 1610 * previous mapping. 1611 */ 1612 public static FormatLocale registerFormatLocale( 1613 FormatLocale formatLocale, Locale locale) 1614 { 1615 String key = locale.toString(); // e.g. "en_us_Boston" 1616 FormatLocale previous = mapLocaleToFormatLocale.put(key, formatLocale); 1617 return previous; 1618 } 1619 1620 // Values for variable numberState below. 1621 static final int NOT_IN_A_NUMBER = 0; 1622 static final int LEFT_OF_POINT = 1; 1623 static final int RIGHT_OF_POINT = 2; 1624 static final int RIGHT_OF_EXP = 3; 1625 1626 /** 1627 * Reads formatString up to the first semi-colon, or to the end if there 1628 * are no semi-colons. Adds a format to alternateFormatList, and returns 1629 * the remains of formatString. 1630 */ 1631 private String parseFormatString( 1632 String formatString, List<BasicFormat> alternateFormatList) 1633 { 1634 // Where we are in a numeric format. 1635 int numberState = NOT_IN_A_NUMBER; 1636 StringBuilder ignored = new StringBuilder(); 1637 String prevIgnored = null; 1638 boolean haveSeenNumber = false; 1639 int digitsLeftOfPoint = 0, 1640 digitsRightOfPoint = 0, 1641 digitsRightOfExp = 0, 1642 zeroesLeftOfPoint = 0, 1643 zeroesRightOfPoint = 0, 1644 zeroesRightOfExp = 0; 1645 int stringCase = CASE_ASIS; 1646 boolean useDecimal = false, 1647 useThouSep = false, 1648 fillFromRight = true; 1649 1650 /** Whether to print numbers in decimal or exponential format. Valid 1651 * values are FORMAT_NULL, FORMAT_E_PLUS_LOWER, FORMAT_E_MINUS_LOWER, 1652 * FORMAT_E_PLUS_UPPER, FORMAT_E_MINUS_UPPER. */ 1653 int expFormat = FORMAT_NULL; 1654 1655 // todo: Parse the string for ;s 1656 1657 // Look for the format string in the table of named formats. 1658 for (int i = 0; i < macroTokens.length; i++) { 1659 if (formatString.equals(macroTokens[i].name)) { 1660 if (macroTokens[i].translation == null) { 1661 // this macro requires special-case code 1662 if (macroTokens[i].name.equals("Currency")) { 1663 // e.g. "$#,##0.00;($#,##0.00)" 1664 formatString = locale.currencyFormat 1665 + ";(" + locale.currencyFormat + ")"; 1666 } else { 1667 throw new Error( 1668 "Format: internal: token " + macroTokens[i].name + 1669 " should have translation"); 1670 } 1671 } else { 1672 formatString = macroTokens[i].translation; 1673 } 1674 break; 1675 } 1676 } 1677 1678 // Add a semi-colon to the end of the string so the end of the string 1679 // looks like the end of an alternate. 1680 if (!formatString.endsWith(";")) { 1681 formatString = formatString + ";"; 1682 } 1683 1684 // Scan through the format string for format elements. 1685 List<BasicFormat> formatList = new ArrayList<BasicFormat>(); 1686 loop: 1687 while (formatString.length() > 0) { 1688 BasicFormat format = null; 1689 String newFormatString = null; 1690 boolean ignoreToken = false; 1691 for (int i = tokens.length - 1; i > 0; i--) { 1692 Token token = tokens[i]; 1693 if (formatString.startsWith(token.token)) { 1694 // Derive the string we will be looking at next time 1695 // around, by chewing the token off the front of the 1696 // string. Special-case code below can change this string, 1697 // if it likes. 1698 String matched = token.token; 1699 newFormatString = formatString.substring(matched.length()); 1700 if (token.isSpecial()) { 1701 switch (token.code) { 1702 case FORMAT_SEMI: 1703 formatString = newFormatString; 1704 break loop; 1705 1706 case FORMAT_POUND: 1707 switch (numberState) { 1708 case NOT_IN_A_NUMBER: 1709 numberState = LEFT_OF_POINT; 1710 // fall through 1711 case LEFT_OF_POINT: 1712 digitsLeftOfPoint++; 1713 break; 1714 case RIGHT_OF_POINT: 1715 digitsRightOfPoint++; 1716 break; 1717 case RIGHT_OF_EXP: 1718 digitsRightOfExp++; 1719 break; 1720 default: 1721 throw new Error(); 1722 } 1723 break; 1724 1725 case FORMAT_0: 1726 switch (numberState) { 1727 case NOT_IN_A_NUMBER: 1728 numberState = LEFT_OF_POINT; 1729 // fall through 1730 case LEFT_OF_POINT: 1731 zeroesLeftOfPoint++; 1732 break; 1733 case RIGHT_OF_POINT: 1734 zeroesRightOfPoint++; 1735 break; 1736 case RIGHT_OF_EXP: 1737 zeroesRightOfExp++; 1738 break; 1739 default: 1740 throw new Error(); 1741 } 1742 break; 1743 1744 case FORMAT_M: 1745 case FORMAT_MM: 1746 { 1747 // "m" and "mm" mean minute if immediately after 1748 // "h" or "hh"; month otherwise. 1749 boolean theyMeantMinute = false; 1750 int j = formatList.size() - 1; 1751 while (j >= 0) { 1752 BasicFormat prevFormat = formatList.get(j); 1753 if (prevFormat instanceof LiteralFormat) { 1754 // ignore boilerplate 1755 j--; 1756 } else if (prevFormat.code == FORMAT_H || 1757 prevFormat.code == FORMAT_HH) { 1758 theyMeantMinute = true; 1759 break; 1760 } else { 1761 theyMeantMinute = false; 1762 break; 1763 } 1764 } 1765 if (theyMeantMinute) { 1766 format = new DateFormat( 1767 (token.code == FORMAT_M 1768 ? FORMAT_N 1769 : FORMAT_NN), 1770 matched, 1771 locale, 1772 false); 1773 } else { 1774 format = token.makeFormat(locale); 1775 } 1776 break; 1777 } 1778 1779 case FORMAT_DECIMAL: 1780 { 1781 numberState = RIGHT_OF_POINT; 1782 useDecimal = true; 1783 break; 1784 } 1785 1786 case FORMAT_THOUSEP: 1787 { 1788 if (numberState == LEFT_OF_POINT) { 1789 // e.g. "#,##" 1790 useThouSep = true; 1791 } else { 1792 // e.g. "ddd, mmm dd, yyy" 1793 format = token.makeFormat(locale); 1794 } 1795 break; 1796 } 1797 1798 case FORMAT_TIMESEP: 1799 { 1800 format = new LiteralFormat(locale.timeSeparator); 1801 break; 1802 } 1803 1804 case FORMAT_DATESEP: 1805 { 1806 format = new LiteralFormat(locale.dateSeparator); 1807 break; 1808 } 1809 1810 case FORMAT_BACKSLASH: 1811 { 1812 // Display the next character in the format string. 1813 String s = ""; 1814 if (formatString.length() == 1) { 1815 // Backslash is the last character in the 1816 // string. 1817 s = ""; 1818 newFormatString = ""; 1819 } else { 1820 s = formatString.substring(1,2); 1821 newFormatString = formatString.substring(2); 1822 } 1823 format = new LiteralFormat(s); 1824 break; 1825 } 1826 1827 case FORMAT_E_MINUS_UPPER: 1828 case FORMAT_E_PLUS_UPPER: 1829 case FORMAT_E_MINUS_LOWER: 1830 case FORMAT_E_PLUS_LOWER: 1831 { 1832 numberState = RIGHT_OF_EXP; 1833 expFormat = token.code; 1834 if (zeroesLeftOfPoint == 0 && 1835 zeroesRightOfPoint == 0) { 1836 // We need a mantissa, so that format(123.45, 1837 // "E+") gives "1E+2", not "0E+2" or "E+2". 1838 zeroesLeftOfPoint = 1; 1839 } 1840 break; 1841 } 1842 1843 case FORMAT_QUOTE: 1844 { 1845 // Display the string inside the double quotation 1846 // marks. 1847 String s; 1848 int j = formatString.indexOf("\"", 1); 1849 if (j == -1) { 1850 // The string did not contain a closing quote. 1851 // Use the whole string. 1852 s = formatString.substring(1); 1853 newFormatString = ""; 1854 } else { 1855 // Take the string inside the quotes. 1856 s = formatString.substring(1, j); 1857 newFormatString = formatString.substring( 1858 j + 1); 1859 } 1860 format = new LiteralFormat(s); 1861 break; 1862 } 1863 1864 case FORMAT_UPPER: 1865 { 1866 stringCase = CASE_UPPER; 1867 break; 1868 } 1869 1870 case FORMAT_LOWER: 1871 { 1872 stringCase = CASE_LOWER; 1873 break; 1874 } 1875 1876 case FORMAT_FILL_FROM_LEFT: 1877 { 1878 fillFromRight = false; 1879 break; 1880 } 1881 1882 case FORMAT_GENERAL_NUMBER: 1883 { 1884 format = new JavaFormat(locale.locale); 1885 break; 1886 } 1887 1888 case FORMAT_GENERAL_DATE: 1889 { 1890 format = new JavaFormat(locale.locale); 1891 break; 1892 } 1893 1894 case FORMAT_INTL_CURRENCY: 1895 { 1896 format = new LiteralFormat(locale.currencySymbol); 1897 break; 1898 } 1899 1900 default: 1901 throw new Error(); 1902 } 1903 if (format == null) { 1904 // If the special-case code does not set format, 1905 // we should not create a format element. (The 1906 // token probably caused some flag to be set.) 1907 ignoreToken = true; 1908 ignored.append(matched); 1909 } else { 1910 prevIgnored = ignored.toString(); 1911 ignored.setLength(0); 1912 } 1913 } else { 1914 format = token.makeFormat(locale); 1915 } 1916 break; 1917 } 1918 } 1919 1920 if (format == null && !ignoreToken) { 1921 // None of the standard format elements matched. Make the 1922 // current character into a literal. 1923 format = new LiteralFormat( 1924 formatString.substring(0,1)); 1925 newFormatString = formatString.substring(1); 1926 } 1927 1928 if (format != null) { 1929 if (numberState != NOT_IN_A_NUMBER) { 1930 // Having seen a few number tokens, we're looking at a 1931 // non-number token. Create the number first. 1932 NumericFormat numericFormat = new NumericFormat( 1933 prevIgnored, locale, expFormat, digitsLeftOfPoint, 1934 zeroesLeftOfPoint, digitsRightOfPoint, 1935 zeroesRightOfPoint, digitsRightOfExp, zeroesRightOfExp, 1936 useDecimal, useThouSep); 1937 formatList.add(numericFormat); 1938 numberState = NOT_IN_A_NUMBER; 1939 haveSeenNumber = true; 1940 } 1941 1942 formatList.add(format); 1943 } 1944 1945 formatString = newFormatString; 1946 } 1947 1948 if (numberState != NOT_IN_A_NUMBER) { 1949 // We're still in a number. Create a number format. 1950 NumericFormat numericFormat = new NumericFormat( 1951 prevIgnored, locale, expFormat, digitsLeftOfPoint, 1952 zeroesLeftOfPoint, digitsRightOfPoint, zeroesRightOfPoint, 1953 digitsRightOfExp, zeroesRightOfExp, useDecimal, useThouSep); 1954 formatList.add(numericFormat); 1955 numberState = NOT_IN_A_NUMBER; 1956 haveSeenNumber = true; 1957 } 1958 1959 // The is the end of an alternate - or of the whole format string. 1960 // Push the current list of formats onto the list of alternates. 1961 BasicFormat[] formats = 1962 formatList.toArray(new BasicFormat[formatList.size()]); 1963 1964 // If they used some symbol like 'AM/PM' in the format string, tell all 1965 // date formats to use twelve hour clock. Likewise, figure out the 1966 // multiplier implied by their use of "%" or ",". 1967 boolean twelveHourClock = false; 1968 int decimalShift = 0; 1969 for (int i = 0; i < formats.length; i++) { 1970 switch (formats[i].code) { 1971 case FORMAT_UPPER_AM_SOLIDUS_PM: 1972 case FORMAT_LOWER_AM_SOLIDUS_PM: 1973 case FORMAT_UPPER_A_SOLIDUS_P: 1974 case FORMAT_LOWER_A_SOLIDUS_P: 1975 case FORMAT_AMPM: 1976 twelveHourClock = true; 1977 break; 1978 1979 case FORMAT_PERCENT: 1980 // If "%" occurs, the number should be multiplied by 100. 1981 decimalShift += 2; 1982 break; 1983 1984 case FORMAT_THOUSEP: 1985 // If there is a thousands separator (",") immediately to the 1986 // left of the point, or at the end of the number, divide the 1987 // number by 1000. (Or by 1000^n if there are more than one.) 1988 if (haveSeenNumber && 1989 i + 1 < formats.length && 1990 formats[i + 1].code != FORMAT_THOUSEP && 1991 formats[i + 1].code != FORMAT_0 && 1992 formats[i + 1].code != FORMAT_POUND) { 1993 for (int j = i; 1994 j >= 0 && formats[j].code == FORMAT_THOUSEP; 1995 j--) { 1996 decimalShift -= 3; 1997 formats[j] = new LiteralFormat(""); // ignore 1998 } 1999 } 2000 break; 2001 2002 default: 2003 } 2004 } 2005 2006 if (twelveHourClock) { 2007 for (int i = 0; i < formats.length; i++) { 2008 if (formats[i] instanceof DateFormat) { 2009 ((DateFormat) formats[i]).setTwelveHourClock(true); 2010 } 2011 } 2012 } 2013 2014 if (decimalShift != 0) { 2015 for (int i = 0; i < formats.length; i++) { 2016 if (formats[i] instanceof NumericFormat) { 2017 ((NumericFormat) formats[i]).decimalShift = decimalShift; 2018 } 2019 } 2020 } 2021 2022 // Create a CompoundFormat containing all of the format elements. 2023 BasicFormat alternateFormat = 2024 formats.length == 0 ? null : 2025 formats.length == 1 ? formats[0] : 2026 new CompoundFormat(formats); 2027 alternateFormatList.add(alternateFormat); 2028 return formatString; 2029 } 2030 2031 public String format(Object o) 2032 { 2033 StringBuilder buf = new StringBuilder(); 2034 format(o, buf); 2035 return buf.toString(); 2036 } 2037 2038 private StringBuilder format(Object o, StringBuilder buf) { 2039 if (o == null) { 2040 format.formatNull(buf); 2041 } else { 2042 // For final classes, it is more efficient to switch using 2043 // class equality than using 'instanceof'. 2044 Class<? extends Object> clazz = o.getClass(); 2045 if (clazz == Double.class) { 2046 format.format(((Double) o).doubleValue(), buf); 2047 } else if (clazz == Float.class) { 2048 format.format(((Float) o).floatValue(), buf); 2049 } else if (clazz == Integer.class) { 2050 format.format(((Integer) o).intValue(), buf); 2051 } else if (clazz == Long.class) { 2052 format.format(((Long) o).longValue(), buf); 2053 } else if (clazz == Short.class) { 2054 format.format(((Short) o).shortValue(), buf); 2055 } else if (clazz == Byte.class) { 2056 format.format(((Byte) o).byteValue(), buf); 2057 } else if (o instanceof BigDecimal) { 2058 format.format(((BigDecimal) o).doubleValue(), buf); 2059 } else if (o instanceof BigInteger) { 2060 format.format(((BigInteger) o).longValue(), buf); 2061 } else if (clazz == String.class) { 2062 format.format((String) o, buf); 2063 } else if (o instanceof java.util.Date) { 2064 // includes java.sql.Date, java.sql.Time and java.sql.Timestamp 2065 format.format((Date) o, buf); 2066 } else { 2067 buf.append(o.toString()); 2068 } 2069 } 2070 return buf; 2071 } 2072 2073 public String getFormatString() 2074 { 2075 return formatString; 2076 } 2077 2078 /** 2079 * Locates a {@link Format.FormatLocale} for a given locale. 2080 */ 2081 public interface LocaleFormatFactory { 2082 FormatLocale get(Locale locale); 2083 } 2084 2085 private static class DummyDecimalFormat extends DecimalFormat { 2086 private FieldPosition pos; 2087 2088 public StringBuffer format( 2089 double number, 2090 StringBuffer result, 2091 FieldPosition fieldPosition) { 2092 pos = fieldPosition; 2093 return result; 2094 } 2095 } 2096 2097 /** 2098 * Copied from <code>java.lang.FloatingDecimal</code>. 2099 */ 2100 static class FloatingDecimal { 2101 boolean isExceptional; 2102 boolean isNegative; 2103 int decExponent; 2104 char digits[]; 2105 int nDigits; 2106 2107 /* 2108 * Constants of the implementation 2109 * Most are IEEE-754 related. 2110 * (There are more really boring constants at the end.) 2111 */ 2112 static final long signMask = 0x8000000000000000L; 2113 static final long expMask = 0x7ff0000000000000L; 2114 static final long fractMask = ~(signMask | expMask); 2115 static final int expShift = 52; 2116 static final int expBias = 1023; 2117 static final long fractHOB = (1L<<expShift); // assumed High-Order bit 2118 static final long expOne = ((long)expBias)<<expShift; // exponent of 1.0 2119 static final int maxSmallBinExp = 62; 2120 static final int minSmallBinExp = -(63 / 3); 2121 2122 static final long highbyte = 0xff00000000000000L; 2123 static final long highbit = 0x8000000000000000L; 2124 static final long lowbytes = ~highbyte; 2125 2126 static final int singleSignMask = 0x80000000; 2127 static final int singleExpMask = 0x7f800000; 2128 static final int singleFractMask = ~(singleSignMask | singleExpMask); 2129 static final int singleExpShift = 23; 2130 static final int singleFractHOB = 1<<singleExpShift; 2131 static final int singleExpBias = 127; 2132 2133 /* 2134 * count number of bits from high-order 1 bit to low-order 1 bit, 2135 * inclusive. 2136 */ 2137 private static int 2138 countBits(long v) { 2139 // 2140 // the strategy is to shift until we get a non-zero sign bit 2141 // then shift until we have no bits left, counting the difference. 2142 // we do byte shifting as a hack. Hope it helps. 2143 // 2144 if (v == 0L) { 2145 return 0; 2146 } 2147 2148 while ((v & highbyte) == 0L) { 2149 v <<= 8; 2150 } 2151 while (v > 0L) { // i.e. while ((v&highbit) == 0L ) 2152 v <<= 1; 2153 } 2154 2155 int n = 0; 2156 while ((v & lowbytes) != 0L) { 2157 v <<= 8; 2158 n += 8; 2159 } 2160 while (v != 0L) { 2161 v <<= 1; 2162 n += 1; 2163 } 2164 return n; 2165 } 2166 2167 /* 2168 * Keep big powers of 5 handy for future reference. 2169 */ 2170 private static FDBigInt b5p[]; 2171 2172 private static FDBigInt 2173 big5pow(int p) { 2174 if (p < 0) 2175 throw new RuntimeException("Assertion botch: negative power of 5"); 2176 if (b5p == null) { 2177 b5p = new FDBigInt[p + 1]; 2178 } else if (b5p.length <= p) { 2179 FDBigInt t[] = new FDBigInt[p + 1]; 2180 System.arraycopy(b5p, 0, t, 0, b5p.length); 2181 b5p = t; 2182 } 2183 if (b5p[p] != null) { 2184 return b5p[p]; 2185 } else if (p < small5pow.length) { 2186 return b5p[p] = new FDBigInt(small5pow[p]); 2187 } else if (p < long5pow.length) { 2188 return b5p[p] = new FDBigInt(long5pow[p]); 2189 } else { 2190 // construct the damn thing. 2191 // recursively. 2192 int q, r; 2193 // in order to compute 5^p, 2194 // compute its square root, 5^(p/2) and square. 2195 // or, let q = p / 2, r = p -q, then 2196 // 5^p = 5^(q+r) = 5^q * 5^r 2197 q = p >> 1; 2198 r = p - q; 2199 FDBigInt bigq = b5p[q]; 2200 if (bigq == null) 2201 bigq = big5pow (q); 2202 if (r < small5pow.length) { 2203 return (b5p[p] = bigq.mult(small5pow[r])); 2204 } else { 2205 FDBigInt bigr = b5p[r]; 2206 if (bigr == null) 2207 bigr = big5pow(r); 2208 return (b5p[p] = bigq.mult(bigr)); 2209 } 2210 } 2211 } 2212 2213 /* 2214 * This is the easy subcase -- 2215 * all the significant bits, after scaling, are held in lvalue. 2216 * negSign and decExponent tell us what processing and scaling 2217 * has already been done. Exceptional cases have already been 2218 * stripped out. 2219 * In particular: 2220 * lvalue is a finite number (not Inf, nor NaN) 2221 * lvalue > 0L (not zero, nor negative). 2222 * 2223 * The only reason that we develop the digits here, rather than 2224 * calling on Long.toString() is that we can do it a little faster, 2225 * and besides want to treat trailing 0s specially. If Long.toString 2226 * changes, we should re-evaluate this strategy! 2227 */ 2228 private void 2229 developLongDigits(int decExponent, long lvalue, long insignificant) { 2230 char digits[]; 2231 int ndigits; 2232 int digitno; 2233 int c; 2234 // 2235 // Discard non-significant low-order bits, while rounding, 2236 // up to insignificant value. 2237 int i; 2238 for (i = 0; insignificant >= 10L; i++) 2239 insignificant /= 10L; 2240 if (i != 0) { 2241 long pow10 = long5pow[i] << i; // 10^i == 5^i * 2^i; 2242 long residue = lvalue % pow10; 2243 lvalue /= pow10; 2244 decExponent += i; 2245 if (residue >= (pow10>>1)) { 2246 // round up based on the low-order bits we're discarding 2247 lvalue++; 2248 } 2249 } 2250 if (lvalue <= Integer.MAX_VALUE) { 2251 if (lvalue <= 0L) 2252 throw new RuntimeException("Assertion botch: value " + lvalue + " <= 0"); 2253 2254 // even easier subcase! 2255 // can do int arithmetic rather than long! 2256 int ivalue = (int)lvalue; 2257 digits = new char[ndigits = 10]; 2258 digitno = ndigits - 1; 2259 c = ivalue % 10; 2260 ivalue /= 10; 2261 while (c == 0) { 2262 decExponent++; 2263 c = ivalue % 10; 2264 ivalue /= 10; 2265 } 2266 while (ivalue != 0) { 2267 digits[digitno--] = (char)(c + '0'); 2268 decExponent++; 2269 c = ivalue % 10; 2270 ivalue /= 10; 2271 } 2272 digits[digitno] = (char)(c + '0'); 2273 } else { 2274 // same algorithm as above (same bugs, too) 2275 // but using long arithmetic. 2276 digits = new char[ndigits = 20]; 2277 digitno = ndigits - 1; 2278 c = (int)(lvalue % 10L); 2279 lvalue /= 10L; 2280 while (c == 0) { 2281 decExponent++; 2282 c = (int)(lvalue % 10L); 2283 lvalue /= 10L; 2284 } 2285 while (lvalue != 0L) { 2286 digits[digitno--] = (char)(c + '0'); 2287 decExponent++; 2288 c = (int)(lvalue % 10L); 2289 lvalue /= 10; 2290 } 2291 digits[digitno] = (char)(c + '0'); 2292 } 2293 char result []; 2294 ndigits -= digitno; 2295 if (digitno == 0) { 2296 result = digits; 2297 } else { 2298 result = new char[ndigits]; 2299 System.arraycopy(digits, digitno, result, 0, ndigits); 2300 } 2301 this.digits = result; 2302 this.decExponent = decExponent + 1; 2303 this.nDigits = ndigits; 2304 } 2305 2306 // 2307 // add one to the least significant digit. 2308 // in the unlikely event there is a carry out, 2309 // deal with it. 2310 // assert that this will only happen where there 2311 // is only one digit, e.g. (float)1e-44 seems to do it. 2312 // 2313 private void 2314 roundup() { 2315 int i; 2316 int q = digits[i = (nDigits - 1)]; 2317 if (q == '9') { 2318 while (q == '9' && i > 0) { 2319 digits[i] = '0'; 2320 q = digits[--i]; 2321 } 2322 if (q == '9') { 2323 // carryout! High-order 1, rest 0s, larger exp. 2324 decExponent += 1; 2325 digits[0] = '1'; 2326 return; 2327 } 2328 // else fall through. 2329 } 2330 digits[i] = (char)(q + 1); 2331 } 2332 2333 /* 2334 * FIRST IMPORTANT CONSTRUCTOR: DOUBLE 2335 */ 2336 public FloatingDecimal(double d) 2337 { 2338 long dBits = Double.doubleToLongBits(d); 2339 long fractBits; 2340 int binExp; 2341 int nSignificantBits; 2342 2343 // discover and delete sign 2344 if ((dBits & signMask) != 0) { 2345 isNegative = true; 2346 dBits ^= signMask; 2347 } else { 2348 isNegative = false; 2349 } 2350 // Begin to unpack 2351 // Discover obvious special cases of NaN and Infinity. 2352 binExp = (int)((dBits & expMask) >> expShift); 2353 fractBits = dBits & fractMask; 2354 if (binExp == (int)(expMask>>expShift)) { 2355 isExceptional = true; 2356 if (fractBits == 0L) { 2357 digits = infinity; 2358 } else { 2359 digits = notANumber; 2360 isNegative = false; // NaN has no sign! 2361 } 2362 nDigits = digits.length; 2363 return; 2364 } 2365 isExceptional = false; 2366 // Finish unpacking 2367 // Normalize denormalized numbers. 2368 // Insert assumed high-order bit for normalized numbers. 2369 // Subtract exponent bias. 2370 if (binExp == 0) { 2371 if (fractBits == 0L) { 2372 // not a denorm, just a 0! 2373 decExponent = 0; 2374 digits = zero; 2375 nDigits = 1; 2376 return; 2377 } 2378 while ((fractBits & fractHOB) == 0L) { 2379 fractBits <<= 1; 2380 binExp -= 1; 2381 } 2382 nSignificantBits = expShift + binExp; // recall binExp is - shift count. 2383 binExp += 1; 2384 } else { 2385 fractBits |= fractHOB; 2386 nSignificantBits = expShift + 1; 2387 } 2388 binExp -= expBias; 2389 // call the routine that actually does all the hard work. 2390 dtoa(binExp, fractBits, nSignificantBits); 2391 } 2392 2393 /* 2394 * SECOND IMPORTANT CONSTRUCTOR: SINGLE 2395 */ 2396 public FloatingDecimal(float f) 2397 { 2398 int fBits = Float.floatToIntBits(f); 2399 int fractBits; 2400 int binExp; 2401 int nSignificantBits; 2402 2403 // discover and delete sign 2404 if ((fBits & singleSignMask) != 0) { 2405 isNegative = true; 2406 fBits ^= singleSignMask; 2407 } else { 2408 isNegative = false; 2409 } 2410 // Begin to unpack 2411 // Discover obvious special cases of NaN and Infinity. 2412 binExp = ((fBits & singleExpMask) >> singleExpShift); 2413 fractBits = fBits & singleFractMask; 2414 if (binExp == (singleExpMask>>singleExpShift)) { 2415 isExceptional = true; 2416 if (fractBits == 0L) { 2417 digits = infinity; 2418 } else { 2419 digits = notANumber; 2420 isNegative = false; // NaN has no sign! 2421 } 2422 nDigits = digits.length; 2423 return; 2424 } 2425 isExceptional = false; 2426 // Finish unpacking 2427 // Normalize denormalized numbers. 2428 // Insert assumed high-order bit for normalized numbers. 2429 // Subtract exponent bias. 2430 if (binExp == 0) { 2431 if (fractBits == 0) { 2432 // not a denorm, just a 0! 2433 decExponent = 0; 2434 digits = zero; 2435 nDigits = 1; 2436 return; 2437 } 2438 while ((fractBits & singleFractHOB) == 0) { 2439 fractBits <<= 1; 2440 binExp -= 1; 2441 } 2442 nSignificantBits = singleExpShift + binExp; // recall binExp is - shift count. 2443 binExp += 1; 2444 } else { 2445 fractBits |= singleFractHOB; 2446 nSignificantBits = singleExpShift + 1; 2447 } 2448 binExp -= singleExpBias; 2449 // call the routine that actually does all the hard work. 2450 dtoa(binExp, ((long)fractBits)<<(expShift - singleExpShift), nSignificantBits); 2451 } 2452 2453 private void 2454 dtoa(int binExp, long fractBits, int nSignificantBits) 2455 { 2456 int nFractBits; // number of significant bits of fractBits; 2457 int nTinyBits; // number of these to the right of the point. 2458 int decExp; 2459 2460 // Examine number. Determine if it is an easy case, 2461 // which we can do pretty trivially using float/long conversion, 2462 // or whether we must do real work. 2463 nFractBits = countBits(fractBits); 2464 nTinyBits = Math.max(0, nFractBits - binExp - 1); 2465 if (binExp <= maxSmallBinExp && binExp >= minSmallBinExp) { 2466 // Look more closely at the number to decide if, 2467 // with scaling by 10^nTinyBits, the result will fit in 2468 // a long. 2469 if ((nTinyBits < long5pow.length) && ((nFractBits + n5bits[nTinyBits]) < 64)) { 2470 /* 2471 * We can do this: 2472 * take the fraction bits, which are normalized. 2473 * (a) nTinyBits == 0: Shift left or right appropriately 2474 * to align the binary point at the extreme right, i.e. 2475 * where a long int point is expected to be. The integer 2476 * result is easily converted to a string. 2477 * (b) nTinyBits > 0: Shift right by expShift-nFractBits, 2478 * which effectively converts to long and scales by 2479 * 2^nTinyBits. Then multiply by 5^nTinyBits to 2480 * complete the scaling. We know this won't overflow 2481 * because we just counted the number of bits necessary 2482 * in the result. The integer you get from this can 2483 * then be converted to a string pretty easily. 2484 */ 2485 long halfULP; 2486 if (nTinyBits == 0) { 2487 if (binExp > nSignificantBits) { 2488 halfULP = 1L << (binExp - nSignificantBits - 1); 2489 } else { 2490 halfULP = 0L; 2491 } 2492 if (binExp >= expShift) { 2493 fractBits <<= (binExp - expShift); 2494 } else { 2495 fractBits >>>= (expShift - binExp); 2496 } 2497 developLongDigits(0, fractBits, halfULP); 2498 return; 2499 } 2500 /* 2501 * The following causes excess digits to be printed 2502 * out in the single-float case. Our manipulation of 2503 * halfULP here is apparently not correct. If we 2504 * better understand how this works, perhaps we can 2505 * use this special case again. But for the time being, 2506 * we do not. 2507 * else { 2508 * fractBits >>>= expShift + 1-nFractBits; 2509 * fractBits *= long5pow[ nTinyBits ]; 2510 * halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits); 2511 * developLongDigits(-nTinyBits, fractBits, halfULP); 2512 * return; 2513 * } 2514 */ 2515 } 2516 } 2517 /* 2518 * This is the hard case. We are going to compute large positive 2519 * integers B and S and integer decExp, s.t. 2520 * d = (B / S) * 10^decExp 2521 * 1 <= B / S < 10 2522 * Obvious choices are: 2523 * decExp = floor(log10(d)) 2524 * B = d * 2^nTinyBits * 10^max(0, -decExp) 2525 * S = 10^max(0, decExp) * 2^nTinyBits 2526 * (noting that nTinyBits has already been forced to non-negative) 2527 * I am also going to compute a large positive integer 2528 * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max(0, -decExp) 2529 * i.e. M is (1/2) of the ULP of d, scaled like B. 2530 * When we iterate through dividing B/S and picking off the 2531 * quotient bits, we will know when to stop when the remainder 2532 * is <= M. 2533 * 2534 * We keep track of powers of 2 and powers of 5. 2535 */ 2536 2537 /* 2538 * Estimate decimal exponent. (If it is small-ish, 2539 * we could double-check.) 2540 * 2541 * First, scale the mantissa bits such that 1 <= d2 < 2. 2542 * We are then going to estimate 2543 * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) 2544 * and so we can estimate 2545 * log10(d) ~=~ log10(d2) + binExp * log10(2) 2546 * take the floor and call it decExp. 2547 * FIXME -- use more precise constants here. It costs no more. 2548 */ 2549 double d2 = Double.longBitsToDouble( 2550 expOne | (fractBits &~ fractHOB)); 2551 decExp = (int)Math.floor( 2552 (d2 - 1.5D) * 0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981); 2553 int B2, B5; // powers of 2 and powers of 5, respectively, in B 2554 int S2, S5; // powers of 2 and powers of 5, respectively, in S 2555 int M2, M5; // powers of 2 and powers of 5, respectively, in M 2556 int Bbits; // binary digits needed to represent B, approx. 2557 int tenSbits; // binary digits needed to represent 10*S, approx. 2558 FDBigInt Sval, Bval, Mval; 2559 2560 B5 = Math.max(0, -decExp); 2561 B2 = B5 + nTinyBits + binExp; 2562 2563 S5 = Math.max(0, decExp); 2564 S2 = S5 + nTinyBits; 2565 2566 M5 = B5; 2567 M2 = B2 - nSignificantBits; 2568 2569 /* 2570 * the long integer fractBits contains the (nFractBits) interesting 2571 * bits from the mantissa of d (hidden 1 added if necessary) followed 2572 * by (expShift + 1-nFractBits) zeros. In the interest of compactness, 2573 * I will shift out those zeros before turning fractBits into a 2574 * FDBigInt. The resulting whole number will be 2575 * d * 2^(nFractBits-1-binExp). 2576 */ 2577 fractBits >>>= (expShift + 1 - nFractBits); 2578 B2 -= nFractBits - 1; 2579 int common2factor = Math.min(B2, S2); 2580 B2 -= common2factor; 2581 S2 -= common2factor; 2582 M2 -= common2factor; 2583 2584 /* 2585 * HACK!! For exact powers of two, the next smallest number 2586 * is only half as far away as we think (because the meaning of 2587 * ULP changes at power-of-two bounds) for this reason, we 2588 * hack M2. Hope this works. 2589 */ 2590 if (nFractBits == 1) 2591 M2 -= 1; 2592 2593 if (M2 < 0) { 2594 // oops. 2595 // since we cannot scale M down far enough, 2596 // we must scale the other values up. 2597 B2 -= M2; 2598 S2 -= M2; 2599 M2 = 0; 2600 } 2601 /* 2602 * Construct, Scale, iterate. 2603 * Some day, we'll write a stopping test that takes 2604 * account of the assymetry of the spacing of floating-point 2605 * numbers below perfect powers of 2 2606 * 26 Sept 96 is not that day. 2607 * So we use a symmetric test. 2608 */ 2609 char digits[] = this.digits = new char[18]; 2610 int ndigit = 0; 2611 boolean low, high; 2612 long lowDigitDifference; 2613 int q; 2614 2615 /* 2616 * Detect the special cases where all the numbers we are about 2617 * to compute will fit in int or long integers. 2618 * In these cases, we will avoid doing FDBigInt arithmetic. 2619 * We use the same algorithms, except that we "normalize" 2620 * our FDBigInts before iterating. This is to make division easier, 2621 * as it makes our fist guess (quotient of high-order words) 2622 * more accurate! 2623 * 2624 * Some day, we'll write a stopping test that takes 2625 * account of the assymetry of the spacing of floating-point 2626 * numbers below perfect powers of 2 2627 * 26 Sept 96 is not that day. 2628 * So we use a symmetric test. 2629 */ 2630 Bbits = nFractBits + B2 + ((B5 < n5bits.length) ? n5bits[B5] : (B5 * 3)); 2631 tenSbits = S2 + 1 + (((S5 + 1) < n5bits.length) ? n5bits[(S5 + 1)] : ((S5 + 1) * 3)); 2632 if (Bbits < 64 && tenSbits < 64) { 2633 if (Bbits < 32 && tenSbits < 32) { 2634 // wa-hoo! They're all ints! 2635 int b = ((int)fractBits * small5pow[B5]) << B2; 2636 int s = small5pow[S5] << S2; 2637 int m = small5pow[M5] << M2; 2638 int tens = s * 10; 2639 /* 2640 * Unroll the first iteration. If our decExp estimate 2641 * was too high, our first quotient will be zero. In this 2642 * case, we discard it and decrement decExp. 2643 */ 2644 ndigit = 0; 2645 q = (b / s); 2646 b = 10 * (b % s); 2647 m *= 10; 2648 low = (b < m); 2649 high = (b + m > tens); 2650 if (q >= 10) { 2651 // bummer, dude 2652 throw new RuntimeException("Assertion botch: excessivly large digit " + q); 2653 } else if ((q == 0) && ! high) { 2654 // oops. Usually ignore leading zero. 2655 decExp--; 2656 } else { 2657 digits[ndigit++] = (char)('0' + q); 2658 } 2659 /* 2660 * HACK! Java spec sez that we always have at least 2661 * one digit after the . in either F- or E-form output. 2662 * Thus we will need more than one digit if we're using 2663 * E-form 2664 */ 2665 if (decExp <= -3 || decExp >= 8) { 2666 high = low = false; 2667 } 2668 while (! low && ! high) { 2669 q = (b / s); 2670 b = 10 * (b % s); 2671 m *= 10; 2672 if (q >= 10) { 2673 // bummer, dude 2674 throw new RuntimeException("Assertion botch: excessivly large digit " + q); 2675 } 2676 if (m > 0L) { 2677 low = (b < m); 2678 high = (b + m > tens); 2679 } else { 2680 // hack -- m might overflow! 2681 // in this case, it is certainly > b, 2682 // which won't 2683 // and b+m > tens, too, since that has overflowed 2684 // either! 2685 low = true; 2686 high = true; 2687 } 2688 digits[ndigit++] = (char)('0' + q); 2689 } 2690 lowDigitDifference = (b<<1) - tens; 2691 } else { 2692 // still good! they're all longs! 2693 long b = (fractBits * long5pow[B5]) << B2; 2694 long s = long5pow[S5] << S2; 2695 long m = long5pow[M5] << M2; 2696 long tens = s * 10L; 2697 /* 2698 * Unroll the first iteration. If our decExp estimate 2699 * was too high, our first quotient will be zero. In this 2700 * case, we discard it and decrement decExp. 2701 */ 2702 ndigit = 0; 2703 q = (int) (b / s); 2704 b = 10L * (b % s); 2705 m *= 10L; 2706 low = (b < m); 2707 high = (b + m > tens); 2708 if (q >= 10) { 2709 // bummer, dude 2710 throw new RuntimeException("Assertion botch: excessivly large digit " + q); 2711 } else if ((q == 0) && ! high) { 2712 // oops. Usually ignore leading zero. 2713 decExp--; 2714 } else { 2715 digits[ndigit++] = (char)('0' + q); 2716 } 2717 /* 2718 * HACK! Java spec sez that we always have at least 2719 * one digit after the . in either F- or E-form output. 2720 * Thus we will need more than one digit if we're using 2721 * E-form 2722 */ 2723 if (decExp <= -3 || decExp >= 8) { 2724 high = low = false; 2725 } 2726 while (! low && ! high) { 2727 q = (int) (b / s); 2728 b = 10 * (b % s); 2729 m *= 10; 2730 if (q >= 10) { 2731 // bummer, dude 2732 throw new RuntimeException("Assertion botch: excessivly large digit " + q); 2733 } 2734 if (m > 0L) { 2735 low = (b < m); 2736 high = (b + m > tens); 2737 } else { 2738 // hack -- m might overflow! 2739 // in this case, it is certainly > b, 2740 // which won't 2741 // and b+m > tens, too, since that has overflowed 2742 // either! 2743 low = true; 2744 high = true; 2745 } 2746 digits[ndigit++] = (char)('0' + q); 2747 } 2748 lowDigitDifference = (b<<1) - tens; 2749 } 2750 } else { 2751 FDBigInt tenSval; 2752 int shiftBias; 2753 2754 /* 2755 * We really must do FDBigInt arithmetic. 2756 * Fist, construct our FDBigInt initial values. 2757 */ 2758 Bval = new FDBigInt(fractBits); 2759 if (B5 != 0) { 2760 if (B5 < small5pow.length) { 2761 Bval = Bval.mult(small5pow[B5]); 2762 } else { 2763 Bval = Bval.mult(big5pow(B5)); 2764 } 2765 } 2766 if (B2 != 0) { 2767 Bval.lshiftMe(B2); 2768 } 2769 Sval = new FDBigInt(big5pow(S5)); 2770 if (S2 != 0) { 2771 Sval.lshiftMe(S2); 2772 } 2773 Mval = new FDBigInt(big5pow(M5)); 2774 if (M2 != 0) { 2775 Mval.lshiftMe(M2); 2776 } 2777 2778 2779 // normalize so that division works better 2780 Bval.lshiftMe(shiftBias = Sval.normalizeMe()); 2781 Mval.lshiftMe(shiftBias); 2782 tenSval = Sval.mult(10); 2783 /* 2784 * Unroll the first iteration. If our decExp estimate 2785 * was too high, our first quotient will be zero. In this 2786 * case, we discard it and decrement decExp. 2787 */ 2788 ndigit = 0; 2789 q = Bval.quoRemIteration(Sval); 2790 Mval = Mval.mult(10); 2791 low = (Bval.cmp(Mval) < 0); 2792 high = (Bval.add(Mval).cmp(tenSval) > 0); 2793 if (q >= 10) { 2794 // bummer, dude 2795 throw new RuntimeException("Assertion botch: excessivly large digit " + q); 2796 } else if ((q == 0) && ! high) { 2797 // oops. Usually ignore leading zero. 2798 decExp--; 2799 } else { 2800 digits[ndigit++] = (char)('0' + q); 2801 } 2802 /* 2803 * HACK! Java spec sez that we always have at least 2804 * one digit after the . in either F- or E-form output. 2805 * Thus we will need more than one digit if we're using 2806 * E-form 2807 */ 2808 if (decExp <= -3 || decExp >= 8) { 2809 high = low = false; 2810 } 2811 while (! low && ! high) { 2812 q = Bval.quoRemIteration(Sval); 2813 Mval = Mval.mult(10); 2814 if (q >= 10) { 2815 // bummer, dude 2816 throw new RuntimeException("Assertion botch: excessivly large digit " + q); 2817 } 2818 low = (Bval.cmp(Mval) < 0); 2819 high = (Bval.add(Mval).cmp(tenSval) > 0); 2820 digits[ndigit++] = (char)('0' + q); 2821 } 2822 if (high && low) { 2823 Bval.lshiftMe(1); 2824 lowDigitDifference = Bval.cmp(tenSval); 2825 } else { 2826 lowDigitDifference = 0L; // this here only for flow analysis! 2827 } 2828 } 2829 this.decExponent = decExp + 1; 2830 this.digits = digits; 2831 this.nDigits = ndigit; 2832 /* 2833 * Last digit gets rounded based on stopping condition. 2834 */ 2835 if (high) { 2836 if (low) { 2837 if (lowDigitDifference == 0L) { 2838 // it's a tie! 2839 // choose based on which digits we like. 2840 if ((digits[nDigits - 1] & 1) != 0) { 2841 roundup(); 2842 } 2843 } else if (lowDigitDifference > 0) { 2844 roundup(); 2845 } 2846 } else { 2847 roundup(); 2848 } 2849 } 2850 } 2851 2852 public String 2853 toString() { 2854 // most brain-dead version 2855 StringBuilder result = new StringBuilder(nDigits + 8); 2856 if (isNegative) { 2857 result.append('-'); 2858 } 2859 if (isExceptional) { 2860 result.append(digits, 0, nDigits); 2861 } else { 2862 result.append("0."); 2863 result.append(digits, 0, nDigits); 2864 result.append('e'); 2865 result.append(decExponent); 2866 } 2867 return new String(result); 2868 } 2869 2870 public String 2871 toJavaFormatString() { 2872 char result[] = new char[nDigits + 10]; 2873 int i = 0; 2874 if (isNegative) { 2875 result[0] = '-'; 2876 i = 1; 2877 } 2878 if (isExceptional) { 2879 System.arraycopy(digits, 0, result, i, nDigits); 2880 i += nDigits; 2881 } else { 2882 if (decExponent > 0 && decExponent < 8) { 2883 // print digits.digits. 2884 int charLength = Math.min(nDigits, decExponent); 2885 System.arraycopy(digits, 0, result, i, charLength); 2886 i += charLength; 2887 if (charLength < decExponent) { 2888 charLength = decExponent - charLength; 2889 System.arraycopy(zero, 0, result, i, charLength); 2890 i += charLength; 2891 result[i++] = '.'; 2892 result[i++] = '0'; 2893 } else { 2894 result[i++] = '.'; 2895 if (charLength < nDigits) { 2896 int t = nDigits - charLength; 2897 System.arraycopy(digits, charLength, result, i, t); 2898 i += t; 2899 } else { 2900 result[i++] = '0'; 2901 } 2902 } 2903 } else if (decExponent <=0 && decExponent > -3) { 2904 result[i++] = '0'; 2905 result[i++] = '.'; 2906 if (decExponent != 0) { 2907 System.arraycopy(zero, 0, result, i, -decExponent); 2908 i -= decExponent; 2909 } 2910 System.arraycopy(digits, 0, result, i, nDigits); 2911 i += nDigits; 2912 } else { 2913 result[i++] = digits[0]; 2914 result[i++] = '.'; 2915 if (nDigits > 1) { 2916 System.arraycopy(digits, 1, result, i, nDigits - 1); 2917 i += nDigits - 1; 2918 } else { 2919 result[i++] = '0'; 2920 } 2921 result[i++] = 'E'; 2922 int e; 2923 if (decExponent <= 0) { 2924 result[i++] = '-'; 2925 e = -decExponent + 1; 2926 } else { 2927 e = decExponent - 1; 2928 } 2929 // decExponent has 1, 2, or 3, digits 2930 if (e <= 9) { 2931 result[i++] = (char)(e + '0'); 2932 } else if (e <= 99) { 2933 result[i++] = (char)(e / 10 + '0'); 2934 result[i++] = (char)(e % 10 + '0'); 2935 } else { 2936 result[i++] = (char)(e / 100 + '0'); 2937 e %= 100; 2938 result[i++] = (char)(e / 10 + '0'); 2939 result[i++] = (char)(e % 10 + '0'); 2940 } 2941 } 2942 } 2943 return new String(result, 0, i); 2944 } 2945 2946 // jhyde added 2947 public FloatingDecimal(long n) 2948 { 2949 isExceptional = false; // I don't think longs can be exceptional 2950 if (n < 0) { 2951 isNegative = true; 2952 n = -n; // if n == MIN_LONG, oops! 2953 } else { 2954 isNegative = false; 2955 } 2956 if (n == 0) { 2957 nDigits = 1; 2958 digits = new char[] {'0','0','0','0','0','0','0','0'}; 2959 decExponent = 0; 2960 } else { 2961 nDigits = 0; 2962 for (long m = n; m != 0; m = m / 10) { 2963 nDigits++; 2964 } 2965 decExponent = nDigits; 2966 digits = new char[nDigits]; 2967 int i = nDigits - 1; 2968 for (long m = n; m != 0; m = m / 10) { 2969 digits[i--] = (char) ('0' + (m % 10)); 2970 } 2971 } 2972 } 2973 2974 // jhyde added 2975 public void shift(int i) 2976 { 2977 if (isExceptional || 2978 nDigits == 1 && digits[0] == '0') { 2979 ; // don't multiply zero 2980 } else { 2981 decExponent += i; 2982 } 2983 } 2984 2985 // jhyde added 2986 public String toJavaFormatString( 2987 int minDigitsLeftOfDecimal, 2988 char decimalChar, // '.' or ',' 2989 int minDigitsRightOfDecimal, 2990 int maxDigitsRightOfDecimal, // todo: use 2991 char expChar, // 'E' or 'e' 2992 boolean expSign, // whether to print '+' if exp is positive 2993 int minExpDigits, // minimum digits in exponent 2994 char thousandChar) // ',' or '.', or 0 2995 { 2996 // char result[] = new char[nDigits + 10]; // crashes for 1.000.000,00 2997 // the result length does *not* depend from nDigits 2998 // it is : decExponent 2999 // +maxDigitsRightOfDecimal 3000 // + 10 (for decimal point and sign or -Infinity) 3001 // +decExponent/3 (for the thousand separators) 3002 int resultLen = 10 + Math.abs(decExponent) * 4 / 3 + maxDigitsRightOfDecimal; 3003 char result[] = new char[resultLen]; 3004 int i = toJavaFormatString( 3005 result, 0, minDigitsLeftOfDecimal, decimalChar, 3006 minDigitsRightOfDecimal, maxDigitsRightOfDecimal, expChar, expSign, 3007 minExpDigits, thousandChar); 3008 return new String(result, 0, i); 3009 } 3010 3011 // jhyde added 3012 private synchronized int toJavaFormatString( 3013 char result[], 3014 int i, 3015 int minDigitsLeftOfDecimal, 3016 char decimalChar, // '.' or ',' 3017 int minDigitsRightOfDecimal, 3018 int maxDigitsRightOfDecimal, // todo: use 3019 char expChar, // 'E' or 'e' 3020 boolean expSign, // whether to print '+' if exp is positive 3021 int minExpDigits, // minimum digits in exponent 3022 char thousandChar) // ',' or '.' or 0 3023 { 3024 if (isNegative) { 3025 result[i++] = '-'; 3026 } 3027 if (isExceptional) { 3028 System.arraycopy(digits, 0, result, i, nDigits); 3029 i += nDigits; 3030 } else if (expChar == 0) { 3031 // Build a new array of digits, padded with 0s at either end. For 3032 // example, here is the array we would build for 1234.56. 3033 // 3034 // | 0 0 1 2 3 . 4 5 6 0 0 | 3035 // | |- nDigits=6 -----------------------| | 3036 // | |- decExponent=3 -| | 3037 // |- minDigitsLeftOfDecimal=5 --| | 3038 // | |- minDigitsRightOfDecimal=5 --| 3039 // |- wholeDigits=5 -------------|- fractionDigits=5 -----------| 3040 // |- totalDigits=10 -------------------------------------------| 3041 // | |- maxDigitsRightOfDecimal=5 --| 3042 int wholeDigits = Math.max(decExponent, minDigitsLeftOfDecimal), 3043 fractionDigits = Math.max( 3044 nDigits - decExponent, minDigitsRightOfDecimal), 3045 totalDigits = wholeDigits + fractionDigits; 3046 char[] digits2 = new char[totalDigits]; 3047 for (int j = 0; j < totalDigits; j++) { 3048 digits2[j] = '0'; 3049 } 3050 for (int j = 0; j < nDigits; j++) { 3051 digits2[wholeDigits - decExponent + j] = digits[j]; 3052 } 3053 3054 // Now round. Suppose that we want to round 1234.56 to 1 decimal 3055 // place (that is, maxDigitsRightOfDecimal = 1). Then lastDigit 3056 // initially points to '5'. We find out that we need to round only 3057 // when we see that the next digit ('6') is non-zero. 3058 // 3059 // | 0 0 1 2 3 . 4 5 6 0 0 | 3060 // | | ^ | | 3061 // | maxDigitsRightOfDecimal=1 | 3062 int lastDigit = wholeDigits + maxDigitsRightOfDecimal; 3063 if (lastDigit < totalDigits) { 3064 // We need to truncate -- also round if the trailing digits are 3065 // 5000... or greater. 3066 int m = totalDigits; 3067 while (true) { 3068 m--; 3069 if (m < 0) { 3070 // The entire number was 9s. Re-allocate, so we can 3071 // prepend a '1'. 3072 wholeDigits++; 3073 totalDigits++; 3074 lastDigit++; 3075 char[] old = digits2; 3076 digits2 = new char[totalDigits]; 3077 digits2[0] = '1'; 3078 System.arraycopy(old, 0, digits2, 1, old.length); 3079 break; 3080 } else if (m == lastDigit) { 3081 char d = digits2[m]; 3082 digits2[m] = '0'; 3083 if (d < '5') { 3084 break; // no need to round 3085 } 3086 } else if (m > lastDigit) { 3087 digits2[m] = '0'; 3088 } else if (digits2[m] == '9') { 3089 digits2[m] = '0'; 3090 // do not break - we have to carry 3091 } else { 3092 digits2[m]++; 3093 break; // nothing to carry 3094 } 3095 } 3096 } 3097 3098 // Find the first non-zero digit and the last non-zero digit. 3099 int firstNonZero = wholeDigits, 3100 firstTrailingZero = 0; 3101 for (int j = 0; j < totalDigits; j++) { 3102 if (digits2[j] != '0') { 3103 if (j < firstNonZero) { 3104 firstNonZero = j; 3105 } 3106 firstTrailingZero = j + 1; 3107 } 3108 } 3109 3110 int firstDigitToPrint = firstNonZero; 3111 if (firstDigitToPrint > wholeDigits - minDigitsLeftOfDecimal) { 3112 firstDigitToPrint = wholeDigits - minDigitsLeftOfDecimal; 3113 } 3114 int lastDigitToPrint = firstTrailingZero; 3115 if (lastDigitToPrint > wholeDigits + maxDigitsRightOfDecimal) { 3116 lastDigitToPrint = wholeDigits + maxDigitsRightOfDecimal; 3117 } 3118 if (lastDigitToPrint < wholeDigits + minDigitsRightOfDecimal) { 3119 lastDigitToPrint = wholeDigits + minDigitsRightOfDecimal; 3120 } 3121 3122 // Now print the number. 3123 for (int j = firstDigitToPrint; j < wholeDigits; j++) { 3124 if (thousandChar != '\0' && 3125 (wholeDigits - j) % 3 == 0 && 3126 j > firstDigitToPrint && 3127 j < wholeDigits - 1) { 3128 result[i++] = thousandChar; 3129 } 3130 result[i++] = digits2[j]; 3131 } 3132 for (int j = wholeDigits; j < lastDigitToPrint; j++) { 3133 if (j == wholeDigits) { 3134 result[i++] = decimalChar; 3135 } 3136 result[i++] = digits2[j]; 3137 } 3138 } else { 3139 // Make a recursive call to print the digits left of the 'E'. 3140 int oldExp = decExponent; 3141 decExponent = Math.min(minDigitsLeftOfDecimal, nDigits); 3142 boolean oldIsNegative = isNegative; 3143 isNegative = false; 3144 i = toJavaFormatString( 3145 result, i, minDigitsLeftOfDecimal, decimalChar, 3146 minDigitsRightOfDecimal, maxDigitsRightOfDecimal, (char) 0, 3147 false, minExpDigits, '\0'); 3148 decExponent = oldExp; 3149 isNegative = oldIsNegative; 3150 3151 result[i++] = expChar; 3152 int de = decExponent; 3153 if (nDigits == 1 && digits[0] == '0') { 3154 de = 1; // 0's exponent is 0, but that's not convenient here 3155 } 3156 int e; 3157 if (de <= 0) { 3158 result[i++] = '-'; 3159 e = -de + 1; 3160 } else { 3161 if (expSign) { 3162 result[i++] = '+'; 3163 } 3164 e = de - 1; 3165 } 3166 // decExponent has 1, 2, or 3, digits 3167 int nExpDigits = e <= 9 ? 1 : e <= 99 ? 2 : 3; 3168 for (int j = nExpDigits; j < minExpDigits; j++) { 3169 result[i++] = '0'; 3170 } 3171 if (e <= 9) { 3172 result[i++] = (char)(e + '0'); 3173 } else if (e <= 99) { 3174 result[i++] = (char)(e / 10 + '0'); 3175 result[i++] = (char)(e % 10 + '0'); 3176 } else { 3177 result[i++] = (char)(e / 100 + '0'); 3178 e %= 100; 3179 result[i++] = (char)(e / 10 + '0'); 3180 result[i++] = (char)(e % 10 + '0'); 3181 } 3182 } 3183 return i; 3184 } 3185 3186 private static final int small5pow[] = { 3187 1, 3188 5, 3189 5 * 5, 3190 5 * 5 * 5, 3191 5 * 5 * 5 * 5, 3192 5 * 5 * 5 * 5 * 5, 3193 5 * 5 * 5 * 5 * 5 * 5, 3194 5 * 5 * 5 * 5 * 5 * 5 * 5, 3195 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3196 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3197 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3198 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3199 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3200 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 3201 }; 3202 3203 private static final long long5pow[] = { 3204 1L, 3205 5L, 3206 5L * 5, 3207 5L * 5 * 5, 3208 5L * 5 * 5 * 5, 3209 5L * 5 * 5 * 5 * 5, 3210 5L * 5 * 5 * 5 * 5 * 5, 3211 5L * 5 * 5 * 5 * 5 * 5 * 5, 3212 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3213 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3214 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3215 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3216 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3217 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3218 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3219 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3220 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3221 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3222 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3223 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3224 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3225 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3226 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3227 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3228 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3229 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3230 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 3231 }; 3232 3233 // approximately ceil(log2(long5pow[i])) 3234 private static final int n5bits[] = { 3235 0, 3236 3, 3237 5, 3238 7, 3239 10, 3240 12, 3241 14, 3242 17, 3243 19, 3244 21, 3245 24, 3246 26, 3247 28, 3248 31, 3249 33, 3250 35, 3251 38, 3252 40, 3253 42, 3254 45, 3255 47, 3256 49, 3257 52, 3258 54, 3259 56, 3260 59, 3261 61, 3262 }; 3263 3264 private static final char infinity[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' }; 3265 private static final char notANumber[] = { 'N', 'a', 'N' }; 3266 private static final char zero[] = { '0', '0', '0', '0', '0', '0', '0', '0' }; 3267 } 3268 3269 /* 3270 * A really, really simple bigint package 3271 * tailored to the needs of floating base conversion. 3272 */ 3273 static class FDBigInt { 3274 int nWords; // number of words used 3275 int data[]; // value: data[0] is least significant 3276 3277 private static boolean debugging = false; 3278 3279 public static void setDebugging(boolean d) { 3280 debugging = d; 3281 } 3282 3283 public FDBigInt(int v) { 3284 nWords = 1; 3285 data = new int[1]; 3286 data[0] = v; 3287 } 3288 3289 public FDBigInt(long v) { 3290 data = new int[2]; 3291 data[0] = (int)v; 3292 data[1] = (int)(v>>>32); 3293 nWords = (data[1] == 0) ? 1 : 2; 3294 } 3295 3296 public FDBigInt(FDBigInt other) { 3297 data = new int[nWords = other.nWords]; 3298 System.arraycopy(other.data, 0, data, 0, nWords); 3299 } 3300 3301 private FDBigInt(int [] d, int n) { 3302 data = d; 3303 nWords = n; 3304 } 3305 3306 /* 3307 * Left shift by c bits. 3308 * Shifts this in place. 3309 */ 3310 public void 3311 lshiftMe(int c)throws IllegalArgumentException { 3312 if (c <= 0) { 3313 if (c == 0) { 3314 return; // silly. 3315 } else { 3316 throw new IllegalArgumentException("negative shift count"); 3317 } 3318 } 3319 int wordcount = c>>5; 3320 int bitcount = c & 0x1f; 3321 int anticount = 32 - bitcount; 3322 int t[] = data; 3323 int s[] = data; 3324 if (nWords + wordcount + 1 > t.length) { 3325 // reallocate. 3326 t = new int[nWords + wordcount + 1]; 3327 } 3328 int target = nWords + wordcount; 3329 int src = nWords - 1; 3330 if (bitcount == 0) { 3331 // special hack, since an anticount of 32 won't go! 3332 System.arraycopy(s, 0, t, wordcount, nWords); 3333 target = wordcount - 1; 3334 } else { 3335 t[target--] = s[src]>>>anticount; 3336 while (src >= 1) { 3337 t[target--] = (s[src]<<bitcount) | (s[--src]>>>anticount); 3338 } 3339 t[target--] = s[src]<<bitcount; 3340 } 3341 while (target >= 0) { 3342 t[target--] = 0; 3343 } 3344 data = t; 3345 nWords += wordcount + 1; 3346 // may have constructed high-order word of 0. 3347 // if so, trim it 3348 while (nWords > 1 && data[nWords - 1] == 0) { 3349 nWords--; 3350 } 3351 } 3352 3353 /* 3354 * normalize this number by shifting until 3355 * the MSB of the number is at 0x08000000. 3356 * This is in preparation for quoRemIteration, below. 3357 * The idea is that, to make division easier, we want the 3358 * divisor to be "normalized" -- usually this means shifting 3359 * the MSB into the high words sign bit. But because we know that 3360 * the quotient will be 0 < q < 10, we would like to arrange that 3361 * the dividend not span up into another word of precision. 3362 * (This needs to be explained more clearly!) 3363 */ 3364 public int 3365 normalizeMe() throws IllegalArgumentException { 3366 int src; 3367 int wordcount = 0; 3368 int bitcount = 0; 3369 int v = 0; 3370 for (src = nWords - 1 ; src >= 0 && (v = data[src]) == 0; src--) { 3371 wordcount += 1; 3372 } 3373 if (src < 0) { 3374 // oops. Value is zero. Cannot normalize it! 3375 throw new IllegalArgumentException("zero value"); 3376 } 3377 /* 3378 * In most cases, we assume that wordcount is zero. This only 3379 * makes sense, as we try not to maintain any high-order 3380 * words full of zeros. In fact, if there are zeros, we will 3381 * simply SHORTEN our number at this point. Watch closely... 3382 */ 3383 nWords -= wordcount; 3384 /* 3385 * Compute how far left we have to shift v s.t. its highest- 3386 * order bit is in the right place. Then call lshiftMe to 3387 * do the work. 3388 */ 3389 if ((v & 0xf0000000) != 0) { 3390 // will have to shift up into the next word. 3391 // too bad. 3392 for (bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount--) { 3393 v >>>= 1; 3394 } 3395 } else { 3396 while (v <= 0x000fffff) { 3397 // hack: byte-at-a-time shifting 3398 v <<= 8; 3399 bitcount += 8; 3400 } 3401 while (v <= 0x07ffffff) { 3402 v <<= 1; 3403 bitcount += 1; 3404 } 3405 } 3406 if (bitcount != 0) 3407 lshiftMe(bitcount); 3408 return bitcount; 3409 } 3410 3411 /* 3412 * Multiply a FDBigInt by an int. 3413 * Result is a new FDBigInt. 3414 */ 3415 public FDBigInt 3416 mult(int iv) { 3417 long v = iv; 3418 int r[]; 3419 long p; 3420 3421 // guess adequate size of r. 3422 r = new int[(v * ((long)data[nWords - 1] & 0xffffffffL) > 0xfffffffL) ? nWords + 1 : nWords]; 3423 p = 0L; 3424 for (int i = 0; i < nWords; i++) { 3425 p += v * ((long)data[i] & 0xffffffffL); 3426 r[i] = (int)p; 3427 p >>>= 32; 3428 } 3429 if (p == 0L) { 3430 return new FDBigInt(r, nWords); 3431 } else { 3432 r[nWords] = (int)p; 3433 return new FDBigInt(r, nWords + 1); 3434 } 3435 } 3436 3437 /* 3438 * Multiply a FDBigInt by another FDBigInt. 3439 * Result is a new FDBigInt. 3440 */ 3441 public FDBigInt 3442 mult(FDBigInt other) { 3443 // crudely guess adequate size for r 3444 int r[] = new int[nWords + other.nWords]; 3445 int i; 3446 // I think I am promised zeros... 3447 3448 for (i = 0; i < this.nWords; i++) { 3449 long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION 3450 long p = 0L; 3451 int j; 3452 for (j = 0; j < other.nWords; j++) { 3453 p += ((long)r[i + j] & 0xffffffffL) + v * ((long)other.data[j] & 0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND. 3454 r[i + j] = (int)p; 3455 p >>>= 32; 3456 } 3457 r[i + j] = (int)p; 3458 } 3459 // compute how much of r we actually needed for all that. 3460 for (i = r.length - 1; i > 0; i--) { 3461 if (r[i] != 0) { 3462 break; 3463 } 3464 } 3465 return new FDBigInt(r, i + 1); 3466 } 3467 3468 /* 3469 * Add one FDBigInt to another. Return a FDBigInt 3470 */ 3471 public FDBigInt 3472 add(FDBigInt other) { 3473 int i; 3474 int a[], b[]; 3475 int n, m; 3476 long c = 0L; 3477 // arrange such that a.nWords >= b.nWords; 3478 // n = a.nWords, m = b.nWords 3479 if (this.nWords >= other.nWords) { 3480 a = this.data; 3481 n = this.nWords; 3482 b = other.data; 3483 m = other.nWords; 3484 } else { 3485 a = other.data; 3486 n = other.nWords; 3487 b = this.data; 3488 m = this.nWords; 3489 } 3490 int r[] = new int[n]; 3491 for (i = 0; i < n; i++) { 3492 c += (long)a[i] & 0xffffffffL; 3493 if (i < m) { 3494 c += (long)b[i] & 0xffffffffL; 3495 } 3496 r[i] = (int) c; 3497 c >>= 32; // signed shift. 3498 } 3499 if (c != 0L) { 3500 // oops -- carry out -- need longer result. 3501 int s[] = new int[r.length + 1]; 3502 System.arraycopy(r, 0, s, 0, r.length); 3503 s[i++] = (int)c; 3504 return new FDBigInt(s, i); 3505 } 3506 return new FDBigInt(r, i); 3507 } 3508 3509 /* 3510 * Subtract one FDBigInt from another. Return a FDBigInt 3511 * Assert that the result is positive. 3512 */ 3513 public FDBigInt 3514 sub(FDBigInt other) { 3515 int r[] = new int[this.nWords]; 3516 int i; 3517 int n = this.nWords; 3518 int m = other.nWords; 3519 int nzeros = 0; 3520 long c = 0L; 3521 for (i = 0; i < n; i++) { 3522 c += (long)this.data[i] & 0xffffffffL; 3523 if (i < m) { 3524 c -= (long)other.data[i] & 0xffffffffL; 3525 } 3526 if ((r[i] = (int) c) == 0) { 3527 nzeros++; 3528 } else { 3529 nzeros = 0; 3530 } 3531 c >>= 32; // signed shift. 3532 } 3533 if (c != 0L) 3534 throw new RuntimeException("Assertion botch: borrow out of subtract"); 3535 while (i < m) 3536 if (other.data[i++] != 0) 3537 throw new RuntimeException("Assertion botch: negative result of subtract"); 3538 return new FDBigInt(r, n - nzeros); 3539 } 3540 3541 /* 3542 * Compare FDBigInt with another FDBigInt. Return an integer 3543 * >0: this > other 3544 * 0: this == other 3545 * <0: this < other 3546 */ 3547 public int 3548 cmp(FDBigInt other) { 3549 int i; 3550 if (this.nWords > other.nWords) { 3551 // if any of my high-order words is non-zero, 3552 // then the answer is evident 3553 int j = other.nWords - 1; 3554 for (i = this.nWords - 1; i > j ; i--) { 3555 if (this.data[i] != 0) { 3556 return 1; 3557 } 3558 } 3559 } else if (this.nWords < other.nWords) { 3560 // if any of other's high-order words is non-zero, 3561 // then the answer is evident 3562 int j = this.nWords - 1; 3563 for (i = other.nWords - 1; i > j ; i--) { 3564 if (other.data[i] != 0) { 3565 return -1; 3566 } 3567 } 3568 } else { 3569 i = this.nWords - 1; 3570 } 3571 for (; i > 0 ; i--) 3572 if (this.data[i] != other.data[i]) 3573 break; 3574 // careful! want unsigned compare! 3575 // use brute force here. 3576 int a = this.data[i]; 3577 int b = other.data[i]; 3578 if (a < 0) { 3579 // a is really big, unsigned 3580 if (b < 0) { 3581 return a - b; // both big, negative 3582 } else { 3583 return 1; // b not big, answer is obvious; 3584 } 3585 } else { 3586 // a is not really big 3587 if (b < 0) { 3588 // but b is really big 3589 return -1; 3590 } else { 3591 return a - b; 3592 } 3593 } 3594 } 3595 3596 /* 3597 * Compute 3598 * q = (int)(this / S) 3599 * this = 10 * (this mod S) 3600 * Return q. 3601 * This is the iteration step of digit development for output. 3602 * We assume that S has been normalized, as above, and that 3603 * "this" has been lshift'ed accordingly. 3604 * Also assume, of course, that the result, q, can be expressed 3605 * as an integer, 0 <= q < 10. 3606 */ 3607 public int 3608 quoRemIteration(FDBigInt S)throws IllegalArgumentException { 3609 // ensure that this and S have the same number of 3610 // digits. If S is properly normalized and q < 10 then 3611 // this must be so. 3612 if (nWords != S.nWords) { 3613 throw new IllegalArgumentException("disparate values"); 3614 } 3615 // estimate q the obvious way. We will usually be 3616 // right. If not, then we're only off by a little and 3617 // will re-add. 3618 int n = nWords - 1; 3619 long q = ((long)data[n] & 0xffffffffL) / (long)S.data[n]; 3620 long diff = 0L; 3621 for (int i = 0; i <= n ; i++) { 3622 diff += ((long)data[i] & 0xffffffffL) - q * ((long)S.data[i] & 0xffffffffL); 3623 data[i] = (int)diff; 3624 diff >>= 32; // N.B. SIGNED shift. 3625 } 3626 if (diff != 0L) { 3627 // damn, damn, damn. q is too big. 3628 // add S back in until this turns +. This should 3629 // not be very many times! 3630 long sum = 0L; 3631 while (sum == 0L) { 3632 sum = 0L; 3633 for (int i = 0; i <= n; i++) { 3634 sum += ((long)data[i] & 0xffffffffL) + ((long)S.data[i] & 0xffffffffL); 3635 data[i] = (int) sum; 3636 sum >>= 32; // Signed or unsigned, answer is 0 or 1 3637 } 3638 /* 3639 * Originally the following line read 3640 * "if (sum !=0 && sum != -1)" 3641 * but that would be wrong, because of the 3642 * treatment of the two values as entirely unsigned, 3643 * it would be impossible for a carry-out to be interpreted 3644 * as -1 -- it would have to be a single-bit carry-out, or 3645 * + 1. 3646 */ 3647 if (sum != 0 && sum != 1) { 3648 throw new RuntimeException("Assertion botch: " + sum + " carry out of division correction"); 3649 } 3650 q -= 1; 3651 } 3652 } 3653 // finally, we can multiply this by 10. 3654 // it cannot overflow, right, as the high-order word has 3655 // at least 4 high-order zeros! 3656 long p = 0L; 3657 for (int i = 0; i <= n; i++) { 3658 p += 10 * ((long)data[i] & 0xffffffffL); 3659 data[i] = (int)p; 3660 p >>= 32; // SIGNED shift. 3661 } 3662 if (p != 0L) 3663 throw new RuntimeException("Assertion botch: carry out of *10"); 3664 3665 return (int)q; 3666 } 3667 3668 public long 3669 longValue() { 3670 // if this can be represented as a long, 3671 // return the value 3672 int i; 3673 for (i = this.nWords - 1; i > 1 ; i--) { 3674 if (data[i] != 0) { 3675 throw new RuntimeException("Assertion botch: value too big"); 3676 } 3677 } 3678 switch (i) { 3679 case 1: 3680 if (data[1] < 0) 3681 throw new RuntimeException("Assertion botch: value too big"); 3682 return ((long)(data[1]) << 32) | ((long)data[0] & 0xffffffffL); 3683 case 0: 3684 return ((long)data[0] & 0xffffffffL); 3685 default: 3686 throw new RuntimeException("Assertion botch: longValue confused"); 3687 } 3688 } 3689 3690 public String 3691 toString() { 3692 StringBuilder r = new StringBuilder(30); 3693 r.append('['); 3694 int i = Math.min(nWords - 1, data.length - 1); 3695 if (nWords > data.length) { 3696 r.append("(" + data.length + "<" + nWords + "!)"); 3697 } 3698 for (; i > 0 ; i--) { 3699 r.append(Integer.toHexString(data[i])); 3700 r.append(' '); 3701 } 3702 r.append(Integer.toHexString(data[0])); 3703 r.append(']'); 3704 return new String(r); 3705 } 3706 } 3707 } 3708 3709 // End Format.java