KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > joda > time > Partial


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;
17
18 import java.io.Serializable JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Arrays JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Locale JavaDoc;
23
24 import org.joda.time.base.AbstractPartial;
25 import org.joda.time.field.AbstractPartialFieldProperty;
26 import org.joda.time.field.FieldUtils;
27 import org.joda.time.format.DateTimeFormat;
28 import org.joda.time.format.DateTimeFormatter;
29 import org.joda.time.format.ISODateTimeFormat;
30
31 /**
32  * Partial is an immutable partial datetime supporting any set of datetime fields.
33  * <p>
34  * A Partial instance can be used to hold any combination of fields.
35  * The instance does not contain a time zone, so any datetime is local.
36  * <p>
37  * A Partial can be matched against an instant using {@link #isMatch(ReadableInstant)}.
38  * This method compares each field on this partial with those of the instant
39  * and determines if the partial matches the instant.
40  * Given this definition, an empty Partial instance represents any datetime
41  * and always matches.
42  * <p>
43  * Calculations on Partial are performed using a {@link Chronology}.
44  * This chronology is set to be in the UTC time zone for all calculations.
45  * <p>
46  * Each individual field can be queried in two ways:
47  * <ul>
48  * <li><code>get(DateTimeFieldType.monthOfYear())</code>
49  * <li><code>property(DateTimeFieldType.monthOfYear()).get()</code>
50  * </ul>
51  * The second technique also provides access to other useful methods on the
52  * field:
53  * <ul>
54  * <li>numeric value - <code>monthOfYear().get()</code>
55  * <li>text value - <code>monthOfYear().getAsText()</code>
56  * <li>short text value - <code>monthOfYear().getAsShortText()</code>
57  * <li>maximum/minimum values - <code>monthOfYear().getMaximumValue()</code>
58  * <li>add/subtract - <code>monthOfYear().addToCopy()</code>
59  * <li>set - <code>monthOfYear().setCopy()</code>
60  * </ul>
61  * <p>
62  * Partial is thread-safe and immutable, provided that the Chronology is as well.
63  * All standard Chronology classes supplied are thread-safe and immutable.
64  *
65  * @author Stephen Colebourne
66  * @since 1.1
67  */

68 public final class Partial
69         extends AbstractPartial
70         implements ReadablePartial, Serializable JavaDoc {
71
72     /** Serialization version */
73     private static final long serialVersionUID = 12324121189002L;
74
75     /** The chronology in use. */
76     private final Chronology iChronology;
77     /** The set of field types. */
78     private final DateTimeFieldType[] iTypes;
79     /** The values of each field in this partial. */
80     private final int[] iValues;
81     /** The formatter to use, [0] may miss some fields, [1] doesn't miss any fields. */
82     private transient DateTimeFormatter[] iFormatter;
83
84     // Constructors
85
//-----------------------------------------------------------------------
86
/**
87      * Constructs a Partial with no fields or values, which can be considered
88      * to represent any date.
89      * <p>
90      * This is most useful when constructing partials, for example:
91      * <pre>
92      * Partial p = new Partial()
93      * .with(DateTimeFieldType.dayOfWeek(), 5)
94      * .with(DateTimeFieldType.hourOfDay(), 12)
95      * .with(DateTimeFieldType.minuteOfHour(), 20);
96      * </pre>
97      * Note that, although this is a clean way to write code, it is fairly
98      * inefficient internally.
99      * <p>
100      * The constructor uses the default ISO chronology.
101      */

102     public Partial() {
103         this((Chronology) null);
104     }
105
106     /**
107      * Constructs a Partial with no fields or values, which can be considered
108      * to represent any date.
109      * <p>
110      * This is most useful when constructing partials, for example:
111      * <pre>
112      * Partial p = new Partial(chrono)
113      * .with(DateTimeFieldType.dayOfWeek(), 5)
114      * .with(DateTimeFieldType.hourOfDay(), 12)
115      * .with(DateTimeFieldType.minuteOfHour(), 20);
116      * </pre>
117      * Note that, although this is a clean way to write code, it is fairly
118      * inefficient internally.
119      *
120      * @param chrono the chronology, null means ISO
121      */

122     public Partial(Chronology chrono) {
123         super();
124         iChronology = DateTimeUtils.getChronology(chrono).withUTC();
125         iTypes = new DateTimeFieldType[0];
126         iValues = new int[0];
127     }
128
129     /**
130      * Constructs a Partial with the specified field and value.
131      * <p>
132      * The constructor uses the default ISO chronology.
133      *
134      * @param type the single type to create the partial from, not null
135      * @param value the value to store
136      * @throws IllegalArgumentException if the type or value is invalid
137      */

138     public Partial(DateTimeFieldType type, int value) {
139         this(type, value, null);
140     }
141
142     /**
143      * Constructs a Partial with the specified field and value.
144      * <p>
145      * The constructor uses the specified chronology.
146      *
147      * @param type the single type to create the partial from, not null
148      * @param value the value to store
149      * @param chronology the chronology, null means ISO
150      * @throws IllegalArgumentException if the type or value is invalid
151      */

152     public Partial(DateTimeFieldType type, int value, Chronology chronology) {
153         super();
154         chronology = DateTimeUtils.getChronology(chronology).withUTC();
155         iChronology = chronology;
156         if (type == null) {
157             throw new IllegalArgumentException JavaDoc("The field type must not be null");
158         }
159         iTypes = new DateTimeFieldType[] {type};
160         iValues = new int[] {value};
161         chronology.validate(this, iValues);
162     }
163
164     /**
165      * Constructs a Partial with the specified fields and values.
166      * The fields must be specified in the order largest to smallest.
167      * <p>
168      * The constructor uses the specified chronology.
169      *
170      * @param types the types to create the partial from, not null
171      * @param values the values to store, not null
172      * @throws IllegalArgumentException if the types or values are invalid
173      */

174     public Partial(DateTimeFieldType[] types, int[] values) {
175         this(types, values, null);
176     }
177
178     /**
179      * Constructs a Partial with the specified fields and values.
180      * The fields must be specified in the order largest to smallest.
181      * <p>
182      * The constructor uses the specified chronology.
183      *
184      * @param types the types to create the partial from, not null
185      * @param values the values to store, not null
186      * @param chronology the chronology, null means ISO
187      * @throws IllegalArgumentException if the types or values are invalid
188      */

189     public Partial(DateTimeFieldType[] types, int[] values, Chronology chronology) {
190         super();
191         chronology = DateTimeUtils.getChronology(chronology).withUTC();
192         iChronology = chronology;
193         if (types == null) {
194             throw new IllegalArgumentException JavaDoc("Types array must not be null");
195         }
196         if (values == null) {
197             throw new IllegalArgumentException JavaDoc("Values array must not be null");
198         }
199         if (values.length != types.length) {
200             throw new IllegalArgumentException JavaDoc("Values array must be the same length as the types array");
201         }
202         if (types.length == 0) {
203             iTypes = types;
204             iValues = values;
205             return;
206         }
207         for (int i = 0; i < types.length; i++) {
208             if (types[i] == null) {
209                 throw new IllegalArgumentException JavaDoc("Types array must not contain null: index " + i);
210             }
211         }
212         DurationField lastUnitField = null;
213         for (int i = 0; i < types.length; i++) {
214             DateTimeFieldType loopType = types[i];
215             DurationField loopUnitField = loopType.getDurationType().getField(iChronology);
216             if (i > 0) {
217                 int compare = lastUnitField.compareTo(loopUnitField);
218                 if (compare < 0 || (compare != 0 && loopUnitField.isSupported() == false)) {
219                     throw new IllegalArgumentException JavaDoc("Types array must be in order largest-smallest: " +
220                             types[i - 1].getName() + " < " + loopType.getName());
221                 } else if (compare == 0) {
222                     if (types[i - 1].getRangeDurationType() == null) {
223                         if (loopType.getRangeDurationType() == null) {
224                             throw new IllegalArgumentException JavaDoc("Types array must not contain duplicate: " + loopType.getName());
225                         }
226                     } else {
227                         if (loopType.getRangeDurationType() == null) {
228                             throw new IllegalArgumentException JavaDoc("Types array must be in order largest-smallest: " +
229                                     types[i - 1].getName() + " < " + loopType.getName());
230                         }
231                         DurationField lastRangeField = types[i - 1].getRangeDurationType().getField(iChronology);
232                         DurationField loopRangeField = loopType.getRangeDurationType().getField(iChronology);
233                         if (lastRangeField.compareTo(loopRangeField) < 0) {
234                             throw new IllegalArgumentException JavaDoc("Types array must be in order largest-smallest: " +
235                                     types[i - 1].getName() + " < " + loopType.getName());
236                         }
237                         if (lastRangeField.compareTo(loopRangeField) == 0) {
238                             throw new IllegalArgumentException JavaDoc("Types array must not contain duplicate: " + loopType.getName());
239                         }
240                     }
241                 }
242             }
243             lastUnitField = loopUnitField;
244         }
245         
246         iTypes = (DateTimeFieldType[]) types.clone();
247         chronology.validate(this, values);
248         iValues = (int[]) values.clone();
249     }
250
251     /**
252      * Constructs a Partial by copying all the fields and types from
253      * another partial.
254      * <p>
255      * This is most useful when copying from a YearMonthDay or TimeOfDay.
256      */

257     public Partial(ReadablePartial partial) {
258         super();
259         if (partial == null) {
260             throw new IllegalArgumentException JavaDoc("The partial must not be null");
261         }
262         iChronology = DateTimeUtils.getChronology(partial.getChronology()).withUTC();
263         iTypes = new DateTimeFieldType[partial.size()];
264         iValues = new int[partial.size()];
265         for (int i = 0; i < partial.size(); i++) {
266             iTypes[i] = partial.getFieldType(i);
267             iValues[i] = partial.getValue(i);
268         }
269     }
270
271     /**
272      * Constructs a Partial with the specified values.
273      * This constructor assigns and performs no validation.
274      *
275      * @param partial the partial to copy
276      * @param values the values to store
277      * @throws IllegalArgumentException if the types or values are invalid
278      */

279     Partial(Partial partial, int[] values) {
280         super();
281         iChronology = partial.iChronology;
282         iTypes = partial.iTypes;
283         iValues = values;
284     }
285
286     /**
287      * Constructs a Partial with the specified chronology, fields and values.
288      * This constructor assigns and performs no validation.
289      *
290      * @param chronology the chronology
291      * @param types the types to create the partial from
292      * @param values the values to store
293      * @throws IllegalArgumentException if the types or values are invalid
294      */

295     Partial(Chronology chronology, DateTimeFieldType[] types, int[] values) {
296         super();
297         iChronology = chronology;
298         iTypes = types;
299         iValues = values;
300     }
301
302     //-----------------------------------------------------------------------
303
/**
304      * Gets the number of fields in this partial.
305      *
306      * @return the field count
307      */

308     public int size() {
309         return iTypes.length;
310     }
311
312     /**
313      * Gets the chronology of the partial which is never null.
314      * <p>
315      * The {@link Chronology} is the calculation engine behind the partial and
316      * provides conversion and validation of the fields in a particular calendar system.
317      *
318      * @return the chronology, never null
319      */

320     public Chronology getChronology() {
321         return iChronology;
322     }
323
324     /**
325      * Gets the field for a specific index in the chronology specified.
326      *
327      * @param index the index to retrieve
328      * @param chrono the chronology to use
329      * @return the field
330      * @throws IndexOutOfBoundsException if the index is invalid
331      */

332     protected DateTimeField getField(int index, Chronology chrono) {
333         return iTypes[index].getField(chrono);
334     }
335
336     /**
337      * Gets the field type at the specified index.
338      *
339      * @param index the index to retrieve
340      * @return the field at the specified index
341      * @throws IndexOutOfBoundsException if the index is invalid
342      */

343     public DateTimeFieldType getFieldType(int index) {
344         return iTypes[index];
345     }
346
347     /**
348      * Gets an array of the field type of each of the fields that
349      * this partial supports.
350      * <p>
351      * The fields are returned largest to smallest.
352      *
353      * @return the array of field types (cloned), largest to smallest
354      */

355     public DateTimeFieldType[] getFieldTypes() {
356         return (DateTimeFieldType[]) iTypes.clone();
357     }
358
359     //-----------------------------------------------------------------------
360
/**
361      * Gets the value of the field at the specifed index.
362      *
363      * @param index the index
364      * @return the value
365      * @throws IndexOutOfBoundsException if the index is invalid
366      */

367     public int getValue(int index) {
368         return iValues[index];
369     }
370
371     /**
372      * Gets an array of the value of each of the fields that
373      * this partial supports.
374      * <p>
375      * The fields are returned largest to smallest.
376      * Each value corresponds to the same array index as <code>getFieldTypes()</code>
377      *
378      * @return the current values of each field (cloned), largest to smallest
379      */

380     public int[] getValues() {
381         return (int[]) iValues.clone();
382     }
383
384     //-----------------------------------------------------------------------
385
/**
386      * Creates a new Partial instance with the specified chronology.
387      * This instance is immutable and unaffected by this method call.
388      * <p>
389      * This method retains the values of the fields, thus the result will
390      * typically refer to a different instant.
391      * <p>
392      * The time zone of the specified chronology is ignored, as Partial
393      * operates without a time zone.
394      *
395      * @param newChronology the new chronology, null means ISO
396      * @return a copy of this datetime with a different chronology
397      * @throws IllegalArgumentException if the values are invalid for the new chronology
398      */

399     public Partial withChronologyRetainFields(Chronology newChronology) {
400         newChronology = DateTimeUtils.getChronology(newChronology);
401         newChronology = newChronology.withUTC();
402         if (newChronology == getChronology()) {
403             return this;
404         } else {
405             Partial newPartial = new Partial(newChronology, iTypes, iValues);
406             newChronology.validate(newPartial, iValues);
407             return newPartial;
408         }
409     }
410
411     //-----------------------------------------------------------------------
412
/**
413      * Gets a copy of this date with the specified field set to a new value.
414      * <p>
415      * If this partial did not previously support the field, the new one will.
416      * Contrast this behaviour with {@link #withField(DateTimeFieldType, int)}.
417      * <p>
418      * For example, if the field type is <code>dayOfMonth</code> then the day
419      * would be changed/added in the returned instance.
420      *
421      * @param fieldType the field type to set, not null
422      * @param value the value to set
423      * @return a copy of this instance with the field set
424      * @throws IllegalArgumentException if the value is null or invalid
425      */

426     public Partial with(DateTimeFieldType fieldType, int value) {
427         if (fieldType == null) {
428             throw new IllegalArgumentException JavaDoc("The field type must not be null");
429         }
430         int index = indexOf(fieldType);
431         if (index == -1) {
432             DateTimeFieldType[] newTypes = new DateTimeFieldType[iTypes.length + 1];
433             int[] newValues = new int[newTypes.length];
434             
435             // find correct insertion point to keep largest-smallest order
436
int i = 0;
437             DurationField unitField = fieldType.getDurationType().getField(iChronology);
438             if (unitField.isSupported()) {
439                 for (; i < iTypes.length; i++) {
440                     DateTimeFieldType loopType = iTypes[i];
441                     DurationField loopUnitField = loopType.getDurationType().getField(iChronology);
442                     if (loopUnitField.isSupported()) {
443                         int compare = unitField.compareTo(loopUnitField);
444                         if (compare > 0) {
445                             break;
446                         } else if (compare == 0) {
447                             DurationField rangeField = fieldType.getRangeDurationType().getField(iChronology);
448                             DurationField loopRangeField = loopType.getRangeDurationType().getField(iChronology);
449                             if (rangeField.compareTo(loopRangeField) > 0) {
450                                 break;
451                             }
452                         }
453                     }
454                 }
455             }
456             System.arraycopy(iTypes, 0, newTypes, 0, i);
457             System.arraycopy(iValues, 0, newValues, 0, i);
458             newTypes[i] = fieldType;
459             newValues[i] = value;
460             System.arraycopy(iTypes, i, newTypes, i + 1, newTypes.length - i - 1);
461             System.arraycopy(iValues, i, newValues, i + 1, newValues.length - i - 1);
462             
463             Partial newPartial = new Partial(iChronology, newTypes, newValues);
464             iChronology.validate(newPartial, newValues);
465             return newPartial;
466         }
467         if (value == getValue(index)) {
468             return this;
469         }
470         int[] newValues = getValues();
471         newValues = getField(index).set(this, index, newValues, value);
472         return new Partial(this, newValues);
473     }
474
475     /**
476      * Gets a copy of this date with the specified field removed.
477      * <p>
478      * If this partial did not previously support the field, no error occurs.
479      *
480      * @param fieldType the field type to remove, may be null
481      * @return a copy of this instance with the field removed
482      */

483     public Partial without(DateTimeFieldType fieldType) {
484         int index = indexOf(fieldType);
485         if (index != -1) {
486             DateTimeFieldType[] newTypes = new DateTimeFieldType[size() - 1];
487             int[] newValues = new int[size() - 1];
488             System.arraycopy(iTypes, 0, newTypes, 0, index);
489             System.arraycopy(iTypes, index + 1, newTypes, index, newTypes.length - index);
490             System.arraycopy(iValues, 0, newValues, 0, index);
491             System.arraycopy(iValues, index + 1, newValues, index, newValues.length - index);
492             Partial newPartial = new Partial(iChronology, newTypes, newValues);
493             iChronology.validate(newPartial, newValues);
494             return newPartial;
495         }
496         return this;
497     }
498
499     //-----------------------------------------------------------------------
500
/**
501      * Gets a copy of this Partial with the specified field set to a new value.
502      * <p>
503      * If this partial does not support the field, an exception is thrown.
504      * Contrast this behaviour with {@link #with(DateTimeFieldType, int)}.
505      * <p>
506      * For example, if the field type is <code>dayOfMonth</code> then the day
507      * would be changed in the returned instance if supported.
508      *
509      * @param fieldType the field type to set, not null
510      * @param value the value to set
511      * @return a copy of this instance with the field set
512      * @throws IllegalArgumentException if the value is null or invalid
513      */

514     public Partial withField(DateTimeFieldType fieldType, int value) {
515         int index = indexOfSupported(fieldType);
516         if (value == getValue(index)) {
517             return this;
518         }
519         int[] newValues = getValues();
520         newValues = getField(index).set(this, index, newValues, value);
521         return new Partial(this, newValues);
522     }
523
524     /**
525      * Gets a copy of this Partial with the value of the specified field increased.
526      * If this partial does not support the field, an exception is thrown.
527      * <p>
528      * If the addition is zero, then <code>this</code> is returned.
529      * The addition will overflow into larger fields (eg. minute to hour).
530      * However, it will not wrap around if the top maximum is reached.
531      *
532      * @param fieldType the field type to add to, not null
533      * @param amount the amount to add
534      * @return a copy of this instance with the field updated
535      * @throws IllegalArgumentException if the value is null or invalid
536      * @throws ArithmeticException if the new datetime exceeds the capacity
537      */

538     public Partial withFieldAdded(DurationFieldType fieldType, int amount) {
539         int index = indexOfSupported(fieldType);
540         if (amount == 0) {
541             return this;
542         }
543         int[] newValues = getValues();
544         newValues = getField(index).add(this, index, newValues, amount);
545         return new Partial(this, newValues);
546     }
547
548     /**
549      * Gets a copy of this Partial with the value of the specified field increased.
550      * If this partial does not support the field, an exception is thrown.
551      * <p>
552      * If the addition is zero, then <code>this</code> is returned.
553      * The addition will overflow into larger fields (eg. minute to hour).
554      * If the maximum is reached, the addition will wra.
555      *
556      * @param fieldType the field type to add to, not null
557      * @param amount the amount to add
558      * @return a copy of this instance with the field updated
559      * @throws IllegalArgumentException if the value is null or invalid
560      * @throws ArithmeticException if the new datetime exceeds the capacity
561      */

562     public Partial withFieldAddWrapped(DurationFieldType fieldType, int amount) {
563         int index = indexOfSupported(fieldType);
564         if (amount == 0) {
565             return this;
566         }
567         int[] newValues = getValues();
568         newValues = getField(index).addWrapPartial(this, index, newValues, amount);
569         return new Partial(this, newValues);
570     }
571
572     /**
573      * Gets a copy of this Partial with the specified period added.
574      * <p>
575      * If the addition is zero, then <code>this</code> is returned.
576      * Fields in the period that aren't present in the partial are ignored.
577      * <p>
578      * This method is typically used to add multiple copies of complex
579      * period instances. Adding one field is best achieved using the method
580      * {@link #withFieldAdded(DurationFieldType, int)}.
581      *
582      * @param period the period to add to this one, null means zero
583      * @param scalar the amount of times to add, such as -1 to subtract once
584      * @return a copy of this instance with the period added
585      * @throws ArithmeticException if the new datetime exceeds the capacity
586      */

587     public Partial withPeriodAdded(ReadablePeriod period, int scalar) {
588         if (period == null || scalar == 0) {
589             return this;
590         }
591         int[] newValues = getValues();
592         for (int i = 0; i < period.size(); i++) {
593             DurationFieldType fieldType = period.getFieldType(i);
594             int index = indexOf(fieldType);
595             if (index >= 0) {
596                 newValues = getField(index).add(this, index, newValues,
597                         FieldUtils.safeMultiply(period.getValue(i), scalar));
598             }
599         }
600         return new Partial(this, newValues);
601     }
602
603     /**
604      * Gets a copy of this instance with the specified period added.
605      * <p>
606      * If the amount is zero or null, then <code>this</code> is returned.
607      *
608      * @param period the duration to add to this one, null means zero
609      * @return a copy of this instance with the period added
610      * @throws ArithmeticException if the new datetime exceeds the capacity of a long
611      */

612     public Partial plus(ReadablePeriod period) {
613         return withPeriodAdded(period, 1);
614     }
615
616     /**
617      * Gets a copy of this instance with the specified period take away.
618      * <p>
619      * If the amount is zero or null, then <code>this</code> is returned.
620      *
621      * @param period the period to reduce this instant by
622      * @return a copy of this instance with the period taken away
623      * @throws ArithmeticException if the new datetime exceeds the capacity of a long
624      */

625     public Partial minus(ReadablePeriod period) {
626         return withPeriodAdded(period, -1);
627     }
628
629     //-----------------------------------------------------------------------
630
/**
631      * Gets the property object for the specified type, which contains
632      * many useful methods for getting and manipulating the partial.
633      * <p>
634      * See also {@link ReadablePartial#get(DateTimeFieldType)}.
635      *
636      * @param type the field type to get the property for, not null
637      * @return the property object
638      * @throws IllegalArgumentException if the field is null or unsupported
639      */

640     public Property property(DateTimeFieldType type) {
641         return new Property(this, indexOfSupported(type));
642     }
643
644     //-----------------------------------------------------------------------
645
/**
646      * Does this partial match the specified instant.
647      * <p>
648      * A match occurs when all the fields of this partial are the same as the
649      * corresponding fields on the specified instant.
650      *
651      * @param instant an instant to check against, null means now in default zone
652      * @return true if this partial matches the specified instant
653      */

654     public boolean isMatch(ReadableInstant instant) {
655         long millis = DateTimeUtils.getInstantMillis(instant);
656         Chronology chrono = DateTimeUtils.getInstantChronology(instant);
657         for (int i = 0; i < iTypes.length; i++) {
658             int value = iTypes[i].getField(chrono).get(millis);
659             if (value != iValues[i]) {
660                 return false;
661             }
662         }
663         return true;
664     }
665
666     //-----------------------------------------------------------------------
667
/**
668      * Gets a formatter suitable for the fields in this partial.
669      * <p>
670      * If there is no appropriate ISO format, null is returned.
671      * This method may return a formatter that does not display all the
672      * fields of the partial. This might occur when you have overlapping
673      * fields, such as dayOfWeek and dayOfMonth.
674      *
675      * @return a formatter suitable for the fields in this partial, null
676      * if none is suitable
677      */

678     public DateTimeFormatter getFormatter() {
679         DateTimeFormatter[] f = iFormatter;
680         if (f == null) {
681             if (size() == 0) {
682                 return null;
683             }
684             f = new DateTimeFormatter[2];
685             try {
686                 List JavaDoc list = new ArrayList JavaDoc(Arrays.asList(iTypes));
687                 f[0] = ISODateTimeFormat.forFields(list, true, false);
688                 if (list.size() == 0) {
689                     f[1] = f[0];
690                 }
691             } catch (IllegalArgumentException JavaDoc ex) {
692                 // ignore
693
}
694             iFormatter = f;
695         }
696         return f[0];
697     }
698
699     //-----------------------------------------------------------------------
700
/**
701      * Output the date in an appropriate ISO8601 format.
702      * <p>
703      * This method will output the partial in one of two ways.
704      * If {@link #getFormatter()}
705      *
706      * If there is no appropriate ISO format a dump of the fields is output
707      * via {@link #toStringList()}. An appropr
708      *
709      * @return ISO8601 formatted string
710      */

711     public String JavaDoc toString() {
712         DateTimeFormatter[] f = iFormatter;
713         if (f == null) {
714             getFormatter();
715             f = iFormatter;
716             if (f == null) {
717                 return toStringList();
718             }
719         }
720         DateTimeFormatter f1 = f[1];
721         if (f1 == null) {
722             return toStringList();
723         }
724         return f1.print(this);
725     }
726
727     /**
728      * Gets a string version of the partial that lists all the fields.
729      * <p>
730      * This method exists to provide a better debugging toString than
731      * the standard toString. This method lists all the fields and their
732      * values in a style similar to the collections framework.
733      *
734      * @return a toString format that lists all the fields
735      */

736     public String JavaDoc toStringList() {
737         int size = size();
738         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(20 * size);
739         buf.append('[');
740         for (int i = 0; i < size; i++) {
741             if (i > 0) {
742                 buf.append(',').append(' ');
743             }
744             buf.append(iTypes[i].getName());
745             buf.append('=');
746             buf.append(iValues[i]);
747         }
748         buf.append(']');
749         return buf.toString();
750     }
751
752     /**
753      * Output the date using the specified format pattern.
754      * Unsupported fields will appear as special unicode characters.
755      *
756      * @param pattern the pattern specification, null means use <code>toString</code>
757      * @see org.joda.time.format.DateTimeFormat
758      */

759     public String JavaDoc toString(String JavaDoc pattern) {
760         if (pattern == null) {
761             return toString();
762         }
763         return DateTimeFormat.forPattern(pattern).print(this);
764     }
765
766     /**
767      * Output the date using the specified format pattern.
768      * Unsupported fields will appear as special unicode characters.
769      *
770      * @param pattern the pattern specification, null means use <code>toString</code>
771      * @param locale Locale to use, null means default
772      * @see org.joda.time.format.DateTimeFormat
773      */

774     public String JavaDoc toString(String JavaDoc pattern, Locale JavaDoc locale) {
775         if (pattern == null) {
776             return toString();
777         }
778         return DateTimeFormat.forPattern(pattern).withLocale(locale).print(this);
779     }
780
781     //-----------------------------------------------------------------------
782
/**
783      * The property class for <code>Partial</code>.
784      * <p>
785      * This class binds a <code>Partial</code> to a <code>DateTimeField</code>.
786      *
787      * @author Stephen Colebourne
788      * @since 1.1
789      */

790     public static class Property extends AbstractPartialFieldProperty implements Serializable JavaDoc {
791
792         /** Serialization version */
793         private static final long serialVersionUID = 53278362873888L;
794
795         /** The partial */
796         private final Partial iPartial;
797         /** The field index */
798         private final int iFieldIndex;
799
800         /**
801          * Constructs a property.
802          *
803          * @param partial the partial instance
804          * @param fieldIndex the index in the partial
805          */

806         Property(Partial partial, int fieldIndex) {
807             super();
808             iPartial = partial;
809             iFieldIndex = fieldIndex;
810         }
811
812         /**
813          * Gets the field that this property uses.
814          *
815          * @return the field
816          */

817         public DateTimeField getField() {
818             return iPartial.getField(iFieldIndex);
819         }
820
821         /**
822          * Gets the partial that this property belongs to.
823          *
824          * @return the partial
825          */

826         protected ReadablePartial getReadablePartial() {
827             return iPartial;
828         }
829
830         /**
831          * Gets the partial that this property belongs to.
832          *
833          * @return the partial
834          */

835         public Partial getPartial() {
836             return iPartial;
837         }
838
839         /**
840          * Gets the value of this field.
841          *
842          * @return the field value
843          */

844         public int get() {
845             return iPartial.getValue(iFieldIndex);
846         }
847
848         //-----------------------------------------------------------------------
849
/**
850          * Adds to the value of this field in a copy of this Partial.
851          * <p>
852          * The value will be added to this field. If the value is too large to be
853          * added solely to this field then it will affect larger fields.
854          * Smaller fields are unaffected.
855          * <p>
856          * If the result would be too large, beyond the maximum year, then an
857          * IllegalArgumentException is thrown.
858          * <p>
859          * The Partial attached to this property is unchanged by this call.
860          * Instead, a new instance is returned.
861          *
862          * @param valueToAdd the value to add to the field in the copy
863          * @return a copy of the Partial with the field value changed
864          * @throws IllegalArgumentException if the value isn't valid
865          */

866         public Partial addToCopy(int valueToAdd) {
867             int[] newValues = iPartial.getValues();
868             newValues = getField().add(iPartial, iFieldIndex, newValues, valueToAdd);
869             return new Partial(iPartial, newValues);
870         }
871
872         /**
873          * Adds to the value of this field in a copy of this Partial wrapping
874          * within this field if the maximum value is reached.
875          * <p>
876          * The value will be added to this field. If the value is too large to be
877          * added solely to this field then it wraps within this field.
878          * Other fields are unaffected.
879          * <p>
880          * For example,
881          * <code>2004-12-20</code> addWrapField one month returns <code>2004-01-20</code>.
882          * <p>
883          * The Partial attached to this property is unchanged by this call.
884          * Instead, a new instance is returned.
885          *
886          * @param valueToAdd the value to add to the field in the copy
887          * @return a copy of the Partial with the field value changed
888          * @throws IllegalArgumentException if the value isn't valid
889          */

890         public Partial addWrapFieldToCopy(int valueToAdd) {
891             int[] newValues = iPartial.getValues();
892             newValues = getField().addWrapField(iPartial, iFieldIndex, newValues, valueToAdd);
893             return new Partial(iPartial, newValues);
894         }
895
896         //-----------------------------------------------------------------------
897
/**
898          * Sets this field in a copy of the Partial.
899          * <p>
900          * The Partial attached to this property is unchanged by this call.
901          * Instead, a new instance is returned.
902          *
903          * @param value the value to set the field in the copy to
904          * @return a copy of the Partial with the field value changed
905          * @throws IllegalArgumentException if the value isn't valid
906          */

907         public Partial setCopy(int value) {
908             int[] newValues = iPartial.getValues();
909             newValues = getField().set(iPartial, iFieldIndex, newValues, value);
910             return new Partial(iPartial, newValues);
911         }
912
913         /**
914          * Sets this field in a copy of the Partial to a parsed text value.
915          * <p>
916          * The Partial attached to this property is unchanged by this call.
917          * Instead, a new instance is returned.
918          *
919          * @param text the text value to set
920          * @param locale optional locale to use for selecting a text symbol
921          * @return a copy of the Partial with the field value changed
922          * @throws IllegalArgumentException if the text value isn't valid
923          */

924         public Partial setCopy(String JavaDoc text, Locale JavaDoc locale) {
925             int[] newValues = iPartial.getValues();
926             newValues = getField().set(iPartial, iFieldIndex, newValues, text, locale);
927             return new Partial(iPartial, newValues);
928         }
929
930         /**
931          * Sets this field in a copy of the Partial to a parsed text value.
932          * <p>
933          * The Partial attached to this property is unchanged by this call.
934          * Instead, a new instance is returned.
935          *
936          * @param text the text value to set
937          * @return a copy of the Partial with the field value changed
938          * @throws IllegalArgumentException if the text value isn't valid
939          */

940         public Partial setCopy(String JavaDoc text) {
941             return setCopy(text, null);
942         }
943
944         //-----------------------------------------------------------------------
945
/**
946          * Returns a new Partial with this field set to the maximum value
947          * for this field.
948          * <p>
949          * The Partial attached to this property is unchanged by this call.
950          *
951          * @return a copy of the Partial with this field set to its maximum
952          * @since 1.2
953          */

954         public Partial withMaximumValue() {
955             return setCopy(getMaximumValue());
956         }
957
958         /**
959          * Returns a new Partial with this field set to the minimum value
960          * for this field.
961          * <p>
962          * The Partial attached to this property is unchanged by this call.
963          *
964          * @return a copy of the Partial with this field set to its minimum
965          * @since 1.2
966          */

967         public Partial withMinimumValue() {
968             return setCopy(getMinimumValue());
969         }
970     }
971
972 }
973
Popular Tags