001/*
002 *  Copyright 2001-2011 Stephen Colebourne
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.joda.time.base;
017
018import java.io.Serializable;
019import java.util.Locale;
020
021import org.joda.time.Chronology;
022import org.joda.time.DateTimeField;
023import org.joda.time.DateTimeUtils;
024import org.joda.time.ReadablePartial;
025import org.joda.time.convert.ConverterManager;
026import org.joda.time.convert.PartialConverter;
027import org.joda.time.format.DateTimeFormat;
028import org.joda.time.format.DateTimeFormatter;
029
030/**
031 * BasePartial is an abstract implementation of ReadablePartial that stores
032 * data in array and <code>Chronology</code> fields.
033 * <p>
034 * This class should generally not be used directly by API users.
035 * The {@link org.joda.time.ReadablePartial} interface should be used when different 
036 * kinds of partial objects are to be referenced.
037 * <p>
038 * BasePartial subclasses may be mutable and not thread-safe.
039 *
040 * @author Stephen Colebourne
041 * @since 1.0
042 */
043public abstract class BasePartial
044        extends AbstractPartial
045        implements ReadablePartial, Serializable {
046
047    /** Serialization version */
048    private static final long serialVersionUID = 2353678632973660L;
049
050    /** The chronology in use */
051    private final Chronology iChronology;
052    /** The values of each field in this partial */
053    private final int[] iValues;
054
055    //-----------------------------------------------------------------------
056    /**
057     * Constructs a partial with the current time, using ISOChronology in
058     * the default zone to extract the fields.
059     * <p>
060     * The constructor uses the default time zone, resulting in the local time
061     * being initialised. Once the constructor is complete, all further calculations
062     * are performed without reference to a timezone (by switching to UTC).
063     */
064    protected BasePartial() {
065        this(DateTimeUtils.currentTimeMillis(), null);
066    }
067
068    /**
069     * Constructs a partial with the current time, using the specified chronology
070     * and zone to extract the fields.
071     * <p>
072     * The constructor uses the time zone of the chronology specified.
073     * Once the constructor is complete, all further calculations are performed
074     * without reference to a timezone (by switching to UTC).
075     *
076     * @param chronology  the chronology, null means ISOChronology in the default zone
077     */
078    protected BasePartial(Chronology chronology) {
079        this(DateTimeUtils.currentTimeMillis(), chronology);
080    }
081
082    /**
083     * Constructs a partial extracting the partial fields from the specified
084     * milliseconds using the ISOChronology in the default zone.
085     * <p>
086     * The constructor uses the default time zone, resulting in the local time
087     * being initialised. Once the constructor is complete, all further calculations
088     * are performed without reference to a timezone (by switching to UTC).
089     *
090     * @param instant  the milliseconds from 1970-01-01T00:00:00Z
091     */
092    protected BasePartial(long instant) {
093        this(instant, null);
094    }
095
096    /**
097     * Constructs a partial extracting the partial fields from the specified
098     * milliseconds using the chronology provided.
099     * <p>
100     * The constructor uses the time zone of the chronology specified.
101     * Once the constructor is complete, all further calculations are performed
102     * without reference to a timezone (by switching to UTC).
103     *
104     * @param instant  the milliseconds from 1970-01-01T00:00:00Z
105     * @param chronology  the chronology, null means ISOChronology in the default zone
106     */
107    protected BasePartial(long instant, Chronology chronology) {
108        super();
109        chronology = DateTimeUtils.getChronology(chronology);
110        iChronology = chronology.withUTC();
111        iValues = chronology.get(this, instant);
112    }
113
114    /**
115     * Constructs a partial from an Object that represents a time, using the
116     * specified chronology.
117     * <p>
118     * The recognised object types are defined in
119     * {@link org.joda.time.convert.ConverterManager ConverterManager} and
120     * include ReadableInstant, String, Calendar and Date.
121     * <p>
122     * The constructor uses the time zone of the chronology specified.
123     * Once the constructor is complete, all further calculations are performed
124     * without reference to a timezone (by switching to UTC).
125     *
126     * @param instant  the datetime object
127     * @param chronology  the chronology, null means use converter
128     * @throws IllegalArgumentException if the date is invalid
129     */
130    protected BasePartial(Object instant, Chronology chronology) {
131        super();
132        PartialConverter converter = ConverterManager.getInstance().getPartialConverter(instant);
133        chronology = converter.getChronology(instant, chronology);
134        chronology = DateTimeUtils.getChronology(chronology);
135        iChronology = chronology.withUTC();
136        iValues = converter.getPartialValues(this, instant, chronology);
137    }
138
139    /**
140     * Constructs a partial from an Object that represents a time, using the
141     * specified chronology.
142     * <p>
143     * The recognised object types are defined in
144     * {@link org.joda.time.convert.ConverterManager ConverterManager} and
145     * include ReadableInstant, String, Calendar and Date.
146     * <p>
147     * The constructor uses the time zone of the chronology specified.
148     * Once the constructor is complete, all further calculations are performed
149     * without reference to a timezone (by switching to UTC).
150     *
151     * @param instant  the datetime object
152     * @param chronology  the chronology, null means use converter
153     * @param parser  if converting from a String, the given parser is preferred
154     * @throws IllegalArgumentException if the date is invalid
155     * @since 1.3
156     */
157    protected BasePartial(Object instant, Chronology chronology, DateTimeFormatter parser) {
158        super();
159        PartialConverter converter = ConverterManager.getInstance().getPartialConverter(instant);
160        chronology = converter.getChronology(instant, chronology);
161        chronology = DateTimeUtils.getChronology(chronology);
162        iChronology = chronology.withUTC();
163        iValues = converter.getPartialValues(this, instant, chronology, parser);
164    }
165
166    /**
167     * Constructs a partial with specified time field values and chronology.
168     * <p>
169     * The constructor uses the time zone of the chronology specified.
170     * Once the constructor is complete, all further calculations are performed
171     * without reference to a timezone (by switching to UTC).
172     * <p>
173     * The array of values is assigned (not cloned) to the new instance.
174     *
175     * @param values  the new set of values
176     * @param chronology  the chronology, null means ISOChronology in the default zone
177     * @throws IllegalArgumentException if the values are invalid
178     */
179    protected BasePartial(int[] values, Chronology chronology) {
180        super();
181        chronology = DateTimeUtils.getChronology(chronology);
182        iChronology = chronology.withUTC();
183        chronology.validate(this, values);
184        iValues = values;
185    }
186
187    /**
188     * Private constructor to be used by subclasses only which performs no validation.
189     * <p>
190     * Data is assigned (not cloned) to the new instance.
191     *
192     * @param base  the base partial
193     * @param values  the new set of values, not cloned, null means use base
194     */
195    protected BasePartial(BasePartial base, int[] values) {
196        super();
197        iChronology = base.iChronology;
198        iValues = values;
199    }
200
201    /**
202     * Private constructor to be used by subclasses only which performs no validation.
203     * <p>
204     * Data is assigned (not cloned) to the new instance.
205     * This should not be used by mutable subclasses.
206     *
207     * @param base  the base partial
208     * @param chrono  the chronology to use, null means use base
209     */
210    protected BasePartial(BasePartial base, Chronology chrono) {
211        super();
212        iChronology = chrono.withUTC();
213        iValues = base.iValues;
214    }
215
216    //-----------------------------------------------------------------------
217    /**
218     * Gets the value of the field at the specifed index.
219     * 
220     * @param index  the index
221     * @return the value
222     * @throws IndexOutOfBoundsException if the index is invalid
223     */
224    public int getValue(int index) {
225        return iValues[index];
226    }
227
228    /**
229     * Gets an array of the value of each of the fields that this partial supports.
230     * <p>
231     * The fields are returned largest to smallest, for example Hour, Minute, Second.
232     * Each value corresponds to the same array index as <code>getFields()</code>
233     *
234     * @return the current values of each field (cloned), largest to smallest
235     */
236    public int[] getValues() {
237        return (int[]) iValues.clone();
238    }
239
240    /**
241     * Gets the chronology of the partial which is never null.
242     * <p>
243     * The {@link Chronology} is the calculation engine behind the partial and
244     * provides conversion and validation of the fields in a particular calendar system.
245     * 
246     * @return the chronology, never null
247     */
248    public Chronology getChronology() {
249        return iChronology;
250    }
251
252    //-----------------------------------------------------------------------
253    /**
254     * Sets the value of the field at the specified index.
255     * <p>
256     * In version 2.0 and later, this method copies the array into the original.
257     * This is because the instance variable has been changed to be final to satisfy the Java Memory Model.
258     * This only impacts subclasses that are mutable.
259     * 
260     * @param index  the index
261     * @param value  the value to set
262     * @throws IndexOutOfBoundsException if the index is invalid
263     */
264    protected void setValue(int index, int value) {
265        DateTimeField field = getField(index);
266        int[] values = field.set(this, index, iValues, value);
267        System.arraycopy(values, 0, iValues, 0, iValues.length);
268    }
269
270    /**
271     * Sets the values of all fields.
272     * <p>
273     * In version 2.0 and later, this method copies the array into the original.
274     * This is because the instance variable has been changed to be final to satisfy the Java Memory Model.
275     * This only impacts subclasses that are mutable.
276     * 
277     * @param values  the array of values
278     */
279    protected void setValues(int[] values) {
280        getChronology().validate(this, values);
281        System.arraycopy(values, 0, iValues, 0, iValues.length);
282    }
283
284    //-----------------------------------------------------------------------
285    /**
286     * Output the date using the specified format pattern.
287     *
288     * @param pattern  the pattern specification, null means use <code>toString</code>
289     * @see org.joda.time.format.DateTimeFormat
290     */
291    public String toString(String pattern) {
292        if (pattern == null) {
293            return toString();
294        }
295        return DateTimeFormat.forPattern(pattern).print(this);
296    }
297
298    /**
299     * Output the date using the specified format pattern.
300     *
301     * @param pattern  the pattern specification, null means use <code>toString</code>
302     * @param locale  Locale to use, null means default
303     * @see org.joda.time.format.DateTimeFormat
304     */
305    public String toString(String pattern, Locale locale) throws IllegalArgumentException {
306        if (pattern == null) {
307            return toString();
308        }
309        return DateTimeFormat.forPattern(pattern).withLocale(locale).print(this);
310    }
311
312}