    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2007, 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-2007, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes
    036     * -------
    037     * 11-Oct-2001 : Version 1 (DG);
    038     * 14-Nov-2001 : Override for toString() method (DG);
    039     * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG);
    040     * 29-Jan-2002 : Worked on parseYear() method (DG);
    041     * 14-Feb-2002 : Fixed bug in Year(Date) constructor (DG);
    042     * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to
    043     *               evaluate with reference to a particular time zone (DG);
    044     * 19-Mar-2002 : Changed API for TimePeriod classes (DG);
    045     * 10-Sep-2002 : Added getSerialIndex() method (DG);
    046     * 04-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    047     * 10-Jan-2003 : Changed base class and method names (DG);
    048     * 05-Mar-2003 : Fixed bug in getFirstMillisecond() picked up in JUnit
    049     *               tests (DG);
    050     * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented
    051     *               Serializable (DG);
    052     * 21-Oct-2003 : Added hashCode() method (DG);
    053     * ------------- JFREECHART 1.0.x ---------------------------------------------
    054     * 05-Oct-2006 : Updated API docs (DG);
    055     * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
    056     *
    057     */
    058
    059    package org.jfree.data.time;
    060
    061    import java.io.Serializable;
    062    import java.util.Calendar;
    063    import java.util.Date;
    064    import java.util.TimeZone;
    065
    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 short year;
    079
    080        /** The first millisecond. */
    081        private long firstMillisecond;
    082
    083        /** The last millisecond. */
    084        private long lastMillisecond;
    085
    086        /**
    087         * Creates a new <code>Year</code>, based on the current system date/time.
    088         */
    089        public Year() {
    090            this(new Date());
    091        }
    092
    093        /**
    094         * Creates a time period representing a single year.
    095         *
    096         * @param year  the year.
    097         */
    098        public Year(int year) {
    099            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    }