Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2005, 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: * Quarter.java 29: * ------------ 30: * (C) Copyright 2001-2005, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * $Id: Quarter.java,v 1.6.2.2 2005/12/10 20:34:21 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 11-Oct-2001 : Version 1 (DG); 40: * 18-Dec-2001 : Changed order of parameters in constructor (DG); 41: * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG); 42: * 29-Jan-2002 : Added a new method parseQuarter(String) (DG); 43: * 14-Feb-2002 : Fixed bug in Quarter(Date) constructor (DG); 44: * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 45: * evaluate with reference to a particular time zone (DG); 46: * 19-Mar-2002 : Changed API for TimePeriod classes (DG); 47: * 24-Jun-2002 : Removed main method (just test code) (DG); 48: * 10-Sep-2002 : Added getSerialIndex() method (DG); 49: * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 50: * 10-Jan-2003 : Changed base class and method names (DG); 51: * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented 52: * Serializable (DG); 53: * 21-Oct-2003 : Added hashCode() method (DG); 54: * 10-Dec-2005 : Fixed argument checking bug (1377239) in constructor (DG); 55: * 56: */ 57: 58: package org.jfree.data.time; 59: 60: import java.io.Serializable; 61: import java.util.Calendar; 62: import java.util.Date; 63: import java.util.TimeZone; 64: 65: import org.jfree.date.MonthConstants; 66: import org.jfree.date.SerialDate; 67: 68: /** 69: * Defines a quarter (in a given year). The range supported is Q1 1900 to 70: * Q4 9999. This class is immutable, which is a requirement for all 71: * {@link RegularTimePeriod} subclasses. 72: */ 73: public class Quarter extends RegularTimePeriod implements Serializable { 74: 75: /** For serialization. */ 76: private static final long serialVersionUID = 3810061714380888671L; 77: 78: /** Constant for quarter 1. */ 79: public static final int FIRST_QUARTER = 1; 80: 81: /** Constant for quarter 4. */ 82: public static final int LAST_QUARTER = 4; 83: 84: /** The first month in each quarter. */ 85: public static final int[] FIRST_MONTH_IN_QUARTER = { 86: 0, MonthConstants.JANUARY, MonthConstants.APRIL, MonthConstants.JULY, 87: MonthConstants.OCTOBER 88: }; 89: 90: /** The last month in each quarter. */ 91: public static final int[] LAST_MONTH_IN_QUARTER = { 92: 0, MonthConstants.MARCH, MonthConstants.JUNE, MonthConstants.SEPTEMBER, 93: MonthConstants.DECEMBER 94: }; 95: 96: /** The year in which the quarter falls. */ 97: private Year year; 98: 99: /** The quarter (1-4). */ 100: private int quarter; 101: 102: /** 103: * Constructs a new Quarter, based on the current system date/time. 104: */ 105: public Quarter() { 106: this(new Date()); 107: } 108: 109: /** 110: * Constructs a new quarter. 111: * 112: * @param year the year (1900 to 9999). 113: * @param quarter the quarter (1 to 4). 114: */ 115: public Quarter(int quarter, int year) { 116: this(quarter, new Year(year)); 117: } 118: 119: /** 120: * Constructs a new quarter. 121: * 122: * @param quarter the quarter (1 to 4). 123: * @param year the year (1900 to 9999). 124: */ 125: public Quarter(int quarter, Year year) { 126: if ((quarter < FIRST_QUARTER) || (quarter > LAST_QUARTER)) { 127: throw new IllegalArgumentException("Quarter outside valid range."); 128: } 129: this.year = year; 130: this.quarter = quarter; 131: } 132: 133: /** 134: * Constructs a new Quarter, based on a date/time and the default time zone. 135: * 136: * @param time the date/time. 137: */ 138: public Quarter(Date time) { 139: this(time, RegularTimePeriod.DEFAULT_TIME_ZONE); 140: } 141: 142: /** 143: * Constructs a Quarter, based on a date/time and time zone. 144: * 145: * @param time the date/time. 146: * @param zone the zone. 147: */ 148: public Quarter(Date time, TimeZone zone) { 149: 150: Calendar calendar = Calendar.getInstance(zone); 151: calendar.setTime(time); 152: int month = calendar.get(Calendar.MONTH) + 1; 153: this.quarter = SerialDate.monthCodeToQuarter(month); 154: this.year = new Year(calendar.get(Calendar.YEAR)); 155: 156: } 157: 158: /** 159: * Returns the quarter. 160: * 161: * @return The quarter. 162: */ 163: public int getQuarter() { 164: return this.quarter; 165: } 166: 167: /** 168: * Returns the year. 169: * 170: * @return The year. 171: */ 172: public Year getYear() { 173: return this.year; 174: } 175: 176: /** 177: * Returns the quarter preceding this one. 178: * 179: * @return The quarter preceding this one (or null if this is Q1 1900). 180: */ 181: public RegularTimePeriod previous() { 182: 183: Quarter result; 184: if (this.quarter > FIRST_QUARTER) { 185: result = new Quarter(this.quarter - 1, this.year); 186: } 187: else { 188: Year prevYear = (Year) this.year.previous(); 189: if (prevYear != null) { 190: result = new Quarter(LAST_QUARTER, prevYear); 191: } 192: else { 193: result = null; 194: } 195: } 196: return result; 197: 198: } 199: 200: /** 201: * Returns the quarter following this one. 202: * 203: * @return The quarter following this one (or null if this is Q4 9999). 204: */ 205: public RegularTimePeriod next() { 206: 207: Quarter result; 208: if (this.quarter < LAST_QUARTER) { 209: result = new Quarter(this.quarter + 1, this.year); 210: } 211: else { 212: Year nextYear = (Year) this.year.next(); 213: if (nextYear != null) { 214: result = new Quarter(FIRST_QUARTER, nextYear); 215: } 216: else { 217: result = null; 218: } 219: } 220: return result; 221: 222: } 223: 224: /** 225: * Returns a serial index number for the quarter. 226: * 227: * @return The serial index number. 228: */ 229: public long getSerialIndex() { 230: return this.year.getYear() * 4L + this.quarter; 231: } 232: 233: /** 234: * Tests the equality of this Quarter object to an arbitrary object. 235: * Returns true if the target is a Quarter instance representing the same 236: * quarter as this object. In all other cases, returns false. 237: * 238: * @param obj the object. 239: * 240: * @return <code>true</code> if quarter and year of this and the object are 241: * the same. 242: */ 243: public boolean equals(Object obj) { 244: 245: if (obj != null) { 246: if (obj instanceof Quarter) { 247: Quarter target = (Quarter) obj; 248: return ( 249: (this.quarter == target.getQuarter()) 250: && (this.year.equals(target.getYear())) 251: ); 252: } 253: else { 254: return false; 255: } 256: } 257: else { 258: return false; 259: } 260: 261: } 262: 263: /** 264: * Returns a hash code for this object instance. The approach described by 265: * Joshua Bloch in "Effective Java" has been used here: 266: * <p> 267: * <code>http://developer.java.sun.com/developer/Books/effectivejava 268: * /Chapter3.pdf</code> 269: * 270: * @return A hash code. 271: */ 272: public int hashCode() { 273: int result = 17; 274: result = 37 * result + this.quarter; 275: result = 37 * result + this.year.hashCode(); 276: return result; 277: } 278: 279: /** 280: * Returns an integer indicating the order of this Quarter object relative 281: * to the specified object: 282: * 283: * negative == before, zero == same, positive == after. 284: * 285: * @param o1 the object to compare 286: * 287: * @return negative == before, zero == same, positive == after. 288: */ 289: public int compareTo(Object o1) { 290: 291: int result; 292: 293: // CASE 1 : Comparing to another Quarter object 294: // -------------------------------------------- 295: if (o1 instanceof Quarter) { 296: Quarter q = (Quarter) o1; 297: result = this.year.getYear() - q.getYear().getYear(); 298: if (result == 0) { 299: result = this.quarter - q.getQuarter(); 300: } 301: } 302: 303: // CASE 2 : Comparing to another TimePeriod object 304: // ----------------------------------------------- 305: else if (o1 instanceof RegularTimePeriod) { 306: // more difficult case - evaluate later... 307: result = 0; 308: } 309: 310: // CASE 3 : Comparing to a non-TimePeriod object 311: // --------------------------------------------- 312: else { 313: // consider time periods to be ordered after general objects 314: result = 1; 315: } 316: 317: return result; 318: 319: } 320: 321: /** 322: * Returns a string representing the quarter (e.g. "Q1/2002"). 323: * 324: * @return A string representing the quarter. 325: */ 326: public String toString() { 327: return "Q" + this.quarter + "/" + this.year; 328: } 329: 330: /** 331: * Returns the first millisecond in the Quarter, evaluated using the 332: * supplied calendar (which determines the time zone). 333: * 334: * @param calendar the calendar. 335: * 336: * @return The first millisecond in the Quarter. 337: */ 338: public long getFirstMillisecond(Calendar calendar) { 339: 340: int month = Quarter.FIRST_MONTH_IN_QUARTER[this.quarter]; 341: Day first = new Day(1, month, this.year.getYear()); 342: return first.getFirstMillisecond(calendar); 343: 344: } 345: 346: /** 347: * Returns the last millisecond of the Quarter, evaluated using the 348: * supplied calendar (which determines the time zone). 349: * 350: * @param calendar the calendar. 351: * 352: * @return The last millisecond of the Quarter. 353: */ 354: public long getLastMillisecond(Calendar calendar) { 355: 356: int month = Quarter.LAST_MONTH_IN_QUARTER[this.quarter]; 357: int eom = SerialDate.lastDayOfMonth(month, this.year.getYear()); 358: Day last = new Day(eom, month, this.year.getYear()); 359: return last.getLastMillisecond(calendar); 360: 361: } 362: 363: /** 364: * Parses the string argument as a quarter. 365: * <P> 366: * This method should accept the following formats: "YYYY-QN" and "QN-YYYY", 367: * where the "-" can be a space, a forward-slash (/), comma or a dash (-). 368: * @param s A string representing the quarter. 369: * 370: * @return The quarter. 371: */ 372: public static Quarter parseQuarter(String s) { 373: 374: // find the Q and the integer following it (remove both from the 375: // string)... 376: int i = s.indexOf("Q"); 377: if (i == -1) { 378: throw new TimePeriodFormatException("Missing Q."); 379: } 380: 381: if (i == s.length() - 1) { 382: throw new TimePeriodFormatException("Q found at end of string."); 383: } 384: 385: String qstr = s.substring(i + 1, i + 2); 386: int quarter = Integer.parseInt(qstr); 387: String remaining = s.substring(0, i) + s.substring(i + 2, s.length()); 388: 389: // replace any / , or - with a space 390: remaining = remaining.replace('/', ' '); 391: remaining = remaining.replace(',', ' '); 392: remaining = remaining.replace('-', ' '); 393: 394: // parse the string... 395: Year year = Year.parseYear(remaining.trim()); 396: Quarter result = new Quarter(quarter, year); 397: return result; 398: 399: } 400: 401: }