001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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-2005, 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.2 2006/08/24 10:01:52 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 * 056 */ 057 058 package org.jfree.data.time; 059 060 import java.io.Serializable; 061 import java.util.Calendar; 062 import java.util.Date; 063 import java.util.TimeZone; 064 065 import org.jfree.date.MonthConstants; 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 int year; 079 080 /** 081 * Creates a new <code>Year</code>, based on the current system date/time. 082 */ 083 public Year() { 084 this(new Date()); 085 } 086 087 /** 088 * Creates a time period representing a single year. 089 * 090 * @param year the year. 091 */ 092 public Year(int year) { 093 094 // check arguments... 095 if ((year < SerialDate.MINIMUM_YEAR_SUPPORTED) 096 || (year > SerialDate.MAXIMUM_YEAR_SUPPORTED)) { 097 098 throw new IllegalArgumentException( 099 "Year constructor: year (" + year + ") outside valid range."); 100 } 101 102 // initialise... 103 this.year = year; 104 105 } 106 107 /** 108 * Creates a new <code>Year</code>, based on a particular instant in time, 109 * using the default time zone. 110 * 111 * @param time the time (<code>null</code> not permitted). 112 */ 113 public Year(Date time) { 114 this(time, RegularTimePeriod.DEFAULT_TIME_ZONE); 115 } 116 117 /** 118 * Constructs a year, based on a particular instant in time and a time zone. 119 * 120 * @param time the time. 121 * @param zone the time zone. 122 */ 123 public Year(Date time, TimeZone zone) { 124 125 Calendar calendar = Calendar.getInstance(zone); 126 calendar.setTime(time); 127 this.year = calendar.get(Calendar.YEAR); 128 129 } 130 131 /** 132 * Returns the year. 133 * 134 * @return The year. 135 */ 136 public int getYear() { 137 return this.year; 138 } 139 140 /** 141 * Returns the year preceding this one. 142 * 143 * @return The year preceding this one (or <code>null</code> if the 144 * current year is 1900). 145 */ 146 public RegularTimePeriod previous() { 147 if (this.year > SerialDate.MINIMUM_YEAR_SUPPORTED) { 148 return new Year(this.year - 1); 149 } 150 else { 151 return null; 152 } 153 } 154 155 /** 156 * Returns the year following this one. 157 * 158 * @return The year following this one (or <code>null</code> if the current 159 * year is 9999). 160 */ 161 public RegularTimePeriod next() { 162 if (this.year < SerialDate.MAXIMUM_YEAR_SUPPORTED) { 163 return new Year(this.year + 1); 164 } 165 else { 166 return null; 167 } 168 } 169 170 /** 171 * Returns a serial index number for the year. 172 * <P> 173 * The implementation simply returns the year number (e.g. 2002). 174 * 175 * @return The serial index number. 176 */ 177 public long getSerialIndex() { 178 return this.year; 179 } 180 181 /** 182 * Returns the first millisecond of the year, evaluated using the supplied 183 * calendar (which determines the time zone). 184 * 185 * @param calendar the calendar. 186 * 187 * @return The first millisecond of the year. 188 */ 189 public long getFirstMillisecond(Calendar calendar) { 190 Day jan1 = new Day(1, MonthConstants.JANUARY, this.year); 191 return jan1.getFirstMillisecond(calendar); 192 } 193 194 /** 195 * Returns the last millisecond of the year, evaluated using the supplied 196 * calendar (which determines the time zone). 197 * 198 * @param calendar the calendar. 199 * 200 * @return The last millisecond of the year. 201 */ 202 public long getLastMillisecond(Calendar calendar) { 203 Day dec31 = new Day(31, MonthConstants.DECEMBER, this.year); 204 return dec31.getLastMillisecond(calendar); 205 } 206 207 /** 208 * Tests the equality of this <code>Year</code> object to an arbitrary 209 * object. Returns <code>true</code> if the target is a <code>Year</code> 210 * instance representing the same year as this object. In all other cases, 211 * returns <code>false</code>. 212 * 213 * @param object the object. 214 * 215 * @return <code>true</code> if the year of this and the object are the 216 * same. 217 */ 218 public boolean equals(Object object) { 219 if (object != null) { 220 if (object instanceof Year) { 221 Year target = (Year) object; 222 return (this.year == target.getYear()); 223 } 224 else { 225 return false; 226 } 227 } 228 else { 229 return false; 230 } 231 } 232 233 /** 234 * Returns a hash code for this object instance. The approach described by 235 * Joshua Bloch in "Effective Java" has been used here: 236 * <p> 237 * <code>http://developer.java.sun.com/developer/Books/effectivejava 238 * /Chapter3.pdf</code> 239 * 240 * @return A hash code. 241 */ 242 public int hashCode() { 243 int result = 17; 244 int c = this.year; 245 result = 37 * result + c; 246 return result; 247 } 248 249 /** 250 * Returns an integer indicating the order of this <code>Year</code> object 251 * relative to the specified object: 252 * 253 * negative == before, zero == same, positive == after. 254 * 255 * @param o1 the object to compare. 256 * 257 * @return negative == before, zero == same, positive == after. 258 */ 259 public int compareTo(Object o1) { 260 261 int result; 262 263 // CASE 1 : Comparing to another Year object 264 // ----------------------------------------- 265 if (o1 instanceof Year) { 266 Year y = (Year) o1; 267 result = this.year - y.getYear(); 268 } 269 270 // CASE 2 : Comparing to another TimePeriod object 271 // ----------------------------------------------- 272 else if (o1 instanceof RegularTimePeriod) { 273 // more difficult case - evaluate later... 274 result = 0; 275 } 276 277 // CASE 3 : Comparing to a non-TimePeriod object 278 // --------------------------------------------- 279 else { 280 // consider time periods to be ordered after general objects 281 result = 1; 282 } 283 284 return result; 285 286 } 287 288 /** 289 * Returns a string representing the year.. 290 * 291 * @return A string representing the year. 292 */ 293 public String toString() { 294 return Integer.toString(this.year); 295 } 296 297 /** 298 * Parses the string argument as a year. 299 * <P> 300 * The string format is YYYY. 301 * 302 * @param s a string representing the year. 303 * 304 * @return <code>null</code> if the string is not parseable, the year 305 * otherwise. 306 */ 307 public static Year parseYear(String s) { 308 309 // parse the string... 310 int y; 311 try { 312 y = Integer.parseInt(s.trim()); 313 } 314 catch (NumberFormatException e) { 315 throw new TimePeriodFormatException("Cannot parse string."); 316 } 317 318 // create the year... 319 try { 320 return new Year(y); 321 } 322 catch (IllegalArgumentException e) { 323 throw new TimePeriodFormatException("Year outside valid range."); 324 } 325 } 326 327 }