Source for org.jfree.data.time.Year

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ---------
  28:  * Year.java
  29:  * ---------
  30:  * (C) Copyright 2001-2007, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 11-Oct-2001 : Version 1 (DG);
  38:  * 14-Nov-2001 : Override for toString() method (DG);
  39:  * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG);
  40:  * 29-Jan-2002 : Worked on parseYear() method (DG);
  41:  * 14-Feb-2002 : Fixed bug in Year(Date) constructor (DG);
  42:  * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 
  43:  *               evaluate with reference to a particular time zone (DG);
  44:  * 19-Mar-2002 : Changed API for TimePeriod classes (DG);
  45:  * 10-Sep-2002 : Added getSerialIndex() method (DG);
  46:  * 04-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  47:  * 10-Jan-2003 : Changed base class and method names (DG);
  48:  * 05-Mar-2003 : Fixed bug in getFirstMillisecond() picked up in JUnit 
  49:  *               tests (DG);
  50:  * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented 
  51:  *               Serializable (DG);
  52:  * 21-Oct-2003 : Added hashCode() method (DG);
  53:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  54:  * 05-Oct-2006 : Updated API docs (DG);
  55:  * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
  56:  * 
  57:  */
  58: 
  59: package org.jfree.data.time;
  60: 
  61: import java.io.Serializable;
  62: import java.util.Calendar;
  63: import java.util.Date;
  64: import java.util.TimeZone;
  65: 
  66: import org.jfree.date.SerialDate;
  67: 
  68: /**
  69:  * Represents a year in the range 1900 to 9999.  This class is immutable, which
  70:  * is a requirement for all {@link RegularTimePeriod} subclasses.
  71:  */
  72: public class Year extends RegularTimePeriod implements Serializable {
  73: 
  74:     /** For serialization. */
  75:     private static final long serialVersionUID = -7659990929736074836L;
  76:     
  77:     /** The year. */
  78:     private short year;
  79: 
  80:     /** The first millisecond. */
  81:     private long firstMillisecond;
  82:     
  83:     /** The last millisecond. */
  84:     private long lastMillisecond;
  85:     
  86:     /**
  87:      * Creates a new <code>Year</code>, based on the current system date/time.
  88:      */
  89:     public Year() {
  90:         this(new Date());
  91:     }
  92: 
  93:     /**
  94:      * Creates a time period representing a single year.
  95:      *
  96:      * @param year  the year.
  97:      */
  98:     public Year(int year) {
  99:         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: }