KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > joda > time > base > BasePeriod


1 /*
2  * Copyright 2001-2006 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.base;
17
18 import java.io.Serializable JavaDoc;
19
20 import org.joda.time.Chronology;
21 import org.joda.time.DateTimeUtils;
22 import org.joda.time.Duration;
23 import org.joda.time.DurationFieldType;
24 import org.joda.time.MutablePeriod;
25 import org.joda.time.PeriodType;
26 import org.joda.time.ReadWritablePeriod;
27 import org.joda.time.ReadableDuration;
28 import org.joda.time.ReadableInstant;
29 import org.joda.time.ReadablePartial;
30 import org.joda.time.ReadablePeriod;
31 import org.joda.time.convert.ConverterManager;
32 import org.joda.time.convert.PeriodConverter;
33 import org.joda.time.field.FieldUtils;
34
35 /**
36  * BasePeriod is an abstract implementation of ReadablePeriod that stores
37  * data in a <code>PeriodType</code> and an <code>int[]</code>.
38  * <p>
39  * This class should generally not be used directly by API users.
40  * The {@link ReadablePeriod} interface should be used when different
41  * kinds of period objects are to be referenced.
42  * <p>
43  * BasePeriod subclasses may be mutable and not thread-safe.
44  *
45  * @author Brian S O'Neill
46  * @author Stephen Colebourne
47  * @since 1.0
48  */

49 public abstract class BasePeriod
50         extends AbstractPeriod
51         implements ReadablePeriod, Serializable JavaDoc {
52
53     /** Serialization version */
54     private static final long serialVersionUID = -2110953284060001145L;
55
56     /** The type of period */
57     private PeriodType iType;
58     /** The values */
59     private int[] iValues;
60
61     //-----------------------------------------------------------------------
62
/**
63      * Creates a period from a set of field values.
64      *
65      * @param years amount of years in this period, which must be zero if unsupported
66      * @param months amount of months in this period, which must be zero if unsupported
67      * @param weeks amount of weeks in this period, which must be zero if unsupported
68      * @param days amount of days in this period, which must be zero if unsupported
69      * @param hours amount of hours in this period, which must be zero if unsupported
70      * @param minutes amount of minutes in this period, which must be zero if unsupported
71      * @param seconds amount of seconds in this period, which must be zero if unsupported
72      * @param millis amount of milliseconds in this period, which must be zero if unsupported
73      * @param type which set of fields this period supports
74      * @throws IllegalArgumentException if period type is invalid
75      * @throws IllegalArgumentException if an unsupported field's value is non-zero
76      */

77     protected BasePeriod(int years, int months, int weeks, int days,
78                          int hours, int minutes, int seconds, int millis,
79                          PeriodType type) {
80         super();
81         type = checkPeriodType(type);
82         iType = type;
83         setPeriodInternal(years, months, weeks, days, hours, minutes, seconds, millis); // internal method
84
}
85
86     /**
87      * Creates a period from the given interval endpoints.
88      *
89      * @param startInstant interval start, in milliseconds
90      * @param endInstant interval end, in milliseconds
91      * @param type which set of fields this period supports, null means standard
92      * @param chrono the chronology to use, null means ISO default
93      * @throws IllegalArgumentException if period type is invalid
94      */

95     protected BasePeriod(long startInstant, long endInstant, PeriodType type, Chronology chrono) {
96         super();
97         type = checkPeriodType(type);
98         chrono = DateTimeUtils.getChronology(chrono);
99         iType = type;
100         iValues = chrono.get(this, startInstant, endInstant);
101     }
102
103     /**
104      * Creates a period from the given interval endpoints.
105      *
106      * @param startInstant interval start, null means now
107      * @param endInstant interval end, null means now
108      * @param type which set of fields this period supports, null means standard
109      * @throws IllegalArgumentException if period type is invalid
110      */

111     protected BasePeriod(ReadableInstant startInstant, ReadableInstant endInstant, PeriodType type) {
112         super();
113         type = checkPeriodType(type);
114         if (startInstant == null && endInstant == null) {
115             iType = type;
116             iValues = new int[size()];
117         } else {
118             long startMillis = DateTimeUtils.getInstantMillis(startInstant);
119             long endMillis = DateTimeUtils.getInstantMillis(endInstant);
120             Chronology chrono = DateTimeUtils.getIntervalChronology(startInstant, endInstant);
121             chrono = DateTimeUtils.getChronology(chrono);
122             iType = type;
123             iValues = chrono.get(this, startMillis, endMillis);
124         }
125     }
126
127     /**
128      * Creates a period from the given duration and end point.
129      * <p>
130      * The two partials must contain the same fields, thus you can
131      * specify two <code>LocalDate</code> objects, or two <code>LocalTime</code>
132      * objects, but not one of each.
133      * As these are Partial objects, time zones have no effect on the result.
134      * <p>
135      * The two partials must also both be contiguous - see
136      * {@link DateTimeUtils#isContiguous(ReadablePartial)} for a
137      * definition. Both <code>LocalDate</code> and <code>LocalTime</code> are contiguous.
138      *
139      * @param start the start of the period, must not be null
140      * @param end the end of the period, must not be null
141      * @param type which set of fields this period supports, null means standard
142      * @throws IllegalArgumentException if the partials are null or invalid
143      * @since 1.1
144      */

145     protected BasePeriod(ReadablePartial start, ReadablePartial end, PeriodType type) {
146         super();
147         if (start == null || end == null) {
148             throw new IllegalArgumentException JavaDoc("ReadablePartial objects must not be null");
149         }
150         if (start.size() != end.size()) {
151             throw new IllegalArgumentException JavaDoc("ReadablePartial objects must have the same set of fields");
152         }
153         for (int i = 0, isize = start.size(); i < isize; i++) {
154             if (start.getFieldType(i) != end.getFieldType(i)) {
155                 throw new IllegalArgumentException JavaDoc("ReadablePartial objects must have the same set of fields");
156             }
157         }
158         if (DateTimeUtils.isContiguous(start) == false) {
159             throw new IllegalArgumentException JavaDoc("ReadablePartial objects must be contiguous");
160         }
161         iType = checkPeriodType(type);
162         Chronology chrono = DateTimeUtils.getChronology(start.getChronology()).withUTC();
163         iValues = chrono.get(this, chrono.set(start, 0L), chrono.set(end, 0L));
164     }
165
166     /**
167      * Creates a period from the given start point and duration.
168      *
169      * @param startInstant the interval start, null means now
170      * @param duration the duration of the interval, null means zero-length
171      * @param type which set of fields this period supports, null means standard
172      */

173     protected BasePeriod(ReadableInstant startInstant, ReadableDuration duration, PeriodType type) {
174         super();
175         type = checkPeriodType(type);
176         long startMillis = DateTimeUtils.getInstantMillis(startInstant);
177         long durationMillis = DateTimeUtils.getDurationMillis(duration);
178         long endMillis = FieldUtils.safeAdd(startMillis, durationMillis);
179         Chronology chrono = DateTimeUtils.getInstantChronology(startInstant);
180         iType = type;
181         iValues = chrono.get(this, startMillis, endMillis);
182     }
183
184     /**
185      * Creates a period from the given duration and end point.
186      *
187      * @param duration the duration of the interval, null means zero-length
188      * @param endInstant the interval end, null means now
189      * @param type which set of fields this period supports, null means standard
190      */

191     protected BasePeriod(ReadableDuration duration, ReadableInstant endInstant, PeriodType type) {
192         super();
193         type = checkPeriodType(type);
194         long durationMillis = DateTimeUtils.getDurationMillis(duration);
195         long endMillis = DateTimeUtils.getInstantMillis(endInstant);
196         long startMillis = FieldUtils.safeSubtract(endMillis, durationMillis);
197         Chronology chrono = DateTimeUtils.getInstantChronology(endInstant);
198         iType = type;
199         iValues = chrono.get(this, startMillis, endMillis);
200     }
201
202     /**
203      * Creates a period from the given millisecond duration, which is only really
204      * suitable for durations less than one day.
205      * <p>
206      * Only fields that are precise will be used.
207      * Thus the largest precise field may have a large value.
208      *
209      * @param duration the duration, in milliseconds
210      * @param type which set of fields this period supports, null means standard
211      * @param chrono the chronology to use, null means ISO default
212      * @throws IllegalArgumentException if period type is invalid
213      */

214     protected BasePeriod(long duration, PeriodType type, Chronology chrono) {
215         super();
216         type = checkPeriodType(type);
217         chrono = DateTimeUtils.getChronology(chrono);
218         iType = type;
219         iValues = chrono.get(this, duration);
220     }
221
222     /**
223      * Creates a new period based on another using the {@link ConverterManager}.
224      *
225      * @param period the period to convert
226      * @param type which set of fields this period supports, null means use type from object
227      * @param chrono the chronology to use, null means ISO default
228      * @throws IllegalArgumentException if period is invalid
229      * @throws IllegalArgumentException if an unsupported field's value is non-zero
230      */

231     protected BasePeriod(Object JavaDoc period, PeriodType type, Chronology chrono) {
232         super();
233         PeriodConverter converter = ConverterManager.getInstance().getPeriodConverter(period);
234         type = (type == null ? converter.getPeriodType(period) : type);
235         type = checkPeriodType(type);
236         iType = type;
237         if (this instanceof ReadWritablePeriod) {
238             iValues = new int[size()];
239             chrono = DateTimeUtils.getChronology(chrono);
240             converter.setInto((ReadWritablePeriod) this, period, chrono);
241         } else {
242             iValues = new MutablePeriod(period, type, chrono).getValues();
243         }
244     }
245
246     /**
247      * Constructor used when we trust ourselves.
248      * Do not expose publically.
249      *
250      * @param values the values to use, not null, not cloned
251      * @param type which set of fields this period supports, not null
252      */

253     protected BasePeriod(int[] values, PeriodType type) {
254         super();
255         iType = type;
256         iValues = values;
257     }
258
259     //-----------------------------------------------------------------------
260
/**
261      * Validates a period type, converting nulls to a default value and
262      * checking the type is suitable for this instance.
263      *
264      * @param type the type to check, may be null
265      * @return the validated type to use, not null
266      * @throws IllegalArgumentException if the period type is invalid
267      */

268     protected PeriodType checkPeriodType(PeriodType type) {
269         return DateTimeUtils.getPeriodType(type);
270     }
271
272     //-----------------------------------------------------------------------
273
/**
274      * Gets the period type.
275      *
276      * @return the period type
277      */

278     public PeriodType getPeriodType() {
279         return iType;
280     }
281
282     //-----------------------------------------------------------------------
283
/**
284      * Gets the number of fields that this period supports.
285      *
286      * @return the number of fields supported
287      */

288     public int size() {
289         return iType.size();
290     }
291
292     /**
293      * Gets the field type at the specified index.
294      *
295      * @param index the index to retrieve
296      * @return the field at the specified index
297      * @throws IndexOutOfBoundsException if the index is invalid
298      */

299     public DurationFieldType getFieldType(int index) {
300         return iType.getFieldType(index);
301     }
302
303     /**
304      * Gets the value at the specified index.
305      *
306      * @param index the index to retrieve
307      * @return the value of the field at the specified index
308      * @throws IndexOutOfBoundsException if the index is invalid
309      */

310     public int getValue(int index) {
311         return iValues[index];
312     }
313
314     //-----------------------------------------------------------------------
315
/**
316      * Gets the total millisecond duration of this period relative to a start instant.
317      * <p>
318      * This method adds the period to the specifed instant.
319      * The difference between the start instant and the result of the add is the duration
320      *
321      * @param startInstant the instant to add the period to, thus obtaining the duration
322      * @return the total length of the period as a duration relative to the start instant
323      * @throws ArithmeticException if the millis exceeds the capacity of the duration
324      */

325     public Duration toDurationFrom(ReadableInstant startInstant) {
326         long startMillis = DateTimeUtils.getInstantMillis(startInstant);
327         Chronology chrono = DateTimeUtils.getInstantChronology(startInstant);
328         long endMillis = chrono.add(this, startMillis, 1);
329         return new Duration(startMillis, endMillis);
330     }
331
332     /**
333      * Gets the total millisecond duration of this period relative to an end instant.
334      * <p>
335      * This method subtracts the period from the specifed instant.
336      *
337      * @param endInstant the instant to subtract the period from, thus obtaining the duration
338      * @return the total length of the period as a duration relative to the end instant
339      * @throws ArithmeticException if the millis exceeds the capacity of the duration
340      */

341     public Duration toDurationTo(ReadableInstant endInstant) {
342         long endMillis = DateTimeUtils.getInstantMillis(endInstant);
343         Chronology chrono = DateTimeUtils.getInstantChronology(endInstant);
344         long startMillis = chrono.add(this, endMillis, -1);
345         return new Duration(startMillis, endMillis);
346     }
347
348     //-----------------------------------------------------------------------
349
/**
350      * Checks whether a field type is supported, and if so adds the new value
351      * to the relevent index in the specified array.
352      *
353      * @param type the field type
354      * @param values the array to update
355      * @param newValue the new value to store if successful
356      */

357     private void checkAndUpdate(DurationFieldType type, int[] values, int newValue) {
358         int index = indexOf(type);
359         if (index == -1) {
360             if (newValue != 0) {
361                 throw new IllegalArgumentException JavaDoc(
362                     "Period does not support field '" + type.getName() + "'");
363             }
364         } else {
365             values[index] = newValue;
366         }
367     }
368
369     //-----------------------------------------------------------------------
370
/**
371      * Sets all the fields of this period from another.
372      *
373      * @param period the period to copy from, not null
374      * @throws IllegalArgumentException if an unsupported field's value is non-zero
375      */

376     protected void setPeriod(ReadablePeriod period) {
377         if (period == null) {
378             setValues(new int[size()]);
379         } else {
380             setPeriodInternal(period);
381         }
382     }
383
384     /**
385      * Private method called from constructor.
386      */

387     private void setPeriodInternal(ReadablePeriod period) {
388         int[] newValues = new int[size()];
389         for (int i = 0, isize = period.size(); i < isize; i++) {
390             DurationFieldType type = period.getFieldType(i);
391             int value = period.getValue(i);
392             checkAndUpdate(type, newValues, value);
393         }
394         iValues = newValues;
395     }
396
397     /**
398      * Sets the eight standard the fields in one go.
399      *
400      * @param years amount of years in this period, which must be zero if unsupported
401      * @param months amount of months in this period, which must be zero if unsupported
402      * @param weeks amount of weeks in this period, which must be zero if unsupported
403      * @param days amount of days in this period, which must be zero if unsupported
404      * @param hours amount of hours in this period, which must be zero if unsupported
405      * @param minutes amount of minutes in this period, which must be zero if unsupported
406      * @param seconds amount of seconds in this period, which must be zero if unsupported
407      * @param millis amount of milliseconds in this period, which must be zero if unsupported
408      * @throws IllegalArgumentException if an unsupported field's value is non-zero
409      */

410     protected void setPeriod(int years, int months, int weeks, int days,
411                              int hours, int minutes, int seconds, int millis) {
412         setPeriodInternal(years, months, weeks, days, hours, minutes, seconds, millis);
413     }
414
415     /**
416      * Private method called from constructor.
417      */

418     private void setPeriodInternal(int years, int months, int weeks, int days,
419                                    int hours, int minutes, int seconds, int millis) {
420         int[] newValues = new int[size()];
421         checkAndUpdate(DurationFieldType.years(), newValues, years);
422         checkAndUpdate(DurationFieldType.months(), newValues, months);
423         checkAndUpdate(DurationFieldType.weeks(), newValues, weeks);
424         checkAndUpdate(DurationFieldType.days(), newValues, days);
425         checkAndUpdate(DurationFieldType.hours(), newValues, hours);
426         checkAndUpdate(DurationFieldType.minutes(), newValues, minutes);
427         checkAndUpdate(DurationFieldType.seconds(), newValues, seconds);
428         checkAndUpdate(DurationFieldType.millis(), newValues, millis);
429         iValues = newValues;
430     }
431
432     //-----------------------------------------------------------------------
433
/**
434      * Sets the value of a field in this period.
435      *
436      * @param field the field to set
437      * @param value the value to set
438      * @throws IllegalArgumentException if field is is null or not supported.
439      */

440     protected void setField(DurationFieldType field, int value) {
441         setFieldInto(iValues, field, value);
442     }
443
444     /**
445      * Sets the value of a field in this period.
446      *
447      * @param values the array of values to update
448      * @param field the field to set
449      * @param value the value to set
450      * @throws IllegalArgumentException if field is null or not supported.
451      */

452     protected void setFieldInto(int[] values, DurationFieldType field, int value) {
453         int index = indexOf(field);
454         if (index == -1) {
455             if (value != 0 || field == null) {
456                 throw new IllegalArgumentException JavaDoc(
457                     "Period does not support field '" + field + "'");
458             }
459         } else {
460             values[index] = value;
461         }
462     }
463
464     /**
465      * Adds the value of a field in this period.
466      *
467      * @param field the field to set
468      * @param value the value to set
469      * @throws IllegalArgumentException if field is is null or not supported.
470      */

471     protected void addField(DurationFieldType field, int value) {
472         addFieldInto(iValues, field, value);
473     }
474
475     /**
476      * Adds the value of a field in this period.
477      *
478      * @param values the array of values to update
479      * @param field the field to set
480      * @param value the value to set
481      * @throws IllegalArgumentException if field is is null or not supported.
482      */

483     protected void addFieldInto(int[] values, DurationFieldType field, int value) {
484         int index = indexOf(field);
485         if (index == -1) {
486             if (value != 0 || field == null) {
487                 throw new IllegalArgumentException JavaDoc(
488                     "Period does not support field '" + field + "'");
489             }
490         } else {
491             values[index] = FieldUtils.safeAdd(values[index], value);
492         }
493     }
494
495     /**
496      * Merges the fields from another period.
497      *
498      * @param period the period to add from, not null
499      * @throws IllegalArgumentException if an unsupported field's value is non-zero
500      */

501     protected void mergePeriod(ReadablePeriod period) {
502         if (period != null) {
503             iValues = mergePeriodInto(getValues(), period);
504         }
505     }
506
507     /**
508      * Merges the fields from another period.
509      *
510      * @param values the array of values to update
511      * @param period the period to add from, not null
512      * @return the updated values
513      * @throws IllegalArgumentException if an unsupported field's value is non-zero
514      */

515     protected int[] mergePeriodInto(int[] values, ReadablePeriod period) {
516          for (int i = 0, isize = period.size(); i < isize; i++) {
517              DurationFieldType type = period.getFieldType(i);
518              int value = period.getValue(i);
519              checkAndUpdate(type, values, value);
520          }
521          return values;
522     }
523
524     /**
525      * Adds the fields from another period.
526      *
527      * @param period the period to add from, not null
528      * @throws IllegalArgumentException if an unsupported field's value is non-zero
529      */

530     protected void addPeriod(ReadablePeriod period) {
531         if (period != null) {
532             iValues = addPeriodInto(getValues(), period);
533         }
534     }
535
536     /**
537      * Adds the fields from another period.
538      *
539      * @param values the array of values to update
540      * @param period the period to add from, not null
541      * @return the updated values
542      * @throws IllegalArgumentException if an unsupported field's value is non-zero
543      */

544     protected int[] addPeriodInto(int[] values, ReadablePeriod period) {
545          for (int i = 0, isize = period.size(); i < isize; i++) {
546              DurationFieldType type = period.getFieldType(i);
547              int value = period.getValue(i);
548              if (value != 0) {
549                  int index = indexOf(type);
550                  if (index == -1) {
551                      throw new IllegalArgumentException JavaDoc(
552                          "Period does not support field '" + type.getName() + "'");
553                  } else {
554                      values[index] = FieldUtils.safeAdd(getValue(index), value);
555                  }
556              }
557          }
558          return values;
559     }
560
561     //-----------------------------------------------------------------------
562
/**
563      * Sets the value of the field at the specifed index.
564      *
565      * @param index the index
566      * @param value the value to set
567      * @throws IndexOutOfBoundsException if the index is invalid
568      */

569     protected void setValue(int index, int value) {
570         iValues[index] = value;
571     }
572
573     /**
574      * Sets the values of all fields.
575      *
576      * @param values the array of values
577      */

578     protected void setValues(int[] values) {
579         iValues = values;
580     }
581
582 }
583
Popular Tags