001    /* Calendar.java --
002       Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.util;
041    
042    import java.io.IOException;
043    import java.io.ObjectInputStream;
044    import java.io.ObjectOutputStream;
045    import java.io.Serializable;
046    
047    import java.lang.reflect.Constructor;
048    import java.lang.reflect.InvocationTargetException;
049    
050    import java.text.DateFormatSymbols;
051    
052    /**
053     * This class is an abstract base class for Calendars, which can be
054     * used to convert between <code>Date</code> objects and a set of
055     * integer fields which represent <code>YEAR</code>,
056     * <code>MONTH</code>, <code>DAY</code>, etc.  The <code>Date</code>
057     * object represents a time in milliseconds since the Epoch. <br>
058     *
059     * This class is locale sensitive.  To get the Object matching the
060     * current locale you can use <code>getInstance</code>.  You can even provide
061     * a locale or a timezone.  <code>getInstance</code> returns currently
062     * a <code>GregorianCalendar</code> for the current date. <br>
063     *
064     * If you want to convert a date from the Year, Month, Day, DayOfWeek,
065     * etc.  Representation to a <code>Date</code>-Object, you can create
066     * a new Calendar with <code>getInstance()</code>,
067     * <code>clear()</code> all fields, <code>set(int,int)</code> the
068     * fields you need and convert it with <code>getTime()</code>. <br>
069     *
070     * If you want to convert a <code>Date</code>-object to the Calendar
071     * representation, create a new Calendar, assign the
072     * <code>Date</code>-Object with <code>setTime()</code>, and read the
073     * fields with <code>get(int)</code>. <br>
074     *
075     * When computing the date from time fields, it may happen, that there
076     * are either two few fields set, or some fields are inconsistent.  This
077     * cases will handled in a calendar specific way.  Missing fields are
078     * replaced by the fields of the epoch: 1970 January 1 00:00. <br>
079     *
080     * To understand, how the day of year is computed out of the fields
081     * look at the following table.  It is traversed from top to bottom,
082     * and for the first line all fields are set, that line is used to
083     * compute the day. <br>
084     *
085     *
086    <pre>month + day_of_month
087    month + week_of_month + day_of_week
088    month + day_of_week_of_month + day_of_week
089    day_of_year
090    day_of_week + week_of_year</pre>
091     *
092     * The hour_of_day-field takes precedence over the ampm and
093     * hour_of_ampm fields. <br>
094     *
095     * <STRONG>Note:</STRONG> This can differ for non-Gregorian calendar. <br>
096     *
097     * To convert a calendar to a human readable form and vice versa,  use
098     * the <code>java.text.DateFormat</code> class. <br>
099     *
100     * Other useful things you can do with an calendar, is
101     * <code>roll</code>ing fields (that means increase/decrease a
102     * specific field by one, propagating overflows), or
103     * <code>add</code>ing/substracting a fixed amount to a field.
104     *
105     * @author Aaron M. Renn (arenn@urbanophile.com)
106     * @author Jochen Hoenicke (Jochen.Hoenicke@Informatik.Uni-Oldenburg.de)
107     * @author Warren Levy (warrenl@cygnus.com)
108     * @author Jeff Sturm (jsturm@one-point.com)
109     * @author Tom Tromey (tromey@redhat.com)
110     * @author Bryce McKinlay (mckinlay@redhat.com)
111     * @author Ingo Proetel (proetel@aicas.com)
112     * @author Jerry Quinn (jlquinn@optonline.net)
113     * @author Jeroen Frijters (jeroen@frijters.net)
114     * @author Noa Resare (noa@resare.com)
115     * @author Sven de Marothy (sven@physto.se)
116     * @author David Gilbert (david.gilbert@object-refinery.com)
117     * @author Olivier Jolly (olivier.jolly@pcedev.com)
118     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
119     * @see Date
120     * @see GregorianCalendar
121     * @see TimeZone
122     * @see java.text.DateFormat
123     */
124    public abstract class Calendar
125      implements Serializable, Cloneable, Comparable<Calendar>
126    {
127      /**
128       * Constant representing the era time field.
129       */
130      public static final int ERA = 0;
131    
132      /**
133       * Constant representing the year time field.
134       */
135      public static final int YEAR = 1;
136    
137      /**
138       * Constant representing the month time field.  This field
139       * should contain one of the JANUARY,...,DECEMBER constants below.
140       */
141      public static final int MONTH = 2;
142    
143      /**
144       * Constant representing the week of the year field.
145       * @see #setFirstDayOfWeek(int)
146       */
147      public static final int WEEK_OF_YEAR = 3;
148    
149      /**
150       * Constant representing the week of the month time field.
151       * @see #setFirstDayOfWeek(int)
152       */
153      public static final int WEEK_OF_MONTH = 4;
154    
155      /**
156       * Constant representing the day time field, synonym for DAY_OF_MONTH.
157       */
158      public static final int DATE = 5;
159    
160      /**
161       * Constant representing the day time field.
162       */
163      public static final int DAY_OF_MONTH = 5;
164    
165      /**
166       * Constant representing the day of year time field.  This is
167       * 1 for the first day in month.
168       */
169      public static final int DAY_OF_YEAR = 6;
170    
171      /**
172       * Constant representing the day of week time field.  This field
173       * should contain one of the SUNDAY,...,SATURDAY constants below.
174       */
175      public static final int DAY_OF_WEEK = 7;
176    
177      /**
178       * Constant representing the day-of-week-in-month field.  For
179       * instance this field contains 2 for the second thursday in a
180       * month.  If you give a negative number here, the day will count
181       * from the end of the month.
182       */
183      public static final int DAY_OF_WEEK_IN_MONTH = 8;
184    
185      /**
186       * Constant representing the part of the day for 12-hour clock.  This
187       * should be one of AM or PM.
188       */
189      public static final int AM_PM = 9;
190    
191      /**
192       * Constant representing the hour time field for 12-hour clock.
193       */
194      public static final int HOUR = 10;
195    
196      /**
197       * Constant representing the hour of day time field for 24-hour clock.
198       */
199      public static final int HOUR_OF_DAY = 11;
200    
201      /**
202       * Constant representing the minute of hour time field.
203       */
204      public static final int MINUTE = 12;
205    
206      /**
207       * Constant representing the second time field.
208       */
209      public static final int SECOND = 13;
210    
211      /**
212       * Constant representing the millisecond time field.
213       */
214      public static final int MILLISECOND = 14;
215    
216      /**
217       * Constant representing the time zone offset time field for the
218       * time given in the other fields.  It is measured in
219       * milliseconds.  The default is the offset of the time zone.
220       */
221      public static final int ZONE_OFFSET = 15;
222    
223      /**
224       * Constant representing the daylight saving time offset in
225       * milliseconds.  The default is the value given by the time zone.
226       */
227      public static final int DST_OFFSET = 16;
228    
229      /**
230       * Number of time fields.
231       */
232      public static final int FIELD_COUNT = 17;
233    
234      /**
235       * Constant representing Sunday.
236       */
237      public static final int SUNDAY = 1;
238    
239      /**
240       * Constant representing Monday.
241       */
242      public static final int MONDAY = 2;
243    
244      /**
245       * Constant representing Tuesday.
246       */
247      public static final int TUESDAY = 3;
248    
249      /**
250       * Constant representing Wednesday.
251       */
252      public static final int WEDNESDAY = 4;
253    
254      /**
255       * Constant representing Thursday.
256       */
257      public static final int THURSDAY = 5;
258    
259      /**
260       * Constant representing Friday.
261       */
262      public static final int FRIDAY = 6;
263    
264      /**
265       * Constant representing Saturday.
266       */
267      public static final int SATURDAY = 7;
268    
269      /**
270       * Constant representing January.
271       */
272      public static final int JANUARY = 0;
273    
274      /**
275       * Constant representing February.
276       */
277      public static final int FEBRUARY = 1;
278    
279      /**
280       * Constant representing March.
281       */
282      public static final int MARCH = 2;
283    
284      /**
285       * Constant representing April.
286       */
287      public static final int APRIL = 3;
288    
289      /**
290       * Constant representing May.
291       */
292      public static final int MAY = 4;
293    
294      /**
295       * Constant representing June.
296       */
297      public static final int JUNE = 5;
298    
299      /**
300       * Constant representing July.
301       */
302      public static final int JULY = 6;
303    
304      /**
305       * Constant representing August.
306       */
307      public static final int AUGUST = 7;
308    
309      /**
310       * Constant representing September.
311       */
312      public static final int SEPTEMBER = 8;
313    
314      /**
315       * Constant representing October.
316       */
317      public static final int OCTOBER = 9;
318    
319      /**
320       * Constant representing November.
321       */
322      public static final int NOVEMBER = 10;
323    
324      /**
325       * Constant representing December.
326       */
327      public static final int DECEMBER = 11;
328    
329      /**
330       * Constant representing Undecimber. This is an artificial name useful
331       * for lunar calendars.
332       */
333      public static final int UNDECIMBER = 12;
334    
335      /**
336       * Useful constant for 12-hour clock.
337       */
338      public static final int AM = 0;
339    
340      /**
341       * Useful constant for 12-hour clock.
342       */
343      public static final int PM = 1;
344    
345      /**
346       * A style specifier for {@link #getDisplayNames(int,int,Locale)}
347       * stating that names should be returned in both long and short variants.
348       *
349       * @since 1.6
350       * @see #SHORT
351       * @see #LONG
352       */
353      public static final int ALL_STYLES = 0;
354    
355      /**
356       * A style specifier for {@link #getDisplayName(int,int,Locale)}
357       * and {@link #getDisplayNames(int,int,Locale)} stating that names
358       * should be returned in their short variant if applicable.
359       *
360       * @since 1.6
361       */
362      public static final int SHORT = 1;
363    
364      /**
365       * A style specifier for {@link #getDisplayName(int,int,Locale)}
366       * and {@link #getDisplayNames(int,int,Locale)} stating that names
367       * should be returned in their long variant if applicable.
368       *
369       * @since 1.6
370       */
371      public static final int LONG = 2;
372    
373      /**
374       * The time fields.  The array is indexed by the constants YEAR to
375       * DST_OFFSET.
376       * @serial
377       */
378      protected int[] fields = new int[FIELD_COUNT];
379    
380      /**
381       * The flags which tell if the fields above have a value.
382       * @serial
383       */
384      protected boolean[] isSet = new boolean[FIELD_COUNT];
385    
386      /**
387       * The time in milliseconds since the epoch.
388       * @serial
389       */
390      protected long time;
391    
392      /**
393       * Tells if the above field has a valid value.
394       * @serial
395       */
396      protected boolean isTimeSet;
397    
398      /**
399       * Tells if the fields have a valid value.  This superseeds the isSet
400       * array.
401       * @serial
402       */
403      protected boolean areFieldsSet;
404    
405      /**
406       * The time zone of this calendar.  Used by sub classes to do UTC / local
407       * time conversion.  Sub classes can access this field with getTimeZone().
408       * @serial
409       */
410      private TimeZone zone;
411    
412      /**
413       * Specifies if the date/time interpretation should be lenient.
414       * If the flag is set, a date such as "February 30, 1996" will be
415       * treated as the 29th day after the February 1.  If this flag
416       * is false, such dates will cause an exception.
417       * @serial
418       */
419      private boolean lenient;
420    
421      /**
422       * Sets what the first day of week is.  This is used for
423       * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
424       * @serial
425       */
426      private int firstDayOfWeek;
427    
428      /**
429       * Sets how many days are required in the first week of the year.
430       * If the first day of the year should be the first week you should
431       * set this value to 1.  If the first week must be a full week, set
432       * it to 7.
433       * @serial
434       */
435      private int minimalDaysInFirstWeek;
436    
437      /**
438       * Is set to true if DST_OFFSET is explicitly set. In that case
439       * it's value overrides the value computed from the current
440       * time and the timezone.
441       */
442      private boolean explicitDSTOffset = false;
443    
444      /**
445       * The version of the serialized data on the stream.
446       * <dl><dt>0 or not present</dt>
447       * <dd> JDK 1.1.5 or later.</dd>
448       * <dt>1</dt>
449       * <dd>JDK 1.1.6 or later.  This always writes a correct `time' value
450       * on the stream, as well as the other fields, to be compatible with
451       * earlier versions</dd></dl>
452       * @since JDK1.1.6
453       * @serial
454       */
455      private int serialVersionOnStream = 1;
456    
457      /**
458       * XXX - I have not checked the compatibility.  The documentation of
459       * the serialized-form is quite hairy...
460       */
461      static final long serialVersionUID = -1807547505821590642L;
462    
463      /**
464       * The name of the resource bundle. Used only by getBundle()
465       */
466      private static final String bundleName = "gnu.java.locale.Calendar";
467    
468      /**
469       * get resource bundle:
470       * The resources should be loaded via this method only. Iff an application
471       * uses this method, the resourcebundle is required.
472       */
473      private static ResourceBundle getBundle(Locale locale)
474      {
475        return ResourceBundle.getBundle(bundleName, locale,
476                                        ClassLoader.getSystemClassLoader());
477      }
478    
479      /**
480       * Constructs a new Calendar with the default time zone and the default
481       * locale.
482       */
483      protected Calendar()
484      {
485        this(TimeZone.getDefault(), Locale.getDefault());
486      }
487    
488      /**
489       * Constructs a new Calendar with the given time zone and the given
490       * locale.
491       * @param zone a time zone.
492       * @param locale a locale.
493       */
494      protected Calendar(TimeZone zone, Locale locale)
495      {
496        this.zone = zone;
497        lenient = true;
498    
499        ResourceBundle rb = getBundle(locale);
500    
501        firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue();
502        minimalDaysInFirstWeek = ((Integer) rb.getObject("minimalDaysInFirstWeek"))
503                                 .intValue();
504        clear();
505      }
506    
507      /**
508       * Creates a calendar representing the actual time, using the default
509       * time zone and locale.
510       * 
511       * @return The new calendar.
512       */
513      public static synchronized Calendar getInstance()
514      {
515        return getInstance(TimeZone.getDefault(), Locale.getDefault());
516      }
517    
518      /**
519       * Creates a calendar representing the actual time, using the given
520       * time zone and the default locale.
521       * 
522       * @param zone a time zone (<code>null</code> not permitted).
523       * 
524       * @return The new calendar.
525       * 
526       * @throws NullPointerException if <code>zone</code> is <code>null</code>.
527       */
528      public static synchronized Calendar getInstance(TimeZone zone)
529      {
530        return getInstance(zone, Locale.getDefault());
531      }
532    
533      /**
534       * Creates a calendar representing the actual time, using the default
535       * time zone and the given locale.
536       * 
537       * @param locale a locale (<code>null</code> not permitted).
538       * 
539       * @return The new calendar.
540       * 
541       * @throws NullPointerException if <code>locale</code> is <code>null</code>.
542       */
543      public static synchronized Calendar getInstance(Locale locale)
544      {
545        return getInstance(TimeZone.getDefault(), locale);
546      }
547    
548      /**
549       * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle
550       * lookup for every getInstance call.
551       */
552      private static HashMap<Locale,Class> cache = new HashMap<Locale,Class>();
553    
554      /** Preset argument types for calendar-class constructor lookup.  */
555      private static Class[] ctorArgTypes = new Class[]
556                                            {
557                                              TimeZone.class, Locale.class
558                                            };
559    
560      /**
561       * Creates a calendar representing the actual time, using the given
562       * time zone and locale.
563       * 
564       * @param zone a time zone (<code>null</code> not permitted).
565       * @param locale a locale (<code>null</code> not permitted).
566       * 
567       * @return The new calendar.
568       * 
569       * @throws NullPointerException if <code>zone</code> or <code>locale</code>
570       *     is <code>null</code>.
571       */
572      public static synchronized Calendar getInstance(TimeZone zone, Locale locale)
573      {
574        Class calendarClass = cache.get(locale);
575        Throwable exception = null;
576    
577        try
578          {
579            if (calendarClass == null)
580              {
581                ResourceBundle rb = getBundle(locale);
582                String calendarClassName = rb.getString("calendarClass");
583    
584                if (calendarClassName != null)
585                  {
586                    calendarClass = Class.forName(calendarClassName);
587                    if (Calendar.class.isAssignableFrom(calendarClass))
588                      cache.put(locale, calendarClass);
589                  }
590              }
591    
592            // GregorianCalendar is by far the most common case. Optimize by 
593            // avoiding reflection.
594            if (calendarClass == GregorianCalendar.class)
595              return new GregorianCalendar(zone, locale);
596    
597            if (Calendar.class.isAssignableFrom(calendarClass))
598              {
599                Constructor ctor = calendarClass.getConstructor(ctorArgTypes);
600                return (Calendar) ctor.newInstance(new Object[] { zone, locale });
601              }
602          }
603        catch (ClassNotFoundException ex)
604          {
605            exception = ex;
606          }
607        catch (IllegalAccessException ex)
608          {
609            exception = ex;
610          }
611        catch (NoSuchMethodException ex)
612          {
613            exception = ex;
614          }
615        catch (InstantiationException ex)
616          {
617            exception = ex;
618          }
619        catch (InvocationTargetException ex)
620          {
621            exception = ex;
622          }
623    
624        throw new RuntimeException("Error instantiating calendar for locale "
625                                   + locale, exception);
626      }
627    
628      /**
629       * Gets the set of locales for which a Calendar is available.
630       * @exception MissingResourceException if locale data couldn't be found.
631       * @return the set of locales.
632       */
633      public static synchronized Locale[] getAvailableLocales()
634      {
635        ResourceBundle rb = getBundle(new Locale("", ""));
636        return (Locale[]) rb.getObject("availableLocales");
637      }
638    
639      /**
640       * Converts the time field values (<code>fields</code>) to
641       * milliseconds since the epoch UTC (<code>time</code>).  Override
642       * this method if you write your own Calendar.  */
643      protected abstract void computeTime();
644    
645      /**
646       * Converts the milliseconds since the epoch UTC
647       * (<code>time</code>) to time fields
648       * (<code>fields</code>). Override this method if you write your
649       * own Calendar.
650       */
651      protected abstract void computeFields();
652    
653      /**
654       * Converts the time represented by this object to a
655       * <code>Date</code>-Object.
656       * @return the Date.
657       */
658      public final Date getTime()
659      {
660        if (! isTimeSet)
661          computeTime();
662        return new Date(time);
663      }
664    
665      /**
666       * Sets this Calendar's time to the given Date.  All time fields
667       * are invalidated by this method.
668       * 
669       * @param date  the date (<code>null</code> not permitted).
670       * 
671       * @throws NullPointerException if <code>date</code> is <code>null</code>.
672       */
673      public final void setTime(Date date)
674      {
675        setTimeInMillis(date.getTime());
676      }
677    
678      /**
679       * Returns the time represented by this Calendar.
680       * @return the time in milliseconds since the epoch.
681       * @specnote This was made public in 1.4.
682       */
683      public long getTimeInMillis()
684      {
685        if (! isTimeSet)
686          computeTime();
687        return time;
688      }
689    
690      /**
691       * Sets this Calendar's time to the given Time.  All time fields
692       * are invalidated by this method.
693       * @param time the time in milliseconds since the epoch
694       * @specnote This was made public in 1.4.
695       */
696      public void setTimeInMillis(long time)
697      {
698        clear();
699        this.time = time;
700        isTimeSet = true;
701        computeFields();
702      }
703    
704      /**
705       * Gets the value of the specified field.  They are recomputed
706       * if they are invalid.
707       * @param field the time field. One of the time field constants.
708       * @return the value of the specified field
709       * @throws ArrayIndexOutOfBoundsException if the field is outside
710       *         the valid range.  The value of field must be >= 0 and
711       *         <= <code>FIELD_COUNT</code>.
712       * @specnote Not final since JDK 1.4
713       */
714      public int get(int field)
715      {
716        // If the requested field is invalid, force all fields to be recomputed.
717        if (! isSet[field])
718          areFieldsSet = false;
719        complete();
720        return fields[field];
721      }
722    
723      /**
724       * Gets the value of the specified field. This method doesn't
725       * recompute the fields, if they are invalid.
726       * @param field the time field. One of the time field constants.
727       * @return the value of the specified field, undefined if
728       * <code>areFieldsSet</code> or <code>isSet[field]</code> is false.
729       * @throws ArrayIndexOutOfBoundsException if the field is outside
730       *         the valid range.  The value of field must be >= 0 and
731       *         <= <code>FIELD_COUNT</code>.
732       */
733      protected final int internalGet(int field)
734      {
735        return fields[field];
736      }
737    
738      /**
739       * Sets the time field with the given value.  This does invalidate
740       * the time in milliseconds.
741       * @param field the time field. One of the time field constants
742       * @param value the value to be set.
743       * @throws ArrayIndexOutOfBoundsException if field is outside
744       *         the valid range.  The value of field must be >= 0 and
745       *         <= <code>FIELD_COUNT</code>.
746       * @specnote Not final since JDK 1.4
747       */
748      public void set(int field, int value)
749      {
750        if (isTimeSet)
751          for (int i = 0; i < FIELD_COUNT; i++)
752            isSet[i] = false;
753        isTimeSet = false;
754        fields[field] = value;
755        isSet[field] = true;
756    
757        // The five valid date patterns, in order of priority
758        // 1  YEAR + MONTH + DAY_OF_MONTH
759        // 2  YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
760        // 3  YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
761        // 4  YEAR + DAY_OF_YEAR
762        // 5  YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
763        switch (field)
764          {
765          case MONTH: // pattern 1,2 or 3
766            isSet[DAY_OF_YEAR] = false;
767            isSet[WEEK_OF_YEAR] = false;
768            break;
769          case DAY_OF_MONTH: // pattern 1
770            isSet[YEAR] = true;
771            isSet[MONTH] = true;
772            isSet[WEEK_OF_MONTH] = true;
773            isSet[DAY_OF_WEEK] = false;
774            isSet[DAY_OF_WEEK_IN_MONTH] = false;
775            isSet[DAY_OF_YEAR] = false;
776            isSet[WEEK_OF_YEAR] = false;
777            break;
778          case WEEK_OF_MONTH: // pattern 2
779            if (! isSet[DAY_OF_WEEK])
780              fields[DAY_OF_WEEK] = getFirstDayOfWeek();
781            isSet[YEAR] = true;
782            isSet[MONTH] = true;
783            isSet[DAY_OF_WEEK] = true;
784            isSet[DAY_OF_MONTH] = false;
785            isSet[DAY_OF_WEEK_IN_MONTH] = false;
786            isSet[DAY_OF_YEAR] = false;
787            isSet[WEEK_OF_YEAR] = false;
788            break;
789          case DAY_OF_WEEK_IN_MONTH: // pattern 3
790            if (! isSet[DAY_OF_WEEK])
791              fields[DAY_OF_WEEK] = getFirstDayOfWeek();
792            isSet[YEAR] = true;
793            isSet[MONTH] = true;
794            isSet[DAY_OF_WEEK] = true;
795            isSet[DAY_OF_YEAR] = false;
796            isSet[DAY_OF_MONTH] = false;
797            isSet[WEEK_OF_MONTH] = false;
798            isSet[WEEK_OF_YEAR] = false;
799            break;
800          case DAY_OF_YEAR: // pattern 4
801            isSet[YEAR] = true;
802            isSet[MONTH] = false;
803            isSet[WEEK_OF_MONTH] = false;
804            isSet[DAY_OF_MONTH] = false;
805            isSet[DAY_OF_WEEK] = false;
806            isSet[WEEK_OF_YEAR] = false;
807            isSet[DAY_OF_WEEK_IN_MONTH] = false;
808            break;
809          case WEEK_OF_YEAR: // pattern 5
810            if (! isSet[DAY_OF_WEEK])
811              fields[DAY_OF_WEEK] = getFirstDayOfWeek();
812            isSet[YEAR] = true;
813            isSet[DAY_OF_WEEK] = true;
814            isSet[MONTH] = false;
815            isSet[DAY_OF_MONTH] = false;
816            isSet[WEEK_OF_MONTH] = false;
817            isSet[DAY_OF_YEAR] = false;
818            isSet[DAY_OF_WEEK_IN_MONTH] = false;
819            break;
820          case AM_PM:
821            isSet[HOUR] = true;
822            isSet[HOUR_OF_DAY] = false;
823            break;
824          case HOUR_OF_DAY:
825            isSet[AM_PM] = false;
826            isSet[HOUR] = false;
827            break;
828          case HOUR:
829            isSet[AM_PM] = true;
830            isSet[HOUR_OF_DAY] = false;
831            break;
832          case DST_OFFSET:
833            explicitDSTOffset = true;
834          }
835    
836        // May have crossed over a DST boundary.
837        if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET))
838          isSet[DST_OFFSET] = false;
839      }
840    
841      /**
842       * Sets the fields for year, month, and date
843       * @param year the year.
844       * @param month the month, one of the constants JANUARY..UNDICEMBER.
845       * @param date the day of the month
846       */
847      public final void set(int year, int month, int date)
848      {
849        isTimeSet = false;
850        fields[YEAR] = year;
851        fields[MONTH] = month;
852        fields[DATE] = date;
853        isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true;
854        isSet[WEEK_OF_YEAR] = false;
855        isSet[DAY_OF_YEAR] = false;
856        isSet[WEEK_OF_MONTH] = false;
857        isSet[DAY_OF_WEEK] = false;
858        isSet[DAY_OF_WEEK_IN_MONTH] = false;
859        isSet[ERA] = false;
860    
861        if (! explicitDSTOffset)
862          isSet[DST_OFFSET] = false; // May have crossed a DST boundary.
863      }
864    
865      /**
866       * Sets the fields for year, month, date, hour, and minute
867       * @param year the year.
868       * @param month the month, one of the constants JANUARY..UNDICEMBER.
869       * @param date the day of the month
870       * @param hour the hour of day.
871       * @param minute the minute.
872       */
873      public final void set(int year, int month, int date, int hour, int minute)
874      {
875        set(year, month, date);
876        fields[HOUR_OF_DAY] = hour;
877        fields[MINUTE] = minute;
878        isSet[HOUR_OF_DAY] = isSet[MINUTE] = true;
879        isSet[AM_PM] = false;
880        isSet[HOUR] = false;
881      }
882    
883      /**
884       * Sets the fields for year, month, date, hour, and minute
885       * @param year the year.
886       * @param month the month, one of the constants JANUARY..UNDICEMBER.
887       * @param date the day of the month
888       * @param hour the hour of day.
889       * @param minute the minute.
890       * @param second the second.
891       */
892      public final void set(int year, int month, int date, int hour, int minute,
893                            int second)
894      {
895        set(year, month, date, hour, minute);
896        fields[SECOND] = second;
897        isSet[SECOND] = true;
898      }
899    
900      /**
901       * Clears the values of all the time fields.
902       */
903      public final void clear()
904      {
905        isTimeSet = false;
906        areFieldsSet = false;
907        int zoneOffs = zone.getRawOffset();
908        int[] tempFields = 
909                           {
910                             1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
911                             0, 0, zoneOffs, 0
912                           };
913        fields = tempFields;
914        for (int i = 0; i < FIELD_COUNT; i++)
915          isSet[i] = false;
916      }
917    
918      /**
919       * Clears the values of the specified time field.
920       * @param field the time field. One of the time field constants.
921       * @throws ArrayIndexOutOfBoundsException if field is outside
922       *         the valid range.  The value of field must be >= 0 and
923       *         <= <code>FIELD_COUNT</code>.
924       */
925      public final void clear(int field)
926      {
927        int[] tempFields = 
928                           {
929                             1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
930                             0, 0, zone.getRawOffset(), 0
931                           };
932        complete();
933        isTimeSet = false;
934        areFieldsSet = false;
935        isSet[field] = false;
936        fields[field] = tempFields[field];
937      }
938    
939      /**
940       * Determines if the specified field has a valid value.
941       * @return true if the specified field has a value.
942       * @throws ArrayIndexOutOfBoundsException if the field is outside
943       *         the valid range.  The value of field must be >= 0 and
944       *         <= <code>FIELD_COUNT</code>.
945       */
946      public final boolean isSet(int field)
947      {
948        return isSet[field];
949      }
950    
951      /**
952       * Fills any unset fields in the time field list
953       */
954      protected void complete()
955      {
956        if (! isTimeSet)
957          computeTime();
958        if (! areFieldsSet)
959          computeFields();
960      }
961    
962      /**
963       * Compares the given calendar with this.
964       * @param o the object to that we should compare.
965       * @return true, if the given object is a calendar, that represents
966       * the same time (but doesn't necessary have the same fields).
967       */
968      public boolean equals(Object o)
969      {
970        if (! (o instanceof Calendar))
971          return false;
972        Calendar cal = (Calendar) o;
973        if (getTimeInMillis() == ((Calendar) o).getTimeInMillis()
974            && cal.getFirstDayOfWeek() == getFirstDayOfWeek()
975            && cal.isLenient() == isLenient()
976            && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek())
977          {
978            TimeZone self = getTimeZone();
979            TimeZone oth = cal.getTimeZone();
980            return self == null ? oth == null : self.equals(oth);
981          }
982        return false;
983      }
984    
985      /**
986       * Returns a hash code for this calendar.
987       * @return a hash code, which fullfits the general contract of
988       * <code>hashCode()</code>
989       */
990      public int hashCode()
991      {
992        long time = getTimeInMillis();
993        int val = (int) ((time & 0xffffffffL) ^ (time >> 32));
994        val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237)
995                + getMinimalDaysInFirstWeek());
996        TimeZone self = getTimeZone();
997        if (self != null)
998          val ^= self.hashCode();
999        return val;
1000      }
1001    
1002      /**
1003       * Compares the given calendar with this.
1004       * @param o the object to that we should compare.
1005       * @return true, if the given object is a calendar, and this calendar
1006       * represents a smaller time than the calendar o.
1007       * @exception ClassCastException if o is not an calendar.
1008       * @since JDK1.2 you don't need to override this method
1009       */
1010      public boolean before(Object o)
1011      {
1012        return getTimeInMillis() < ((Calendar) o).getTimeInMillis();
1013      }
1014    
1015      /**
1016       * Compares the given calendar with this.
1017       * @param o the object to that we should compare.
1018       * @return true, if the given object is a calendar, and this calendar
1019       * represents a bigger time than the calendar o.
1020       * @exception ClassCastException if o is not an calendar.
1021       * @since JDK1.2 you don't need to override this method
1022       */
1023      public boolean after(Object o)
1024      {
1025        return getTimeInMillis() > ((Calendar) o).getTimeInMillis();
1026      }
1027    
1028      /**
1029       * Adds the specified amount of time to the given time field.  The
1030       * amount may be negative to subtract the time.  If the field overflows
1031       * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
1032       * @param field the time field. One of the time field constants.
1033       * @param amount the amount of time.
1034       * @throws ArrayIndexOutOfBoundsException if the field is outside
1035       *         the valid range.  The value of field must be >= 0 and
1036       *         <= <code>FIELD_COUNT</code>.
1037       */
1038      public abstract void add(int field, int amount);
1039    
1040      /**
1041       * Rolls the specified time field up or down.  This means add one
1042       * to the specified field, but don't change the other fields.  If
1043       * the maximum for this field is reached, start over with the
1044       * minimum value.  <br>
1045       *
1046       * <strong>Note:</strong> There may be situation, where the other
1047       * fields must be changed, e.g rolling the month on May, 31.
1048       * The date June, 31 is automatically converted to July, 1.
1049       * @param field the time field. One of the time field constants.
1050       * @param up the direction, true for up, false for down.
1051       * @throws ArrayIndexOutOfBoundsException if the field is outside
1052       *         the valid range.  The value of field must be >= 0 and
1053       *         <= <code>FIELD_COUNT</code>.
1054       */
1055      public abstract void roll(int field, boolean up);
1056    
1057      /**
1058       * Rolls up or down the specified time field by the given amount.
1059       * A negative amount rolls down.  The default implementation is
1060       * call <code>roll(int, boolean)</code> for the specified amount.
1061       *
1062       * Subclasses should override this method to do more intuitiv things.
1063       *
1064       * @param field the time field. One of the time field constants.
1065       * @param amount the amount to roll by, positive for rolling up,
1066       * negative for rolling down.
1067       * @throws ArrayIndexOutOfBoundsException if the field is outside
1068       *         the valid range.  The value of field must be >= 0 and
1069       *         <= <code>FIELD_COUNT</code>.
1070       * @since JDK1.2
1071       */
1072      public void roll(int field, int amount)
1073      {
1074        while (amount > 0)
1075          {
1076            roll(field, true);
1077            amount--;
1078          }
1079        while (amount < 0)
1080          {
1081            roll(field, false);
1082            amount++;
1083          }
1084      }
1085    
1086      /**
1087       * Sets the time zone to the specified value.
1088       * @param zone the new time zone
1089       */
1090      public void setTimeZone(TimeZone zone)
1091      {
1092        this.zone = zone;
1093        computeTime();
1094        computeFields();
1095      }
1096    
1097      /**
1098       * Gets the time zone of this calendar
1099       * @return the current time zone.
1100       */
1101      public TimeZone getTimeZone()
1102      {
1103        return zone;
1104      }
1105    
1106      /**
1107       * Specifies if the date/time interpretation should be lenient.
1108       * If the flag is set, a date such as "February 30, 1996" will be
1109       * treated as the 29th day after the February 1.  If this flag
1110       * is false, such dates will cause an exception.
1111       * @param lenient true, if the date should be interpreted linient,
1112       * false if it should be interpreted strict.
1113       */
1114      public void setLenient(boolean lenient)
1115      {
1116        this.lenient = lenient;
1117      }
1118    
1119      /**
1120       * Tells if the date/time interpretation is lenient.
1121       * @return true, if the date should be interpreted linient,
1122       * false if it should be interpreted strict.
1123       */
1124      public boolean isLenient()
1125      {
1126        return lenient;
1127      }
1128    
1129      /**
1130       * Sets what the first day of week is.  This is used for
1131       * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
1132       * @param value the first day of week.  One of SUNDAY to SATURDAY.
1133       */
1134      public void setFirstDayOfWeek(int value)
1135      {
1136        firstDayOfWeek = value;
1137      }
1138    
1139      /**
1140       * Gets what the first day of week is.  This is used for
1141       * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
1142       * @return the first day of week.  One of SUNDAY to SATURDAY.
1143       */
1144      public int getFirstDayOfWeek()
1145      {
1146        return firstDayOfWeek;
1147      }
1148    
1149      /**
1150       * Sets how many days are required in the first week of the year.
1151       * If the first day of the year should be the first week you should
1152       * set this value to 1.  If the first week must be a full week, set
1153       * it to 7.
1154       * @param value the minimal days required in the first week.
1155       */
1156      public void setMinimalDaysInFirstWeek(int value)
1157      {
1158        minimalDaysInFirstWeek = value;
1159      }
1160    
1161      /**
1162       * Gets how many days are required in the first week of the year.
1163       * @return the minimal days required in the first week.
1164       * @see #setMinimalDaysInFirstWeek
1165       */
1166      public int getMinimalDaysInFirstWeek()
1167      {
1168        return minimalDaysInFirstWeek;
1169      }
1170    
1171      /**
1172       * Gets the smallest value that is allowed for the specified field.
1173       * @param field the time field. One of the time field constants.
1174       * @return the smallest value.
1175       */
1176      public abstract int getMinimum(int field);
1177    
1178      /**
1179       * Gets the biggest value that is allowed for the specified field.
1180       * @param field the time field. One of the time field constants.
1181       * @return the biggest value.
1182       */
1183      public abstract int getMaximum(int field);
1184    
1185      /**
1186       * Gets the greatest minimum value that is allowed for the specified field.
1187       * @param field the time field. One of the time field constants.
1188       * @return the greatest minimum value.
1189       */
1190      public abstract int getGreatestMinimum(int field);
1191    
1192      /**
1193       * Gets the smallest maximum value that is allowed for the
1194       * specified field.  For example this is 28 for DAY_OF_MONTH.
1195       * @param field the time field. One of the time field constants.
1196       * @return the least maximum value.
1197       */
1198      public abstract int getLeastMaximum(int field);
1199    
1200      /**
1201       * Gets the actual minimum value that is allowed for the specified field.
1202       * This value is dependent on the values of the other fields.
1203       * @param field the time field. One of the time field constants.
1204       * @return the actual minimum value.
1205       * @throws ArrayIndexOutOfBoundsException if the field is outside
1206       *         the valid range.  The value of field must be >= 0 and
1207       *         <= <code>FIELD_COUNT</code>.
1208       * @since jdk1.2
1209       */
1210      public int getActualMinimum(int field)
1211      {
1212        Calendar tmp = (Calendar) clone(); // To avoid restoring state
1213        int min = tmp.getGreatestMinimum(field);
1214        int end = tmp.getMinimum(field);
1215        tmp.set(field, min);
1216        for (; min > end; min--)
1217          {
1218            tmp.add(field, -1); // Try to get smaller
1219            if (tmp.get(field) != min - 1)
1220              break; // Done if not successful
1221          }
1222        return min;
1223      }
1224    
1225      /**
1226       * Gets the actual maximum value that is allowed for the specified field.
1227       * This value is dependent on the values of the other fields.
1228       * @param field the time field. One of the time field constants.
1229       * @return the actual maximum value.
1230       * @throws ArrayIndexOutOfBoundsException if the field is outside
1231       *         the valid range.  The value of field must be >= 0 and
1232       *         <= <code>FIELD_COUNT</code>.
1233       * @since jdk1.2
1234       */
1235      public int getActualMaximum(int field)
1236      {
1237        Calendar tmp = (Calendar) clone(); // To avoid restoring state
1238        int max = tmp.getLeastMaximum(field);
1239        int end = tmp.getMaximum(field);
1240        tmp.set(field, max);
1241        for (; max < end; max++)
1242          {
1243            tmp.add(field, 1);
1244            if (tmp.get(field) != max + 1)
1245              break;
1246          }
1247        return max;
1248      }
1249    
1250      /**
1251       * Compares the time of two calendar instances.
1252       * @param calendar the calendar to which the time should be compared.
1253       * @return 0 if the two calendars are set to the same time, 
1254       * less than 0 if the time of this calendar is before that of 
1255       * <code>cal</code>, or more than 0 if the time of this calendar is after
1256       * that of <code>cal</code>.
1257       *
1258       * @param cal the calendar to compare this instance with.
1259       * @throws NullPointerException if <code>cal</code> is null.
1260       * @throws IllegalArgumentException if either calendar has fields set to 
1261       * invalid values.
1262       * @since 1.5
1263       */
1264      public int compareTo(Calendar cal)
1265      {
1266        long t1 = getTimeInMillis();
1267        long t2 = cal.getTimeInMillis();
1268        if(t1 == t2)
1269          return 0;
1270        if(t1 > t2)
1271          return 1;
1272        return -1;
1273      }
1274    
1275      /**
1276       * Return a clone of this object.
1277       */
1278      public Object clone()
1279      {
1280        try
1281          {
1282            Calendar cal = (Calendar) super.clone();
1283            cal.fields = (int[]) fields.clone();
1284            cal.isSet = (boolean[]) isSet.clone();
1285            return cal;
1286          }
1287        catch (CloneNotSupportedException ex)
1288          {
1289            return null;
1290          }
1291      }
1292    
1293      private static final String[] fieldNames = 
1294                                                 {
1295                                                   ",ERA=", ",YEAR=", ",MONTH=",
1296                                                   ",WEEK_OF_YEAR=",
1297                                                   ",WEEK_OF_MONTH=",
1298                                                   ",DAY_OF_MONTH=",
1299                                                   ",DAY_OF_YEAR=", ",DAY_OF_WEEK=",
1300                                                   ",DAY_OF_WEEK_IN_MONTH=",
1301                                                   ",AM_PM=", ",HOUR=",
1302                                                   ",HOUR_OF_DAY=", ",MINUTE=",
1303                                                   ",SECOND=", ",MILLISECOND=",
1304                                                   ",ZONE_OFFSET=", ",DST_OFFSET="
1305                                                 };
1306    
1307      /**
1308       * Returns a string representation of this object.  It is mainly
1309       * for debugging purposes and its content is implementation
1310       * specific.
1311       */
1312      public String toString()
1313      {
1314        StringBuffer sb = new StringBuffer();
1315        sb.append(getClass().getName()).append('[');
1316        sb.append("time=");
1317        if (isTimeSet)
1318          sb.append(time);
1319        else
1320          sb.append("?");
1321        sb.append(",zone=" + zone);
1322        sb.append(",areFieldsSet=" + areFieldsSet);
1323        for (int i = 0; i < FIELD_COUNT; i++)
1324          {
1325            sb.append(fieldNames[i]);
1326            if (isSet[i])
1327              sb.append(fields[i]);
1328            else
1329              sb.append("?");
1330          }
1331        sb.append(",lenient=").append(lenient);
1332        sb.append(",firstDayOfWeek=").append(firstDayOfWeek);
1333        sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek);
1334        sb.append("]");
1335        return sb.toString();
1336      }
1337    
1338      /**
1339       * Saves the state of the object to the stream.  Ideally we would
1340       * only write the time field, but we need to be compatible with
1341       * earlier versions. <br>
1342       *
1343       * This doesn't write the JDK1.1 field nextStamp to the stream, as
1344       * I don't know what it is good for, and because the documentation
1345       * says, that it could be omitted.  */
1346      private void writeObject(ObjectOutputStream stream) throws IOException
1347      {
1348        if (! isTimeSet)
1349          computeTime();
1350        stream.defaultWriteObject();
1351      }
1352    
1353      /**
1354       * Reads the object back from stream (deserialization).
1355       */
1356      private void readObject(ObjectInputStream stream)
1357        throws IOException, ClassNotFoundException
1358      {
1359        stream.defaultReadObject();
1360        if (! isTimeSet)
1361          computeTime();
1362    
1363        if (serialVersionOnStream > 1)
1364          {
1365            // This is my interpretation of the serial number:
1366            // Sun wants to remove all fields from the stream someday
1367            // and will then increase the serialVersion number again.
1368            // We prepare to be compatible.
1369            fields = new int[FIELD_COUNT];
1370            isSet = new boolean[FIELD_COUNT];
1371            areFieldsSet = false;
1372          }
1373      }
1374    
1375      /**
1376       * Returns a localised textual representation of the current value
1377       * of the given field using the specified style.  If there is no
1378       * applicable textual representation (e.g. the field has a numeric
1379       * value), then <code>null</code> is returned.  If one does exist,
1380       * then the value is obtained from {@link #get(int)} and converted
1381       * appropriately.  For example, if the <code>MONTH</code> field is
1382       * requested, then <code>get(MONTH)</code> is called.  This is then
1383       * converted to a textual representation based on its value and
1384       * the style requested; if the <code>LONG</code> style is requested
1385       * and the returned value is <code>11</code> from a
1386       * {@link GregorianCalendar} implementation, then <code>"December"</code>
1387       * is returned.  By default, a textual representation is available
1388       * for all fields which have an applicable value obtainable from
1389       * {@link java.text.DateFormatSymbols}.
1390       *
1391       * @param field the calendar field whose textual representation should
1392       *              be obtained.
1393       * @param style the style to use; either {@link #LONG} or {@link #SHORT}.
1394       * @param locale the locale to use for translation.
1395       * @return the textual representation of the given field in the specified
1396       *         style, or <code>null</code> if none is applicable.
1397       * @throws IllegalArgumentException if <code>field</code> or <code>style</code>
1398       *                                  or invalid, or the calendar is non-lenient
1399       *                                  and has invalid values.
1400       * @throws NullPointerException if <code>locale</code> is <code>null</code>.
1401       * @since 1.6
1402       */
1403      public String getDisplayName(int field, int style, Locale locale)
1404      {
1405        if (field < 0 || field >= FIELD_COUNT)
1406          throw new IllegalArgumentException("The field value, " + field +
1407                                             ", is invalid.");
1408        if (style != SHORT && style != LONG)
1409          throw new IllegalArgumentException("The style must be either " +
1410                                             "short or long.");
1411        if (field == YEAR || field == WEEK_OF_YEAR ||
1412            field == WEEK_OF_MONTH || field == DAY_OF_MONTH ||
1413            field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH ||
1414            field == HOUR || field == HOUR_OF_DAY || field == MINUTE ||
1415            field == SECOND || field == MILLISECOND)
1416          return null;
1417    
1418        int value = get(field);
1419        DateFormatSymbols syms = DateFormatSymbols.getInstance(locale);
1420        if (field == ERA)
1421          return syms.getEras()[value];
1422        if (field == MONTH)
1423          if (style == LONG)
1424            return syms.getMonths()[value];
1425          else 
1426            return syms.getShortMonths()[value];
1427        if (field == DAY_OF_WEEK)
1428          if (style == LONG)
1429            return syms.getWeekdays()[value];
1430          else
1431            return syms.getShortWeekdays()[value];
1432        if (field == AM_PM)
1433          return syms.getAmPmStrings()[value];
1434        if (field == ZONE_OFFSET)
1435          if (style == LONG)
1436            return syms.getZoneStrings()[value][1];
1437          else
1438            return syms.getZoneStrings()[value][2];
1439        if (field == DST_OFFSET)
1440          if (style == LONG)
1441            return syms.getZoneStrings()[value][3];
1442          else
1443            return syms.getZoneStrings()[value][4];
1444    
1445        throw new InternalError("Failed to resolve field " + field +
1446                                " with style " + style + " for locale " +
1447                                locale);
1448      }
1449    
1450      /**
1451       * Returns a map linking all specified textual representations
1452       * of the given field to their numerical values.  The textual
1453       * representations included are determined by the specified
1454       * style and locale.  For example, if the style <code>LONG</code>
1455       * is specified and the German locale, then the map will
1456       * contain "Montag" to {@link #MONDAY}, "Dienstag" to
1457       * {@link #TUESDAY}, "Mittwoch" to {@link #WEDNESDAY} and
1458       * so on.  The default implementation uses the values returned
1459       * by {@link DateFormatSymbols} so, for example, the style
1460       * {@link #ALL_STYLES} and the field {@link #MONTH} will return
1461       * a map filled with the values returned from
1462       * {@link DateFormatSymbols#getMonths()} and
1463       * {@link DateFormatSymbols#getShortMonths()}.  If there are
1464       * no textual representations for a given field (usually because
1465       * it is purely numeric, such as the year in the
1466       * {@link GregorianCalendar}), <code>null</code> is returned.
1467       *
1468       * @param field the calendar field whose textual representation should
1469       *              be obtained.
1470       * @param style the style to use; either {@link #LONG}, {@link #SHORT}
1471       *              or {@link ALL_STYLES}.
1472       * @param locale the locale to use for translation.
1473       * @return a map of the textual representations of the given field in the
1474       *         specified style to their numeric values, or <code>null</code>
1475       *         if none is applicable.
1476       * @throws IllegalArgumentException if <code>field</code> or <code>style</code>
1477       *                                  or invalid, or the calendar is non-lenient
1478       *                                  and has invalid values.
1479       * @throws NullPointerException if <code>locale</code> is <code>null</code>.
1480       * @since 1.6
1481       */
1482      public Map<String,Integer> getDisplayNames(int field, int style, Locale locale)
1483      {
1484        if (field < 0 || field >= FIELD_COUNT)
1485          throw new IllegalArgumentException("The field value, " + field +
1486                                             ", is invalid.");
1487        if (style != SHORT && style != LONG && style != ALL_STYLES)
1488          throw new IllegalArgumentException("The style must be either " +
1489                                             "short, long or all styles.");
1490        if (field == YEAR || field == WEEK_OF_YEAR ||
1491            field == WEEK_OF_MONTH || field == DAY_OF_MONTH ||
1492            field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH ||
1493            field == HOUR || field == HOUR_OF_DAY || field == MINUTE ||
1494            field == SECOND || field == MILLISECOND)
1495          return null;
1496    
1497        DateFormatSymbols syms = DateFormatSymbols.getInstance(locale);
1498        Map<String,Integer> map = new HashMap<String,Integer>();
1499        if (field == ERA)
1500          {
1501            String[] eras = syms.getEras();
1502            for (int a = 0; a < eras.length; ++a)
1503              map.put(eras[a], a);
1504            return map;
1505          }
1506        if (field == MONTH)
1507          {
1508            if (style == LONG || style == ALL_STYLES)
1509              {
1510                String[] months = syms.getMonths();
1511                for (int a = 0; a < months.length; ++a)
1512                  map.put(months[a], a);
1513              }
1514            if (style == SHORT || style == ALL_STYLES)
1515              {
1516                String[] months = syms.getShortMonths();
1517                for (int a = 0; a < months.length; ++a)
1518                  map.put(months[a], a);
1519              }
1520            return map;
1521          }
1522        if (field == DAY_OF_WEEK)
1523          {
1524            if (style == LONG || style == ALL_STYLES)
1525              {
1526                String[] weekdays = syms.getWeekdays();
1527                for (int a = SUNDAY; a < weekdays.length; ++a)
1528                  map.put(weekdays[a], a);
1529              }
1530            if (style == SHORT || style == ALL_STYLES)
1531              {
1532                String[] weekdays = syms.getShortWeekdays();
1533                for (int a = SUNDAY; a < weekdays.length; ++a)
1534                  map.put(weekdays[a], a);
1535              }
1536            return map;
1537          }
1538        if (field == AM_PM)
1539          {
1540            String[] ampms = syms.getAmPmStrings();
1541            for (int a = 0; a < ampms.length; ++a)
1542              map.put(ampms[a], a);
1543            return map;
1544          }
1545        if (field == ZONE_OFFSET)
1546          {
1547            String[][] zones = syms.getZoneStrings();
1548            for (int a = 0; a < zones.length; ++a)
1549              {
1550                if (style == LONG || style == ALL_STYLES) 
1551                  map.put(zones[a][1], a);
1552                if (style == SHORT || style == ALL_STYLES)
1553                  map.put(zones[a][2], a);
1554              }
1555            return map;
1556          }
1557        if (field == DST_OFFSET)
1558          {
1559            String[][] zones = syms.getZoneStrings();
1560            for (int a = 0; a < zones.length; ++a)
1561              {
1562                if (style == LONG || style == ALL_STYLES) 
1563                  map.put(zones[a][3], a);
1564                if (style == SHORT || style == ALL_STYLES)
1565                  map.put(zones[a][4], a);
1566              }
1567            return map;
1568          }
1569        
1570        throw new InternalError("Failed to resolve field " + field +
1571                                " with style " + style + " for locale " +
1572                                locale);
1573      }
1574    
1575    }