001/*
002 *  Copyright 2001-2005 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;
017
018import java.io.Serializable;
019
020import org.joda.time.base.BaseInterval;
021import org.joda.time.field.FieldUtils;
022import org.joda.time.format.ISODateTimeFormat;
023import org.joda.time.format.ISOPeriodFormat;
024
025/**
026 * MutableInterval is the standard implementation of a mutable time interval.
027 * <p>
028 * A time interval represents a period of time between two instants.
029 * Intervals are inclusive of the start instant and exclusive of the end.
030 * The end instant is always greater than or equal to the start instant.
031 * <p>
032 * Intervals have a fixed millisecond duration.
033 * This is the difference between the start and end instants.
034 * The duration is represented separately by {@link ReadableDuration}.
035 * As a result, intervals are not comparable.
036 * To compare the length of two intervals, you should compare their durations.
037 * <p>
038 * An interval can also be converted to a {@link ReadablePeriod}.
039 * This represents the difference between the start and end points in terms of fields
040 * such as years and days.
041 * <p>
042 * If performing significant calculations on an interval, it may be faster to
043 * convert an Interval object to a MutableInterval one.
044 * <p>
045 * MutableInterval is mutable and not thread-safe, unless concurrent threads
046 * are not invoking mutator methods.
047 *
048 * @author Stephen Colebourne
049 * @author Brian S O'Neill
050 * @since 1.0
051 */
052public class MutableInterval
053        extends BaseInterval
054        implements ReadWritableInterval, Cloneable, Serializable {
055
056    /** Serialization version */
057    private static final long serialVersionUID = -5982824024992428470L;
058
059    //-----------------------------------------------------------------------
060    /**
061     * Parses a {@code MutableInterval} from the specified string.
062     * <p>
063     * The String formats are described by {@link ISODateTimeFormat#dateTimeParser()}
064     * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime',
065     * 'datetime/period' or 'period/datetime'.
066     * 
067     * @param str  the string to parse, not null
068     * @since 2.0
069     */
070    public static MutableInterval parse(String str) {
071        return new MutableInterval(str);
072    }
073
074    //-----------------------------------------------------------------------
075    /**
076     * Constructs a zero length time interval from 1970-01-01 to 1970-01-01.
077     */
078    public MutableInterval() {
079        super(0L, 0L, null);
080    }
081
082    /**
083     * Constructs an interval from a start and end instant with the ISO default chronology.
084     * 
085     * @param startInstant  start of this interval, as milliseconds from 1970-01-01T00:00:00Z.
086     * @param endInstant  end of this interval, as milliseconds from 1970-01-01T00:00:00Z.
087     * @throws IllegalArgumentException if the end is before the start
088     */
089    public MutableInterval(long startInstant, long endInstant) {
090        super(startInstant, endInstant, null);
091    }
092
093    /**
094     * Constructs an interval from a start and end instant with a chronology.
095     * 
096     * @param chronology  the chronology to use, null is ISO default
097     * @param startInstant  start of this interval, as milliseconds from 1970-01-01T00:00:00Z.
098     * @param endInstant  end of this interval, as milliseconds from 1970-01-01T00:00:00Z.
099     * @throws IllegalArgumentException if the end is before the start
100     */
101    public MutableInterval(long startInstant, long endInstant, Chronology chronology) {
102        super(startInstant, endInstant, chronology);
103    }
104
105    /**
106     * Constructs an interval from a start and end instant.
107     * <p>
108     * The chronology used is that of the start instant.
109     * 
110     * @param start  start of this interval, null means now
111     * @param end  end of this interval, null means now
112     * @throws IllegalArgumentException if the end is before the start
113     */
114    public MutableInterval(ReadableInstant start, ReadableInstant end) {
115        super(start, end);
116    }
117
118    /**
119     * Constructs an interval from a start instant and a duration.
120     * 
121     * @param start  start of this interval, null means now
122     * @param duration  the duration of this interval, null means zero length
123     * @throws IllegalArgumentException if the end is before the start
124     * @throws ArithmeticException if the end instant exceeds the capacity of a long
125     */
126    public MutableInterval(ReadableInstant start, ReadableDuration duration) {
127        super(start, duration);
128    }
129
130    /**
131     * Constructs an interval from a millisecond duration and an end instant.
132     * 
133     * @param duration  the duration of this interval, null means zero length
134     * @param end  end of this interval, null means now
135     * @throws IllegalArgumentException if the end is before the start
136     * @throws ArithmeticException if the start instant exceeds the capacity of a long
137     */
138    public MutableInterval(ReadableDuration duration, ReadableInstant end) {
139        super(duration, end);
140    }
141
142    /**
143     * Constructs an interval from a start instant and a time period.
144     * <p>
145     * When forming the interval, the chronology from the instant is used
146     * if present, otherwise the chronology of the period is used.
147     * 
148     * @param start  start of this interval, null means now
149     * @param period  the period of this interval, null means zero length
150     * @throws IllegalArgumentException if the end is before the start
151     * @throws ArithmeticException if the end instant exceeds the capacity of a long
152     */
153    public MutableInterval(ReadableInstant start, ReadablePeriod period) {
154        super(start, period);
155    }
156
157    /**
158     * Constructs an interval from a time period and an end instant.
159     * <p>
160     * When forming the interval, the chronology from the instant is used
161     * if present, otherwise the chronology of the period is used.
162     * 
163     * @param period  the period of this interval, null means zero length
164     * @param end  end of this interval, null means now
165     * @throws IllegalArgumentException if the end is before the start
166     * @throws ArithmeticException if the start instant exceeds the capacity of a long
167     */
168    public MutableInterval(ReadablePeriod period, ReadableInstant end) {
169        super(period, end);
170    }
171
172    /**
173     * Constructs a time interval by converting or copying from another object.
174     * <p>
175     * The recognised object types are defined in
176     * {@link org.joda.time.convert.ConverterManager ConverterManager} and
177     * include ReadableInterval and String.
178     * The String formats are described by {@link ISODateTimeFormat#dateTimeParser()}
179     * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime',
180     * 'datetime/period' or 'period/datetime'.
181     * 
182     * @param interval  the time interval to copy
183     * @throws IllegalArgumentException if the interval is invalid
184     */
185    public MutableInterval(Object interval) {
186        super(interval, null);
187    }
188
189    /**
190     * Constructs a time interval by converting or copying from another object,
191     * overriding the chronology.
192     * <p>
193     * The recognised object types are defined in
194     * {@link org.joda.time.convert.ConverterManager ConverterManager} and
195     * include ReadableInterval and String.
196     * The String formats are described by {@link ISODateTimeFormat#dateTimeParser()}
197     * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime',
198     * 'datetime/period' or 'period/datetime'.
199     * 
200     * @param interval  the time interval to copy
201     * @param chronology  the chronology to use, null means ISO default
202     * @throws IllegalArgumentException if the interval is invalid
203     */
204    public MutableInterval(Object interval, Chronology chronology) {
205        super(interval, chronology);
206    }
207
208    //-----------------------------------------------------------------------
209    /**
210     * Sets this interval from two millisecond instants retaining the chronology.
211     *
212     * @param startInstant  the start of the time interval
213     * @param endInstant  the start of the time interval
214     * @throws IllegalArgumentException if the end is before the start
215     */
216    public void setInterval(long startInstant, long endInstant) {
217        super.setInterval(startInstant, endInstant, getChronology());
218    }
219
220    /**
221     * Sets this interval to be the same as another.
222     *
223     * @param interval  the interval to copy
224     * @throws IllegalArgumentException if the interval is null
225     */
226    public void setInterval(ReadableInterval interval) {
227        if (interval == null) {
228            throw new IllegalArgumentException("Interval must not be null");
229        }
230        long startMillis = interval.getStartMillis();
231        long endMillis = interval.getEndMillis();
232        Chronology chrono = interval.getChronology();
233        super.setInterval(startMillis, endMillis, chrono);
234    }
235
236    /**
237     * Sets this interval from two instants, replacing the chronology with
238     * that from the start instant.
239     *
240     * @param start  the start of the time interval
241     * @param end  the start of the time interval
242     * @throws IllegalArgumentException if the end is before the start
243     */
244    public void setInterval(ReadableInstant start, ReadableInstant end) {
245        if (start == null && end == null) {
246            long now = DateTimeUtils.currentTimeMillis();
247            setInterval(now, now);
248        } else {
249            long startMillis = DateTimeUtils.getInstantMillis(start);
250            long endMillis = DateTimeUtils.getInstantMillis(end);
251            Chronology chrono = DateTimeUtils.getInstantChronology(start);
252            super.setInterval(startMillis, endMillis, chrono);
253        }
254    }
255
256    //-----------------------------------------------------------------------
257    /**
258     * Sets the chronology of this time interval.
259     *
260     * @param chrono  the chronology to use, null means ISO default
261     */
262    public void setChronology(Chronology chrono) {
263        super.setInterval(getStartMillis(), getEndMillis(), chrono);
264    }
265
266    /**
267     * Sets the start of this time interval.
268     *
269     * @param startInstant  the start of the time interval,
270     *  millisecond instant from 1970-01-01T00:00:00Z
271     * @throws IllegalArgumentException if the end is before the start
272     */
273    public void setStartMillis(long startInstant) {
274        super.setInterval(startInstant, getEndMillis(), getChronology());
275    }
276
277    /**
278     * Sets the start of this time interval as an Instant.
279     *
280     * @param start  the start of the time interval, null means now
281     * @throws IllegalArgumentException if the end is before the start
282     */
283    public void setStart(ReadableInstant start) {
284        long startMillis = DateTimeUtils.getInstantMillis(start);
285        super.setInterval(startMillis, getEndMillis(), getChronology());
286    }
287
288    /** 
289     * Sets the end of this time interval.
290     *
291     * @param endInstant  the end of the time interval,
292     *  millisecond instant from 1970-01-01T00:00:00Z
293     * @throws IllegalArgumentException if the end is before the start
294     */
295    public void setEndMillis(long endInstant) {
296        super.setInterval(getStartMillis(), endInstant, getChronology());
297    }
298
299    /** 
300     * Sets the end of this time interval as an Instant.
301     *
302     * @param end  the end of the time interval, null means now
303     * @throws IllegalArgumentException if the end is before the start
304     */
305    public void setEnd(ReadableInstant end) {
306        long endMillis = DateTimeUtils.getInstantMillis(end);
307        super.setInterval(getStartMillis(), endMillis, getChronology());
308    }
309
310    //-----------------------------------------------------------------------
311    /**
312     * Sets the duration of this time interval, preserving the start instant.
313     *
314     * @param duration  new duration for interval
315     * @throws IllegalArgumentException if the end is before the start
316     * @throws ArithmeticException if the end instant exceeds the capacity of a long
317     */
318    public void setDurationAfterStart(long duration) {
319        setEndMillis(FieldUtils.safeAdd(getStartMillis(), duration));
320    }
321
322    /**
323     * Sets the duration of this time interval, preserving the end instant.
324     *
325     * @param duration  new duration for interval
326     * @throws IllegalArgumentException if the end is before the start
327     * @throws ArithmeticException if the start instant exceeds the capacity of a long
328     */
329    public void setDurationBeforeEnd(long duration) {
330        setStartMillis(FieldUtils.safeAdd(getEndMillis(), -duration));
331    }
332
333    //-----------------------------------------------------------------------
334    /**
335     * Sets the duration of this time interval, preserving the start instant.
336     *
337     * @param duration  new duration for interval, null means zero length
338     * @throws IllegalArgumentException if the end is before the start
339     * @throws ArithmeticException if the end instant exceeds the capacity of a long
340     */
341    public void setDurationAfterStart(ReadableDuration duration) {
342        long durationMillis = DateTimeUtils.getDurationMillis(duration);
343        setEndMillis(FieldUtils.safeAdd(getStartMillis(), durationMillis));
344    }
345
346    /**
347     * Sets the duration of this time interval, preserving the end instant.
348     *
349     * @param duration  new duration for interval, null means zero length
350     * @throws IllegalArgumentException if the end is before the start
351     * @throws ArithmeticException if the start instant exceeds the capacity of a long
352     */
353    public void setDurationBeforeEnd(ReadableDuration duration) {
354        long durationMillis = DateTimeUtils.getDurationMillis(duration);
355        setStartMillis(FieldUtils.safeAdd(getEndMillis(), -durationMillis));
356    }
357
358    //-----------------------------------------------------------------------
359    /**
360     * Sets the period of this time interval, preserving the start instant
361     * and using the ISOChronology in the default zone for calculations.
362     *
363     * @param period  new period for interval, null means zero length
364     * @throws IllegalArgumentException if the end is before the start
365     * @throws ArithmeticException if the end instant exceeds the capacity of a long
366     */
367    public void setPeriodAfterStart(ReadablePeriod period) {
368        if (period == null) {
369            setEndMillis(getStartMillis());
370        } else {
371            setEndMillis(getChronology().add(period, getStartMillis(), 1));
372        }
373    }
374
375    /**
376     * Sets the period of this time interval, preserving the end instant
377     * and using the ISOChronology in the default zone for calculations.
378     *
379     * @param period  new period for interval, null means zero length
380     * @throws IllegalArgumentException if the end is before the start
381     * @throws ArithmeticException if the start instant exceeds the capacity of a long
382     */
383    public void setPeriodBeforeEnd(ReadablePeriod period) {
384        if (period == null) {
385            setStartMillis(getEndMillis());
386        } else {
387            setStartMillis(getChronology().add(period, getEndMillis(), -1));
388        }
389    }
390
391    //-----------------------------------------------------------------------
392    /**
393     * Clone this object without having to cast the returned object.
394     *
395     * @return a clone of the this object.
396     */
397    public MutableInterval copy() {
398        return (MutableInterval) clone();
399    }
400
401    /**
402     * Clone this object.
403     *
404     * @return a clone of this object.
405     */
406    public Object clone() {
407        try {
408            return super.clone();
409        } catch (CloneNotSupportedException ex) {
410            throw new InternalError("Clone error");
411        }
412    }
413
414}