001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2009, 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-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    }