001    /*
002     *  Copyright 2001-2009 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     */
016    package org.joda.time;
017    
018    import java.io.Serializable;
019    import java.util.ArrayList;
020    import java.util.Arrays;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import org.joda.time.field.FieldUtils;
026    
027    /**
028     * Controls a period implementation by specifying which duration fields are to be used.
029     * <p>
030     * The following implementations are provided:
031     * <ul>
032     * <li>Standard - years, months, weeks, days, hours, minutes, seconds, millis
033     * <li>YearMonthDayTime - years, months, days, hours, minutes, seconds, millis
034     * <li>YearMonthDay - years, months, days
035     * <li>YearWeekDayTime - years, weeks, days, hours, minutes, seconds, millis
036     * <li>YearWeekDay - years, weeks, days
037     * <li>YearDayTime - years, days, hours, minutes, seconds, millis
038     * <li>YearDay - years, days, hours
039     * <li>DayTime - days, hours, minutes, seconds, millis
040     * <li>Time - hours, minutes, seconds, millis
041     * <li>plus one for each single type
042     * </ul>
043     *
044     * <p>
045     * PeriodType is thread-safe and immutable, and all subclasses must be as well.
046     *
047     * @author Brian S O'Neill
048     * @author Stephen Colebourne
049     * @since 1.0
050     */
051    public class PeriodType implements Serializable {
052        /** Serialization version */
053        private static final long serialVersionUID = 2274324892792009998L;
054    
055        /** Cache of all the known types. */
056        private static final Map<PeriodType, Object> cTypes = new HashMap<PeriodType, Object>(32);
057    
058        static int YEAR_INDEX = 0;
059        static int MONTH_INDEX = 1;
060        static int WEEK_INDEX = 2;
061        static int DAY_INDEX = 3;
062        static int HOUR_INDEX = 4;
063        static int MINUTE_INDEX = 5;
064        static int SECOND_INDEX = 6;
065        static int MILLI_INDEX = 7;
066        
067        private static PeriodType cStandard;
068        private static PeriodType cYMDTime;
069        private static PeriodType cYMD;
070        private static PeriodType cYWDTime;
071        private static PeriodType cYWD;
072        private static PeriodType cYDTime;
073        private static PeriodType cYD;
074        private static PeriodType cDTime;
075        private static PeriodType cTime;
076        
077        private static PeriodType cYears;
078        private static PeriodType cMonths;
079        private static PeriodType cWeeks;
080        private static PeriodType cDays;
081        private static PeriodType cHours;
082        private static PeriodType cMinutes;
083        private static PeriodType cSeconds;
084        private static PeriodType cMillis;
085    
086        /**
087         * Gets a type that defines all standard fields.
088         * <ul>
089         * <li>years
090         * <li>months
091         * <li>weeks
092         * <li>days
093         * <li>hours
094         * <li>minutes
095         * <li>seconds
096         * <li>milliseconds
097         * </ul>
098         *
099         * @return the period type
100         */
101        public static PeriodType standard() {
102            PeriodType type = cStandard;
103            if (type == null) {
104                type = new PeriodType(
105                    "Standard",
106                    new DurationFieldType[] {
107                        DurationFieldType.years(), DurationFieldType.months(),
108                        DurationFieldType.weeks(), DurationFieldType.days(),
109                        DurationFieldType.hours(), DurationFieldType.minutes(),
110                        DurationFieldType.seconds(), DurationFieldType.millis(),
111                    },
112                    new int[] { 0, 1, 2, 3, 4, 5, 6, 7, }
113                );
114                cStandard = type;
115            }
116            return type;
117        }
118    
119        /**
120         * Gets a type that defines all standard fields except weeks.
121         * <ul>
122         * <li>years
123         * <li>months
124         * <li>days
125         * <li>hours
126         * <li>minutes
127         * <li>seconds
128         * <li>milliseconds
129         * </ul>
130         *
131         * @return the period type
132         */
133        public static PeriodType yearMonthDayTime() {
134            PeriodType type = cYMDTime;
135            if (type == null) {
136                type = new PeriodType(
137                    "YearMonthDayTime",
138                    new DurationFieldType[] {
139                        DurationFieldType.years(), DurationFieldType.months(),
140                        DurationFieldType.days(),
141                        DurationFieldType.hours(), DurationFieldType.minutes(),
142                        DurationFieldType.seconds(), DurationFieldType.millis(),
143                    },
144                    new int[] { 0, 1, -1, 2, 3, 4, 5, 6, }
145                );
146                cYMDTime = type;
147            }
148            return type;
149        }
150    
151        /**
152         * Gets a type that defines the year, month and day fields.
153         * <ul>
154         * <li>years
155         * <li>months
156         * <li>days
157         * </ul>
158         *
159         * @return the period type
160         * @since 1.1
161         */
162        public static PeriodType yearMonthDay() {
163            PeriodType type = cYMD;
164            if (type == null) {
165                type = new PeriodType(
166                    "YearMonthDay",
167                    new DurationFieldType[] {
168                        DurationFieldType.years(), DurationFieldType.months(),
169                        DurationFieldType.days(),
170                    },
171                    new int[] { 0, 1, -1, 2, -1, -1, -1, -1, }
172                );
173                cYMD = type;
174            }
175            return type;
176        }
177    
178        /**
179         * Gets a type that defines all standard fields except months.
180         * <ul>
181         * <li>years
182         * <li>weeks
183         * <li>days
184         * <li>hours
185         * <li>minutes
186         * <li>seconds
187         * <li>milliseconds
188         * </ul>
189         *
190         * @return the period type
191         */
192        public static PeriodType yearWeekDayTime() {
193            PeriodType type = cYWDTime;
194            if (type == null) {
195                type = new PeriodType(
196                    "YearWeekDayTime",
197                    new DurationFieldType[] {
198                        DurationFieldType.years(),
199                        DurationFieldType.weeks(), DurationFieldType.days(),
200                        DurationFieldType.hours(), DurationFieldType.minutes(),
201                        DurationFieldType.seconds(), DurationFieldType.millis(),
202                    },
203                    new int[] { 0, -1, 1, 2, 3, 4, 5, 6, }
204                );
205                cYWDTime = type;
206            }
207            return type;
208        }
209    
210        /**
211         * Gets a type that defines year, week and day fields.
212         * <ul>
213         * <li>years
214         * <li>weeks
215         * <li>days
216         * </ul>
217         *
218         * @return the period type
219         * @since 1.1
220         */
221        public static PeriodType yearWeekDay() {
222            PeriodType type = cYWD;
223            if (type == null) {
224                type = new PeriodType(
225                    "YearWeekDay",
226                    new DurationFieldType[] {
227                        DurationFieldType.years(),
228                        DurationFieldType.weeks(), DurationFieldType.days(),
229                    },
230                    new int[] { 0, -1, 1, 2, -1, -1, -1, -1, }
231                );
232                cYWD = type;
233            }
234            return type;
235        }
236    
237        /**
238         * Gets a type that defines all standard fields except months and weeks.
239         * <ul>
240         * <li>years
241         * <li>days
242         * <li>hours
243         * <li>minutes
244         * <li>seconds
245         * <li>milliseconds
246         * </ul>
247         *
248         * @return the period type
249         */
250        public static PeriodType yearDayTime() {
251            PeriodType type = cYDTime;
252            if (type == null) {
253                type = new PeriodType(
254                    "YearDayTime",
255                    new DurationFieldType[] {
256                        DurationFieldType.years(), DurationFieldType.days(),
257                        DurationFieldType.hours(), DurationFieldType.minutes(),
258                        DurationFieldType.seconds(), DurationFieldType.millis(),
259                    },
260                    new int[] { 0, -1, -1, 1, 2, 3, 4, 5, }
261                );
262                cYDTime = type;
263            }
264            return type;
265        }
266    
267        /**
268         * Gets a type that defines the year and day fields.
269         * <ul>
270         * <li>years
271         * <li>days
272         * </ul>
273         *
274         * @return the period type
275         * @since 1.1
276         */
277        public static PeriodType yearDay() {
278            PeriodType type = cYD;
279            if (type == null) {
280                type = new PeriodType(
281                    "YearDay",
282                    new DurationFieldType[] {
283                        DurationFieldType.years(), DurationFieldType.days(),
284                    },
285                    new int[] { 0, -1, -1, 1, -1, -1, -1, -1, }
286                );
287                cYD = type;
288            }
289            return type;
290        }
291    
292        /**
293         * Gets a type that defines all standard fields from days downwards.
294         * <ul>
295         * <li>days
296         * <li>hours
297         * <li>minutes
298         * <li>seconds
299         * <li>milliseconds
300         * </ul>
301         *
302         * @return the period type
303         */
304        public static PeriodType dayTime() {
305            PeriodType type = cDTime;
306            if (type == null) {
307                type = new PeriodType(
308                    "DayTime",
309                    new DurationFieldType[] {
310                        DurationFieldType.days(),
311                        DurationFieldType.hours(), DurationFieldType.minutes(),
312                        DurationFieldType.seconds(), DurationFieldType.millis(),
313                    },
314                    new int[] { -1, -1, -1, 0, 1, 2, 3, 4, }
315                );
316                cDTime = type;
317            }
318            return type;
319        }
320    
321        /**
322         * Gets a type that defines all standard time fields.
323         * <ul>
324         * <li>hours
325         * <li>minutes
326         * <li>seconds
327         * <li>milliseconds
328         * </ul>
329         *
330         * @return the period type
331         */
332        public static PeriodType time() {
333            PeriodType type = cTime;
334            if (type == null) {
335                type = new PeriodType(
336                    "Time",
337                    new DurationFieldType[] {
338                        DurationFieldType.hours(), DurationFieldType.minutes(),
339                        DurationFieldType.seconds(), DurationFieldType.millis(),
340                    },
341                    new int[] { -1, -1, -1, -1, 0, 1, 2, 3, }
342                );
343                cTime = type;
344            }
345            return type;
346        }
347    
348        /**
349         * Gets a type that defines just the years field.
350         *
351         * @return the period type
352         */
353        public static PeriodType years() {
354            PeriodType type = cYears;
355            if (type == null) {
356                type = new PeriodType(
357                    "Years",
358                    new DurationFieldType[] { DurationFieldType.years() },
359                    new int[] { 0, -1, -1, -1, -1, -1, -1, -1, }
360                );
361                cYears = type;
362            }
363            return type;
364        }
365    
366        /**
367         * Gets a type that defines just the months field.
368         *
369         * @return the period type
370         */
371        public static PeriodType months() {
372            PeriodType type = cMonths;
373            if (type == null) {
374                type = new PeriodType(
375                    "Months",
376                    new DurationFieldType[] { DurationFieldType.months() },
377                    new int[] { -1, 0, -1, -1, -1, -1, -1, -1, }
378                );
379                cMonths = type;
380            }
381            return type;
382        }
383    
384        /**
385         * Gets a type that defines just the weeks field.
386         *
387         * @return the period type
388         */
389        public static PeriodType weeks() {
390            PeriodType type = cWeeks;
391            if (type == null) {
392                type = new PeriodType(
393                    "Weeks",
394                    new DurationFieldType[] { DurationFieldType.weeks() },
395                    new int[] { -1, -1, 0, -1, -1, -1, -1, -1, }
396                );
397                cWeeks = type;
398            }
399            return type;
400        }
401    
402        /**
403         * Gets a type that defines just the days field.
404         *
405         * @return the period type
406         */
407        public static PeriodType days() {
408            PeriodType type = cDays;
409            if (type == null) {
410                type = new PeriodType(
411                    "Days",
412                    new DurationFieldType[] { DurationFieldType.days() },
413                    new int[] { -1, -1, -1, 0, -1, -1, -1, -1, }
414                );
415                cDays = type;
416            }
417            return type;
418        }
419    
420        /**
421         * Gets a type that defines just the hours field.
422         *
423         * @return the period type
424         */
425        public static PeriodType hours() {
426            PeriodType type = cHours;
427            if (type == null) {
428                type = new PeriodType(
429                    "Hours",
430                    new DurationFieldType[] { DurationFieldType.hours() },
431                    new int[] { -1, -1, -1, -1, 0, -1, -1, -1, }
432                );
433                cHours = type;
434            }
435            return type;
436        }
437    
438        /**
439         * Gets a type that defines just the minutes field.
440         *
441         * @return the period type
442         */
443        public static PeriodType minutes() {
444            PeriodType type = cMinutes;
445            if (type == null) {
446                type = new PeriodType(
447                    "Minutes",
448                    new DurationFieldType[] { DurationFieldType.minutes() },
449                    new int[] { -1, -1, -1, -1, -1, 0, -1, -1, }
450                );
451                cMinutes = type;
452            }
453            return type;
454        }
455    
456        /**
457         * Gets a type that defines just the seconds field.
458         *
459         * @return the period type
460         */
461        public static PeriodType seconds() {
462            PeriodType type = cSeconds;
463            if (type == null) {
464                type = new PeriodType(
465                    "Seconds",
466                    new DurationFieldType[] { DurationFieldType.seconds() },
467                    new int[] { -1, -1, -1, -1, -1, -1, 0, -1, }
468                );
469                cSeconds = type;
470            }
471            return type;
472        }
473    
474        /**
475         * Gets a type that defines just the millis field.
476         *
477         * @return the period type
478         */
479        public static PeriodType millis() {
480            PeriodType type = cMillis;
481            if (type == null) {
482                type = new PeriodType(
483                    "Millis",
484                    new DurationFieldType[] { DurationFieldType.millis() },
485                    new int[] { -1, -1, -1, -1, -1, -1, -1, 0, }
486                );
487                cMillis = type;
488            }
489            return type;
490        }
491    
492        /**
493         * Gets a period type that contains the duration types of the array.
494         * <p>
495         * Only the 8 standard duration field types are supported.
496         *
497         * @param types  the types to include in the array.
498         * @return the period type
499         * @since 1.1
500         */
501        public static synchronized PeriodType forFields(DurationFieldType[] types) {
502            if (types == null || types.length == 0) {
503                throw new IllegalArgumentException("Types array must not be null or empty");
504            }
505            for (int i = 0; i < types.length; i++) {
506                if (types[i] == null) {
507                    throw new IllegalArgumentException("Types array must not contain null");
508                }
509            }
510            Map<PeriodType, Object> cache = cTypes;
511            if (cache.isEmpty()) {
512                cache.put(standard(), standard());
513                cache.put(yearMonthDayTime(), yearMonthDayTime());
514                cache.put(yearMonthDay(), yearMonthDay());
515                cache.put(yearWeekDayTime(), yearWeekDayTime());
516                cache.put(yearWeekDay(), yearWeekDay());
517                cache.put(yearDayTime(), yearDayTime());
518                cache.put(yearDay(), yearDay());
519                cache.put(dayTime(), dayTime());
520                cache.put(time(), time());
521                cache.put(years(), years());
522                cache.put(months(), months());
523                cache.put(weeks(), weeks());
524                cache.put(days(), days());
525                cache.put(hours(), hours());
526                cache.put(minutes(), minutes());
527                cache.put(seconds(), seconds());
528                cache.put(millis(), millis());
529            }
530            PeriodType inPartType = new PeriodType(null, types, null);
531            Object cached = cache.get(inPartType);
532            if (cached instanceof PeriodType) {
533                return (PeriodType) cached;
534            }
535            if (cached != null) {
536                throw new IllegalArgumentException("PeriodType does not support fields: " + cached);
537            }
538            PeriodType type = standard();
539            List<DurationFieldType> list = new ArrayList<DurationFieldType>(Arrays.asList(types));
540            if (list.remove(DurationFieldType.years()) == false) {
541                type = type.withYearsRemoved();
542            }
543            if (list.remove(DurationFieldType.months()) == false) {
544                type = type.withMonthsRemoved();
545            }
546            if (list.remove(DurationFieldType.weeks()) == false) {
547                type = type.withWeeksRemoved();
548            }
549            if (list.remove(DurationFieldType.days()) == false) {
550                type = type.withDaysRemoved();
551            }
552            if (list.remove(DurationFieldType.hours()) == false) {
553                type = type.withHoursRemoved();
554            }
555            if (list.remove(DurationFieldType.minutes()) == false) {
556                type = type.withMinutesRemoved();
557            }
558            if (list.remove(DurationFieldType.seconds()) == false) {
559                type = type.withSecondsRemoved();
560            }
561            if (list.remove(DurationFieldType.millis()) == false) {
562                type = type.withMillisRemoved();
563            }
564            if (list.size() > 0) {
565                cache.put(inPartType, list);
566                throw new IllegalArgumentException("PeriodType does not support fields: " + list);
567            }
568            // recheck cache in case initial array order was wrong
569            PeriodType checkPartType = new PeriodType(null, type.iTypes, null);
570            PeriodType checkedType = (PeriodType) cache.get(checkPartType);
571            if (checkedType != null) {
572                cache.put(checkPartType, checkedType);
573                return checkedType;
574            }
575            cache.put(checkPartType, type);
576            return type;
577        }
578    
579        //-----------------------------------------------------------------------    
580        /** The name of the type */
581        private final String iName;
582        /** The array of types */
583        private final DurationFieldType[] iTypes;
584        /** The array of indices */
585        private final int[] iIndices;
586    
587        /**
588         * Constructor.
589         *
590         * @param name  the name
591         * @param types  the types
592         * @param indices  the indices
593         */
594        protected PeriodType(String name, DurationFieldType[] types, int[] indices) {
595            super();
596            iName = name;
597            iTypes = types;
598            iIndices = indices;
599        }
600    
601        //-----------------------------------------------------------------------
602        /**
603         * Gets the name of the period type.
604         * 
605         * @return the name
606         */
607        public String getName() {
608            return iName;
609        }
610    
611        /**
612         * Gets the number of fields in the period type.
613         * 
614         * @return the number of fields
615         */
616        public int size() {
617            return iTypes.length;
618        }
619    
620        /**
621         * Gets the field type by index.
622         * 
623         * @param index  the index to retrieve
624         * @return the field type
625         * @throws IndexOutOfBoundsException if the index is invalid
626         */
627        public DurationFieldType getFieldType(int index) {
628            return iTypes[index];
629        }
630    
631        /**
632         * Checks whether the field specified is supported by this period.
633         *
634         * @param type  the type to check, may be null which returns false
635         * @return true if the field is supported
636         */
637        public boolean isSupported(DurationFieldType type) {
638            return (indexOf(type) >= 0);
639        }
640    
641        /**
642         * Gets the index of the field in this period.
643         *
644         * @param type  the type to check, may be null which returns -1
645         * @return the index of -1 if not supported
646         */
647        public int indexOf(DurationFieldType type) {
648            for (int i = 0, isize = size(); i < isize; i++) {
649                if (iTypes[i] == type) {
650                    return i;
651                }
652            }
653            return -1;
654        }
655    
656        /**
657         * Gets a debugging to string.
658         * 
659         * @return a string
660         */
661        public String toString() {
662            return "PeriodType[" + getName() + "]";
663        }
664    
665        //-----------------------------------------------------------------------
666        /**
667         * Gets the indexed field part of the period.
668         * 
669         * @param period  the period to query
670         * @param index  the index to use
671         * @return the value of the field, zero if unsupported
672         */
673        int getIndexedField(ReadablePeriod period, int index) {
674            int realIndex = iIndices[index];
675            return (realIndex == -1 ? 0 : period.getValue(realIndex));
676        }
677    
678        /**
679         * Sets the indexed field part of the period.
680         * 
681         * @param period  the period to query
682         * @param index  the index to use
683         * @param values  the array to populate
684         * @param newValue  the value to set
685         * @throws UnsupportedOperationException if not supported
686         */
687        boolean setIndexedField(ReadablePeriod period, int index, int[] values, int newValue) {
688            int realIndex = iIndices[index];
689            if (realIndex == -1) {
690                throw new UnsupportedOperationException("Field is not supported");
691            }
692            values[realIndex] = newValue;
693            return true;
694        }
695    
696        /**
697         * Adds to the indexed field part of the period.
698         * 
699         * @param period  the period to query
700         * @param index  the index to use
701         * @param values  the array to populate
702         * @param valueToAdd  the value to add
703         * @return true if the array is updated
704         * @throws UnsupportedOperationException if not supported
705         */
706        boolean addIndexedField(ReadablePeriod period, int index, int[] values, int valueToAdd) {
707            if (valueToAdd == 0) {
708                return false;
709            }
710            int realIndex = iIndices[index];
711            if (realIndex == -1) {
712                throw new UnsupportedOperationException("Field is not supported");
713            }
714            values[realIndex] = FieldUtils.safeAdd(values[realIndex], valueToAdd);
715            return true;
716        }
717    
718        //-----------------------------------------------------------------------
719        /**
720         * Returns a version of this PeriodType instance that does not support years.
721         * 
722         * @return a new period type that supports the original set of fields except years
723         */
724        public PeriodType withYearsRemoved() {
725            return withFieldRemoved(0, "NoYears");
726        }
727    
728        /**
729         * Returns a version of this PeriodType instance that does not support months.
730         * 
731         * @return a new period type that supports the original set of fields except months
732         */
733        public PeriodType withMonthsRemoved() {
734            return withFieldRemoved(1, "NoMonths");
735        }
736    
737        /**
738         * Returns a version of this PeriodType instance that does not support weeks.
739         * 
740         * @return a new period type that supports the original set of fields except weeks
741         */
742        public PeriodType withWeeksRemoved() {
743            return withFieldRemoved(2, "NoWeeks");
744        }
745    
746        /**
747         * Returns a version of this PeriodType instance that does not support days.
748         * 
749         * @return a new period type that supports the original set of fields except days
750         */
751        public PeriodType withDaysRemoved() {
752            return withFieldRemoved(3, "NoDays");
753        }
754    
755        /**
756         * Returns a version of this PeriodType instance that does not support hours.
757         * 
758         * @return a new period type that supports the original set of fields except hours
759         */
760        public PeriodType withHoursRemoved() {
761            return withFieldRemoved(4, "NoHours");
762        }
763    
764        /**
765         * Returns a version of this PeriodType instance that does not support minutes.
766         * 
767         * @return a new period type that supports the original set of fields except minutes
768         */
769        public PeriodType withMinutesRemoved() {
770            return withFieldRemoved(5, "NoMinutes");
771        }
772    
773        /**
774         * Returns a version of this PeriodType instance that does not support seconds.
775         * 
776         * @return a new period type that supports the original set of fields except seconds
777         */
778        public PeriodType withSecondsRemoved() {
779            return withFieldRemoved(6, "NoSeconds");
780        }
781    
782        /**
783         * Returns a version of this PeriodType instance that does not support milliseconds.
784         * 
785         * @return a new period type that supports the original set of fields except milliseconds
786         */
787        public PeriodType withMillisRemoved() {
788            return withFieldRemoved(7, "NoMillis");
789        }
790    
791        /**
792         * Removes the field specified by indices index.
793         * 
794         * @param indicesIndex  the index to remove
795         * @param name  the name addition
796         * @return the new type
797         */
798        private PeriodType withFieldRemoved(int indicesIndex, String name) {
799            int fieldIndex = iIndices[indicesIndex];
800            if (fieldIndex == -1) {
801                return this;
802            }
803            
804            DurationFieldType[] types = new DurationFieldType[size() - 1];
805            for (int i = 0; i < iTypes.length; i++) {
806                if (i < fieldIndex) {
807                    types[i] = iTypes[i];
808                } else if (i > fieldIndex) {
809                    types[i - 1] = iTypes[i];
810                }
811            }
812            
813            int[] indices = new int[8];
814            for (int i = 0; i < indices.length; i++) {
815                if (i < indicesIndex) {
816                    indices[i] = iIndices[i];
817                } else if (i > indicesIndex) {
818                    indices[i] = (iIndices[i] == -1 ? -1 : iIndices[i] - 1);
819                } else {
820                    indices[i] = -1;
821                }
822            }
823            return new PeriodType(getName() + name, types, indices);
824        }
825    
826        //-----------------------------------------------------------------------
827        /**
828         * Compares this type to another object.
829         * To be equal, the object must be a PeriodType with the same set of fields.
830         * 
831         * @param obj  the object to compare to
832         * @return true if equal
833         */
834        public boolean equals(Object obj) {
835            if (this == obj) {
836                return true;
837            }
838            if (obj instanceof PeriodType == false) {
839                return false;
840            }
841            PeriodType other = (PeriodType) obj;
842            return (Arrays.equals(iTypes, other.iTypes));
843        }
844    
845        /**
846         * Returns a hashcode based on the field types.
847         * 
848         * @return a suitable hashcode
849         */
850        public int hashCode() {
851            int hash = 0;
852            for (int i = 0; i < iTypes.length; i++) {
853                hash += iTypes[i].hashCode();
854            }
855            return hash;
856        }
857    
858    }