001/*
002 *  Copyright 2001-2005 Stephen Colebourne
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.joda.time.chrono;
017
018import java.io.IOException;
019import java.io.ObjectInputStream;
020
021import org.joda.time.Chronology;
022import org.joda.time.DateTimeField;
023import org.joda.time.DateTimeZone;
024import org.joda.time.DurationField;
025
026/**
027 * Abstract Chronology that enables chronologies to be assembled from
028 * a container of fields.
029 * <p>
030 * AssembledChronology is thread-safe and immutable.
031 *
032 * @author Brian S O'Neill
033 * @since 1.0
034 */
035public abstract class AssembledChronology extends BaseChronology {
036
037    private static final long serialVersionUID = -6728465968995518215L;
038
039    private final Chronology iBase;
040    private final Object iParam;
041
042    private transient DurationField iMillis;
043    private transient DurationField iSeconds;
044    private transient DurationField iMinutes;
045    private transient DurationField iHours;
046    private transient DurationField iHalfdays;
047
048    private transient DurationField iDays;
049    private transient DurationField iWeeks;
050    private transient DurationField iWeekyears;
051    private transient DurationField iMonths;
052    private transient DurationField iYears;
053    private transient DurationField iCenturies;
054    private transient DurationField iEras;
055
056    private transient DateTimeField iMillisOfSecond;
057    private transient DateTimeField iMillisOfDay;
058    private transient DateTimeField iSecondOfMinute;
059    private transient DateTimeField iSecondOfDay;
060    private transient DateTimeField iMinuteOfHour;
061    private transient DateTimeField iMinuteOfDay;
062    private transient DateTimeField iHourOfDay;
063    private transient DateTimeField iClockhourOfDay;
064    private transient DateTimeField iHourOfHalfday;
065    private transient DateTimeField iClockhourOfHalfday;
066    private transient DateTimeField iHalfdayOfDay;
067
068    private transient DateTimeField iDayOfWeek;
069    private transient DateTimeField iDayOfMonth;
070    private transient DateTimeField iDayOfYear;
071    private transient DateTimeField iWeekOfWeekyear;
072    private transient DateTimeField iWeekyear;
073    private transient DateTimeField iWeekyearOfCentury;
074    private transient DateTimeField iMonthOfYear;
075    private transient DateTimeField iYear;
076    private transient DateTimeField iYearOfEra;
077    private transient DateTimeField iYearOfCentury;
078    private transient DateTimeField iCenturyOfEra;
079    private transient DateTimeField iEra;
080
081    // Bit set determines which base fields are used
082    // bit 1 set: hourOfDay, minuteOfHour, secondOfMinute, and millisOfSecond fields
083    // bit 2 set: millisOfDayField
084    // bit 3 set: year, monthOfYear, and dayOfMonth fields
085    private transient int iBaseFlags;
086
087    /**
088     * Constructor calls the assemble method, enabling subclasses to define its
089     * supported fields. If a base chronology is supplied, the field set
090     * initially contains references to each base chronology field.
091     * <p>
092     * Other methods in this class will delegate to the base chronology, if it
093     * can be determined that the base chronology will produce the same results
094     * as AbstractChronology.
095     *
096     * @param base optional base chronology to copy initial fields from
097     * @param param optional param object avalable for assemble method
098     */
099    protected AssembledChronology(Chronology base, Object param) {
100        iBase = base;
101        iParam = param;
102        setFields();
103    }
104
105    public DateTimeZone getZone() {
106        Chronology base;
107        if ((base = iBase) != null) {
108            return base.getZone();
109        }
110        return null;
111    }
112
113    public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
114                                  int millisOfDay)
115        throws IllegalArgumentException
116    {
117        Chronology base;
118        if ((base = iBase) != null && (iBaseFlags & 6) == 6) {
119            // Only call specialized implementation if applicable fields are the same.
120            return base.getDateTimeMillis(year, monthOfYear, dayOfMonth, millisOfDay);
121        }
122        return super.getDateTimeMillis(year, monthOfYear, dayOfMonth, millisOfDay);
123    }
124
125    public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
126                                  int hourOfDay, int minuteOfHour,
127                                  int secondOfMinute, int millisOfSecond)
128        throws IllegalArgumentException
129    {
130        Chronology base;
131        if ((base = iBase) != null && (iBaseFlags & 5) == 5) {
132            // Only call specialized implementation if applicable fields are the same.
133            return base.getDateTimeMillis(year, monthOfYear, dayOfMonth,
134                                          hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
135        }
136        return super.getDateTimeMillis(year, monthOfYear, dayOfMonth,
137                                       hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
138    }
139
140    public long getDateTimeMillis(long instant,
141                                  int hourOfDay, int minuteOfHour,
142                                  int secondOfMinute, int millisOfSecond)
143        throws IllegalArgumentException
144    {
145        Chronology base;
146        if ((base = iBase) != null && (iBaseFlags & 1) == 1) {
147            // Only call specialized implementation if applicable fields are the same.
148            return base.getDateTimeMillis
149                (instant, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
150        }
151        return super.getDateTimeMillis
152            (instant, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
153    }
154
155    public final DurationField millis() {
156        return iMillis;
157    }
158
159    public final DateTimeField millisOfSecond() {
160        return iMillisOfSecond;
161    }
162
163    public final DateTimeField millisOfDay() {
164        return iMillisOfDay;
165    }
166
167    public final DurationField seconds() {
168        return iSeconds;
169    }
170
171    public final DateTimeField secondOfMinute() {
172        return iSecondOfMinute;
173    }
174
175    public final DateTimeField secondOfDay() {
176        return iSecondOfDay;
177    }
178
179    public final DurationField minutes() {
180        return iMinutes;
181    }
182
183    public final DateTimeField minuteOfHour() {
184        return iMinuteOfHour;
185    }
186
187    public final DateTimeField minuteOfDay() {
188        return iMinuteOfDay;
189    }
190
191    public final DurationField hours() {
192        return iHours;
193    }
194
195    public final DateTimeField hourOfDay() {
196        return iHourOfDay;
197    }
198
199    public final DateTimeField clockhourOfDay() {
200        return iClockhourOfDay;
201    }
202
203    public final DurationField halfdays() {
204        return iHalfdays;
205    }
206
207    public final DateTimeField hourOfHalfday() {
208        return iHourOfHalfday;
209    }
210
211    public final DateTimeField clockhourOfHalfday() {
212        return iClockhourOfHalfday;
213    }
214
215    public final DateTimeField halfdayOfDay() {
216        return iHalfdayOfDay;
217    }
218
219    public final DurationField days() {
220        return iDays;
221    }
222
223    public final DateTimeField dayOfWeek() {
224        return iDayOfWeek;
225    }
226
227    public final DateTimeField dayOfMonth() {
228        return iDayOfMonth;
229    }
230
231    public final DateTimeField dayOfYear() {
232        return iDayOfYear;
233    }
234
235    public final DurationField weeks() {
236        return iWeeks;
237    }
238
239    public final DateTimeField weekOfWeekyear() {
240        return iWeekOfWeekyear;
241    }
242
243    public final DurationField weekyears() {
244        return iWeekyears;
245    }
246
247    public final DateTimeField weekyear() {
248        return iWeekyear;
249    }
250
251    public final DateTimeField weekyearOfCentury() {
252        return iWeekyearOfCentury;
253    }
254
255    public final DurationField months() {
256        return iMonths;
257    }
258
259    public final DateTimeField monthOfYear() {
260        return iMonthOfYear;
261    }
262
263    public final DurationField years() {
264        return iYears;
265    }
266
267    public final DateTimeField year() {
268        return iYear;
269    }
270
271    public final DateTimeField yearOfEra() {
272        return iYearOfEra;
273    }
274
275    public final DateTimeField yearOfCentury() {
276        return iYearOfCentury;
277    }
278
279    public final DurationField centuries() {
280        return iCenturies;
281    }
282
283    public final DateTimeField centuryOfEra() {
284        return iCenturyOfEra;
285    }
286
287    public final DurationField eras() {
288        return iEras;
289    }
290
291    public final DateTimeField era() {
292        return iEra;
293    }
294
295    /**
296     * Invoked by the constructor and after deserialization to allow subclasses
297     * to define all of its supported fields. All unset fields default to
298     * unsupported instances.
299     *
300     * @param fields container of fields
301     */
302    protected abstract void assemble(Fields fields);
303
304    /**
305     * Returns the same base chronology as passed into the constructor.
306     */
307    protected final Chronology getBase() {
308        return iBase;
309    }
310
311    /**
312     * Returns the same param object as passed into the constructor.
313     */
314    protected final Object getParam() {
315        return iParam;
316    }
317
318    private void setFields() {
319        Fields fields = new Fields();
320        if (iBase != null) {
321            fields.copyFieldsFrom(iBase);
322        }
323        assemble(fields);
324
325        {
326            DurationField f;
327            iMillis    = (f = fields.millis)    != null ? f : super.millis();
328            iSeconds   = (f = fields.seconds)   != null ? f : super.seconds();
329            iMinutes   = (f = fields.minutes)   != null ? f : super.minutes();
330            iHours     = (f = fields.hours)     != null ? f : super.hours();
331            iHalfdays  = (f = fields.halfdays)  != null ? f : super.halfdays();
332            iDays      = (f = fields.days)      != null ? f : super.days();
333            iWeeks     = (f = fields.weeks)     != null ? f : super.weeks();
334            iWeekyears = (f = fields.weekyears) != null ? f : super.weekyears();
335            iMonths    = (f = fields.months)    != null ? f : super.months();
336            iYears     = (f = fields.years)     != null ? f : super.years();
337            iCenturies = (f = fields.centuries) != null ? f : super.centuries();
338            iEras      = (f = fields.eras)      != null ? f : super.eras();
339        }
340
341        {
342            DateTimeField f;
343            iMillisOfSecond     = (f = fields.millisOfSecond)     != null ? f : super.millisOfSecond();
344            iMillisOfDay        = (f = fields.millisOfDay)        != null ? f : super.millisOfDay();
345            iSecondOfMinute     = (f = fields.secondOfMinute)     != null ? f : super.secondOfMinute();
346            iSecondOfDay        = (f = fields.secondOfDay)        != null ? f : super.secondOfDay();
347            iMinuteOfHour       = (f = fields.minuteOfHour)       != null ? f : super.minuteOfHour();
348            iMinuteOfDay        = (f = fields.minuteOfDay)        != null ? f : super.minuteOfDay();
349            iHourOfDay          = (f = fields.hourOfDay)          != null ? f : super.hourOfDay();
350            iClockhourOfDay     = (f = fields.clockhourOfDay)     != null ? f : super.clockhourOfDay();
351            iHourOfHalfday      = (f = fields.hourOfHalfday)      != null ? f : super.hourOfHalfday();
352            iClockhourOfHalfday = (f = fields.clockhourOfHalfday) != null ? f : super.clockhourOfHalfday();
353            iHalfdayOfDay       = (f = fields.halfdayOfDay)       != null ? f : super.halfdayOfDay();
354            iDayOfWeek          = (f = fields.dayOfWeek)          != null ? f : super.dayOfWeek();
355            iDayOfMonth         = (f = fields.dayOfMonth)         != null ? f : super.dayOfMonth();
356            iDayOfYear          = (f = fields.dayOfYear)          != null ? f : super.dayOfYear();
357            iWeekOfWeekyear     = (f = fields.weekOfWeekyear)     != null ? f : super.weekOfWeekyear();
358            iWeekyear           = (f = fields.weekyear)           != null ? f : super.weekyear();
359            iWeekyearOfCentury  = (f = fields.weekyearOfCentury)  != null ? f : super.weekyearOfCentury();
360            iMonthOfYear        = (f = fields.monthOfYear)        != null ? f : super.monthOfYear();
361            iYear               = (f = fields.year)               != null ? f : super.year();
362            iYearOfEra          = (f = fields.yearOfEra)          != null ? f : super.yearOfEra();
363            iYearOfCentury      = (f = fields.yearOfCentury)      != null ? f : super.yearOfCentury();
364            iCenturyOfEra       = (f = fields.centuryOfEra)       != null ? f : super.centuryOfEra();
365            iEra                = (f = fields.era)                != null ? f : super.era();
366        }
367
368        int flags;
369        if (iBase == null) {
370            flags = 0;
371        } else {
372            flags = 
373                ((iHourOfDay      == iBase.hourOfDay()      &&
374                  iMinuteOfHour   == iBase.minuteOfHour()   &&
375                  iSecondOfMinute == iBase.secondOfMinute() &&
376                  iMillisOfSecond == iBase.millisOfSecond()   ) ? 1 : 0) |
377
378                ((iMillisOfDay == iBase.millisOfDay()) ? 2 : 0) |
379
380                ((iYear        == iBase.year()        &&
381                  iMonthOfYear == iBase.monthOfYear() &&
382                  iDayOfMonth  == iBase.dayOfMonth()    ) ? 4 : 0);
383        }
384
385        iBaseFlags = flags;
386    }
387
388    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
389        in.defaultReadObject();
390        setFields();
391    }
392
393    /**
394     * A container of fields used for assembling a chronology.
395     */
396    public static final class Fields {
397        public DurationField millis;
398        public DurationField seconds;
399        public DurationField minutes;
400        public DurationField hours;
401        public DurationField halfdays;
402    
403        public DurationField days;
404        public DurationField weeks;
405        public DurationField weekyears;
406        public DurationField months;
407        public DurationField years;
408        public DurationField centuries;
409        public DurationField eras;
410    
411        public DateTimeField millisOfSecond;
412        public DateTimeField millisOfDay;
413        public DateTimeField secondOfMinute;
414        public DateTimeField secondOfDay;
415        public DateTimeField minuteOfHour;
416        public DateTimeField minuteOfDay;
417        public DateTimeField hourOfDay;
418        public DateTimeField clockhourOfDay;
419        public DateTimeField hourOfHalfday;
420        public DateTimeField clockhourOfHalfday;
421        public DateTimeField halfdayOfDay;
422    
423        public DateTimeField dayOfWeek;
424        public DateTimeField dayOfMonth;
425        public DateTimeField dayOfYear;
426        public DateTimeField weekOfWeekyear;
427        public DateTimeField weekyear;
428        public DateTimeField weekyearOfCentury;
429        public DateTimeField monthOfYear;
430        public DateTimeField year;
431        public DateTimeField yearOfEra;
432        public DateTimeField yearOfCentury;
433        public DateTimeField centuryOfEra;
434        public DateTimeField era;
435
436        Fields() {
437        }
438
439        /**
440         * Copy the supported fields from a chronology into this container.
441         */
442        public void copyFieldsFrom(Chronology chrono) {
443            {
444                DurationField f;
445                if (isSupported(f = chrono.millis())) {
446                    millis = f;
447                }
448                if (isSupported(f = chrono.seconds())) {
449                    seconds = f;
450                }
451                if (isSupported(f = chrono.minutes())) {
452                    minutes = f;
453                }
454                if (isSupported(f = chrono.hours())) {
455                    hours = f;
456                }
457                if (isSupported(f = chrono.halfdays())) {
458                    halfdays = f;
459                }
460                if (isSupported(f = chrono.days())) {
461                    days = f;
462                }
463                if (isSupported(f = chrono.weeks())) {
464                    weeks = f;
465                }
466                if (isSupported(f = chrono.weekyears())) {
467                    weekyears = f;
468                }
469                if (isSupported(f = chrono.months())) {
470                    months = f;
471                }
472                if (isSupported(f = chrono.years())) {
473                    years = f;
474                }
475                if (isSupported(f = chrono.centuries())) {
476                    centuries = f;
477                }
478                if (isSupported(f = chrono.eras())) {
479                    eras = f;
480                }
481            }
482
483            {
484                DateTimeField f;
485                if (isSupported(f = chrono.millisOfSecond())) {
486                    millisOfSecond = f;
487                }
488                if (isSupported(f = chrono.millisOfDay())) {
489                    millisOfDay = f;
490                }
491                if (isSupported(f = chrono.secondOfMinute())) {
492                    secondOfMinute = f;
493                }
494                if (isSupported(f = chrono.secondOfDay())) {
495                    secondOfDay = f;
496                }
497                if (isSupported(f = chrono.minuteOfHour())) {
498                    minuteOfHour = f;
499                }
500                if (isSupported(f = chrono.minuteOfDay())) {
501                    minuteOfDay = f;
502                }
503                if (isSupported(f = chrono.hourOfDay())) {
504                    hourOfDay = f;
505                }
506                if (isSupported(f = chrono.clockhourOfDay())) {
507                    clockhourOfDay = f;
508                }
509                if (isSupported(f = chrono.hourOfHalfday())) {
510                    hourOfHalfday = f;
511                }
512                if (isSupported(f = chrono.clockhourOfHalfday())) {
513                    clockhourOfHalfday = f;
514                }
515                if (isSupported(f = chrono.halfdayOfDay())) {
516                    halfdayOfDay = f;
517                }
518                if (isSupported(f = chrono.dayOfWeek())) {
519                    dayOfWeek = f;
520                }
521                if (isSupported(f = chrono.dayOfMonth())) {
522                    dayOfMonth = f;
523                }
524                if (isSupported(f = chrono.dayOfYear())) {
525                    dayOfYear = f;
526                }
527                if (isSupported(f = chrono.weekOfWeekyear())) {
528                    weekOfWeekyear = f;
529                }
530                if (isSupported(f = chrono.weekyear())) {
531                    weekyear = f;
532                }
533                if (isSupported(f = chrono.weekyearOfCentury())) {
534                    weekyearOfCentury = f;
535                }
536                if (isSupported(f = chrono.monthOfYear())) {
537                    monthOfYear = f;
538                }
539                if (isSupported(f = chrono.year())) {
540                    year = f;
541                }
542                if (isSupported(f = chrono.yearOfEra())) {
543                    yearOfEra = f;
544                }
545                if (isSupported(f = chrono.yearOfCentury())) {
546                    yearOfCentury = f;
547                }
548                if (isSupported(f = chrono.centuryOfEra())) {
549                    centuryOfEra = f;
550                }
551                if (isSupported(f = chrono.era())) {
552                    era = f;
553                }
554            }
555        }
556
557        private static boolean isSupported(DurationField field) {
558            return field == null ? false : field.isSupported();
559        }
560
561        private static boolean isSupported(DateTimeField field) {
562            return field == null ? false : field.isSupported();
563        }
564    }
565}