View Javadoc

1   /*
2    *  Copyright 2001-2009 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.joda.time;
17  
18  import java.io.Serializable;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.joda.time.field.FieldUtils;
26  
27  /**
28   * Controls a period implementation by specifying which duration fields are to be used.
29   * <p>
30   * The following implementations are provided:
31   * <ul>
32   * <li>Standard - years, months, weeks, days, hours, minutes, seconds, millis
33   * <li>YearMonthDayTime - years, months, days, hours, minutes, seconds, millis
34   * <li>YearMonthDay - years, months, days
35   * <li>YearWeekDayTime - years, weeks, days, hours, minutes, seconds, millis
36   * <li>YearWeekDay - years, weeks, days
37   * <li>YearDayTime - years, days, hours, minutes, seconds, millis
38   * <li>YearDay - years, days, hours
39   * <li>DayTime - days, hours, minutes, seconds, millis
40   * <li>Time - hours, minutes, seconds, millis
41   * <li>plus one for each single type
42   * </ul>
43   *
44   * <p>
45   * PeriodType is thread-safe and immutable, and all subclasses must be as well.
46   *
47   * @author Brian S O'Neill
48   * @author Stephen Colebourne
49   * @since 1.0
50   */
51  public class PeriodType implements Serializable {
52      /** Serialization version */
53      private static final long serialVersionUID = 2274324892792009998L;
54  
55      /** Cache of all the known types. */
56      private static final Map<PeriodType, Object> cTypes = new HashMap<PeriodType, Object>(32);
57  
58      static int YEAR_INDEX = 0;
59      static int MONTH_INDEX = 1;
60      static int WEEK_INDEX = 2;
61      static int DAY_INDEX = 3;
62      static int HOUR_INDEX = 4;
63      static int MINUTE_INDEX = 5;
64      static int SECOND_INDEX = 6;
65      static int MILLI_INDEX = 7;
66      
67      private static PeriodType cStandard;
68      private static PeriodType cYMDTime;
69      private static PeriodType cYMD;
70      private static PeriodType cYWDTime;
71      private static PeriodType cYWD;
72      private static PeriodType cYDTime;
73      private static PeriodType cYD;
74      private static PeriodType cDTime;
75      private static PeriodType cTime;
76      
77      private static PeriodType cYears;
78      private static PeriodType cMonths;
79      private static PeriodType cWeeks;
80      private static PeriodType cDays;
81      private static PeriodType cHours;
82      private static PeriodType cMinutes;
83      private static PeriodType cSeconds;
84      private static PeriodType cMillis;
85  
86      /**
87       * Gets a type that defines all standard fields.
88       * <ul>
89       * <li>years
90       * <li>months
91       * <li>weeks
92       * <li>days
93       * <li>hours
94       * <li>minutes
95       * <li>seconds
96       * <li>milliseconds
97       * </ul>
98       *
99       * @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 }