    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2011, 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     * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025     * Other names may be trademarks of their respective owners.]
    026     *
    027     * -----------------
    028     * DateTickUnit.java
    029     * -----------------
    030     * (C) Copyright 2000-2009, 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     * 09-Jun-2008 : Deprecated addToDate(Date) (DG);
    051     * 09-Jan-2009 : Replaced the unit and rollUnit fields with an enumerated
    052     *               type (DG);
    053     *
    054     */
    055
    056    package org.jfree.chart.axis;
    057
    058    import java.io.Serializable;
    059    import java.text.DateFormat;
    060    import java.util.Calendar;
    061    import java.util.Date;
    062    import java.util.TimeZone;
    063
    064    import org.jfree.util.ObjectUtilities;
    065
    066    /**
    067     * A tick unit for use by subclasses of {@link DateAxis}.  Instances of this
    068     * class are immutable.
    069     */
    070    public class DateTickUnit extends TickUnit implements Serializable {
    071
    072        /** For serialization. */
    073        private static final long serialVersionUID = -7289292157229621901L;
    074
    075        /**
    076         * The units.
    077         *
    078         * @since 1.0.13
    079         */
    080        private DateTickUnitType unitType;
    081
    082        /** The unit count. */
    083        private int count;
    084
    085        /**
    086         * The roll unit type.
    087         *
    088         * @since 1.0.13
    089         */
    090        private DateTickUnitType rollUnitType;
    091
    092        /** The roll count. */
    093        private int rollCount;
    094
    095        /** The date formatter. */
    096        private DateFormat formatter;
    097
    098        /**
    099         * Creates a new date tick unit.
    100         *
    101         * @param unitType  the unit type (<code>null</code> not permitted).
    102         * @param multiple  the multiple (of the unit type, must be > 0).
    103         *
    104         * @since 1.0.13
    105         */
    106        public DateTickUnit(DateTickUnitType unitType, int multiple) {
    107            this(unitType, multiple, DateFormat.getDateInstance(DateFormat.SHORT));
    108        }
    109
    110        /**
    111         * Creates a new date tick unit.
    112         *
    113         * @param unitType  the unit type (<code>null</code> not permitted).
    114         * @param multiple  the multiple (of the unit type, must be > 0).
    115         * @param formatter  the date formatter (<code>null</code> not permitted).
    116         *
    117         * @since 1.0.13
    118         */
    119        public DateTickUnit(DateTickUnitType unitType, int multiple,
    120                DateFormat formatter) {
    121            this(unitType, multiple, unitType, multiple, formatter);
    122        }
    123
    124        /**
    125         * Creates a new unit.
    126         *
    127         * @param unitType  the unit.
    128         * @param multiple  the multiple.
    129         * @param rollUnitType  the roll unit.
    130         * @param rollMultiple  the roll multiple.
    131         * @param formatter  the date formatter (<code>null</code> not permitted).
    132         *
    133         * @since 1.0.13
    134         */
    135        public DateTickUnit(DateTickUnitType unitType, int multiple,
    136                DateTickUnitType rollUnitType, int rollMultiple,
    137                DateFormat formatter) {
    138            super(DateTickUnit.getMillisecondCount(unitType, multiple));
    139            if (formatter == null) {
    140                throw new IllegalArgumentException("Null 'formatter' argument.");
    141            }
    142            if (multiple <= 0) {
    143                throw new IllegalArgumentException("Requires 'multiple' > 0.");
    144            }
    145            if (rollMultiple <= 0) {
    146                throw new IllegalArgumentException("Requires 'rollMultiple' > 0.");
    147            }
    148            this.unitType = unitType;
    149            this.count = multiple;
    150            this.rollUnitType = rollUnitType;
    151            this.rollCount = rollMultiple;
    152            this.formatter = formatter;
    153
    154            // populate deprecated fields
    155            this.unit = unitTypeToInt(unitType);
    156            this.rollUnit = unitTypeToInt(rollUnitType);
    157        }
    158
    159        /**
    160         * Returns the unit type.
    161         *
    162         * @return The unit type (never <code>null</code>).
    163         *
    164         * @since 1.0.13
    165         */
    166        public DateTickUnitType getUnitType() {
    167            return this.unitType;
    168        }
    169
    170        /**
    171         * Returns the unit multiple.
    172         *
    173         * @return The unit multiple (always > 0).
    174         */
    175        public int getMultiple() {
    176            return this.count;
    177        }
    178
    179        /**
    180         * Returns the roll unit type.
    181         *
    182         * @return The roll unit type (never <code>null</code>).
    183         *
    184         * @since 1.0.13
    185         */
    186        public DateTickUnitType getRollUnitType() {
    187            return this.rollUnitType;
    188        }
    189
    190        /**
    191         * Returns the roll unit multiple.
    192         *
    193         * @return The roll unit multiple.
    194         *
    195         * @since 1.0.13
    196         */
    197        public int getRollMultiple() {
    198            return this.rollCount;
    199        }
    200
    201        /**
    202         * Formats a value.
    203         *
    204         * @param milliseconds  date in milliseconds since 01-01-1970.
    205         *
    206         * @return The formatted date.
    207         */
    208        public String valueToString(double milliseconds) {
    209            return this.formatter.format(new Date((long) milliseconds));
    210        }
    211
    212        /**
    213         * Formats a date using the tick unit's formatter.
    214         *
    215         * @param date  the date.
    216         *
    217         * @return The formatted date.
    218         */
    219        public String dateToString(Date date) {
    220            return this.formatter.format(date);
    221        }
    222
    223        /**
    224         * Calculates a new date by adding this unit to the base date.
    225         *
    226         * @param base  the base date.
    227         * @param zone  the time zone for the date calculation.
    228         *
    229         * @return A new date one unit after the base date.
    230         *
    231         * @since 1.0.6
    232         */
    233        public Date addToDate(Date base, TimeZone zone) {
    234            // as far as I know, the Locale for the calendar only affects week
    235            // number calculations, and since DateTickUnit doesn't do week
    236            // arithmetic, the default locale (whatever it is) should be fine
    237            // here...
    238            Calendar calendar = Calendar.getInstance(zone);
    239            calendar.setTime(base);
    240            calendar.add(this.unitType.getCalendarField(), this.count);
    241            return calendar.getTime();
    242        }
    243
    244        /**
    245         * Rolls the date forward by the amount specified by the roll unit and
    246         * count.
    247         *
    248         * @param base  the base date.
    249
    250         * @return The rolled date.
    251         *
    252         * @see #rollDate(Date, TimeZone)
    253         */
    254        public Date rollDate(Date base) {
    255            return rollDate(base, TimeZone.getDefault());
    256        }
    257
    258        /**
    259         * Rolls the date forward by the amount specified by the roll unit and
    260         * count.
    261         *
    262         * @param base  the base date.
    263         * @param zone  the time zone.
    264         *
    265         * @return The rolled date.
    266         *
    267         * @since 1.0.6
    268         */
    269        public Date rollDate(Date base, TimeZone zone) {
    270            // as far as I know, the Locale for the calendar only affects week
    271            // number calculations, and since DateTickUnit doesn't do week
    272            // arithmetic, the default locale (whatever it is) should be fine
    273            // here...
    274            Calendar calendar = Calendar.getInstance(zone);
    275            calendar.setTime(base);
    276            calendar.add(this.rollUnitType.getCalendarField(), this.rollCount);
    277            return calendar.getTime();
    278        }
    279
    280        /**
    281         * Returns a field code that can be used with the <code>Calendar</code>
    282         * class.
    283         *
    284         * @return The field code.
    285         */
    286        public int getCalendarField() {
    287            return this.unitType.getCalendarField();
    288        }
    289
    290        /**
    291         * Returns the (approximate) number of milliseconds for the given unit and
    292         * unit count.
    293         * <P>
    294         * This value is an approximation some of the time (e.g. months are
    295         * assumed to have 31 days) but this shouldn't matter.
    296         *
    297         * @param unit  the unit.
    298         * @param count  the unit count.
    299         *
    300         * @return The number of milliseconds.
    301         *
    302         * @since 1.0.13
    303         */
    304        private static long getMillisecondCount(DateTickUnitType unit, int count) {
    305
    306            if (unit.equals(DateTickUnitType.YEAR)) {
    307                return (365L * 24L * 60L * 60L * 1000L) * count;
    308            }
    309            else if (unit.equals(DateTickUnitType.MONTH)) {
    310                return (31L * 24L * 60L * 60L * 1000L) * count;
    311            }
    312            else if (unit.equals(DateTickUnitType.DAY)) {
    313                return (24L * 60L * 60L * 1000L) * count;
    314            }
    315            else if (unit.equals(DateTickUnitType.HOUR)) {
    316                return (60L * 60L * 1000L) * count;
    317            }
    318            else if (unit.equals(DateTickUnitType.MINUTE)) {
    319                return (60L * 1000L) * count;
    320            }
    321            else if (unit.equals(DateTickUnitType.SECOND)) {
    322                return 1000L * count;
    323            }
    324            else if (unit.equals(DateTickUnitType.MILLISECOND)) {
    325                return count;
    326            }
    327            else {
    328                throw new IllegalArgumentException("The 'unit' argument has a "
    329                        + "value that is not recognised.");
    330            }
    331
    332        }
    333
    334        /**
    335         * A utility method that is used internally to convert the old unit
    336         * constants into the corresponding enumerated value.
    337         *
    338         * @param unit  the unit specified using the deprecated integer codes.
    339         *
    340         * @return The unit type.
    341         *
    342         * @since 1.0.13
    343         */
    344        private static DateTickUnitType intToUnitType(int unit) {
    345            switch (unit) {
    346                case YEAR: return DateTickUnitType.YEAR;
    347                case MONTH: return DateTickUnitType.MONTH;
    348                case DAY: return DateTickUnitType.DAY;
    349                case HOUR: return DateTickUnitType.HOUR;
    350                case MINUTE: return DateTickUnitType.MINUTE;
    351                case SECOND: return DateTickUnitType.SECOND;
    352                case MILLISECOND: return DateTickUnitType.MILLISECOND;
    353                default: throw new IllegalArgumentException(
    354                        "Unrecognised 'unit' value " + unit + ".");
    355            }
    356        }
    357
    358        /**
    359         * Converts a unit type to the corresponding deprecated integer constant.
    360         *
    361         * @param unitType  the unit type (<code>null</code> not permitted).
    362         *
    363         * @return The int code.
    364         *
    365         * @since 1.0.13
    366         */
    367        private static int unitTypeToInt(DateTickUnitType unitType) {
    368            if (unitType == null) {
    369                throw new IllegalArgumentException("Null 'unitType' argument.");
    370            }
    371            if (unitType.equals(DateTickUnitType.YEAR)) {
    372                return YEAR;
    373            }
    374            else if (unitType.equals(DateTickUnitType.MONTH)) {
    375                return MONTH;
    376            }
    377            else if (unitType.equals(DateTickUnitType.DAY)) {
    378                return DAY;
    379            }
    380            else if (unitType.equals(DateTickUnitType.HOUR)) {
    381                return HOUR;
    382            }
    383            else if (unitType.equals(DateTickUnitType.MINUTE)) {
    384                return MINUTE;
    385            }
    386            else if (unitType.equals(DateTickUnitType.SECOND)) {
    387                return SECOND;
    388            }
    389            else if (unitType.equals(DateTickUnitType.MILLISECOND)) {
    390                return MILLISECOND;
    391            }
    392            else {
    393                throw new IllegalArgumentException(
    394                        "The 'unitType' is not recognised");
    395            }
    396        }
    397
    398        /**
    399         * A utility method to put a default in place if a null formatter is
    400         * supplied.
    401         *
    402         * @param formatter  the formatter (<code>null</code> permitted).
    403         *
    404         * @return The formatter if it is not null, otherwise a default.
    405         */
    406        private static DateFormat notNull(DateFormat formatter) {
    407            if (formatter == null) {
    408                return DateFormat.getDateInstance(DateFormat.SHORT);
    409            }
    410            else {
    411                return formatter;
    412            }
    413        }
    414
    415        /**
    416         * Tests this unit for equality with another object.
    417         *
    418         * @param obj  the object (<code>null</code> permitted).
    419         *
    420         * @return <code>true</code> or <code>false</code>.
    421         */
    422        public boolean equals(Object obj) {
    423            if (obj == this) {
    424                return true;
    425            }
    426            if (!(obj instanceof DateTickUnit)) {
    427                return false;
    428            }
    429            if (!super.equals(obj)) {
    430                return false;
    431            }
    432            DateTickUnit that = (DateTickUnit) obj;
    433            if (!(this.unitType.equals(that.unitType))) {
    434                return false;
    435            }
    436            if (this.count != that.count) {
    437                return false;
    438            }
    439            if (!ObjectUtilities.equal(this.formatter, that.formatter)) {
    440                return false;
    441            }
    442            return true;
    443        }
    444
    445        /**
    446         * Returns a hash code for this object.
    447         *
    448         * @return A hash code.
    449         */
    450        public int hashCode() {
    451            int result = 19;
    452            result = 37 * result + this.unitType.hashCode();
    453            result = 37 * result + this.count;
    454            result = 37 * result + this.formatter.hashCode();
    455            return result;
    456        }
    457
    458        /**
    459         * Returns a string representation of this instance, primarily used for
    460         * debugging purposes.
    461         *
    462         * @return A string representation of this instance.
    463         */
    464        public String toString() {
    465            return "DateTickUnit[" + this.unitType.toString() + ", "
    466                    + this.count + "]";
    467        }
    468
    469        /**
    470         * A constant for years.
    471         *
    472         * @deprecated As of version 1.0.13, use {@link DateTickUnitType} instead.
    473         */
    474        public static final int YEAR = 0;
    475
    476        /**
    477         * A constant for months.
    478         *
    479         * @deprecated As of version 1.0.13, use {@link DateTickUnitType} instead.
    480         */
    481        public static final int MONTH = 1;
    482
    483        /**
    484         * A constant for days.
    485         *
    486         * @deprecated As of version 1.0.13, use {@link DateTickUnitType} instead.
    487         */
    488        public static final int DAY = 2;
    489
    490        /**
    491         * A constant for hours.
    492         *
    493         * @deprecated As of version 1.0.13, use {@link DateTickUnitType} instead.
    494         */
    495        public static final int HOUR = 3;
    496
    497        /**
    498         * A constant for minutes.
    499         *
    500         * @deprecated As of version 1.0.13, use {@link DateTickUnitType} instead.
    501         */
    502        public static final int MINUTE = 4;
    503
    504        /**
    505         * A constant for seconds.
    506         *
    507         * @deprecated As of version 1.0.13, use {@link DateTickUnitType} instead.
    508         */
    509        public static final int SECOND = 5;
    510
    511        /**
    512         * A constant for milliseconds.
    513         *
    514         * @deprecated As of version 1.0.13, use {@link DateTickUnitType} instead.
    515         */
    516        public static final int MILLISECOND = 6;
    517
    518        /**
    519         * The unit.
    520         *
    521         * @deprecated As of version 1.0.13, use the unitType field.
    522         */
    523        private int unit;
    524
    525        /**
    526         * The roll unit.
    527         *
    528         * @deprecated As of version 1.0.13, use the rollUnitType field.
    529         */
    530        private int rollUnit;
    531
    532        /**
    533         * Creates a new date tick unit.  You can specify the units using one of
    534         * the constants YEAR, MONTH, DAY, HOUR, MINUTE, SECOND or MILLISECOND.
    535         * In addition, you can specify a unit count, and a date format.
    536         *
    537         * @param unit  the unit.
    538         * @param count  the unit count.
    539         * @param formatter  the date formatter (defaults to DateFormat.SHORT).
    540         *
    541         * @deprecated As of version 1.0.13, use {@link #DateTickUnit(
    542         *     DateTickUnitType, int, DateFormat)}.
    543         */
    544        public DateTickUnit(int unit, int count, DateFormat formatter) {
    545            this(unit, count, unit, count, formatter);
    546        }
    547
    548        /**
    549         * Creates a new date tick unit.  The dates will be formatted using a
    550         * SHORT format for the default locale.
    551         *
    552         * @param unit  the unit.
    553         * @param count  the unit count.
    554         *
    555         * @deprecated As of version 1.0.13, use {@link #DateTickUnit(
    556         *     DateTickUnitType, int)}.
    557         */
    558        public DateTickUnit(int unit, int count) {
    559            this(unit, count, null);
    560        }
    561
    562        /**
    563         * Creates a new unit.
    564         *
    565         * @param unit  the unit.
    566         * @param count  the count.
    567         * @param rollUnit  the roll unit.
    568         * @param rollCount  the roll count.
    569         * @param formatter  the date formatter (defaults to DateFormat.SHORT).
    570         *
    571         * @deprecated As of version 1.0.13, use {@link #DateTickUnit(
    572         *     DateTickUnitType, int, DateTickUnitType, int, DateFormat)}.
    573         */
    574        public DateTickUnit(int unit, int count, int rollUnit, int rollCount,
    575                            DateFormat formatter) {
    576            this(intToUnitType(unit), count, intToUnitType(rollUnit), rollCount,
    577                    notNull(formatter));
    578        }
    579
    580        /**
    581         * Returns the date unit.  This will be one of the constants
    582         * <code>YEAR</code>, <code>MONTH</code>, <code>DAY</code>,
    583         * <code>HOUR</code>, <code>MINUTE</code>, <code>SECOND</code> or
    584         * <code>MILLISECOND</code>, defined by this class.  Note that these
    585         * constants do NOT correspond to those defined in Java's
    586         * <code>Calendar</code> class.
    587         *
    588         * @return The date unit.
    589         *
    590         * @deprecated As of 1.0.13, use the getUnitType() method.
    591         */
    592        public int getUnit() {
    593            return this.unit;
    594        }
    595
    596        /**
    597         * Returns the unit count.
    598         *
    599         * @return The unit count.
    600         *
    601         * @deprecated As of version 1.0.13, use {@link #getMultiple()}.
    602         */
    603        public int getCount() {
    604            return this.count;
    605        }
    606
    607        /**
    608         * Returns the roll unit.  This is the amount by which the tick advances if
    609         * it is "hidden" when displayed on a segmented date axis.  Typically the
    610         * roll will be smaller than the regular tick unit (for example, a 7 day
    611         * tick unit might use a 1 day roll).
    612         *
    613         * @return The roll unit.
    614         *
    615         * @deprecated As of version 1.0.13, use {@link #getRollUnitType()}.
    616         */
    617        public int getRollUnit() {
    618            return this.rollUnit;
    619        }
    620
    621        /**
    622         * Returns the roll count.
    623         *
    624         * @return The roll count.
    625         *
    626         * @deprecated As of version 1.0.13, use the {@link #getRollMultiple()}
    627         *
    628         */
    629        public int getRollCount() {
    630            return this.rollCount;
    631        }
    632
    633        /**
    634         * Calculates a new date by adding this unit to the base date, with
    635         * calculations performed in the default timezone and locale.
    636         *
    637         * @param base  the base date.
    638         *
    639         * @return A new date one unit after the base date.
    640         *
    641         * @see #addToDate(Date, TimeZone)
    642         *
    643         * @deprecated As of JFreeChart 1.0.10, this method is deprecated - you
    644         *     should use {@link #addToDate(Date, TimeZone)} instead.
    645         */
    646        public Date addToDate(Date base) {
    647            return addToDate(base, TimeZone.getDefault());
    648        }
    649
    650    }