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 * Millisecond.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 * 19-Dec-2001 : Added new constructors as suggested by Paul English (DG);
039 * 26-Feb-2002 : Added new getStart() and getEnd() methods (DG);
040 * 29-Mar-2002 : Fixed bug in getStart(), getEnd() and compareTo() methods (DG);
041 * 10-Sep-2002 : Added getSerialIndex() method (DG);
042 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
043 * 10-Jan-2003 : Changed base class and method names (DG);
044 * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented 
045 *               Serializable (DG);
046 * 21-Oct-2003 : Added hashCode() method (DG);
047 * ------------- JFREECHART 1.0.x ---------------------------------------------
048 * 05-Oct-2006 : Updated API docs (DG);
049 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
050 * 04-Apr-2007 : In Millisecond(Date, TimeZone), peg milliseconds to the
051 *               specified zone (DG);
052 *
053 */
054
055package org.jfree.data.time;
056
057import java.io.Serializable;
058import java.util.Calendar;
059import java.util.Date;
060import java.util.TimeZone;
061
062/**
063 * Represents a millisecond.  This class is immutable, which is a requirement 
064 * for all {@link RegularTimePeriod} subclasses.
065 */
066public class Millisecond extends RegularTimePeriod implements Serializable {
067
068    /** For serialization. */
069    static final long serialVersionUID = -5316836467277638485L;
070    
071    /** A constant for the first millisecond in a second. */
072    public static final int FIRST_MILLISECOND_IN_SECOND = 0;
073
074    /** A constant for the last millisecond in a second. */
075    public static final int LAST_MILLISECOND_IN_SECOND = 999;
076
077    /** The day. */
078    private Day day;
079    
080    /** The hour in the day. */
081    private byte hour;
082    
083    /** The minute. */
084    private byte minute;
085
086    /** The second. */
087    private byte second;
088
089    /** The millisecond. */
090    private int millisecond;
091
092    /**
093     * The pegged millisecond. 
094     */
095    private long firstMillisecond;
096    
097    /**
098     * Constructs a millisecond based on the current system time.
099     */
100    public Millisecond() {
101        this(new Date());
102    }
103
104    /**
105     * Constructs a millisecond.
106     *
107     * @param millisecond  the millisecond (0-999).
108     * @param second  the second.
109     */
110    public Millisecond(int millisecond, Second second) {
111        this.millisecond = millisecond;
112        this.second = (byte) second.getSecond();
113        this.minute = (byte) second.getMinute().getMinute();
114        this.hour = (byte) second.getMinute().getHourValue();
115        this.day = second.getMinute().getDay();
116        peg(Calendar.getInstance());
117    }
118
119    /**
120     * Creates a new millisecond.
121     * 
122     * @param millisecond  the millisecond (0-999).
123     * @param second  the second (0-59).
124     * @param minute  the minute (0-59).
125     * @param hour  the hour (0-23).
126     * @param day  the day (1-31).
127     * @param month  the month (1-12).
128     * @param year  the year (1900-9999).
129     */    
130    public Millisecond(int millisecond, int second, int minute, int hour,
131                       int day, int month, int year) {
132                           
133        this(millisecond, new Second(second, minute, hour, day, month, year));
134    
135    }
136
137    /**
138     * Constructs a millisecond.
139     *
140     * @param time  the time.
141     */
142    public Millisecond(Date time) {
143        this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
144    }
145
146    /**
147     * Creates a millisecond.
148     *
149     * @param time  the instant in time.
150     * @param zone  the time zone.
151     */
152    public Millisecond(Date time, TimeZone zone) {
153        Calendar calendar = Calendar.getInstance(zone);
154        calendar.setTime(time);
155        this.millisecond = calendar.get(Calendar.MILLISECOND);
156        this.second = (byte) calendar.get(Calendar.SECOND);
157        this.minute = (byte) calendar.get(Calendar.MINUTE);
158        this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY);
159        this.day = new Day(time, zone);
160        peg(calendar);
161    }
162
163    /**
164     * Returns the second.
165     *
166     * @return The second.
167     */
168    public Second getSecond() {
169        return new Second(this.second, this.minute, this.hour, 
170                this.day.getDayOfMonth(), this.day.getMonth(), 
171                this.day.getYear());
172    }
173
174    /**
175     * Returns the millisecond.
176     *
177     * @return The millisecond.
178     */
179    public long getMillisecond() {
180        return this.millisecond;
181    }
182
183    /**
184     * Returns the first millisecond of the second.  This will be determined 
185     * relative to the time zone specified in the constructor, or in the 
186     * calendar instance passed in the most recent call to the 
187     * {@link #peg(Calendar)} method.
188     *
189     * @return The first millisecond of the second.
190     * 
191     * @see #getLastMillisecond()
192     */
193    public long getFirstMillisecond() {
194        return this.firstMillisecond;
195    }
196
197    /**
198     * Returns the last millisecond of the second.  This will be 
199     * determined relative to the time zone specified in the constructor, or
200     * in the calendar instance passed in the most recent call to the 
201     * {@link #peg(Calendar)} method.
202     *
203     * @return The last millisecond of the second.
204     * 
205     * @see #getFirstMillisecond()
206     */
207    public long getLastMillisecond() {
208        return this.firstMillisecond;
209    }
210    
211    /** 
212     * Recalculates the start date/time and end date/time for this time period 
213     * relative to the supplied calendar (which incorporates a time zone).
214     * 
215     * @param calendar  the calendar (<code>null</code> not permitted).
216     * 
217     * @since 1.0.3
218     */
219    public void peg(Calendar calendar) {
220        this.firstMillisecond = getFirstMillisecond(calendar);
221    }
222
223    /**
224     * Returns the millisecond preceding this one.
225     *
226     * @return The millisecond preceding this one.
227     */
228    public RegularTimePeriod previous() {
229
230        RegularTimePeriod result = null;
231
232        if (this.millisecond != FIRST_MILLISECOND_IN_SECOND) {
233            result = new Millisecond(this.millisecond - 1, getSecond());
234        }
235        else {
236            Second previous = (Second) getSecond().previous();
237            if (previous != null) {
238                result = new Millisecond(LAST_MILLISECOND_IN_SECOND, previous);
239            }
240        }
241        return result;
242
243    }
244
245    /**
246     * Returns the millisecond following this one.
247     *
248     * @return The millisecond following this one.
249     */
250    public RegularTimePeriod next() {
251
252        RegularTimePeriod result = null;
253        if (this.millisecond != LAST_MILLISECOND_IN_SECOND) {
254            result = new Millisecond(this.millisecond + 1, getSecond());
255        }
256        else {
257            Second next = (Second) getSecond().next();
258            if (next != null) {
259                result = new Millisecond(FIRST_MILLISECOND_IN_SECOND, next);
260            }
261        }
262        return result;
263
264    }
265
266    /**
267     * Returns a serial index number for the millisecond.
268     *
269     * @return The serial index number.
270     */
271    public long getSerialIndex() {
272        long hourIndex = this.day.getSerialIndex() * 24L + this.hour;
273        long minuteIndex = hourIndex * 60L + this.minute;
274        long secondIndex = minuteIndex * 60L + this.second;
275        return secondIndex * 1000L + this.millisecond;
276    }
277
278    /**
279     * Tests the equality of this object against an arbitrary Object.
280     * <P>
281     * This method will return true ONLY if the object is a Millisecond object
282     * representing the same millisecond as this instance.
283     *
284     * @param obj  the object to compare
285     *
286     * @return <code>true</code> if milliseconds and seconds of this and object
287     *      are the same.
288     */
289    public boolean equals(Object obj) {
290        if (obj == this) {
291            return true;
292        }
293        if (!(obj instanceof Millisecond)) {
294            return false;
295        }
296        Millisecond that = (Millisecond) obj;
297        if (this.millisecond != that.millisecond) {
298            return false;
299        }
300        if (this.second != that.second) {
301            return false;
302        }
303        if (this.minute != that.minute) {
304            return false;
305        }
306        if (this.hour != that.hour) {
307            return false;
308        }
309        if (!this.day.equals(that.day)) {
310            return false;
311        }
312        return true;
313    }
314
315    /**
316     * Returns a hash code for this object instance.  The approach described by 
317     * Joshua Bloch in "Effective Java" has been used here:
318     * <p>
319     * <code>http://developer.java.sun.com/developer/Books/effectivejava
320     * /Chapter3.pdf</code>
321     * 
322     * @return A hashcode.
323     */
324    public int hashCode() {
325        int result = 17;
326        result = 37 * result + this.millisecond;
327        result = 37 * result + getSecond().hashCode();
328        return result;
329    }
330
331    /**
332     * Returns an integer indicating the order of this Millisecond object
333     * relative to the specified object:
334     *
335     * negative == before, zero == same, positive == after.
336     *
337     * @param obj  the object to compare
338     *
339     * @return negative == before, zero == same, positive == after.
340     */
341    public int compareTo(Object obj) {
342
343        int result;
344        long difference;
345
346        // CASE 1 : Comparing to another Second object
347        // -------------------------------------------
348        if (obj instanceof Millisecond) {
349            Millisecond ms = (Millisecond) obj;
350            difference = getFirstMillisecond() - ms.getFirstMillisecond();
351            if (difference > 0) {
352                result = 1;
353            }
354            else {
355                if (difference < 0) {
356                    result = -1;
357                }
358                else {
359                    result = 0;
360                }
361            }
362        }
363
364        // CASE 2 : Comparing to another TimePeriod object
365        // -----------------------------------------------
366        else if (obj instanceof RegularTimePeriod) {
367            // more difficult case - evaluate later...
368            result = 0;
369        }
370
371        // CASE 3 : Comparing to a non-TimePeriod object
372        // ---------------------------------------------
373        else {
374            // consider time periods to be ordered after general objects
375            result = 1;
376        }
377
378        return result;
379
380    }
381
382    /**
383     * Returns the first millisecond of the time period.
384     *
385     * @param calendar  the calendar (<code>null</code> not permitted).
386     *
387     * @return The first millisecond of the time period.
388     *
389     * @throws NullPointerException if <code>calendar</code> is 
390     *     <code>null</code>.
391     */
392    public long getFirstMillisecond(Calendar calendar) {
393        int year = this.day.getYear();
394        int month = this.day.getMonth() - 1;
395        int day = this.day.getDayOfMonth();
396        calendar.clear();
397        calendar.set(year, month, day, this.hour, this.minute, this.second);
398        calendar.set(Calendar.MILLISECOND, this.millisecond);
399        //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
400        return calendar.getTime().getTime();
401    }
402
403    /**
404     * Returns the last millisecond of the time period.
405     *
406     * @param calendar  the calendar (<code>null</code> not permitted).
407     *
408     * @return The last millisecond of the time period.
409     *
410     * @throws NullPointerException if <code>calendar</code> is 
411     *     <code>null</code>.
412     */
413    public long getLastMillisecond(Calendar calendar) {
414        return getFirstMillisecond(calendar);
415    }
416
417}