001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it 
010 * under the terms of the GNU Lesser General Public License as published by 
011 * the Free Software Foundation; either version 2.1 of the License, or 
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but 
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022 * USA.  
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 *
027 * ----------------------
028 * RegularTimePeriod.java
029 * ----------------------
030 * (C) Copyright 2001-2007, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes
036 * -------
037 * 11-Oct-2001 : Version 1 (DG);
038 * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 
039 *               evaluate with reference to a particular time zone (DG);
040 * 29-May-2002 : Implemented MonthConstants interface, so that these constants 
041 *               are conveniently available (DG);
042 * 10-Sep-2002 : Added getSerialIndex() method (DG);
043 * 10-Jan-2003 : Renamed TimePeriod --> RegularTimePeriod (DG);
044 * 13-Mar-2003 : Moved to com.jrefinery.data.time package (DG);
045 * 29-Apr-2004 : Changed getMiddleMillisecond() methods to fix bug 943985 (DG);
046 * 25-Nov-2004 : Added utility methods (DG);
047 * ------------- JFREECHART 1.0.x ---------------------------------------------
048 * 06-Oct-2006 : Deprecated the WORKING_CALENDAR field and several methods,
049 *               added new peg() method (DG);
050 *
051 */
052
053package org.jfree.data.time;
054
055import java.lang.reflect.Constructor;
056import java.util.Calendar;
057import java.util.Date;
058import java.util.TimeZone;
059
060import org.jfree.date.MonthConstants;
061
062/**
063 * An abstract class representing a unit of time.  Convenient methods are 
064 * provided for calculating the next and previous time periods.  Conversion 
065 * methods are defined that return the first and last milliseconds of the time 
066 * period.  The results from these methods are timezone dependent.
067 * <P>
068 * This class is immutable, and all subclasses should be immutable also.
069 */
070public abstract class RegularTimePeriod implements TimePeriod, Comparable, 
071                                                   MonthConstants {
072
073    /**
074     * Creates a time period that includes the specified millisecond, assuming 
075     * the given time zone.
076     * 
077     * @param c  the time period class.
078     * @param millisecond  the time.
079     * @param zone  the time zone.
080     * 
081     * @return The time period.
082     */
083    public static RegularTimePeriod createInstance(Class c, Date millisecond, 
084                                                   TimeZone zone) {
085        RegularTimePeriod result = null;
086        try {
087            Constructor constructor = c.getDeclaredConstructor(
088                    new Class[] {Date.class, TimeZone.class});
089            result = (RegularTimePeriod) constructor.newInstance(
090                    new Object[] {millisecond, zone});
091        }
092        catch (Exception e) {
093            // do nothing, so null is returned            
094        }
095        return result;  
096    }
097    
098    /**
099     * Returns a subclass of {@link RegularTimePeriod} that is smaller than
100     * the specified class.
101     * 
102     * @param c  a subclass of {@link RegularTimePeriod}.
103     * 
104     * @return A class.
105     */
106    public static Class downsize(Class c) {
107        if (c.equals(Year.class)) {
108            return Quarter.class;
109        }
110        else if (c.equals(Quarter.class)) {
111            return Month.class;
112        }
113        else if (c.equals(Month.class)) {
114            return Day.class;
115        }
116        else if (c.equals(Day.class)) {
117            return Hour.class;
118        }
119        else if (c.equals(Hour.class)) {
120            return Minute.class;
121        }
122        else if (c.equals(Minute.class)) {
123            return Second.class;
124        }
125        else if (c.equals(Second.class)) {
126            return Millisecond.class;
127        }
128        else {
129            return Millisecond.class;
130        }
131    }
132    
133    /**
134     * Returns the time period preceding this one, or <code>null</code> if some
135     * lower limit has been reached.
136     *
137     * @return The previous time period (possibly <code>null</code>).
138     */
139    public abstract RegularTimePeriod previous();
140
141    /**
142     * Returns the time period following this one, or <code>null</code> if some
143     * limit has been reached.
144     *
145     * @return The next time period (possibly <code>null</code>).
146     */
147    public abstract RegularTimePeriod next();
148
149    /**
150     * Returns a serial index number for the time unit.
151     *
152     * @return The serial index number.
153     */
154    public abstract long getSerialIndex();
155
156    //////////////////////////////////////////////////////////////////////////
157
158    /** 
159     * The default time zone. 
160     */
161    public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
162
163    /** 
164     * A working calendar (recycle to avoid unnecessary object creation). 
165     * 
166     * @deprecated This was a bad idea, don't use it!
167     */
168    public static final Calendar WORKING_CALENDAR 
169        = Calendar.getInstance(DEFAULT_TIME_ZONE);
170
171    /** 
172     * Recalculates the start date/time and end date/time for this time period 
173     * relative to the supplied calendar (which incorporates a time zone).
174     * 
175     * @param calendar  the calendar (<code>null</code> not permitted).
176     * 
177     * @since 1.0.3
178     */
179    public abstract void peg(Calendar calendar);
180    
181    /**
182     * Returns the date/time that marks the start of the time period.  This 
183     * method returns a new <code>Date</code> instance every time it is called.
184     *
185     * @return The start date/time.
186     * 
187     * @see #getFirstMillisecond()
188     */
189    public Date getStart() {
190        return new Date(getFirstMillisecond());
191    }
192
193    /**
194     * Returns the date/time that marks the end of the time period.  This 
195     * method returns a new <code>Date</code> instance every time it is called.
196     *
197     * @return The end date/time.
198     * 
199     * @see #getLastMillisecond()
200     */
201    public Date getEnd() {
202        return new Date(getLastMillisecond());
203    }
204
205    /**
206     * Returns the first millisecond of the time period.  This will be 
207     * determined relative to the time zone specified in the constructor, or
208     * in the calendar instance passed in the most recent call to the 
209     * {@link #peg(Calendar)} method.
210     *
211     * @return The first millisecond of the time period.
212     * 
213     * @see #getLastMillisecond()
214     */
215    public abstract long getFirstMillisecond();
216
217    /**
218     * Returns the first millisecond of the time period, evaluated within a 
219     * specific time zone.
220     *
221     * @param zone  the time zone (<code>null</code> not permitted).
222     *
223     * @return The first millisecond of the time period.
224     * 
225     * @deprecated As of 1.0.3, you should avoid using this method (it creates
226     *     a new Calendar instance every time it is called).  You are advised
227     *     to call {@link #getFirstMillisecond(Calendar)} instead.
228     *     
229     * @see #getLastMillisecond(TimeZone)
230     */
231    public long getFirstMillisecond(TimeZone zone) {
232        Calendar calendar = Calendar.getInstance(zone);
233        return getFirstMillisecond(calendar);
234    }
235
236    /**
237     * Returns the first millisecond of the time period, evaluated using the 
238     * supplied calendar (which incorporates a timezone).
239     *
240     * @param calendar  the calendar (<code>null</code> not permitted).
241     *
242     * @return The first millisecond of the time period.
243     * 
244     * @throws NullPointerException if <code>calendar,/code> is 
245     *     </code>null</code>.
246     *     
247     * @see #getLastMillisecond(Calendar)
248     */
249    public abstract long getFirstMillisecond(Calendar calendar);
250
251    /**
252     * Returns the last millisecond of the time period.  This will be 
253     * determined relative to the time zone specified in the constructor, or
254     * in the calendar instance passed in the most recent call to the 
255     * {@link #peg(Calendar)} method.
256     *
257     * @return The last millisecond of the time period.
258     * 
259     * @see #getFirstMillisecond()
260     */
261    public abstract long getLastMillisecond();
262
263    /**
264     * Returns the last millisecond of the time period, evaluated within a 
265     * specific time zone.
266     *
267     * @param zone  the time zone (<code>null</code> not permitted).
268     *
269     * @return The last millisecond of the time period.
270     * 
271     * @deprecated As of 1.0.3, you should avoid using this method (it creates
272     *     a new Calendar instance every time it is called).  You are advised
273     *     to call {@link #getLastMillisecond(Calendar)} instead.
274     *     
275     * @see #getFirstMillisecond(TimeZone)
276     */
277    public long getLastMillisecond(TimeZone zone) {
278        Calendar calendar = Calendar.getInstance(zone);
279        return getLastMillisecond(calendar);
280    }
281
282    /**
283     * Returns the last millisecond of the time period, evaluated using the 
284     * supplied calendar (which incorporates a timezone).
285     *
286     * @param calendar  the calendar (<code>null</code> not permitted).
287     *
288     * @return The last millisecond of the time period.
289     * 
290     * @see #getFirstMillisecond(Calendar)
291     */
292    public abstract long getLastMillisecond(Calendar calendar);
293
294    /**
295     * Returns the millisecond closest to the middle of the time period.
296     *
297     * @return The middle millisecond.
298     */
299    public long getMiddleMillisecond() {
300        long m1 = getFirstMillisecond();
301        long m2 = getLastMillisecond();
302        return m1 + (m2 - m1) / 2;
303    }
304
305    /**
306     * Returns the millisecond closest to the middle of the time period,
307     * evaluated within a specific time zone.
308     *
309     * @param zone  the time zone (<code>null</code> not permitted).
310     *
311     * @return The middle millisecond.
312     * 
313     * @deprecated As of 1.0.3, you should avoid using this method (it creates
314     *     a new Calendar instance every time it is called).  You are advised
315     *     to call {@link #getMiddleMillisecond(Calendar)} instead.
316     */
317    public long getMiddleMillisecond(TimeZone zone) {
318        Calendar calendar = Calendar.getInstance(zone);
319        long m1 = getFirstMillisecond(calendar);
320        long m2 = getLastMillisecond(calendar);
321        return m1 + (m2 - m1) / 2;
322    }
323
324    /**
325     * Returns the millisecond closest to the middle of the time period,
326     * evaluated using the supplied calendar (which incorporates a timezone).
327     *
328     * @param calendar  the calendar.
329     *
330     * @return The middle millisecond.
331     */
332    public long getMiddleMillisecond(Calendar calendar) {
333        long m1 = getFirstMillisecond(calendar);
334        long m2 = getLastMillisecond(calendar);
335        return m1 + (m2 - m1) / 2;
336    }
337
338    /**
339     * Returns a string representation of the time period.
340     *
341     * @return The string.
342     */
343    public String toString() {
344        return String.valueOf(getStart());
345    }
346
347}