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 }