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