001    /* DateFormatSymbols.java -- Format over a range of numbers
002       Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2006  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.text;
040    
041    import gnu.java.locale.LocaleHelper;
042    
043    import java.io.IOException;
044    
045    import java.text.spi.DateFormatSymbolsProvider;
046    
047    import java.util.ArrayList;
048    import java.util.HashMap;
049    import java.util.List;
050    import java.util.Locale;
051    import java.util.Map;
052    import java.util.MissingResourceException;
053    import java.util.Properties;
054    import java.util.ResourceBundle;
055    import java.util.ServiceLoader;
056    import java.util.TimeZone;
057    
058    import java.util.spi.TimeZoneNameProvider;
059    
060    /**
061     * This class acts as container for locale specific date/time formatting
062     * information such as the days of the week and the months of the year.
063     *
064     * @author Per Bothner (bothner@cygnus.com)
065     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
066     * @date October 24, 1998.
067     */
068    /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3.
069     * Status:  Believed complete and correct.
070     */
071    public class DateFormatSymbols implements java.io.Serializable, Cloneable
072    {
073      String[] ampms;
074      String[] eras;
075      private String localPatternChars;
076      String[] months;
077      String[] shortMonths;
078      String[] shortWeekdays;
079      String[] weekdays;
080    
081      /**
082       * The set of properties for obtaining the metazone data.
083       */
084      private static transient final Properties properties;
085    
086      /**
087       * Reads in the properties.
088       */
089      static
090      {
091        properties = new Properties();
092        try
093          {
094            properties.load(DateFormatSymbols.class.getResourceAsStream("metazones.properties"));
095          }
096        catch (IOException exception)
097          {
098            System.out.println("Failed to load weeks resource: " + exception);
099          }
100      }
101    
102      /**
103       * The timezone strings supplied by the runtime.
104       */
105      private String[][] runtimeZoneStrings;
106    
107      /**
108       * Custom timezone strings supplied by {@link #setZoneStrings()}.
109       */
110      private String[][] zoneStrings;
111    
112      private static final long serialVersionUID = -5987973545549424702L;
113    
114      // The order of these prefixes must be the same as in DateFormat
115      private static final String[] formatPrefixes =
116      {
117        "full", "long", "medium", "short"
118      };
119    
120      // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL,
121      // and DEFAULT (constants defined in java.text.DateFormat).  While
122      // not part of the official spec, we need a way to get at locale-specific
123      // default formatting patterns.  They are declared package scope so
124      // as to be easily accessible where needed (DateFormat, SimpleDateFormat).
125      transient String[] dateFormats;
126      transient String[] timeFormats;
127    
128      private static String[] getStringArray(ResourceBundle res, String name)
129      {
130        return res.getString(name).split("\u00ae");
131      }
132    
133      private String[][] getZoneStrings(ResourceBundle res, Locale locale)
134      {
135        List<String[]> allZones = new ArrayList<String[]>();
136        try
137          {
138            Map<String,String[]> systemZones = new HashMap<String,String[]>();
139            while (true)
140              {
141                int index = 0;
142                String country = locale.getCountry();
143                String data = res.getString("zoneStrings");
144                String[] zones = data.split("\u00a9");
145                for (int a = 0; a < zones.length; ++a)
146                  {
147                    String[] strings = zones[a].split("\u00ae");
148                    String type = properties.getProperty(strings[0] + "." + country);
149                    if (type == null)
150                      type = properties.getProperty(strings[0] + ".DEFAULT");
151                    if (type != null)
152                      strings[0] = type;
153                    if (strings.length < 5)
154                      {
155                        String[] newStrings = new String[5];
156                        System.arraycopy(strings, 0, newStrings, 0, strings.length);
157                        for (int b = strings.length; b < newStrings.length; ++b)
158                          newStrings[b] = "";
159                        strings = newStrings;
160                      }
161                    String[] existing = systemZones.get(strings[0]);
162                    if (existing != null && existing.length > 1)
163                      {
164                        for (int b = 1; b < existing.length; ++b)
165                          if (!existing[b].equals(""))
166                            strings[b] = existing[b];
167                      }
168                    systemZones.put(strings[0], strings);
169                  }
170                if (res.getLocale() == Locale.ROOT)
171                  break;
172                else
173                  res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
174                                                 LocaleHelper.getFallbackLocale(res.getLocale()),
175                                                 ClassLoader.getSystemClassLoader());
176              }
177            /* Final sanity check for missing values */
178            for (String[] zstrings : systemZones.values())
179              {
180                if (zstrings[1].equals("") && zstrings[2].equals(""))
181                  {
182                    for (Map.Entry<Object,Object> entry : properties.entrySet())
183                      {
184                        String val = (String) entry.getValue();
185                        if (val.equals(zstrings[0]))
186                          {
187                            String key = (String) entry.getKey();
188                            String metazone = key.substring(0, key.indexOf("."));
189                            String type = properties.getProperty(metazone + "." + locale.getCountry());
190                            if (type == null)
191                              type = properties.getProperty(metazone + ".DEFAULT");
192                            if (type != null)
193                              {
194                                String[] ostrings = systemZones.get(type);
195                                zstrings[1] = ostrings[1];
196                                zstrings[2] = ostrings[2];
197                              }
198                          }
199                      }
200                  }
201              }
202            allZones.addAll(systemZones.values());
203          }
204        catch (MissingResourceException e)
205          {
206            /* This means runtime support for the locale
207             * is not available, so we just include providers. */
208          }
209        for (TimeZoneNameProvider p :
210               ServiceLoader.load(TimeZoneNameProvider.class))
211          {
212            for (Locale loc : p.getAvailableLocales())
213              {
214                if (loc.equals(locale))
215                  {
216                    for (String id : TimeZone.getAvailableIDs())
217                      {
218                        String[] z = new String[5];
219                        z[0] = id;
220                        z[1] = p.getDisplayName(id, false,
221                                                TimeZone.LONG,
222                                                locale);
223                        z[2] = p.getDisplayName(id, false,
224                                                TimeZone.SHORT,
225                                                locale);
226                        z[3] = p.getDisplayName(id, true,
227                                                TimeZone.LONG,
228                                                locale);
229                        z[4] = p.getDisplayName(id, true,
230                                                TimeZone.SHORT,
231                                                locale);
232                        allZones.add(z);
233                      }
234                    break;
235                  }
236              }
237          }
238        return allZones.toArray(new String[allZones.size()][]);
239      }
240    
241      private String[] formatsForKey(ResourceBundle res, String key)
242      {
243        String[] values = new String[formatPrefixes.length];
244    
245        for (int i = 0; i < formatPrefixes.length; i++)
246          values[i] = res.getString(formatPrefixes[i] + key);
247    
248        return values;
249      }
250    
251      /**
252       * This method initializes a new instance of <code>DateFormatSymbols</code>
253       * by loading the date format information for the specified locale.
254       * This constructor only obtains instances using the runtime's resources;
255       * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances,
256       * call {@link #getInstance(java.util.Locale)} instead.
257       *
258       * @param locale The locale for which date formatting symbols should
259       *               be loaded.
260       * @throws MissingResourceException if the resources for the specified
261       *                                  locale could not be found or loaded.
262       * @see #getInstance(java.util.Locale)
263       */
264      public DateFormatSymbols (Locale locale)
265        throws MissingResourceException
266      {
267        ResourceBundle res
268          = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale,
269                                     ClassLoader.getSystemClassLoader());
270    
271        ampms = getStringArray(res, "ampms");
272        eras = getStringArray(res, "eras");
273        localPatternChars = res.getString("localPatternChars");
274        months = getStringArray(res, "months");
275        shortMonths = getStringArray(res, "shortMonths");
276        shortWeekdays = getStringArray(res, "shortWeekdays");
277        weekdays = getStringArray(res, "weekdays");
278        dateFormats = formatsForKey(res, "DateFormat");
279        timeFormats = formatsForKey(res, "TimeFormat");
280        runtimeZoneStrings = getZoneStrings(res, locale);
281      }
282    
283      /**
284       * This method loads the format symbol information for the default
285       * locale. This constructor only obtains instances using the runtime's resources;
286       * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances,
287       * call {@link #getInstance()} instead.
288       *
289       * @throws MissingResourceException if the resources for the default
290       *                                  locale could not be found or loaded.
291       * @see #getInstance()
292       */
293      public DateFormatSymbols()
294        throws MissingResourceException
295      {
296        this (Locale.getDefault());
297      }
298    
299      /**
300       * This method returns the list of strings used for displaying AM or PM.
301       * This is a two element <code>String</code> array indexed by
302       * <code>Calendar.AM</code> and <code>Calendar.PM</code>
303       *
304       * @return The list of AM/PM display strings.
305       */
306      public String[] getAmPmStrings()
307      {
308        return ampms;
309      }
310    
311      /**
312        * This method returns the list of strings used for displaying eras
313        * (e.g., "BC" and "AD").  This is a two element <code>String</code>
314        * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
315        *
316        * @return The list of era disply strings.
317        */
318      public String[] getEras()
319      {
320        return eras;
321      }
322    
323      /**
324        * This method returns the pattern character information for this
325        * object.  This is an 18 character string that contains the characters
326        * that are used in creating the date formatting strings in
327        * <code>SimpleDateFormat</code>.   The following are the character
328        * positions in the string and which format character they correspond
329        * to (the character in parentheses is the default value in the US English
330        * locale):
331        * <p>
332        * <ul>
333        * <li>0 - era (G)</li>
334        * <li>1 - year (y)</li>
335        * <li>2 - month (M)</li>
336        * <li>3 - day of month (d)</li>
337        * <li>4 - hour out of 12, from 1-12 (h)</li>
338        * <li>5 - hour out of 24, from 0-23 (H)</li>
339        * <li>6 - minute (m)</li>
340        * <li>7 - second (s)</li>
341        * <li>8 - millisecond (S)</li>
342        * <li>9 - date of week (E)</li>
343        * <li>10 - date of year (D)</li>
344        * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
345        * <li>12 - week in year (w)</li>
346        * <li>13 - week in month (W)</li>
347        * <li>14 - am/pm (a)</li>
348        * <li>15 - hour out of 24, from 1-24 (k)</li>
349        * <li>16 - hour out of 12, from 0-11 (K)</li>
350        * <li>17 - time zone (z)</li>
351        * </ul>
352        *
353        * @return The format patter characters
354        */
355      public String getLocalPatternChars()
356      {
357        return localPatternChars;
358      }
359    
360      /**
361       * This method returns the list of strings used for displaying month
362       * names (e.g., "January" and "February").  This is a thirteen element
363       * string array indexed by <code>Calendar.JANUARY</code> through
364       * <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
365       * elements because some calendars have thriteen months.
366       *
367       * @return The list of month display strings.
368       */
369      public String[] getMonths ()
370      {
371        return months;
372      }
373    
374      /**
375       * This method returns the list of strings used for displaying abbreviated
376       * month names (e.g., "Jan" and "Feb").  This is a thirteen element
377       * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
378       * through <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
379       * elements because some calendars have thirteen months.
380       *
381       * @return The list of abbreviated month display strings.
382       */
383      public String[] getShortMonths ()
384      {
385        return shortMonths;
386      }
387    
388      /**
389       * This method returns the list of strings used for displaying abbreviated
390       * weekday names (e.g., "Sun" and "Mon").  This is an eight element
391       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
392       * through <code>Calendar.SATURDAY</code>.  Note that the first element
393       * of this array is ignored.
394       *
395       * @return This list of abbreviated weekday display strings.
396       */
397      public String[] getShortWeekdays ()
398      {
399        return shortWeekdays;
400      }
401    
402      /**
403       * This method returns the list of strings used for displaying weekday
404       * names (e.g., "Sunday" and "Monday").  This is an eight element
405       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
406       * through <code>Calendar.SATURDAY</code>.  Note that the first element
407       * of this array is ignored.
408       *
409       * @return This list of weekday display strings.
410       */
411      public String[] getWeekdays ()
412      {
413        return weekdays;
414      }
415    
416      /**
417       * This method returns this list of localized timezone display strings.
418       * This is a two dimensional <code>String</code> array where each row in
419       * the array contains five values:
420       * <P>
421       * <ul>
422       * <li>0 - The non-localized time zone id string.</li>
423       * <li>1 - The long name of the time zone (standard time).</li>
424       * <li>2 - The short name of the time zone (standard time).</li>
425       * <li>3 - The long name of the time zone (daylight savings time).</li>
426       * <li>4 - the short name of the time zone (daylight savings time).</li>
427       * </ul>
428       * <p>
429       * If {@link #setZoneStrings(String[][])} has been called, then the value
430       * passed to this will be returned.  Otherwise the returned array contains
431       * zone names provided by the runtime environment and any
432       * {@link java.util.spi.TimeZoneProvider} instances.
433       * </p>
434       *
435       * @return The list of time zone display strings.
436       * @see #setZoneStrings(String[][])
437       */
438      public String[][] getZoneStrings()
439      {
440        if (zoneStrings != null)
441          return zoneStrings;
442        return runtimeZoneStrings;
443      }
444    
445      /**
446       * This method sets the list of strings used to display AM/PM values to
447       * the specified list.
448       * This is a two element <code>String</code> array indexed by
449       * <code>Calendar.AM</code> and <code>Calendar.PM</code>
450       *
451       * @param value The new list of AM/PM display strings.
452       */
453      public void setAmPmStrings (String[] value)
454      {
455        if(value==null)
456          throw new NullPointerException();
457        ampms = value;
458      }
459    
460      /**
461       * This method sets the list of strings used to display time eras to
462       * to the specified list.
463       * This is a two element <code>String</code>
464       * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
465       *
466       * @param labels The new list of era display strings.
467       */
468      public void setEras (String[] labels)
469      {
470        if(labels==null)
471          throw new NullPointerException();
472        eras = labels;
473      }
474    
475      /**
476        * This method sets the list of characters used to specific date/time
477        * formatting strings.
478        * This is an 18 character string that contains the characters
479        * that are used in creating the date formatting strings in
480        * <code>SimpleDateFormat</code>.   The following are the character
481        * positions in the string and which format character they correspond
482        * to (the character in parentheses is the default value in the US English
483        * locale):
484        * <p>
485        * <ul>
486        * <li>0 - era (G)</li>
487        * <li>1 - year (y)</li>
488        * <li>2 - month (M)</li>
489        * <li>3 - day of month (d)</li>
490        * <li>4 - hour out of 12, from 1-12 (h)</li>
491        * <li>5 - hour out of 24, from 0-23 (H)</li>
492        * <li>6 - minute (m)</li>
493        * <li>7 - second (s)</li>
494        * <li>8 - millisecond (S)</li>
495        * <li>9 - date of week (E)</li>
496        * <li>10 - date of year (D)</li>
497        * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li>
498        * <li>12 - week in year (w)</li>
499        * <li>13 - week in month (W)</li>
500        * <li>14 - am/pm (a)</li>
501        * <li>15 - hour out of 24, from 1-24 (k)</li>
502        * <li>16 - hour out of 12, from 0-11 (K)</li>
503        * <li>17 - time zone (z)</li>
504        * </ul>
505        *
506        * @param chars The new format pattern characters
507        */
508      public void setLocalPatternChars (String chars)
509      {
510        if(chars==null)
511          throw new NullPointerException();
512        localPatternChars = chars;
513      }
514    
515      /**
516        * This method sets the list of strings used to display month names.
517        * This is a thirteen element
518        * string array indexed by <code>Calendar.JANUARY</code> through
519        * <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
520        * elements because some calendars have thriteen months.
521        *
522        * @param labels The list of month display strings.
523        */
524      public void setMonths (String[] labels)
525      {
526        if(labels==null)
527          throw new NullPointerException();
528        months = labels;
529      }
530    
531      /**
532       * This method sets the list of strings used to display abbreviated month
533       * names.
534       * This is a thirteen element
535       * <code>String</code> array indexed by <code>Calendar.JANUARY</code>
536       * through <code>Calendar.UNDECEMBER</code>.  Note that there are thirteen
537       * elements because some calendars have thirteen months.
538       *
539       * @param labels The new list of abbreviated month display strings.
540       */
541      public void setShortMonths (String[] labels)
542      {
543        if(labels==null)
544          throw new NullPointerException();
545        shortMonths = labels;
546      }
547    
548      /**
549       * This method sets the list of strings used to display abbreviated
550       * weekday names.
551       * This is an eight element
552       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
553       * through <code>Calendar.SATURDAY</code>.  Note that the first element
554       * of this array is ignored.
555       *
556       * @param labels This list of abbreviated weekday display strings.
557       */
558      public void setShortWeekdays (String[] labels)
559      {
560        if(labels==null)
561          throw new NullPointerException();
562        shortWeekdays = labels;
563      }
564    
565      /**
566       * This method sets the list of strings used to display weekday names.
567       * This is an eight element
568       * <code>String</code> array indexed by <code>Calendar.SUNDAY</code>
569       * through <code>Calendar.SATURDAY</code>.  Note that the first element
570       * of this array is ignored.
571       *
572       * @param labels This list of weekday display strings.
573       */
574      public void setWeekdays (String[] labels)
575      {
576        if(labels==null)
577          throw new NullPointerException();
578        weekdays = labels;
579      }
580    
581      /**
582       * This method sets the list of display strings for time zones.
583       * This is a two dimensional <code>String</code> array where each row in
584       * the array contains five values:
585       * <P>
586       * <ul>
587       * <li>0 - The non-localized time zone id string.</li>
588       * <li>1 - The long name of the time zone (standard time).</li>
589       * <li>2 - The short name of the time zone (standard time).</li>
590       * <li>3 - The long name of the time zone (daylight savings time).</li>
591       * <li>4 - the short name of the time zone (daylight savings time).</li>
592       * </ul>
593       *
594       * @params zones The list of time zone display strings.
595       */
596      public void setZoneStrings (String[][] zones)
597      {
598        if(zones==null)
599          throw new NullPointerException();
600        zoneStrings = zones;
601      }
602    
603      /* Does a "deep" equality test - recurses into arrays. */
604      private static boolean equals (Object x, Object y)
605      {
606        if (x == y)
607          return true;
608        if (x == null || y == null)
609          return false;
610        if (! (x instanceof Object[]) || ! (y instanceof Object[]))
611          return x.equals(y);
612        Object[] xa = (Object[]) x;
613        Object[] ya = (Object[]) y;
614        if (xa.length != ya.length)
615          return false;
616        for (int i = xa.length;  --i >= 0; )
617          {
618            if (! equals(xa[i], ya[i]))
619              return false;
620          }
621        return true;
622      }
623    
624      private static int hashCode (Object x)
625      {
626        if (x == null)
627          return 0;
628        if (! (x instanceof Object[]))
629          return x.hashCode();
630        Object[] xa = (Object[]) x;
631        int hash = 0;
632        for (int i = 0;  i < xa.length;  i++)
633          hash = 37 * hashCode(xa[i]);
634        return hash;
635      }
636    
637      /**
638       * This method tests a specified object for equality against this object.
639       * This will be true if and only if the specified object:
640       * <p>
641       * <ul>
642       * <li> Is not <code>null</code>.</li>
643       * <li> Is an instance of <code>DateFormatSymbols</code>.</li>
644       * <li> Contains identical formatting symbols to this object.</li>
645       * </ul>
646       *
647       * @param obj The <code>Object</code> to test for equality against.
648       *
649       * @return <code>true</code> if the specified object is equal to this one,
650       * <code>false</code> otherwise.
651       */
652      public boolean equals (Object obj)
653      {
654        if (! (obj instanceof DateFormatSymbols))
655          return false;
656        DateFormatSymbols other = (DateFormatSymbols) obj;
657        return (equals(ampms, other.ampms)
658                && equals(eras, other.eras)
659                && equals(localPatternChars, other.localPatternChars)
660                && equals(months, other.months)
661                && equals(shortMonths, other.shortMonths)
662                && equals(shortWeekdays, other.shortWeekdays)
663                && equals(weekdays, other.weekdays)
664                && equals(zoneStrings, other.zoneStrings));
665      }
666    
667      /**
668       * Returns a new copy of this object.
669       *
670       * @return A copy of this object
671       */
672      public Object clone ()
673      {
674        try
675          {
676            return super.clone ();
677          }
678        catch (CloneNotSupportedException e)
679          {
680            return null;
681          }
682      }
683    
684      /**
685       * This method returns a hash value for this object.
686       *
687       * @return A hash value for this object.
688       */
689      public int hashCode ()
690      {
691        return (hashCode(ampms)
692                ^ hashCode(eras)
693                ^ hashCode(localPatternChars)
694                ^ hashCode(months)
695                ^ hashCode(shortMonths)
696                ^ hashCode(shortWeekdays)
697                ^ hashCode(weekdays)
698                ^ hashCode(zoneStrings));
699      }
700    
701      /**
702       * Returns a {@link DateFormatSymbols} instance for the
703       * default locale obtained from either the runtime itself
704       * or one of the installed
705       * {@link java.text.spi.DateFormatSymbolsProvider} instances.
706       * This is equivalent to calling
707       * <code>getInstance(Locale.getDefault())</code>.
708       *
709       * @return a {@link DateFormatSymbols} instance for the default
710       *         locale.
711       * @since 1.6
712       */
713      public static final DateFormatSymbols getInstance()
714      {
715        return getInstance(Locale.getDefault());
716      }
717    
718      /**
719       * Returns a {@link DateFormatSymbols} instance for the
720       * specified locale obtained from either the runtime itself
721       * or one of the installed
722       * {@link java.text.spi.DateFormatSymbolsProvider} instances.
723       *
724       * @param locale the locale for which an instance should be
725       *               returned.
726       * @return a {@link DateFormatSymbols} instance for the specified
727       *         locale.
728       * @throws NullPointerException if <code>locale</code> is
729       *                              <code>null</code>.
730       * @since 1.6
731       */
732      public static final DateFormatSymbols getInstance(Locale locale)
733      {
734        try
735          {
736            DateFormatSymbols syms = new DateFormatSymbols(locale);
737            return syms;
738          }
739        catch (MissingResourceException e)
740          {
741            /* This means runtime support for the locale
742             * is not available, so we check providers. */
743          }
744        for (DateFormatSymbolsProvider p :
745               ServiceLoader.load(DateFormatSymbolsProvider.class))
746          {
747            for (Locale loc : p.getAvailableLocales())
748              {
749                if (loc.equals(locale))
750                  {
751                    DateFormatSymbols syms = p.getInstance(locale);
752                    if (syms != null)
753                      return syms;
754                    break;
755                  }
756              }
757          }
758        return getInstance(LocaleHelper.getFallbackLocale(locale));
759      }
760    
761    }