001/* DateFormatSymbols.java -- Format over a range of numbers
002   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2006  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.text;
040
041import gnu.java.locale.LocaleHelper;
042
043import java.io.IOException;
044
045import java.text.spi.DateFormatSymbolsProvider;
046
047import java.util.ArrayList;
048import java.util.HashMap;
049import java.util.List;
050import java.util.Locale;
051import java.util.Map;
052import java.util.MissingResourceException;
053import java.util.Properties;
054import java.util.ResourceBundle;
055import java.util.ServiceLoader;
056import java.util.TimeZone;
057
058import 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 */
071public 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}