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