001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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-2008, 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 * 052 */ 053 054 package org.jfree.chart.axis; 055 056 import java.io.Serializable; 057 import java.text.DateFormat; 058 import java.util.Calendar; 059 import java.util.Date; 060 import java.util.TimeZone; 061 062 import org.jfree.util.ObjectUtilities; 063 064 /** 065 * A tick unit for use by subclasses of {@link DateAxis}. Instances of this 066 * class are immutable. 067 */ 068 public class DateTickUnit extends TickUnit implements Serializable { 069 070 /** For serialization. */ 071 private static final long serialVersionUID = -7289292157229621901L; 072 073 /** A constant for years. */ 074 public static final int YEAR = 0; 075 076 /** A constant for months. */ 077 public static final int MONTH = 1; 078 079 /** A constant for days. */ 080 public static final int DAY = 2; 081 082 /** A constant for hours. */ 083 public static final int HOUR = 3; 084 085 /** A constant for minutes. */ 086 public static final int MINUTE = 4; 087 088 /** A constant for seconds. */ 089 public static final int SECOND = 5; 090 091 /** A constant for milliseconds. */ 092 public static final int MILLISECOND = 6; 093 094 /** The unit. */ 095 private int unit; 096 097 /** The unit count. */ 098 private int count; 099 100 /** The roll unit. */ 101 private int rollUnit; 102 103 /** The roll count. */ 104 private int rollCount; 105 106 /** The date formatter. */ 107 private DateFormat formatter; 108 109 /** 110 * Creates a new date tick unit. The dates will be formatted using a 111 * SHORT format for the default locale. 112 * 113 * @param unit the unit. 114 * @param count the unit count. 115 */ 116 public DateTickUnit(int unit, int count) { 117 this(unit, count, null); 118 } 119 120 /** 121 * Creates a new date tick unit. You can specify the units using one of 122 * the constants YEAR, MONTH, DAY, HOUR, MINUTE, SECOND or MILLISECOND. 123 * In addition, you can specify a unit count, and a date format. 124 * 125 * @param unit the unit. 126 * @param count the unit count. 127 * @param formatter the date formatter (defaults to DateFormat.SHORT). 128 */ 129 public DateTickUnit(int unit, int count, DateFormat formatter) { 130 131 this(unit, count, unit, count, formatter); 132 133 } 134 135 /** 136 * Creates a new unit. 137 * 138 * @param unit the unit. 139 * @param count the count. 140 * @param rollUnit the roll unit. 141 * @param rollCount the roll count. 142 * @param formatter the date formatter (defaults to DateFormat.SHORT). 143 */ 144 public DateTickUnit(int unit, int count, int rollUnit, int rollCount, 145 DateFormat formatter) { 146 super(DateTickUnit.getMillisecondCount(unit, count)); 147 this.unit = unit; 148 this.count = count; 149 this.rollUnit = rollUnit; 150 this.rollCount = rollCount; 151 this.formatter = formatter; 152 if (formatter == null) { 153 this.formatter = DateFormat.getDateInstance(DateFormat.SHORT); 154 } 155 } 156 157 /** 158 * Returns the date unit. This will be one of the constants 159 * <code>YEAR</code>, <code>MONTH</code>, <code>DAY</code>, 160 * <code>HOUR</code>, <code>MINUTE</code>, <code>SECOND</code> or 161 * <code>MILLISECOND</code>, defined by this class. Note that these 162 * constants do NOT correspond to those defined in Java's 163 * <code>Calendar</code> class. 164 * 165 * @return The date unit. 166 */ 167 public int getUnit() { 168 return this.unit; 169 } 170 171 /** 172 * Returns the unit count. 173 * 174 * @return The unit count. 175 */ 176 public int getCount() { 177 return this.count; 178 } 179 180 /** 181 * Returns the roll unit. This is the amount by which the tick advances if 182 * it is "hidden" when displayed on a segmented date axis. Typically the 183 * roll will be smaller than the regular tick unit (for example, a 7 day 184 * tick unit might use a 1 day roll). 185 * 186 * @return The roll unit. 187 */ 188 public int getRollUnit() { 189 return this.rollUnit; 190 } 191 192 /** 193 * Returns the roll count. 194 * 195 * @return The roll count. 196 */ 197 public int getRollCount() { 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, with 225 * calculations performed in the default timezone and locale. 226 * 227 * @param base the base date. 228 * 229 * @return A new date one unit after the base date. 230 * 231 * @see #addToDate(Date, TimeZone) 232 * 233 * @deprecated As of JFreeChart 1.0.10, this method is deprecated - you 234 * should use {@link #addToDate(Date, TimeZone)} instead. 235 */ 236 public Date addToDate(Date base) { 237 return addToDate(base, TimeZone.getDefault()); 238 } 239 240 /** 241 * Calculates a new date by adding this unit to the base date. 242 * 243 * @param base the base date. 244 * @param zone the time zone for the date calculation. 245 * 246 * @return A new date one unit after the base date. 247 * 248 * @since 1.0.6 249 */ 250 public Date addToDate(Date base, TimeZone zone) { 251 // as far as I know, the Locale for the calendar only affects week 252 // number calculations, and since DateTickUnit doesn't do week 253 // arithmetic, the default locale (whatever it is) should be fine 254 // here... 255 Calendar calendar = Calendar.getInstance(zone); 256 calendar.setTime(base); 257 calendar.add(getCalendarField(this.unit), this.count); 258 return calendar.getTime(); 259 } 260 261 /** 262 * Rolls the date forward by the amount specified by the roll unit and 263 * count. 264 * 265 * @param base the base date. 266 267 * @return The rolled date. 268 * 269 * @see #rollDate(Date, TimeZone) 270 */ 271 public Date rollDate(Date base) { 272 return rollDate(base, TimeZone.getDefault()); 273 } 274 275 /** 276 * Rolls the date forward by the amount specified by the roll unit and 277 * count. 278 * 279 * @param base the base date. 280 * @param zone the time zone. 281 * 282 * @return The rolled date. 283 * 284 * @since 1.0.6 285 */ 286 public Date rollDate(Date base, TimeZone zone) { 287 // as far as I know, the Locale for the calendar only affects week 288 // number calculations, and since DateTickUnit doesn't do week 289 // arithmetic, the default locale (whatever it is) should be fine 290 // here... 291 Calendar calendar = Calendar.getInstance(zone); 292 calendar.setTime(base); 293 calendar.add(getCalendarField(this.rollUnit), this.rollCount); 294 return calendar.getTime(); 295 } 296 297 /** 298 * Returns a field code that can be used with the <code>Calendar</code> 299 * class. 300 * 301 * @return The field code. 302 */ 303 public int getCalendarField() { 304 return getCalendarField(this.unit); 305 } 306 307 /** 308 * Returns a field code (that can be used with the Calendar class) for a 309 * given 'unit' code. The 'unit' is one of: {@link #YEAR}, {@link #MONTH}, 310 * {@link #DAY}, {@link #HOUR}, {@link #MINUTE}, {@link #SECOND} and 311 * {@link #MILLISECOND}. 312 * 313 * @param tickUnit the unit. 314 * 315 * @return The field code. 316 */ 317 private int getCalendarField(int tickUnit) { 318 319 switch (tickUnit) { 320 case (YEAR): 321 return Calendar.YEAR; 322 case (MONTH): 323 return Calendar.MONTH; 324 case (DAY): 325 return Calendar.DATE; 326 case (HOUR): 327 return Calendar.HOUR_OF_DAY; 328 case (MINUTE): 329 return Calendar.MINUTE; 330 case (SECOND): 331 return Calendar.SECOND; 332 case (MILLISECOND): 333 return Calendar.MILLISECOND; 334 default: 335 return Calendar.MILLISECOND; 336 } 337 338 } 339 340 /** 341 * Returns the (approximate) number of milliseconds for the given unit and 342 * unit count. 343 * <P> 344 * This value is an approximation some of the time (e.g. months are 345 * assumed to have 31 days) but this shouldn't matter. 346 * 347 * @param unit the unit. 348 * @param count the unit count. 349 * 350 * @return The number of milliseconds. 351 */ 352 private static long getMillisecondCount(int unit, int count) { 353 354 switch (unit) { 355 case (YEAR): 356 return (365L * 24L * 60L * 60L * 1000L) * count; 357 case (MONTH): 358 return (31L * 24L * 60L * 60L * 1000L) * count; 359 case (DAY): 360 return (24L * 60L * 60L * 1000L) * count; 361 case (HOUR): 362 return (60L * 60L * 1000L) * count; 363 case (MINUTE): 364 return (60L * 1000L) * count; 365 case (SECOND): 366 return 1000L * count; 367 case (MILLISECOND): 368 return count; 369 default: 370 throw new IllegalArgumentException( 371 "DateTickUnit.getMillisecondCount() : unit must " 372 + "be one of the constants YEAR, MONTH, DAY, HOUR, MINUTE, " 373 + "SECOND or MILLISECOND defined in the DateTickUnit " 374 + "class. Do *not* use the constants defined in " 375 + "java.util.Calendar." 376 ); 377 } 378 379 } 380 381 /** 382 * Tests this unit for equality with another object. 383 * 384 * @param obj the object (<code>null</code> permitted). 385 * 386 * @return <code>true</code> or <code>false</code>. 387 */ 388 public boolean equals(Object obj) { 389 if (obj == this) { 390 return true; 391 } 392 if (!(obj instanceof DateTickUnit)) { 393 return false; 394 } 395 if (!super.equals(obj)) { 396 return false; 397 } 398 DateTickUnit that = (DateTickUnit) obj; 399 if (this.unit != that.unit) { 400 return false; 401 } 402 if (this.count != that.count) { 403 return false; 404 } 405 if (!ObjectUtilities.equal(this.formatter, that.formatter)) { 406 return false; 407 } 408 return true; 409 } 410 411 /** 412 * Returns a hash code for this object. 413 * 414 * @return A hash code. 415 */ 416 public int hashCode() { 417 int result = 19; 418 result = 37 * result + this.unit; 419 result = 37 * result + this.count; 420 result = 37 * result + this.formatter.hashCode(); 421 return result; 422 } 423 424 /** 425 * Strings for use by the toString() method. 426 */ 427 private static final String[] units = {"YEAR", "MONTH", "DAY", "HOUR", 428 "MINUTE", "SECOND", "MILLISECOND"}; 429 430 /** 431 * Returns a string representation of this instance, primarily used for 432 * debugging purposes. 433 * 434 * @return A string representation of this instance. 435 */ 436 public String toString() { 437 return "DateTickUnit[" + DateTickUnit.units[this.unit] + ", " 438 + this.count + "]"; 439 } 440 441 }