1 | /* |
2 | * Copyright 2001-2005 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 cTypes = new HashMap(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 cache = cTypes; |
511 | if (cTypes.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 list = new ArrayList(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(inPartType, checkedType); |
573 | return checkedType; |
574 | } |
575 | cache.put(inPartType, 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 | } |