001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2006, 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-2006, by Object Refinery Limited.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * $Id: Millisecond.java,v 1.5.2.3 2006/10/06 14:00:15 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 11-Oct-2001 : Version 1 (DG);
040     * 19-Dec-2001 : Added new constructors as suggested by Paul English (DG);
041     * 26-Feb-2002 : Added new getStart() and getEnd() methods (DG);
042     * 29-Mar-2002 : Fixed bug in getStart(), getEnd() and compareTo() methods (DG);
043     * 10-Sep-2002 : Added getSerialIndex() method (DG);
044     * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
045     * 10-Jan-2003 : Changed base class and method names (DG);
046     * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented 
047     *               Serializable (DG);
048     * 21-Oct-2003 : Added hashCode() method (DG);
049     * ------------- JFREECHART 1.0.x ---------------------------------------------
050     * 05-Oct-2006 : Updated API docs (DG);
051     * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
052     *
053     */
054    
055    package org.jfree.data.time;
056    
057    import java.io.Serializable;
058    import java.util.Calendar;
059    import java.util.Date;
060    import java.util.TimeZone;
061    
062    /**
063     * Represents a millisecond.  This class is immutable, which is a requirement 
064     * for all {@link RegularTimePeriod} subclasses.
065     */
066    public 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.getInstance());
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    }