    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.1 2005/10/25 21:35:24 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.
    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    }