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
106: + ") outside valid range.");
107: }
108: this .year = (short) year;
109: peg(Calendar.getInstance());
110: }
111:
112: /**
113: * Creates a new <code>Year</code>, based on a particular instant in time,
114: * using the default time zone.
115: *
116: * @param time the time (<code>null</code> not permitted).
117: */
118: public Year(Date time) {
119: this (time, RegularTimePeriod.DEFAULT_TIME_ZONE);
120: }
121:
122: /**
123: * Constructs a year, based on a particular instant in time and a time zone.
124: *
125: * @param time the time.
126: * @param zone the time zone.
127: */
128: public Year(Date time, TimeZone zone) {
129: Calendar calendar = Calendar.getInstance(zone);
130: calendar.setTime(time);
131: this .year = (short) calendar.get(Calendar.YEAR);
132: peg(calendar);
133: }
134:
135: /**
136: * Returns the year.
137: *
138: * @return The year.
139: */
140: public int getYear() {
141: return this .year;
142: }
143:
144: /**
145: * Returns the first millisecond of the year. This will be determined
146: * relative to the time zone specified in the constructor, or in the
147: * calendar instance passed in the most recent call to the
148: * {@link #peg(Calendar)} method.
149: *
150: * @return The first millisecond of the year.
151: *
152: * @see #getLastMillisecond()
153: */
154: public long getFirstMillisecond() {
155: return this .firstMillisecond;
156: }
157:
158: /**
159: * Returns the last millisecond of the year. This will be
160: * determined relative to the time zone specified in the constructor, or
161: * in the calendar instance passed in the most recent call to the
162: * {@link #peg(Calendar)} method.
163: *
164: * @return The last millisecond of the year.
165: *
166: * @see #getFirstMillisecond()
167: */
168: public long getLastMillisecond() {
169: return this .lastMillisecond;
170: }
171:
172: /**
173: * Recalculates the start date/time and end date/time for this time period
174: * relative to the supplied calendar (which incorporates a time zone).
175: *
176: * @param calendar the calendar (<code>null</code> not permitted).
177: *
178: * @since 1.0.3
179: */
180: public void peg(Calendar calendar) {
181: this .firstMillisecond = getFirstMillisecond(calendar);
182: this .lastMillisecond = getLastMillisecond(calendar);
183: }
184:
185: /**
186: * Returns the year preceding this one.
187: *
188: * @return The year preceding this one (or <code>null</code> if the
189: * current year is 1900).
190: */
191: public RegularTimePeriod previous() {
192: if (this .year > SerialDate.MINIMUM_YEAR_SUPPORTED) {
193: return new Year(this .year - 1);
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: } else {
209: return null;
210: }
211: }
212:
213: /**
214: * Returns a serial index number for the year.
215: * <P>
216: * The implementation simply returns the year number (e.g. 2002).
217: *
218: * @return The serial index number.
219: */
220: public long getSerialIndex() {
221: return this .year;
222: }
223:
224: /**
225: * Returns the first millisecond of the year, evaluated using the supplied
226: * calendar (which determines the time zone).
227: *
228: * @param calendar the calendar (<code>null</code> not permitted).
229: *
230: * @return The first millisecond of the year.
231: *
232: * @throws NullPointerException if <code>calendar</code> is
233: * <code>null</code>.
234: */
235: public long getFirstMillisecond(Calendar calendar) {
236: calendar.set(this .year, Calendar.JANUARY, 1, 0, 0, 0);
237: calendar.set(Calendar.MILLISECOND, 0);
238: // in the following line, we'd rather call calendar.getTimeInMillis()
239: // to avoid object creation, but that isn't supported in Java 1.3.1
240: return calendar.getTime().getTime();
241: }
242:
243: /**
244: * Returns the last millisecond of the year, evaluated using the supplied
245: * calendar (which determines the time zone).
246: *
247: * @param calendar the calendar (<code>null</code> not permitted).
248: *
249: * @return The last millisecond of the year.
250: *
251: * @throws NullPointerException if <code>calendar</code> is
252: * <code>null</code>.
253: */
254: public long getLastMillisecond(Calendar calendar) {
255: calendar.set(this .year, Calendar.DECEMBER, 31, 23, 59, 59);
256: calendar.set(Calendar.MILLISECOND, 999);
257: // in the following line, we'd rather call calendar.getTimeInMillis()
258: // to avoid object creation, but that isn't supported in Java 1.3.1
259: return calendar.getTime().getTime();
260: }
261:
262: /**
263: * Tests the equality of this <code>Year</code> object to an arbitrary
264: * object. Returns <code>true</code> if the target is a <code>Year</code>
265: * instance representing the same year as this object. In all other cases,
266: * returns <code>false</code>.
267: *
268: * @param object the object (<code>null</code> permitted).
269: *
270: * @return <code>true</code> if the year of this and the object are the
271: * same.
272: */
273: public boolean equals(Object object) {
274: if (object != null) {
275: if (object instanceof Year) {
276: Year target = (Year) object;
277: return (this .year == target.getYear());
278: } else {
279: return false;
280: }
281: } else {
282: return false;
283: }
284: }
285:
286: /**
287: * Returns a hash code for this object instance. The approach described by
288: * Joshua Bloch in "Effective Java" has been used here:
289: * <p>
290: * <code>http://developer.java.sun.com/developer/Books/effectivejava
291: * /Chapter3.pdf</code>
292: *
293: * @return A hash code.
294: */
295: public int hashCode() {
296: int result = 17;
297: int c = this .year;
298: result = 37 * result + c;
299: return result;
300: }
301:
302: /**
303: * Returns an integer indicating the order of this <code>Year</code> object
304: * relative to the specified object:
305: *
306: * negative == before, zero == same, positive == after.
307: *
308: * @param o1 the object to compare.
309: *
310: * @return negative == before, zero == same, positive == after.
311: */
312: public int compareTo(Object o1) {
313:
314: int result;
315:
316: // CASE 1 : Comparing to another Year object
317: // -----------------------------------------
318: if (o1 instanceof Year) {
319: Year y = (Year) o1;
320: result = this .year - y.getYear();
321: }
322:
323: // CASE 2 : Comparing to another TimePeriod object
324: // -----------------------------------------------
325: else if (o1 instanceof RegularTimePeriod) {
326: // more difficult case - evaluate later...
327: result = 0;
328: }
329:
330: // CASE 3 : Comparing to a non-TimePeriod object
331: // ---------------------------------------------
332: else {
333: // consider time periods to be ordered after general objects
334: result = 1;
335: }
336:
337: return result;
338:
339: }
340:
341: /**
342: * Returns a string representing the year..
343: *
344: * @return A string representing the year.
345: */
346: public String toString() {
347: return Integer.toString(this .year);
348: }
349:
350: /**
351: * Parses the string argument as a year.
352: * <P>
353: * The string format is YYYY.
354: *
355: * @param s a string representing the year.
356: *
357: * @return <code>null</code> if the string is not parseable, the year
358: * otherwise.
359: */
360: public static Year parseYear(String s) {
361:
362: // parse the string...
363: int y;
364: try {
365: y = Integer.parseInt(s.trim());
366: } catch (NumberFormatException e) {
367: throw new TimePeriodFormatException("Cannot parse string.");
368: }
369:
370: // create the year...
371: try {
372: return new Year(y);
373: } catch (IllegalArgumentException e) {
374: throw new TimePeriodFormatException(
375: "Year outside valid range.");
376: }
377: }
378:
379: }
|