    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     * DateTickUnit.java
    029     * -----------------
    030     * (C) Copyright 2000-2007, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Chris Boek;
    034     *
    035     * Changes
    036     * -------
    037     * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
    038     * 27-Nov-2002 : Added IllegalArgumentException to getMillisecondCount()
    039     *               method (DG);
    040     * 26-Mar-2003 : Implemented Serializable (DG);
    041     * 12-Nov-2003 : Added roll fields that can improve the labelling on segmented
    042     *               date axes (DG);
    043     * 03-Dec-2003 : DateFormat constructor argument is now filled with an default
    044     *               if null (TM);
    045     * 07-Dec-2003 : Fixed bug (null pointer exception) in constructor (DG);
    046     * ------------- JFREECHART 1.0.x ---------------------------------------------
    047     * 21-Mar-2007 : Added toString() for debugging (DG);
    048     * 04-Apr-2007 : Added new methods addToDate(Date, TimeZone) and rollDate(Date,
    049     *               TimeZone) (CB);
    050     *
    051     */
    052
    053    package org.jfree.chart.axis;
    054
    055    import java.io.Serializable;
    056    import java.text.DateFormat;
    057    import java.util.Calendar;
    058    import java.util.Date;
    059    import java.util.TimeZone;
    060
    061    import org.jfree.util.ObjectUtilities;
    062
    063    /**
    064     * A tick unit for use by subclasses of {@link DateAxis}. Instances of this
    065     * class are immutable.
    066     */
    067    public class DateTickUnit extends TickUnit implements Serializable {
    068
    069        /** For serialization. */
    070        private static final long serialVersionUID = -7289292157229621901L;
    071
    072        /** A constant for years. */
    073        public static final int YEAR = 0;
    074
    075        /** A constant for months. */
    076        public static final int MONTH = 1;
    077
    078        /** A constant for days. */
    079        public static final int DAY = 2;
    080
    081        /** A constant for hours. */
    082        public static final int HOUR = 3;
    083
    084        /** A constant for minutes. */
    085        public static final int MINUTE = 4;
    086
    087        /** A constant for seconds. */
    088        public static final int SECOND = 5;
    089
    090        /** A constant for milliseconds. */
    091        public static final int MILLISECOND = 6;
    092
    093        /** The unit. */
    094        private int unit;
    095
    096        /** The unit count. */
    097        private int count;
    098
    099        /** The roll unit. */
    100        private int rollUnit;
    101
    102        /** The roll count. */
    103        private int rollCount;
    104
    105        /** The date formatter. */
    106        private DateFormat formatter;
    107
    108        /**
    109         * Creates a new date tick unit.  The dates will be formatted using a
    110         * SHORT format for the default locale.
    111         *
    112         * @param unit  the unit.
    113         * @param count  the unit count.
    114         */
    115        public DateTickUnit(int unit, int count) {
    116            this(unit, count, null);
    117        }
    118
    119        /**
    120         * Creates a new date tick unit.  You can specify the units using one of
    121         * the constants YEAR, MONTH, DAY, HOUR, MINUTE, SECOND or MILLISECOND.
    122         * In addition, you can specify a unit count, and a date format.
    123         *
    124         * @param unit  the unit.
    125         * @param count  the unit count.
    126         * @param formatter  the date formatter (defaults to DateFormat.SHORT).
    127         */
    128        public DateTickUnit(int unit, int count, DateFormat formatter) {
    129
    130            this(unit, count, unit, count, formatter);
    131
    132        }
    133
    134        /**
    135         * Creates a new unit.
    136         *
    137         * @param unit  the unit.
    138         * @param count  the count.
    139         * @param rollUnit  the roll unit.
    140         * @param rollCount  the roll count.
    141         * @param formatter  the date formatter (defaults to DateFormat.SHORT).
    142         */
    143        public DateTickUnit(int unit, int count, int rollUnit, int rollCount,
    144                            DateFormat formatter) {
    145            super(DateTickUnit.getMillisecondCount(unit, count));
    146            this.unit = unit;
    147            this.count = count;
    148            this.rollUnit = rollUnit;
    149            this.rollCount = rollCount;
    150            this.formatter = formatter;
    151            if (formatter == null) {
    152                this.formatter = DateFormat.getDateInstance(DateFormat.SHORT);
    153            }
    154        }
    155
    156        /**
    157         * Returns the date unit.  This will be one of the constants
    158         * <code>YEAR</code>, <code>MONTH</code>, <code>DAY</code>,
    159         * <code>HOUR</code>, <code>MINUTE</code>, <code>SECOND</code> or
    160         * <code>MILLISECOND</code>, defined by this class.  Note that these
    161         * constants do NOT correspond to those defined in Java's
    162         * <code>Calendar</code> class.
    163         *
    164         * @return The date unit.
    165         */
    166        public int getUnit() {
    167            return this.unit;
    168        }
    169
    170        /**
    171         * Returns the unit count.
    172         *
    173         * @return The unit count.
    174         */
    175        public int getCount() {
    176            return this.count;
    177        }
    178
    179        /**
    180         * Returns the roll unit.  This is the amount by which the tick advances if
    181         * it is "hidden" when displayed on a segmented date axis.  Typically the
    182         * roll will be smaller than the regular tick unit (for example, a 7 day
    183         * tick unit might use a 1 day roll).
    184         *
    185         * @return The roll unit.
    186         */
    187        public int getRollUnit() {
    188            return this.rollUnit;
    189        }
    190
    191        /**
    192         * Returns the roll count.
    193         *
    194         * @return The roll count.
    195         */
    196        public int getRollCount() {
    197            return this.rollCount;
    198        }
    199
    200        /**
    201         * Formats a value.
    202         *
    203         * @param milliseconds  date in milliseconds since 01-01-1970.
    204         *
    205         * @return The formatted date.
    206         */
    207        public String valueToString(double milliseconds) {
    208            return this.formatter.format(new Date((long) milliseconds));
    209        }
    210
    211        /**
    212         * Formats a date using the tick unit's formatter.
    213         *
    214         * @param date  the date.
    215         *
    216         * @return The formatted date.
    217         */
    218        public String dateToString(Date date) {
    219            return this.formatter.format(date);
    220        }
    221
    222        /**
    223         * Calculates a new date by adding this unit to the base date.
    224         *
    225         * @param base  the base date.
    226         *
    227         * @return A new date one unit after the base date.
    228         *
    229         * @see #addToDate(Date, TimeZone)
    230         */
    231        public Date addToDate(Date base) {
    232            Calendar calendar = Calendar.getInstance();
    233            calendar.setTime(base);
    234            calendar.add(getCalendarField(this.unit), this.count);
    235            return calendar.getTime();
    236        }
    237
    238        /**
    239         * Calculates a new date by adding this unit to the base date.
    240         *
    241         * @param base  the base date.
    242         * @param zone  the time zone for the date calculation.
    243         *
    244         * @return A new date one unit after the base date.
    245         *
    246         * @since 1.0.6
    247         * @see #addToDate(Date)
    248         */
    249        public Date addToDate(Date base, TimeZone zone) {
    250            Calendar calendar = Calendar.getInstance(zone);
    251            calendar.setTime(base);
    252            calendar.add(getCalendarField(this.unit), this.count);
    253            return calendar.getTime();
    254        }
    255
    256        /**
    257         * Rolls the date forward by the amount specified by the roll unit and
    258         * count.
    259         *
    260         * @param base  the base date.
    261
    262         * @return The rolled date.
    263         *
    264         * @see #rollDate(Date, TimeZone)
    265         */
    266        public Date rollDate(Date base) {
    267            Calendar calendar = Calendar.getInstance();
    268            calendar.setTime(base);
    269            calendar.add(getCalendarField(this.rollUnit), this.rollCount);
    270            return calendar.getTime();
    271        }
    272
    273        /**
    274         * Rolls the date forward by the amount specified by the roll unit and
    275         * count.
    276         *
    277         * @param base  the base date.
    278         * @param zone  the time zone.
    279         *
    280         * @return The rolled date.
    281         *
    282         * @since 1.0.6
    283         * @see #rollDate(Date)
    284         */
    285        public Date rollDate(Date base, TimeZone zone) {
    286            Calendar calendar = Calendar.getInstance(zone);
    287            calendar.setTime(base);
    288            calendar.add(getCalendarField(this.rollUnit), this.rollCount);
    289            return calendar.getTime();
    290        }
    291
    292        /**
    293         * Returns a field code that can be used with the <code>Calendar</code>
    294         * class.
    295         *
    296         * @return The field code.
    297         */
    298        public int getCalendarField() {
    299            return getCalendarField(this.unit);
    300        }
    301
    302        /**
    303         * Returns a field code (that can be used with the Calendar class) for a
    304         * given 'unit' code.  The 'unit' is one of:  {@link #YEAR}, {@link #MONTH},
    305         * {@link #DAY}, {@link #HOUR}, {@link #MINUTE}, {@link #SECOND} and
    306         * {@link #MILLISECOND}.
    307         *
    308         * @param tickUnit  the unit.
    309         *
    310         * @return The field code.
    311         */
    312        private int getCalendarField(int tickUnit) {
    313
    314            switch (tickUnit) {
    315                case (YEAR):
    316                    return Calendar.YEAR;
    317                case (MONTH):
    318                    return Calendar.MONTH;
    319                case (DAY):
    320                    return Calendar.DATE;
    321                case (HOUR):
    322                    return Calendar.HOUR_OF_DAY;
    323                case (MINUTE):
    324                    return Calendar.MINUTE;
    325                case (SECOND):
    326                    return Calendar.SECOND;
    327                case (MILLISECOND):
    328                    return Calendar.MILLISECOND;
    329                default:
    330                    return Calendar.MILLISECOND;
    331            }
    332
    333        }
    334
    335        /**
    336         * Returns the (approximate) number of milliseconds for the given unit and
    337         * unit count.
    338         * <P>
    339         * This value is an approximation some of the time (e.g. months are
    340         * assumed to have 31 days) but this shouldn't matter.
    341         *
    342         * @param unit  the unit.
    343         * @param count  the unit count.
    344         *
    345         * @return The number of milliseconds.
    346         */
    347        private static long getMillisecondCount(int unit, int count) {
    348
    349            switch (unit) {
    350                case (YEAR):
    351                    return (365L * 24L * 60L * 60L * 1000L) * count;
    352                case (MONTH):
    353                    return (31L * 24L * 60L * 60L * 1000L) * count;
    354                case (DAY):
    355                    return (24L * 60L * 60L * 1000L) * count;
    356                case (HOUR):
    357                    return (60L * 60L * 1000L) * count;
    358                case (MINUTE):
    359                    return (60L * 1000L) * count;
    360                case (SECOND):
    361                    return 1000L * count;
    362                case (MILLISECOND):
    363                    return count;
    364                default:
    365                    throw new IllegalArgumentException(
    366                        "DateTickUnit.getMillisecondCount() : unit must "
    367                        + "be one of the constants YEAR, MONTH, DAY, HOUR, MINUTE, "
    368                        + "SECOND or MILLISECOND defined in the DateTickUnit "
    369                        + "class. Do *not* use the constants defined in "
    370                        + "java.util.Calendar."
    371                    );
    372            }
    373
    374        }
    375
    376        /**
    377         * Tests this unit for equality with another object.
    378         *
    379         * @param obj  the object (<code>null</code> permitted).
    380         *
    381         * @return <code>true</code> or <code>false</code>.
    382         */
    383        public boolean equals(Object obj) {
    384            if (obj == this) {
    385                return true;
    386            }
    387            if (!(obj instanceof DateTickUnit)) {
    388                return false;
    389            }
    390            if (!super.equals(obj)) {
    391                return false;
    392            }
    393            DateTickUnit that = (DateTickUnit) obj;
    394            if (this.unit != that.unit) {
    395                return false;
    396            }
    397            if (this.count != that.count) {
    398                return false;
    399            }
    400            if (!ObjectUtilities.equal(this.formatter, that.formatter)) {
    401                return false;
    402            }
    403            return true;
    404        }
    405
    406        /**
    407         * Returns a hash code for this object.
    408         *
    409         * @return A hash code.
    410         */
    411        public int hashCode() {
    412            int result = 19;
    413            result = 37 * result + this.unit;
    414            result = 37 * result + this.count;
    415            result = 37 * result + this.formatter.hashCode();
    416            return result;
    417        }
    418
    419        /**
    420         * Strings for use by the toString() method.
    421         */
    422        private static final String[] units = {"YEAR", "MONTH", "DAY", "HOUR",
    423                "MINUTE", "SECOND", "MILLISECOND"};
    424
    425        /**
    426         * Returns a string representation of this instance, primarily used for
    427         * debugging purposes.
    428         *
    429         * @return A string representation of this instance.
    430         */
    431        public String toString() {
    432            return "DateTickUnit[" + DateTickUnit.units[this.unit] + ", "
    433                    + this.count + "]";
    434        }
    435
    436    }