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     */
016    package org.joda.time.base;
017    
018    import java.io.Serializable;
019    import java.util.Locale;
020    
021    import org.joda.time.Chronology;
022    import org.joda.time.DateTimeField;
023    import org.joda.time.DateTimeUtils;
024    import org.joda.time.ReadablePartial;
025    import org.joda.time.convert.ConverterManager;
026    import org.joda.time.convert.PartialConverter;
027    import org.joda.time.format.DateTimeFormat;
028    import 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     */
043    public 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    }