001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * Year.java 029 * --------- 030 * (C) Copyright 2001-2007, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 11-Oct-2001 : Version 1 (DG); 038 * 14-Nov-2001 : Override for toString() method (DG); 039 * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG); 040 * 29-Jan-2002 : Worked on parseYear() method (DG); 041 * 14-Feb-2002 : Fixed bug in Year(Date) constructor (DG); 042 * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 043 * evaluate with reference to a particular time zone (DG); 044 * 19-Mar-2002 : Changed API for TimePeriod classes (DG); 045 * 10-Sep-2002 : Added getSerialIndex() method (DG); 046 * 04-Oct-2002 : Fixed errors reported by Checkstyle (DG); 047 * 10-Jan-2003 : Changed base class and method names (DG); 048 * 05-Mar-2003 : Fixed bug in getFirstMillisecond() picked up in JUnit 049 * tests (DG); 050 * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented 051 * Serializable (DG); 052 * 21-Oct-2003 : Added hashCode() method (DG); 053 * ------------- JFREECHART 1.0.x --------------------------------------------- 054 * 05-Oct-2006 : Updated API docs (DG); 055 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG); 056 * 057 */ 058 059 package org.jfree.data.time; 060 061 import java.io.Serializable; 062 import java.util.Calendar; 063 import java.util.Date; 064 import java.util.TimeZone; 065 066 import org.jfree.date.SerialDate; 067 068 /** 069 * Represents a year in the range 1900 to 9999. This class is immutable, which 070 * is a requirement for all {@link RegularTimePeriod} subclasses. 071 */ 072 public class Year extends RegularTimePeriod implements Serializable { 073 074 /** For serialization. */ 075 private static final long serialVersionUID = -7659990929736074836L; 076 077 /** The year. */ 078 private short year; 079 080 /** The first millisecond. */ 081 private long firstMillisecond; 082 083 /** The last millisecond. */ 084 private long lastMillisecond; 085 086 /** 087 * Creates a new <code>Year</code>, based on the current system date/time. 088 */ 089 public Year() { 090 this(new Date()); 091 } 092 093 /** 094 * Creates a time period representing a single year. 095 * 096 * @param year the year. 097 */ 098 public Year(int year) { 099 if ((year < SerialDate.MINIMUM_YEAR_SUPPORTED) 100 || (year > SerialDate.MAXIMUM_YEAR_SUPPORTED)) { 101 102 throw new IllegalArgumentException( 103 "Year constructor: year (" + year + ") outside valid range."); 104 } 105 this.year = (short) year; 106 peg(Calendar.getInstance()); 107 } 108 109 /** 110 * Creates a new <code>Year</code>, based on a particular instant in time, 111 * using the default time zone. 112 * 113 * @param time the time (<code>null</code> not permitted). 114 */ 115 public Year(Date time) { 116 this(time, RegularTimePeriod.DEFAULT_TIME_ZONE); 117 } 118 119 /** 120 * Constructs a year, based on a particular instant in time and a time zone. 121 * 122 * @param time the time. 123 * @param zone the time zone. 124 */ 125 public Year(Date time, TimeZone zone) { 126 Calendar calendar = Calendar.getInstance(zone); 127 calendar.setTime(time); 128 this.year = (short) calendar.get(Calendar.YEAR); 129 peg(calendar); 130 } 131 132 /** 133 * Returns the year. 134 * 135 * @return The year. 136 */ 137 public int getYear() { 138 return this.year; 139 } 140 141 /** 142 * Returns the first millisecond of the year. This will be determined 143 * relative to the time zone specified in the constructor, or in the 144 * calendar instance passed in the most recent call to the 145 * {@link #peg(Calendar)} method. 146 * 147 * @return The first millisecond of the year. 148 * 149 * @see #getLastMillisecond() 150 */ 151 public long getFirstMillisecond() { 152 return this.firstMillisecond; 153 } 154 155 /** 156 * Returns the last millisecond of the year. This will be 157 * determined relative to the time zone specified in the constructor, or 158 * in the calendar instance passed in the most recent call to the 159 * {@link #peg(Calendar)} method. 160 * 161 * @return The last millisecond of the year. 162 * 163 * @see #getFirstMillisecond() 164 */ 165 public long getLastMillisecond() { 166 return this.lastMillisecond; 167 } 168 169 /** 170 * Recalculates the start date/time and end date/time for this time period 171 * relative to the supplied calendar (which incorporates a time zone). 172 * 173 * @param calendar the calendar (<code>null</code> not permitted). 174 * 175 * @since 1.0.3 176 */ 177 public void peg(Calendar calendar) { 178 this.firstMillisecond = getFirstMillisecond(calendar); 179 this.lastMillisecond = getLastMillisecond(calendar); 180 } 181 182 /** 183 * Returns the year preceding this one. 184 * 185 * @return The year preceding this one (or <code>null</code> if the 186 * current year is 1900). 187 */ 188 public RegularTimePeriod previous() { 189 if (this.year > SerialDate.MINIMUM_YEAR_SUPPORTED) { 190 return new Year(this.year - 1); 191 } 192 else { 193 return null; 194 } 195 } 196 197 /** 198 * Returns the year following this one. 199 * 200 * @return The year following this one (or <code>null</code> if the current 201 * year is 9999). 202 */ 203 public RegularTimePeriod next() { 204 if (this.year < SerialDate.MAXIMUM_YEAR_SUPPORTED) { 205 return new Year(this.year + 1); 206 } 207 else { 208 return null; 209 } 210 } 211 212 /** 213 * Returns a serial index number for the year. 214 * <P> 215 * The implementation simply returns the year number (e.g. 2002). 216 * 217 * @return The serial index number. 218 */ 219 public long getSerialIndex() { 220 return this.year; 221 } 222 223 /** 224 * Returns the first millisecond of the year, evaluated using the supplied 225 * calendar (which determines the time zone). 226 * 227 * @param calendar the calendar (<code>null</code> not permitted). 228 * 229 * @return The first millisecond of the year. 230 * 231 * @throws NullPointerException if <code>calendar</code> is 232 * <code>null</code>. 233 */ 234 public long getFirstMillisecond(Calendar calendar) { 235 calendar.set(this.year, Calendar.JANUARY, 1, 0, 0, 0); 236 calendar.set(Calendar.MILLISECOND, 0); 237 // in the following line, we'd rather call calendar.getTimeInMillis() 238 // to avoid object creation, but that isn't supported in Java 1.3.1 239 return calendar.getTime().getTime(); 240 } 241 242 /** 243 * Returns the last millisecond of the year, evaluated using the supplied 244 * calendar (which determines the time zone). 245 * 246 * @param calendar the calendar (<code>null</code> not permitted). 247 * 248 * @return The last millisecond of the year. 249 * 250 * @throws NullPointerException if <code>calendar</code> is 251 * <code>null</code>. 252 */ 253 public long getLastMillisecond(Calendar calendar) { 254 calendar.set(this.year, Calendar.DECEMBER, 31, 23, 59, 59); 255 calendar.set(Calendar.MILLISECOND, 999); 256 // in the following line, we'd rather call calendar.getTimeInMillis() 257 // to avoid object creation, but that isn't supported in Java 1.3.1 258 return calendar.getTime().getTime(); 259 } 260 261 /** 262 * Tests the equality of this <code>Year</code> object to an arbitrary 263 * object. Returns <code>true</code> if the target is a <code>Year</code> 264 * instance representing the same year as this object. In all other cases, 265 * returns <code>false</code>. 266 * 267 * @param object the object (<code>null</code> permitted). 268 * 269 * @return <code>true</code> if the year of this and the object are the 270 * same. 271 */ 272 public boolean equals(Object object) { 273 if (object != null) { 274 if (object instanceof Year) { 275 Year target = (Year) object; 276 return (this.year == target.getYear()); 277 } 278 else { 279 return false; 280 } 281 } 282 else { 283 return false; 284 } 285 } 286 287 /** 288 * Returns a hash code for this object instance. The approach described by 289 * Joshua Bloch in "Effective Java" has been used here: 290 * <p> 291 * <code>http://developer.java.sun.com/developer/Books/effectivejava 292 * /Chapter3.pdf</code> 293 * 294 * @return A hash code. 295 */ 296 public int hashCode() { 297 int result = 17; 298 int c = this.year; 299 result = 37 * result + c; 300 return result; 301 } 302 303 /** 304 * Returns an integer indicating the order of this <code>Year</code> object 305 * relative to the specified object: 306 * 307 * negative == before, zero == same, positive == after. 308 * 309 * @param o1 the object to compare. 310 * 311 * @return negative == before, zero == same, positive == after. 312 */ 313 public int compareTo(Object o1) { 314 315 int result; 316 317 // CASE 1 : Comparing to another Year object 318 // ----------------------------------------- 319 if (o1 instanceof Year) { 320 Year y = (Year) o1; 321 result = this.year - y.getYear(); 322 } 323 324 // CASE 2 : Comparing to another TimePeriod object 325 // ----------------------------------------------- 326 else if (o1 instanceof RegularTimePeriod) { 327 // more difficult case - evaluate later... 328 result = 0; 329 } 330 331 // CASE 3 : Comparing to a non-TimePeriod object 332 // --------------------------------------------- 333 else { 334 // consider time periods to be ordered after general objects 335 result = 1; 336 } 337 338 return result; 339 340 } 341 342 /** 343 * Returns a string representing the year.. 344 * 345 * @return A string representing the year. 346 */ 347 public String toString() { 348 return Integer.toString(this.year); 349 } 350 351 /** 352 * Parses the string argument as a year. 353 * <P> 354 * The string format is YYYY. 355 * 356 * @param s a string representing the year. 357 * 358 * @return <code>null</code> if the string is not parseable, the year 359 * otherwise. 360 */ 361 public static Year parseYear(String s) { 362 363 // parse the string... 364 int y; 365 try { 366 y = Integer.parseInt(s.trim()); 367 } 368 catch (NumberFormatException e) { 369 throw new TimePeriodFormatException("Cannot parse string."); 370 } 371 372 // create the year... 373 try { 374 return new Year(y); 375 } 376 catch (IllegalArgumentException e) { 377 throw new TimePeriodFormatException("Year outside valid range."); 378 } 379 } 380 381 }