1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
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  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  public class PeriodType implements Serializable {
52      
53      private static final long serialVersionUID = 2274324892792009998L;
54  
55      
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  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
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 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
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 
153 
154 
155 
156 
157 
158 
159 
160 
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 
180 
181 
182 
183 
184 
185 
186 
187 
188 
189 
190 
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 
212 
213 
214 
215 
216 
217 
218 
219 
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 
239 
240 
241 
242 
243 
244 
245 
246 
247 
248 
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 
269 
270 
271 
272 
273 
274 
275 
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 
294 
295 
296 
297 
298 
299 
300 
301 
302 
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 
323 
324 
325 
326 
327 
328 
329 
330 
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 
350 
351 
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 
368 
369 
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 
386 
387 
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 
404 
405 
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 
422 
423 
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 
440 
441 
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 
458 
459 
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 
476 
477 
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 
494 
495 
496 
497 
498 
499 
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         
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     
581     private final String iName;
582     
583     private final DurationFieldType[] iTypes;
584     
585     private final int[] iIndices;
586 
587     
588 
589 
590 
591 
592 
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 
604 
605 
606 
607     public String getName() {
608         return iName;
609     }
610 
611     
612 
613 
614 
615 
616     public int size() {
617         return iTypes.length;
618     }
619 
620     
621 
622 
623 
624 
625 
626 
627     public DurationFieldType getFieldType(int index) {
628         return iTypes[index];
629     }
630 
631     
632 
633 
634 
635 
636 
637     public boolean isSupported(DurationFieldType type) {
638         return (indexOf(type) >= 0);
639     }
640 
641     
642 
643 
644 
645 
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 
658 
659 
660 
661     public String toString() {
662         return "PeriodType[" + getName() + "]";
663     }
664 
665     
666     
667 
668 
669 
670 
671 
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 
680 
681 
682 
683 
684 
685 
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 
698 
699 
700 
701 
702 
703 
704 
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 
721 
722 
723 
724     public PeriodType withYearsRemoved() {
725         return withFieldRemoved(0, "NoYears");
726     }
727 
728     
729 
730 
731 
732 
733     public PeriodType withMonthsRemoved() {
734         return withFieldRemoved(1, "NoMonths");
735     }
736 
737     
738 
739 
740 
741 
742     public PeriodType withWeeksRemoved() {
743         return withFieldRemoved(2, "NoWeeks");
744     }
745 
746     
747 
748 
749 
750 
751     public PeriodType withDaysRemoved() {
752         return withFieldRemoved(3, "NoDays");
753     }
754 
755     
756 
757 
758 
759 
760     public PeriodType withHoursRemoved() {
761         return withFieldRemoved(4, "NoHours");
762     }
763 
764     
765 
766 
767 
768 
769     public PeriodType withMinutesRemoved() {
770         return withFieldRemoved(5, "NoMinutes");
771     }
772 
773     
774 
775 
776 
777 
778     public PeriodType withSecondsRemoved() {
779         return withFieldRemoved(6, "NoSeconds");
780     }
781 
782     
783 
784 
785 
786 
787     public PeriodType withMillisRemoved() {
788         return withFieldRemoved(7, "NoMillis");
789     }
790 
791     
792 
793 
794 
795 
796 
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 
829 
830 
831 
832 
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 
847 
848 
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 }