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 }