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