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 }