Print this page
rev 5615 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o Jigsaw. by Naoto Sato and Masayoshi Okutsu)

Split Close
Expand all
Collapse all
          --- old/src/share/classes/java/text/SimpleDateFormat.java
          +++ new/src/share/classes/java/text/SimpleDateFormat.java
↓ open down ↓ 33 lines elided ↑ open up ↑
  34   34   * patents. This notice and attribution to Taligent may not be removed.
  35   35   *   Taligent is a registered trademark of Taligent, Inc.
  36   36   *
  37   37   */
  38   38  
  39   39  package java.text;
  40   40  
  41   41  import java.io.IOException;
  42   42  import java.io.InvalidObjectException;
  43   43  import java.io.ObjectInputStream;
       44 +import static java.text.DateFormatSymbols.*;
  44   45  import java.util.Calendar;
  45   46  import java.util.Date;
  46   47  import java.util.GregorianCalendar;
  47   48  import java.util.Locale;
  48   49  import java.util.Map;
  49      -import java.util.MissingResourceException;
  50      -import java.util.ResourceBundle;
  51   50  import java.util.SimpleTimeZone;
  52   51  import java.util.TimeZone;
  53   52  import java.util.concurrent.ConcurrentHashMap;
  54   53  import java.util.concurrent.ConcurrentMap;
       54 +import sun.util.locale.provider.LocaleProviderAdapter;
  55   55  import sun.util.calendar.CalendarUtils;
  56   56  import sun.util.calendar.ZoneInfoFile;
  57      -import sun.util.resources.LocaleData;
  58   57  
  59      -import static java.text.DateFormatSymbols.*;
  60      -
  61   58  /**
  62   59   * <code>SimpleDateFormat</code> is a concrete class for formatting and
  63   60   * parsing dates in a locale-sensitive manner. It allows for formatting
  64   61   * (date -> text), parsing (text -> date), and normalization.
  65   62   *
  66   63   * <p>
  67   64   * <code>SimpleDateFormat</code> allows you to start by choosing
  68   65   * any user-defined patterns for date-time formatting. However, you
  69   66   * are encouraged to create a date-time formatter with either
  70   67   * <code>getTimeInstance</code>, <code>getDateInstance</code>, or
↓ open down ↓ 39 lines elided ↑ open up ↑
 110  107   *         <td>Year
 111  108   *         <td><a href="#year">Year</a>
 112  109   *         <td><code>1996</code>; <code>96</code>
 113  110   *     <tr>
 114  111   *         <td><code>Y</code>
 115  112   *         <td>Week year
 116  113   *         <td><a href="#year">Year</a>
 117  114   *         <td><code>2009</code>; <code>09</code>
 118  115   *     <tr bgcolor="#eeeeff">
 119  116   *         <td><code>M</code>
 120      - *         <td>Month in year
      117 + *         <td>Month in year (context sensitive)
 121  118   *         <td><a href="#month">Month</a>
 122  119   *         <td><code>July</code>; <code>Jul</code>; <code>07</code>
 123  120   *     <tr>
      121 + *         <td><code>L</code>
      122 + *         <td>Month in year (standalone form)
      123 + *         <td><a href="#month">Month</a>
      124 + *         <td><code>July</code>; <code>Jul</code>; <code>07</code>
      125 + *     <tr bgcolor="#eeeeff">
 124  126   *         <td><code>w</code>
 125  127   *         <td>Week in year
 126  128   *         <td><a href="#number">Number</a>
 127  129   *         <td><code>27</code>
 128      - *     <tr bgcolor="#eeeeff">
      130 + *     <tr>
 129  131   *         <td><code>W</code>
 130  132   *         <td>Week in month
 131  133   *         <td><a href="#number">Number</a>
 132  134   *         <td><code>2</code>
 133      - *     <tr>
      135 + *     <tr bgcolor="#eeeeff">
 134  136   *         <td><code>D</code>
 135  137   *         <td>Day in year
 136  138   *         <td><a href="#number">Number</a>
 137  139   *         <td><code>189</code>
 138      - *     <tr bgcolor="#eeeeff">
      140 + *     <tr>
 139  141   *         <td><code>d</code>
 140  142   *         <td>Day in month
 141  143   *         <td><a href="#number">Number</a>
 142  144   *         <td><code>10</code>
 143      - *     <tr>
      145 + *     <tr bgcolor="#eeeeff">
 144  146   *         <td><code>F</code>
 145  147   *         <td>Day of week in month
 146  148   *         <td><a href="#number">Number</a>
 147  149   *         <td><code>2</code>
 148      - *     <tr bgcolor="#eeeeff">
      150 + *     <tr>
 149  151   *         <td><code>E</code>
 150  152   *         <td>Day name in week
 151  153   *         <td><a href="#text">Text</a>
 152  154   *         <td><code>Tuesday</code>; <code>Tue</code>
 153      - *     <tr>
      155 + *     <tr bgcolor="#eeeeff">
 154  156   *         <td><code>u</code>
 155  157   *         <td>Day number of week (1 = Monday, ..., 7 = Sunday)
 156  158   *         <td><a href="#number">Number</a>
 157  159   *         <td><code>1</code>
 158      - *     <tr bgcolor="#eeeeff">
      160 + *     <tr>
 159  161   *         <td><code>a</code>
 160  162   *         <td>Am/pm marker
 161  163   *         <td><a href="#text">Text</a>
 162  164   *         <td><code>PM</code>
 163      - *     <tr>
      165 + *     <tr bgcolor="#eeeeff">
 164  166   *         <td><code>H</code>
 165  167   *         <td>Hour in day (0-23)
 166  168   *         <td><a href="#number">Number</a>
 167  169   *         <td><code>0</code>
 168      - *     <tr bgcolor="#eeeeff">
      170 + *     <tr>
 169  171   *         <td><code>k</code>
 170  172   *         <td>Hour in day (1-24)
 171  173   *         <td><a href="#number">Number</a>
 172  174   *         <td><code>24</code>
 173      - *     <tr>
      175 + *     <tr bgcolor="#eeeeff">
 174  176   *         <td><code>K</code>
 175  177   *         <td>Hour in am/pm (0-11)
 176  178   *         <td><a href="#number">Number</a>
 177  179   *         <td><code>0</code>
 178      - *     <tr bgcolor="#eeeeff">
      180 + *     <tr>
 179  181   *         <td><code>h</code>
 180  182   *         <td>Hour in am/pm (1-12)
 181  183   *         <td><a href="#number">Number</a>
 182  184   *         <td><code>12</code>
 183      - *     <tr>
      185 + *     <tr bgcolor="#eeeeff">
 184  186   *         <td><code>m</code>
 185  187   *         <td>Minute in hour
 186  188   *         <td><a href="#number">Number</a>
 187  189   *         <td><code>30</code>
 188      - *     <tr bgcolor="#eeeeff">
      190 + *     <tr>
 189  191   *         <td><code>s</code>
 190  192   *         <td>Second in minute
 191  193   *         <td><a href="#number">Number</a>
 192  194   *         <td><code>55</code>
 193      - *     <tr>
      195 + *     <tr bgcolor="#eeeeff">
 194  196   *         <td><code>S</code>
 195  197   *         <td>Millisecond
 196  198   *         <td><a href="#number">Number</a>
 197  199   *         <td><code>978</code>
 198      - *     <tr bgcolor="#eeeeff">
      200 + *     <tr>
 199  201   *         <td><code>z</code>
 200  202   *         <td>Time zone
 201  203   *         <td><a href="#timezone">General time zone</a>
 202  204   *         <td><code>Pacific Standard Time</code>; <code>PST</code>; <code>GMT-08:00</code>
 203      - *     <tr>
      205 + *     <tr bgcolor="#eeeeff">
 204  206   *         <td><code>Z</code>
 205  207   *         <td>Time zone
 206  208   *         <td><a href="#rfc822timezone">RFC 822 time zone</a>
 207  209   *         <td><code>-0800</code>
 208      - *     <tr bgcolor="#eeeeff">
      210 + *     <tr>
 209  211   *         <td><code>X</code>
 210  212   *         <td>Time zone
 211  213   *         <td><a href="#iso8601timezone">ISO 8601 time zone</a>
 212  214   *         <td><code>-08</code>; <code>-0800</code>;  <code>-08:00</code>
 213  215   * </table>
 214  216   * </blockquote>
 215  217   * Pattern letters are usually repeated, as their number determines the
 216  218   * exact presentation:
 217  219   * <ul>
 218  220   * <li><strong><a name="text">Text:</a></strong>
↓ open down ↓ 44 lines elided ↑ open up ↑
 263  265   *     #getCalendar() calendar} doesn't support any <a
 264  266   *     href="../util/GregorianCalendar.html#week_year"> week
 265  267   *     years</a>, the calendar year ({@code 'y'}) is used instead. The
 266  268   *     support of week years can be tested with a call to {@link
 267  269   *     DateFormat#getCalendar() getCalendar()}.{@link
 268  270   *     java.util.Calendar#isWeekDateSupported()
 269  271   *     isWeekDateSupported()}.<br><br></li>
 270  272   * <li><strong><a name="month">Month:</a></strong>
 271  273   *     If the number of pattern letters is 3 or more, the month is
 272  274   *     interpreted as <a href="#text">text</a>; otherwise,
 273      - *     it is interpreted as a <a href="#number">number</a>.<br><br></li>
      275 + *     it is interpreted as a <a href="#number">number</a>.<br>
      276 + *     <ul>
      277 + *     <li>Letter <em>M</em> produces context-sensitive month names, such as the
      278 + *         embedded form of names. If a {@code DateFormatSymbols} has been set
      279 + *         explicitly with constructor {@link #SimpleDateFormat(String,
      280 + *         DateFormatSymbols)} or method {@link
      281 + *         #setDateFormatSymbols(DateFormatSymbols)}, the month names given by
      282 + *         the {@code DateFormatSymbols} are used.</li>
      283 + *     <li>Letter <em>L</em> produces the standalone form of month names.</li>
      284 + *     </ul>
      285 + *     <br></li>
 274  286   * <li><strong><a name="timezone">General time zone:</a></strong>
 275  287   *     Time zones are interpreted as <a href="#text">text</a> if they have
 276  288   *     names. For time zones representing a GMT offset value, the
 277  289   *     following syntax is used:
 278  290   *     <pre>
 279  291   *     <a name="GMTOffsetTimeZone"><i>GMTOffsetTimeZone:</i></a>
 280  292   *             <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
 281  293   *     <i>Sign:</i> one of
 282  294   *             <code>+ -</code>
 283  295   *     <i>Hours:</i>
↓ open down ↓ 168 lines elided ↑ open up ↑
 452  464       */
 453  465      transient private char minusSign = '-';
 454  466  
 455  467      /**
 456  468       * True when a negative sign follows a number.
 457  469       * (True as default in Arabic.)
 458  470       */
 459  471      transient private boolean hasFollowingMinusSign = false;
 460  472  
 461  473      /**
      474 +     * True if standalone form needs to be used.
      475 +     */
      476 +    transient private boolean forceStandaloneForm = false;
      477 +
      478 +    /**
 462  479       * The compiled pattern.
 463  480       */
 464  481      transient private char[] compiledPattern;
 465  482  
 466  483      /**
 467  484       * Tags for the compiled pattern.
 468  485       */
 469  486      private final static int TAG_QUOTE_ASCII_CHAR       = 100;
 470  487      private final static int TAG_QUOTE_CHARS            = 101;
 471  488  
↓ open down ↓ 23 lines elided ↑ open up ↑
 495  512  
 496  513      transient private int defaultCenturyStartYear;
 497  514  
 498  515      private static final int MILLIS_PER_MINUTE = 60 * 1000;
 499  516  
 500  517      // For time zones that have no names, use strings GMT+minutes and
 501  518      // GMT-minutes. For instance, in France the time zone is GMT+60.
 502  519      private static final String GMT = "GMT";
 503  520  
 504  521      /**
 505      -     * Cache to hold the DateTimePatterns of a Locale.
 506      -     */
 507      -    private static final ConcurrentMap<Locale, String[]> cachedLocaleData
 508      -        = new ConcurrentHashMap<Locale, String[]>(3);
 509      -
 510      -    /**
 511  522       * Cache NumberFormat instances with Locale key.
 512  523       */
 513  524      private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
 514      -        = new ConcurrentHashMap<Locale, NumberFormat>(3);
      525 +        = new ConcurrentHashMap<>(3);
 515  526  
 516  527      /**
 517  528       * The Locale used to instantiate this
 518  529       * <code>SimpleDateFormat</code>. The value may be null if this object
 519  530       * has been created by an older <code>SimpleDateFormat</code> and
 520  531       * deserialized.
 521  532       *
 522  533       * @serial
 523  534       * @since 1.6
 524  535       */
↓ open down ↓ 9 lines elided ↑ open up ↑
 534  545      transient boolean useDateFormatSymbols;
 535  546  
 536  547      /**
 537  548       * Constructs a <code>SimpleDateFormat</code> using the default pattern and
 538  549       * date format symbols for the default locale.
 539  550       * <b>Note:</b> This constructor may not support all locales.
 540  551       * For full coverage, use the factory methods in the {@link DateFormat}
 541  552       * class.
 542  553       */
 543  554      public SimpleDateFormat() {
 544      -        this(SHORT, SHORT, Locale.getDefault(Locale.Category.FORMAT));
      555 +        this("", Locale.getDefault(Locale.Category.FORMAT));
      556 +        applyPatternImpl(LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale)
      557 +                         .getDateTimePattern(SHORT, SHORT, calendar));
 545  558      }
 546  559  
 547  560      /**
 548  561       * Constructs a <code>SimpleDateFormat</code> using the given pattern and
 549  562       * the default date format symbols for the default locale.
 550  563       * <b>Note:</b> This constructor may not support all locales.
 551  564       * For full coverage, use the factory methods in the {@link DateFormat}
 552  565       * class.
 553  566       *
 554  567       * @param pattern the pattern describing the date and time format
↓ open down ↓ 46 lines elided ↑ open up ↑
 601  614          }
 602  615  
 603  616          this.pattern = pattern;
 604  617          this.formatData = (DateFormatSymbols) formatSymbols.clone();
 605  618          this.locale = Locale.getDefault(Locale.Category.FORMAT);
 606  619          initializeCalendar(this.locale);
 607  620          initialize(this.locale);
 608  621          useDateFormatSymbols = true;
 609  622      }
 610  623  
 611      -    /* Package-private, called by DateFormat factory methods */
 612      -    SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) {
 613      -        if (loc == null) {
 614      -            throw new NullPointerException();
 615      -        }
 616      -
 617      -        this.locale = loc;
 618      -        // initialize calendar and related fields
 619      -        initializeCalendar(loc);
 620      -
 621      -        /* try the cache first */
 622      -        String[] dateTimePatterns = cachedLocaleData.get(loc);
 623      -        if (dateTimePatterns == null) { /* cache miss */
 624      -            ResourceBundle r = LocaleData.getDateFormatData(loc);
 625      -            if (!isGregorianCalendar()) {
 626      -                try {
 627      -                    dateTimePatterns = r.getStringArray(getCalendarName() + ".DateTimePatterns");
 628      -                } catch (MissingResourceException e) {
 629      -                }
 630      -            }
 631      -            if (dateTimePatterns == null) {
 632      -                dateTimePatterns = r.getStringArray("DateTimePatterns");
 633      -            }
 634      -            /* update cache */
 635      -            cachedLocaleData.putIfAbsent(loc, dateTimePatterns);
 636      -        }
 637      -        formatData = DateFormatSymbols.getInstanceRef(loc);
 638      -        if ((timeStyle >= 0) && (dateStyle >= 0)) {
 639      -            Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
 640      -                                     dateTimePatterns[dateStyle + 4]};
 641      -            pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
 642      -        }
 643      -        else if (timeStyle >= 0) {
 644      -            pattern = dateTimePatterns[timeStyle];
 645      -        }
 646      -        else if (dateStyle >= 0) {
 647      -            pattern = dateTimePatterns[dateStyle + 4];
 648      -        }
 649      -        else {
 650      -            throw new IllegalArgumentException("No date or time style specified");
 651      -        }
 652      -
 653      -        initialize(loc);
 654      -    }
 655      -
 656  624      /* Initialize compiledPattern and numberFormat fields */
 657  625      private void initialize(Locale loc) {
 658  626          // Verify and compile the given pattern.
 659  627          compiledPattern = compile(pattern);
 660  628  
 661  629          /* try the cache first */
 662  630          numberFormat = cachedNumberFormatData.get(loc);
 663  631          if (numberFormat == null) { /* cache miss */
 664  632              numberFormat = NumberFormat.getIntegerInstance(loc);
 665  633              numberFormat.setGroupingUsed(false);
↓ open down ↓ 77 lines elided ↑ open up ↑
 743  711       * character in place of Length. For example, if the given pattern
 744  712       * is "'o'", the TaggedData entry is
 745  713       * <code>((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o')</code>.
 746  714       *
 747  715       * @exception NullPointerException if the given pattern is null
 748  716       * @exception IllegalArgumentException if the given pattern is invalid
 749  717       */
 750  718      private char[] compile(String pattern) {
 751  719          int length = pattern.length();
 752  720          boolean inQuote = false;
 753      -        StringBuilder compiledPattern = new StringBuilder(length * 2);
      721 +        StringBuilder compiledCode = new StringBuilder(length * 2);
 754  722          StringBuilder tmpBuffer = null;
 755      -        int count = 0;
 756      -        int lastTag = -1;
      723 +        int count = 0, tagcount = 0;
      724 +        int lastTag = -1, prevTag = -1;
 757  725  
 758  726          for (int i = 0; i < length; i++) {
 759  727              char c = pattern.charAt(i);
 760  728  
 761  729              if (c == '\'') {
 762  730                  // '' is treated as a single quote regardless of being
 763  731                  // in a quoted section.
 764  732                  if ((i + 1) < length) {
 765  733                      c = pattern.charAt(i + 1);
 766  734                      if (c == '\'') {
 767  735                          i++;
 768  736                          if (count != 0) {
 769      -                            encode(lastTag, count, compiledPattern);
      737 +                            encode(lastTag, count, compiledCode);
      738 +                            tagcount++;
      739 +                            prevTag = lastTag;
 770  740                              lastTag = -1;
 771  741                              count = 0;
 772  742                          }
 773  743                          if (inQuote) {
 774  744                              tmpBuffer.append(c);
 775  745                          } else {
 776      -                            compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
      746 +                            compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
 777  747                          }
 778  748                          continue;
 779  749                      }
 780  750                  }
 781  751                  if (!inQuote) {
 782  752                      if (count != 0) {
 783      -                        encode(lastTag, count, compiledPattern);
      753 +                        encode(lastTag, count, compiledCode);
      754 +                        tagcount++;
      755 +                        prevTag = lastTag;
 784  756                          lastTag = -1;
 785  757                          count = 0;
 786  758                      }
 787  759                      if (tmpBuffer == null) {
 788  760                          tmpBuffer = new StringBuilder(length);
 789  761                      } else {
 790  762                          tmpBuffer.setLength(0);
 791  763                      }
 792  764                      inQuote = true;
 793  765                  } else {
 794  766                      int len = tmpBuffer.length();
 795  767                      if (len == 1) {
 796  768                          char ch = tmpBuffer.charAt(0);
 797  769                          if (ch < 128) {
 798      -                            compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
      770 +                            compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
 799  771                          } else {
 800      -                            compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | 1));
 801      -                            compiledPattern.append(ch);
      772 +                            compiledCode.append((char)(TAG_QUOTE_CHARS << 8 | 1));
      773 +                            compiledCode.append(ch);
 802  774                          }
 803  775                      } else {
 804      -                        encode(TAG_QUOTE_CHARS, len, compiledPattern);
 805      -                        compiledPattern.append(tmpBuffer);
      776 +                        encode(TAG_QUOTE_CHARS, len, compiledCode);
      777 +                        compiledCode.append(tmpBuffer);
 806  778                      }
 807  779                      inQuote = false;
 808  780                  }
 809  781                  continue;
 810  782              }
 811  783              if (inQuote) {
 812  784                  tmpBuffer.append(c);
 813  785                  continue;
 814  786              }
 815  787              if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
 816  788                  if (count != 0) {
 817      -                    encode(lastTag, count, compiledPattern);
      789 +                    encode(lastTag, count, compiledCode);
      790 +                    tagcount++;
      791 +                    prevTag = lastTag;
 818  792                      lastTag = -1;
 819  793                      count = 0;
 820  794                  }
 821  795                  if (c < 128) {
 822  796                      // In most cases, c would be a delimiter, such as ':'.
 823      -                    compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
      797 +                    compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
 824  798                  } else {
 825  799                      // Take any contiguous non-ASCII alphabet characters and
 826  800                      // put them in a single TAG_QUOTE_CHARS.
 827  801                      int j;
 828  802                      for (j = i + 1; j < length; j++) {
 829  803                          char d = pattern.charAt(j);
 830  804                          if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) {
 831  805                              break;
 832  806                          }
 833  807                      }
 834      -                    compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
      808 +                    compiledCode.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
 835  809                      for (; i < j; i++) {
 836      -                        compiledPattern.append(pattern.charAt(i));
      810 +                        compiledCode.append(pattern.charAt(i));
 837  811                      }
 838  812                      i--;
 839  813                  }
 840  814                  continue;
 841  815              }
 842  816  
 843  817              int tag;
 844  818              if ((tag = DateFormatSymbols.patternChars.indexOf(c)) == -1) {
 845  819                  throw new IllegalArgumentException("Illegal pattern character " +
 846  820                                                     "'" + c + "'");
 847  821              }
 848  822              if (lastTag == -1 || lastTag == tag) {
 849  823                  lastTag = tag;
 850  824                  count++;
 851  825                  continue;
 852  826              }
 853      -            encode(lastTag, count, compiledPattern);
      827 +            encode(lastTag, count, compiledCode);
      828 +            tagcount++;
      829 +            prevTag = lastTag;
 854  830              lastTag = tag;
 855  831              count = 1;
 856  832          }
 857  833  
 858  834          if (inQuote) {
 859  835              throw new IllegalArgumentException("Unterminated quote");
 860  836          }
 861  837  
 862  838          if (count != 0) {
 863      -            encode(lastTag, count, compiledPattern);
      839 +            encode(lastTag, count, compiledCode);
      840 +            tagcount++;
      841 +            prevTag = lastTag;
 864  842          }
 865  843  
      844 +        forceStandaloneForm = (tagcount == 1 && prevTag == PATTERN_MONTH);
      845 +
 866  846          // Copy the compiled pattern to a char array
 867      -        int len = compiledPattern.length();
      847 +        int len = compiledCode.length();
 868  848          char[] r = new char[len];
 869      -        compiledPattern.getChars(0, len, r, 0);
      849 +        compiledCode.getChars(0, len, r, 0);
 870  850          return r;
 871  851      }
 872  852  
 873  853      /**
 874  854       * Encodes the given tag and length and puts encoded char(s) into buffer.
 875  855       */
 876      -    private static final void encode(int tag, int length, StringBuilder buffer) {
      856 +    private static void encode(int tag, int length, StringBuilder buffer) {
 877  857          if (tag == PATTERN_ISO_ZONE && length >= 4) {
 878  858              throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length);
 879  859          }
 880  860          if (length < 255) {
 881  861              buffer.append((char)(tag << 8 | length));
 882  862          } else {
 883  863              buffer.append((char)((tag << 8) | 0xff));
 884  864              buffer.append((char)(length >>> 16));
 885  865              buffer.append((char)(length & 0xffff));
 886  866          }
↓ open down ↓ 47 lines elided ↑ open up ↑
 934  914       * Formats the given <code>Date</code> into a date/time string and appends
 935  915       * the result to the given <code>StringBuffer</code>.
 936  916       *
 937  917       * @param date the date-time value to be formatted into a date-time string.
 938  918       * @param toAppendTo where the new date-time text is to be appended.
 939  919       * @param pos the formatting position. On input: an alignment field,
 940  920       * if desired. On output: the offsets of the alignment field.
 941  921       * @return the formatted date-time string.
 942  922       * @exception NullPointerException if the given {@code date} is {@code null}.
 943  923       */
      924 +    @Override
 944  925      public StringBuffer format(Date date, StringBuffer toAppendTo,
 945  926                                 FieldPosition pos)
 946  927      {
 947  928          pos.beginIndex = pos.endIndex = 0;
 948  929          return format(date, toAppendTo, pos.getFieldDelegate());
 949  930      }
 950  931  
 951  932      // Called from Format after creating a FieldDelegate
 952  933      private StringBuffer format(Date date, StringBuffer toAppendTo,
 953  934                                  FieldDelegate delegate) {
↓ open down ↓ 38 lines elided ↑ open up ↑
 992  973       * <code>DateFormat.Field</code>, with the corresponding attribute value
 993  974       * being the same as the attribute key.
 994  975       *
 995  976       * @exception NullPointerException if obj is null.
 996  977       * @exception IllegalArgumentException if the Format cannot format the
 997  978       *            given object, or if the Format's pattern string is invalid.
 998  979       * @param obj The object to format
 999  980       * @return AttributedCharacterIterator describing the formatted value.
1000  981       * @since 1.4
1001  982       */
      983 +    @Override
1002  984      public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
1003  985          StringBuffer sb = new StringBuffer();
1004  986          CharacterIteratorFieldDelegate delegate = new
1005  987                           CharacterIteratorFieldDelegate();
1006  988  
1007  989          if (obj instanceof Date) {
1008  990              format((Date)obj, sb, delegate);
1009  991          }
1010  992          else if (obj instanceof Number) {
1011  993              format(new Date(((Number)obj).longValue()), sb, delegate);
↓ open down ↓ 3 lines elided ↑ open up ↑
1015  997                     "formatToCharacterIterator must be passed non-null object");
1016  998          }
1017  999          else {
1018 1000              throw new IllegalArgumentException(
1019 1001                               "Cannot format given Object as a Date");
1020 1002          }
1021 1003          return delegate.getIterator(sb.toString());
1022 1004      }
1023 1005  
1024 1006      // Map index into pattern character string to Calendar field number
1025      -    private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD =
1026      -    {
1027      -        Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE,
1028      -        Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE,
1029      -        Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK,
1030      -        Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH,
1031      -        Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,
1032      -        Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET,
     1007 +    private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD = {
     1008 +        Calendar.ERA,
     1009 +        Calendar.YEAR,
     1010 +        Calendar.MONTH,
     1011 +        Calendar.DATE,
     1012 +        Calendar.HOUR_OF_DAY,
     1013 +        Calendar.HOUR_OF_DAY,
     1014 +        Calendar.MINUTE,
     1015 +        Calendar.SECOND,
     1016 +        Calendar.MILLISECOND,
     1017 +        Calendar.DAY_OF_WEEK,
     1018 +        Calendar.DAY_OF_YEAR,
     1019 +        Calendar.DAY_OF_WEEK_IN_MONTH,
     1020 +        Calendar.WEEK_OF_YEAR,
     1021 +        Calendar.WEEK_OF_MONTH,
     1022 +        Calendar.AM_PM,
     1023 +        Calendar.HOUR,
     1024 +        Calendar.HOUR,
1033 1025          Calendar.ZONE_OFFSET,
1034      -        // Pseudo Calendar fields
1035      -        CalendarBuilder.WEEK_YEAR,
1036      -        CalendarBuilder.ISO_DAY_OF_WEEK,
1037      -        Calendar.ZONE_OFFSET
     1026 +        Calendar.ZONE_OFFSET,
     1027 +        CalendarBuilder.WEEK_YEAR,         // Pseudo Calendar field
     1028 +        CalendarBuilder.ISO_DAY_OF_WEEK,   // Pseudo Calendar field
     1029 +        Calendar.ZONE_OFFSET,
     1030 +        Calendar.MONTH
1038 1031      };
1039 1032  
1040 1033      // Map index into pattern character string to DateFormat field number
1041 1034      private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
1042      -        DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD,
1043      -        DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD,
1044      -        DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD,
1045      -        DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD,
1046      -        DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD,
1047      -        DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, DateFormat.WEEK_OF_YEAR_FIELD,
1048      -        DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD,
1049      -        DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD,
1050      -        DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD,
1051      -        DateFormat.YEAR_FIELD, DateFormat.DAY_OF_WEEK_FIELD,
1052      -        DateFormat.TIMEZONE_FIELD
     1035 +        DateFormat.ERA_FIELD,
     1036 +        DateFormat.YEAR_FIELD,
     1037 +        DateFormat.MONTH_FIELD,
     1038 +        DateFormat.DATE_FIELD,
     1039 +        DateFormat.HOUR_OF_DAY1_FIELD,
     1040 +        DateFormat.HOUR_OF_DAY0_FIELD,
     1041 +        DateFormat.MINUTE_FIELD,
     1042 +        DateFormat.SECOND_FIELD,
     1043 +        DateFormat.MILLISECOND_FIELD,
     1044 +        DateFormat.DAY_OF_WEEK_FIELD,
     1045 +        DateFormat.DAY_OF_YEAR_FIELD,
     1046 +        DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD,
     1047 +        DateFormat.WEEK_OF_YEAR_FIELD,
     1048 +        DateFormat.WEEK_OF_MONTH_FIELD,
     1049 +        DateFormat.AM_PM_FIELD,
     1050 +        DateFormat.HOUR1_FIELD,
     1051 +        DateFormat.HOUR0_FIELD,
     1052 +        DateFormat.TIMEZONE_FIELD,
     1053 +        DateFormat.TIMEZONE_FIELD,
     1054 +        DateFormat.YEAR_FIELD,
     1055 +        DateFormat.DAY_OF_WEEK_FIELD,
     1056 +        DateFormat.TIMEZONE_FIELD,
     1057 +        DateFormat.MONTH_FIELD
1053 1058      };
1054 1059  
1055 1060      // Maps from DecimalFormatSymbols index to Field constant
1056 1061      private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = {
1057      -        Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH,
1058      -        Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE,
1059      -        Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK,
1060      -        Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH,
1061      -        Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH,
1062      -        Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE,
     1062 +        Field.ERA,
     1063 +        Field.YEAR,
     1064 +        Field.MONTH,
     1065 +        Field.DAY_OF_MONTH,
     1066 +        Field.HOUR_OF_DAY1,
     1067 +        Field.HOUR_OF_DAY0,
     1068 +        Field.MINUTE,
     1069 +        Field.SECOND,
     1070 +        Field.MILLISECOND,
     1071 +        Field.DAY_OF_WEEK,
     1072 +        Field.DAY_OF_YEAR,
     1073 +        Field.DAY_OF_WEEK_IN_MONTH,
     1074 +        Field.WEEK_OF_YEAR,
     1075 +        Field.WEEK_OF_MONTH,
     1076 +        Field.AM_PM,
     1077 +        Field.HOUR1,
     1078 +        Field.HOUR0,
1063 1079          Field.TIME_ZONE,
1064      -        Field.YEAR, Field.DAY_OF_WEEK,
1065      -        Field.TIME_ZONE
     1080 +        Field.TIME_ZONE,
     1081 +        Field.YEAR,
     1082 +        Field.DAY_OF_WEEK,
     1083 +        Field.TIME_ZONE,
     1084 +        Field.MONTH
1066 1085      };
1067 1086  
1068 1087      /**
1069 1088       * Private member function that does the real date/time formatting.
1070 1089       */
1071 1090      private void subFormat(int patternCharIndex, int count,
1072 1091                             FieldDelegate delegate, StringBuffer buffer,
1073 1092                             boolean useDateFormatSymbols)
1074 1093      {
1075 1094          int     maxIntCount = Integer.MAX_VALUE;
↓ open down ↓ 11 lines elided ↑ open up ↑
1087 1106                  field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1088 1107                  value = calendar.get(field);
1089 1108              }
1090 1109          } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) {
1091 1110              value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK));
1092 1111          } else {
1093 1112              value = calendar.get(field);
1094 1113          }
1095 1114  
1096 1115          int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
1097      -        if (!useDateFormatSymbols && field != CalendarBuilder.ISO_DAY_OF_WEEK) {
     1116 +        if (!useDateFormatSymbols && field < Calendar.ZONE_OFFSET
     1117 +            && patternCharIndex != PATTERN_MONTH_STANDALONE) {
1098 1118              current = calendar.getDisplayName(field, style, locale);
1099 1119          }
1100 1120  
1101 1121          // Note: zeroPaddingNumber() assumes that maxDigits is either
1102 1122          // 2 or maxIntCount. If we make any changes to this,
1103 1123          // zeroPaddingNumber() must be fixed.
1104 1124  
1105 1125          switch (patternCharIndex) {
1106 1126          case PATTERN_ERA: // 'G'
1107 1127              if (useDateFormatSymbols) {
1108 1128                  String[] eras = formatData.getEras();
1109      -                if (value < eras.length)
     1129 +                if (value < eras.length) {
1110 1130                      current = eras[value];
     1131 +                }
1111 1132              }
1112      -            if (current == null)
     1133 +            if (current == null) {
1113 1134                  current = "";
     1135 +            }
1114 1136              break;
1115 1137  
1116 1138          case PATTERN_WEEK_YEAR: // 'Y'
1117 1139          case PATTERN_YEAR:      // 'y'
1118 1140              if (calendar instanceof GregorianCalendar) {
1119      -                if (count != 2)
     1141 +                if (count != 2) {
1120 1142                      zeroPaddingNumber(value, count, maxIntCount, buffer);
1121      -                else // count == 2
1122      -                    zeroPaddingNumber(value, 2, 2, buffer); // clip 1996 to 96
     1143 +                } else {
     1144 +                    zeroPaddingNumber(value, 2, 2, buffer);
     1145 +                } // clip 1996 to 96
1123 1146              } else {
1124 1147                  if (current == null) {
1125 1148                      zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
1126 1149                                        maxIntCount, buffer);
1127 1150                  }
1128 1151              }
1129 1152              break;
1130 1153  
1131      -        case PATTERN_MONTH: // 'M'
     1154 +        case PATTERN_MONTH:            // 'M' (context seinsive)
1132 1155              if (useDateFormatSymbols) {
1133 1156                  String[] months;
1134 1157                  if (count >= 4) {
1135 1158                      months = formatData.getMonths();
1136 1159                      current = months[value];
1137 1160                  } else if (count == 3) {
1138 1161                      months = formatData.getShortMonths();
1139 1162                      current = months[value];
1140 1163                  }
1141 1164              } else {
1142 1165                  if (count < 3) {
1143 1166                      current = null;
     1167 +                } else if (forceStandaloneForm) {
     1168 +                    current = calendar.getDisplayName(field, style | 0x8000, locale);
     1169 +                    if (current == null) {
     1170 +                        current = calendar.getDisplayName(field, style, locale);
     1171 +                    }
1144 1172                  }
1145 1173              }
1146 1174              if (current == null) {
1147 1175                  zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1148 1176              }
1149 1177              break;
1150 1178  
     1179 +        case PATTERN_MONTH_STANDALONE: // 'L'
     1180 +            assert current == null;
     1181 +            if (locale == null) {
     1182 +                String[] months;
     1183 +                if (count >= 4) {
     1184 +                    months = formatData.getMonths();
     1185 +                    current = months[value];
     1186 +                } else if (count == 3) {
     1187 +                    months = formatData.getShortMonths();
     1188 +                    current = months[value];
     1189 +                }
     1190 +            } else {
     1191 +                if (count >= 3) {
     1192 +                    current = calendar.getDisplayName(field, style | 0x8000, locale);
     1193 +                }
     1194 +            }
     1195 +            if (current == null) {
     1196 +                zeroPaddingNumber(value+1, count, maxIntCount, buffer);
     1197 +            }
     1198 +            break;
     1199 +
1151 1200          case PATTERN_HOUR_OF_DAY1: // 'k' 1-based.  eg, 23:59 + 1 hour =>> 24:59
1152 1201              if (current == null) {
1153      -                if (value == 0)
1154      -                    zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1,
     1202 +                if (value == 0) {
     1203 +                    zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
1155 1204                                        count, maxIntCount, buffer);
1156      -                else
     1205 +                } else {
1157 1206                      zeroPaddingNumber(value, count, maxIntCount, buffer);
     1207 +                }
1158 1208              }
1159 1209              break;
1160 1210  
1161 1211          case PATTERN_DAY_OF_WEEK: // 'E'
1162 1212              if (useDateFormatSymbols) {
1163 1213                  String[] weekdays;
1164 1214                  if (count >= 4) {
1165 1215                      weekdays = formatData.getWeekdays();
1166 1216                      current = weekdays[value];
1167 1217                  } else { // count < 4, use abbreviated form if exists
↓ open down ↓ 5 lines elided ↑ open up ↑
1173 1223  
1174 1224          case PATTERN_AM_PM:    // 'a'
1175 1225              if (useDateFormatSymbols) {
1176 1226                  String[] ampm = formatData.getAmPmStrings();
1177 1227                  current = ampm[value];
1178 1228              }
1179 1229              break;
1180 1230  
1181 1231          case PATTERN_HOUR1:    // 'h' 1-based.  eg, 11PM + 1 hour =>> 12 AM
1182 1232              if (current == null) {
1183      -                if (value == 0)
1184      -                    zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1,
     1233 +                if (value == 0) {
     1234 +                    zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR) + 1,
1185 1235                                        count, maxIntCount, buffer);
1186      -                else
     1236 +                } else {
1187 1237                      zeroPaddingNumber(value, count, maxIntCount, buffer);
     1238 +                }
1188 1239              }
1189 1240              break;
1190 1241  
1191 1242          case PATTERN_ZONE_NAME: // 'z'
1192 1243              if (current == null) {
1193 1244                  if (formatData.locale == null || formatData.isZoneStringsSet) {
1194 1245                      int zoneIndex =
1195 1246                          formatData.getZoneIndex(calendar.getTimeZone().getID());
1196 1247                      if (zoneIndex == -1) {
1197 1248                          value = calendar.get(Calendar.ZONE_OFFSET) +
↓ open down ↓ 84 lines elided ↑ open up ↑
1282 1333  
1283 1334          int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex];
1284 1335          Field f = PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID[patternCharIndex];
1285 1336  
1286 1337          delegate.formatted(fieldID, f, f, beginOffset, buffer.length(), buffer);
1287 1338      }
1288 1339  
1289 1340      /**
1290 1341       * Formats a number with the specified minimum and maximum number of digits.
1291 1342       */
1292      -    private final void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
     1343 +    private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
1293 1344      {
1294 1345          // Optimization for 1, 2 and 4 digit numbers. This should
1295 1346          // cover most cases of formatting date/time related items.
1296 1347          // Note: This optimization code assumes that maxDigits is
1297 1348          // either 2 or Integer.MAX_VALUE (maxIntCount in format()).
1298 1349          try {
1299 1350              if (zeroDigit == 0) {
1300 1351                  zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit();
1301 1352              }
1302 1353              if (value >= 0) {
↓ open down ↓ 61 lines elided ↑ open up ↑
1364 1415       * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
1365 1416       * to be restored for further operations.
1366 1417       *
1367 1418       * @param text  A <code>String</code>, part of which should be parsed.
1368 1419       * @param pos   A <code>ParsePosition</code> object with index and error
1369 1420       *              index information as described above.
1370 1421       * @return A <code>Date</code> parsed from the string. In case of
1371 1422       *         error, returns null.
1372 1423       * @exception NullPointerException if <code>text</code> or <code>pos</code> is null.
1373 1424       */
     1425 +    @Override
1374 1426      public Date parse(String text, ParsePosition pos)
1375 1427      {
1376 1428          checkNegativeNumberExpression();
1377 1429  
1378 1430          int start = pos.index;
1379 1431          int oldStart = start;
1380 1432          int textLength = text.length();
1381 1433  
1382 1434          boolean[] ambiguousYear = {false};
1383 1435  
↓ open down ↓ 113 lines elided ↑ open up ↑
1497 1549       * @param field the date field being parsed.
1498 1550       * @param data the string array to parsed.
1499 1551       * @return the new start position if matching succeeded; a negative number
1500 1552       * indicating matching failure, otherwise.
1501 1553       */
1502 1554      private int matchString(String text, int start, int field, String[] data, CalendarBuilder calb)
1503 1555      {
1504 1556          int i = 0;
1505 1557          int count = data.length;
1506 1558  
1507      -        if (field == Calendar.DAY_OF_WEEK) i = 1;
     1559 +        if (field == Calendar.DAY_OF_WEEK) {
     1560 +            i = 1;
     1561 +        }
1508 1562  
1509 1563          // There may be multiple strings in the data[] array which begin with
1510 1564          // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1511 1565          // We keep track of the longest match, and return that.  Note that this
1512 1566          // unfortunately requires us to test all array elements.
1513 1567          int bestMatchLength = 0, bestMatch = -1;
1514 1568          for (; i<count; ++i)
1515 1569          {
1516 1570              int length = data[i].length();
1517 1571              // Always compare if we have no match yet; otherwise only compare
↓ open down ↓ 242 lines elided ↑ open up ↑
1760 1814          int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1761 1815  
1762 1816          // If there are any spaces here, skip over them.  If we hit the end
1763 1817          // of the string, then fail.
1764 1818          for (;;) {
1765 1819              if (pos.index >= text.length()) {
1766 1820                  origPos.errorIndex = start;
1767 1821                  return -1;
1768 1822              }
1769 1823              char c = text.charAt(pos.index);
1770      -            if (c != ' ' && c != '\t') break;
     1824 +            if (c != ' ' && c != '\t') {
     1825 +                break;
     1826 +            }
1771 1827              ++pos.index;
1772 1828          }
1773 1829  
1774 1830        parsing:
1775 1831          {
1776 1832              // We handle a few special cases here where we need to parse
1777 1833              // a number value.  We handle further, more generic cases below.  We need
1778 1834              // to handle some of them here because some fields require extra processing on
1779 1835              // the parsed value.
1780 1836              if (patternCharIndex == PATTERN_HOUR_OF_DAY1 ||
↓ open down ↓ 124 lines elided ↑ open up ↑
1905 1961                  break parsing;
1906 1962  
1907 1963              case PATTERN_HOUR_OF_DAY1: // 'k' 1-based.  eg, 23:59 + 1 hour =>> 24:59
1908 1964                  if (!isLenient()) {
1909 1965                      // Validate the hour value in non-lenient
1910 1966                      if (value < 1 || value > 24) {
1911 1967                          break parsing;
1912 1968                      }
1913 1969                  }
1914 1970                  // [We computed 'value' above.]
1915      -                if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY)+1)
     1971 +                if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1) {
1916 1972                      value = 0;
     1973 +                }
1917 1974                  calb.set(Calendar.HOUR_OF_DAY, value);
1918 1975                  return pos.index;
1919 1976  
1920 1977              case PATTERN_DAY_OF_WEEK:  // 'E'
1921 1978                  {
1922 1979                      if (useDateFormatSymbols) {
1923 1980                          // Want to be able to parse both short and long forms.
1924 1981                          // Try count == 4 (DDDD) first:
1925 1982                          int newStart = 0;
1926 1983                          if ((newStart=matchString(text, start, Calendar.DAY_OF_WEEK,
↓ open down ↓ 32 lines elided ↑ open up ↑
1959 2016                  break parsing;
1960 2017  
1961 2018              case PATTERN_HOUR1: // 'h' 1-based.  eg, 11PM + 1 hour =>> 12 AM
1962 2019                  if (!isLenient()) {
1963 2020                      // Validate the hour value in non-lenient
1964 2021                      if (value < 1 || value > 12) {
1965 2022                          break parsing;
1966 2023                      }
1967 2024                  }
1968 2025                  // [We computed 'value' above.]
1969      -                if (value == calendar.getLeastMaximum(Calendar.HOUR)+1)
     2026 +                if (value == calendar.getLeastMaximum(Calendar.HOUR) + 1) {
1970 2027                      value = 0;
     2028 +                }
1971 2029                  calb.set(Calendar.HOUR, value);
1972 2030                  return pos.index;
1973 2031  
1974 2032              case PATTERN_ZONE_NAME:  // 'z'
1975 2033              case PATTERN_ZONE_VALUE: // 'Z'
1976 2034                  {
1977 2035                      int sign = 0;
1978 2036                      try {
1979 2037                          char c = text.charAt(pos.index);
1980 2038                          if (c == '+') {
↓ open down ↓ 123 lines elided ↑ open up ↑
2104 2162                  }
2105 2163                  break parsing;
2106 2164              }
2107 2165          }
2108 2166  
2109 2167          // Parsing failed.
2110 2168          origPos.errorIndex = pos.index;
2111 2169          return -1;
2112 2170      }
2113 2171  
2114      -    private final String getCalendarName() {
2115      -        return calendar.getClass().getName();
2116      -    }
2117      -
     2172 +    /**
     2173 +     * Returns true if the DateFormatSymbols has been set explicitly or locale
     2174 +     * is null.
     2175 +     */
2118 2176      private boolean useDateFormatSymbols() {
2119      -        if (useDateFormatSymbols) {
2120      -            return true;
2121      -        }
2122      -        return isGregorianCalendar() || locale == null;
     2177 +        return useDateFormatSymbols || locale == null;
2123 2178      }
2124 2179  
2125      -    private boolean isGregorianCalendar() {
2126      -        return "java.util.GregorianCalendar".equals(getCalendarName());
2127      -    }
2128      -
2129 2180      /**
2130 2181       * Translates a pattern, mapping each character in the from string to the
2131 2182       * corresponding character in the to string.
2132 2183       *
2133 2184       * @exception IllegalArgumentException if the given pattern is invalid
2134 2185       */
2135 2186      private String translatePattern(String pattern, String from, String to) {
2136 2187          StringBuilder result = new StringBuilder();
2137 2188          boolean inQuote = false;
2138 2189          for (int i = 0; i < pattern.length(); ++i) {
2139 2190              char c = pattern.charAt(i);
2140 2191              if (inQuote) {
2141      -                if (c == '\'')
     2192 +                if (c == '\'') {
2142 2193                      inQuote = false;
     2194 +                }
2143 2195              }
2144 2196              else {
2145      -                if (c == '\'')
     2197 +                if (c == '\'') {
2146 2198                      inQuote = true;
2147      -                else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
     2199 +                } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2148 2200                      int ci = from.indexOf(c);
2149 2201                      if (ci >= 0) {
2150 2202                          // patternChars is longer than localPatternChars due
2151 2203                          // to serialization compatibility. The pattern letters
2152 2204                          // unsupported by localPatternChars pass through.
2153 2205                          if (ci < to.length()) {
2154 2206                              c = to.charAt(ci);
2155 2207                          }
2156 2208                      } else {
2157 2209                          throw new IllegalArgumentException("Illegal pattern " +
2158 2210                                                             " character '" +
2159 2211                                                             c + "'");
2160 2212                      }
2161 2213                  }
2162 2214              }
2163 2215              result.append(c);
2164 2216          }
2165      -        if (inQuote)
     2217 +        if (inQuote) {
2166 2218              throw new IllegalArgumentException("Unfinished quote in pattern");
     2219 +        }
2167 2220          return result.toString();
2168 2221      }
2169 2222  
2170 2223      /**
2171 2224       * Returns a pattern string describing this date format.
2172 2225       *
2173 2226       * @return a pattern string describing this date format.
2174 2227       */
2175 2228      public String toPattern() {
2176 2229          return pattern;
↓ open down ↓ 12 lines elided ↑ open up ↑
2189 2242  
2190 2243      /**
2191 2244       * Applies the given pattern string to this date format.
2192 2245       *
2193 2246       * @param pattern the new date and time pattern for this date format
2194 2247       * @exception NullPointerException if the given pattern is null
2195 2248       * @exception IllegalArgumentException if the given pattern is invalid
2196 2249       */
2197 2250      public void applyPattern(String pattern)
2198 2251      {
     2252 +        applyPatternImpl(pattern);
     2253 +    }
     2254 +
     2255 +    private void applyPatternImpl(String pattern) {
2199 2256          compiledPattern = compile(pattern);
2200 2257          this.pattern = pattern;
2201 2258      }
2202 2259  
2203 2260      /**
2204 2261       * Applies the given localized pattern string to this date format.
2205 2262       *
2206 2263       * @param pattern a String to be mapped to the new date and time format
2207 2264       *        pattern for this format
2208 2265       * @exception NullPointerException if the given pattern is null
↓ open down ↓ 30 lines elided ↑ open up ↑
2239 2296          this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
2240 2297          useDateFormatSymbols = true;
2241 2298      }
2242 2299  
2243 2300      /**
2244 2301       * Creates a copy of this <code>SimpleDateFormat</code>. This also
2245 2302       * clones the format's date format symbols.
2246 2303       *
2247 2304       * @return a clone of this <code>SimpleDateFormat</code>
2248 2305       */
     2306 +    @Override
2249 2307      public Object clone() {
2250 2308          SimpleDateFormat other = (SimpleDateFormat) super.clone();
2251 2309          other.formatData = (DateFormatSymbols) formatData.clone();
2252 2310          return other;
2253 2311      }
2254 2312  
2255 2313      /**
2256 2314       * Returns the hash code value for this <code>SimpleDateFormat</code> object.
2257 2315       *
2258 2316       * @return the hash code value for this <code>SimpleDateFormat</code> object.
2259 2317       */
     2318 +    @Override
2260 2319      public int hashCode()
2261 2320      {
2262 2321          return pattern.hashCode();
2263 2322          // just enough fields for a reasonable distribution
2264 2323      }
2265 2324  
2266 2325      /**
2267 2326       * Compares the given object with this <code>SimpleDateFormat</code> for
2268 2327       * equality.
2269 2328       *
2270 2329       * @return true if the given object is equal to this
2271 2330       * <code>SimpleDateFormat</code>
2272 2331       */
     2332 +    @Override
2273 2333      public boolean equals(Object obj)
2274 2334      {
2275      -        if (!super.equals(obj)) return false; // super does class check
     2335 +        if (!super.equals(obj)) {
     2336 +            return false; // super does class check
     2337 +        }
2276 2338          SimpleDateFormat that = (SimpleDateFormat) obj;
2277 2339          return (pattern.equals(that.pattern)
2278 2340                  && formatData.equals(that.formatData));
2279 2341      }
2280 2342  
2281 2343      /**
2282 2344       * After reading an object from the input stream, the format
2283 2345       * pattern in the object is verified.
2284 2346       * <p>
2285 2347       * @exception InvalidObjectException if the pattern is invalid
↓ open down ↓ 64 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX