001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2020, 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-2020, 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.ObjectUtils; 065import org.jfree.chart.util.Args; 066 067/** 068 * A tick unit for use by subclasses of {@link DateAxis}. Instances of this 069 * class are immutable. 070 */ 071public class DateTickUnit extends TickUnit implements Serializable { 072 073 /** For serialization. */ 074 private static final long serialVersionUID = -7289292157229621901L; 075 076 /** 077 * The units. 078 * 079 * @since 1.0.13 080 */ 081 private DateTickUnitType unitType; 082 083 /** The unit count. */ 084 private int count; 085 086 /** 087 * The roll unit type. 088 * 089 * @since 1.0.13 090 */ 091 private DateTickUnitType rollUnitType; 092 093 /** The roll count. */ 094 private int rollCount; 095 096 /** The date formatter. */ 097 private DateFormat formatter; 098 099 /** 100 * Creates a new date tick unit. 101 * 102 * @param unitType the unit type ({@code null} not permitted). 103 * @param multiple the multiple (of the unit type, must be > 0). 104 * 105 * @since 1.0.13 106 */ 107 public DateTickUnit(DateTickUnitType unitType, int multiple) { 108 this(unitType, multiple, DateFormat.getDateInstance(DateFormat.SHORT)); 109 } 110 111 /** 112 * Creates a new date tick unit. 113 * 114 * @param unitType the unit type ({@code null} not permitted). 115 * @param multiple the multiple (of the unit type, must be > 0). 116 * @param formatter the date formatter ({@code null} not permitted). 117 * 118 * @since 1.0.13 119 */ 120 public DateTickUnit(DateTickUnitType unitType, int multiple, 121 DateFormat formatter) { 122 this(unitType, multiple, unitType, multiple, formatter); 123 } 124 125 /** 126 * Creates a new unit. 127 * 128 * @param unitType the unit. 129 * @param multiple the multiple. 130 * @param rollUnitType the roll unit. 131 * @param rollMultiple the roll multiple. 132 * @param formatter the date formatter ({@code null} not permitted). 133 * 134 * @since 1.0.13 135 */ 136 public DateTickUnit(DateTickUnitType unitType, int multiple, 137 DateTickUnitType rollUnitType, int rollMultiple, 138 DateFormat formatter) { 139 super(DateTickUnit.getMillisecondCount(unitType, multiple)); 140 Args.nullNotPermitted(formatter, "formatter"); 141 if (multiple <= 0) { 142 throw new IllegalArgumentException("Requires 'multiple' > 0."); 143 } 144 if (rollMultiple <= 0) { 145 throw new IllegalArgumentException("Requires 'rollMultiple' > 0."); 146 } 147 this.unitType = unitType; 148 this.count = multiple; 149 this.rollUnitType = rollUnitType; 150 this.rollCount = rollMultiple; 151 this.formatter = formatter; 152 } 153 154 /** 155 * Returns the unit type. 156 * 157 * @return The unit type (never {@code null}). 158 * 159 * @since 1.0.13 160 */ 161 public DateTickUnitType getUnitType() { 162 return this.unitType; 163 } 164 165 /** 166 * Returns the unit multiple. 167 * 168 * @return The unit multiple (always > 0). 169 */ 170 public int getMultiple() { 171 return this.count; 172 } 173 174 /** 175 * Returns the roll unit type. 176 * 177 * @return The roll unit type (never {@code null}). 178 * 179 * @since 1.0.13 180 */ 181 public DateTickUnitType getRollUnitType() { 182 return this.rollUnitType; 183 } 184 185 /** 186 * Returns the roll unit multiple. 187 * 188 * @return The roll unit multiple. 189 * 190 * @since 1.0.13 191 */ 192 public int getRollMultiple() { 193 return this.rollCount; 194 } 195 196 /** 197 * Formats a value. 198 * 199 * @param milliseconds date in milliseconds since 01-01-1970. 200 * 201 * @return The formatted date. 202 */ 203 @Override 204 public String valueToString(double milliseconds) { 205 return this.formatter.format(new Date((long) milliseconds)); 206 } 207 208 /** 209 * Formats a date using the tick unit's formatter. 210 * 211 * @param date the date. 212 * 213 * @return The formatted date. 214 */ 215 public String dateToString(Date date) { 216 return this.formatter.format(date); 217 } 218 219 /** 220 * Calculates a new date by adding this unit to the base date. 221 * 222 * @param base the base date. 223 * @param zone the time zone for the date calculation. 224 * 225 * @return A new date one unit after the base date. 226 * 227 * @since 1.0.6 228 */ 229 public Date addToDate(Date base, TimeZone zone) { 230 // as far as I know, the Locale for the calendar only affects week 231 // number calculations, and since DateTickUnit doesn't do week 232 // arithmetic, the default locale (whatever it is) should be fine 233 // here... 234 Calendar calendar = Calendar.getInstance(zone); 235 calendar.setTime(base); 236 calendar.add(this.unitType.getCalendarField(), this.count); 237 return calendar.getTime(); 238 } 239 240 /** 241 * Rolls the date forward by the amount specified by the roll unit and 242 * count. 243 * 244 * @param base the base date. 245 246 * @return The rolled date. 247 * 248 * @see #rollDate(Date, TimeZone) 249 */ 250 public Date rollDate(Date base) { 251 return rollDate(base, TimeZone.getDefault()); 252 } 253 254 /** 255 * Rolls the date forward by the amount specified by the roll unit and 256 * count. 257 * 258 * @param base the base date. 259 * @param zone the time zone. 260 * 261 * @return The rolled date. 262 * 263 * @since 1.0.6 264 */ 265 public Date rollDate(Date base, TimeZone zone) { 266 // as far as I know, the Locale for the calendar only affects week 267 // number calculations, and since DateTickUnit doesn't do week 268 // arithmetic, the default locale (whatever it is) should be fine 269 // here... 270 Calendar calendar = Calendar.getInstance(zone); 271 calendar.setTime(base); 272 calendar.add(this.rollUnitType.getCalendarField(), this.rollCount); 273 return calendar.getTime(); 274 } 275 276 /** 277 * Returns a field code that can be used with the {@code Calendar} 278 * class. 279 * 280 * @return The field code. 281 */ 282 public int getCalendarField() { 283 return this.unitType.getCalendarField(); 284 } 285 286 /** 287 * Returns the (approximate) number of milliseconds for the given unit and 288 * unit count. 289 * <P> 290 * This value is an approximation some of the time (e.g. months are 291 * assumed to have 31 days) but this shouldn't matter. 292 * 293 * @param unit the unit. 294 * @param count the unit count. 295 * 296 * @return The number of milliseconds. 297 * 298 * @since 1.0.13 299 */ 300 private static long getMillisecondCount(DateTickUnitType unit, int count) { 301 302 if (unit.equals(DateTickUnitType.YEAR)) { 303 return (365L * 24L * 60L * 60L * 1000L) * count; 304 } 305 else if (unit.equals(DateTickUnitType.MONTH)) { 306 return (31L * 24L * 60L * 60L * 1000L) * count; 307 } 308 else if (unit.equals(DateTickUnitType.DAY)) { 309 return (24L * 60L * 60L * 1000L) * count; 310 } 311 else if (unit.equals(DateTickUnitType.HOUR)) { 312 return (60L * 60L * 1000L) * count; 313 } 314 else if (unit.equals(DateTickUnitType.MINUTE)) { 315 return (60L * 1000L) * count; 316 } 317 else if (unit.equals(DateTickUnitType.SECOND)) { 318 return 1000L * count; 319 } 320 else if (unit.equals(DateTickUnitType.MILLISECOND)) { 321 return count; 322 } 323 else { 324 throw new IllegalArgumentException("The 'unit' argument has a " 325 + "value that is not recognised."); 326 } 327 328 } 329 330 /** 331 * Tests this unit for equality with another object. 332 * 333 * @param obj the object ({@code null} permitted). 334 * 335 * @return {@code true} or {@code false}. 336 */ 337 @Override 338 public boolean equals(Object obj) { 339 if (obj == this) { 340 return true; 341 } 342 if (!(obj instanceof DateTickUnit)) { 343 return false; 344 } 345 if (!super.equals(obj)) { 346 return false; 347 } 348 DateTickUnit that = (DateTickUnit) obj; 349 if (!(this.unitType.equals(that.unitType))) { 350 return false; 351 } 352 if (this.count != that.count) { 353 return false; 354 } 355 if (!ObjectUtils.equal(this.formatter, that.formatter)) { 356 return false; 357 } 358 return true; 359 } 360 361 /** 362 * Returns a hash code for this object. 363 * 364 * @return A hash code. 365 */ 366 @Override 367 public int hashCode() { 368 int result = 19; 369 result = 37 * result + this.unitType.hashCode(); 370 result = 37 * result + this.count; 371 result = 37 * result + this.formatter.hashCode(); 372 return result; 373 } 374 375 /** 376 * Returns a string representation of this instance, primarily used for 377 * debugging purposes. 378 * 379 * @return A string representation of this instance. 380 */ 381 @Override 382 public String toString() { 383 return "DateTickUnit[" + this.unitType.toString() + ", " 384 + this.count + "]"; 385 } 386 387}