    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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-2008, 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     * 16-Sep-2008 : Extended range of valid years, and deprecated
    057     *               DEFAULT_TIME_ZONE (DG);
    058     * 25-Nov-2008 : Added new constructor with Locale (DG);
    059     *
    060     */
    061
    062    package org.jfree.data.time;
    063
    064    import java.io.Serializable;
    065    import java.util.Calendar;
    066    import java.util.Date;
    067    import java.util.Locale;
    068    import java.util.TimeZone;
    069
    070    /**
    071     * Represents a year in the range -9999 to 9999.  This class is immutable,
    072     * which is a requirement for all {@link RegularTimePeriod} subclasses.
    073     */
    074    public class Year extends RegularTimePeriod implements Serializable {
    075
    076        /**
    077         * The minimum year value.
    078         *
    079         * @since 1.0.11
    080         */
    081        public static final int MINIMUM_YEAR = -9999;
    082
    083        /**
    084         * The maximum year value.
    085         *
    086         * @since 1.0.11
    087         */
    088        public static final int MAXIMUM_YEAR = 9999;
    089
    090        /** For serialization. */
    091        private static final long serialVersionUID = -7659990929736074836L;
    092
    093        /** The year. */
    094        private short year;
    095
    096        /** The first millisecond. */
    097        private long firstMillisecond;
    098
    099        /** The last millisecond. */
    100        private long lastMillisecond;
    101
    102        /**
    103         * Creates a new <code>Year</code>, based on the current system date/time.
    104         */
    105        public Year() {
    106            this(new Date());
    107        }
    108
    109        /**
    110         * Creates a time period representing a single year.
    111         *
    112         * @param year  the year.
    113         */
    114        public Year(int year) {
    115            if ((year < Year.MINIMUM_YEAR) || (year > Year.MAXIMUM_YEAR)) {
    116                throw new IllegalArgumentException(
    117                    "Year constructor: year (" + year + ") outside valid range.");
    118            }
    119            this.year = (short) year;
    120            peg(Calendar.getInstance());
    121        }
    122
    123        /**
    124         * Creates a new <code>Year</code>, based on a particular instant in time,
    125         * using the default time zone.
    126         *
    127         * @param time  the time (<code>null</code> not permitted).
    128         *
    129         * @see #Year(Date, TimeZone)
    130         */
    131        public Year(Date time) {
    132            this(time, TimeZone.getDefault());
    133        }
    134
    135        /**
    136         * Constructs a year, based on a particular instant in time and a time zone.
    137         *
    138         * @param time  the time (<code>null</code> not permitted).
    139         * @param zone  the time zone.
    140         *
    141         * @deprecated Since 1.0.12, use {@link #Year(Date, TimeZone, Locale)}
    142         *     instead.
    143         */
    144        public Year(Date time, TimeZone zone) {
    145            this(time, zone, Locale.getDefault());
    146        }
    147
    148        /**
    149         * Creates a new <code>Year</code> instance, for the specified time zone
    150         * and locale.
    151         *
    152         * @param time  the current time (<code>null</code> not permitted).
    153         * @param zone  the time zone.
    154         * @param locale  the locale.
    155         *
    156         * @since 1.0.12
    157         */
    158        public Year(Date time, TimeZone zone, Locale locale) {
    159            Calendar calendar = Calendar.getInstance(zone, locale);
    160            calendar.setTime(time);
    161            this.year = (short) calendar.get(Calendar.YEAR);
    162            peg(calendar);
    163        }
    164
    165        /**
    166         * Returns the year.
    167         *
    168         * @return The year.
    169         */
    170        public int getYear() {
    171            return this.year;
    172        }
    173
    174        /**
    175         * Returns the first millisecond of the year.  This will be determined
    176         * relative to the time zone specified in the constructor, or in the
    177         * calendar instance passed in the most recent call to the
    178         * {@link #peg(Calendar)} method.
    179         *
    180         * @return The first millisecond of the year.
    181         *
    182         * @see #getLastMillisecond()
    183         */
    184        public long getFirstMillisecond() {
    185            return this.firstMillisecond;
    186        }
    187
    188        /**
    189         * Returns the last millisecond of the year.  This will be
    190         * determined relative to the time zone specified in the constructor, or
    191         * in the calendar instance passed in the most recent call to the
    192         * {@link #peg(Calendar)} method.
    193         *
    194         * @return The last millisecond of the year.
    195         *
    196         * @see #getFirstMillisecond()
    197         */
    198        public long getLastMillisecond() {
    199            return this.lastMillisecond;
    200        }
    201
    202        /**
    203         * Recalculates the start date/time and end date/time for this time period
    204         * relative to the supplied calendar (which incorporates a time zone).
    205         *
    206         * @param calendar  the calendar (<code>null</code> not permitted).
    207         *
    208         * @since 1.0.3
    209         */
    210        public void peg(Calendar calendar) {
    211            this.firstMillisecond = getFirstMillisecond(calendar);
    212            this.lastMillisecond = getLastMillisecond(calendar);
    213        }
    214
    215        /**
    216         * Returns the year preceding this one.
    217         *
    218         * @return The year preceding this one (or <code>null</code> if the
    219         *         current year is -9999).
    220         */
    221        public RegularTimePeriod previous() {
    222            if (this.year > Year.MINIMUM_YEAR) {
    223                return new Year(this.year - 1);
    224            }
    225            else {
    226                return null;
    227            }
    228        }
    229
    230        /**
    231         * Returns the year following this one.
    232         *
    233         * @return The year following this one (or <code>null</code> if the current
    234         *         year is 9999).
    235         */
    236        public RegularTimePeriod next() {
    237            if (this.year < Year.MAXIMUM_YEAR) {
    238                return new Year(this.year + 1);
    239            }
    240            else {
    241                return null;
    242            }
    243        }
    244
    245        /**
    246         * Returns a serial index number for the year.
    247         * <P>
    248         * The implementation simply returns the year number (e.g. 2002).
    249         *
    250         * @return The serial index number.
    251         */
    252        public long getSerialIndex() {
    253            return this.year;
    254        }
    255
    256        /**
    257         * Returns the first millisecond of the year, evaluated using the supplied
    258         * calendar (which determines the time zone).
    259         *
    260         * @param calendar  the calendar (<code>null</code> not permitted).
    261         *
    262         * @return The first millisecond of the year.
    263         *
    264         * @throws NullPointerException if <code>calendar</code> is
    265         *     <code>null</code>.
    266         */
    267        public long getFirstMillisecond(Calendar calendar) {
    268            calendar.set(this.year, Calendar.JANUARY, 1, 0, 0, 0);
    269            calendar.set(Calendar.MILLISECOND, 0);
    270            // in the following line, we'd rather call calendar.getTimeInMillis()
    271            // to avoid object creation, but that isn't supported in Java 1.3.1
    272            return calendar.getTime().getTime();
    273        }
    274
    275        /**
    276         * Returns the last millisecond of the year, evaluated using the supplied
    277         * calendar (which determines the time zone).
    278         *
    279         * @param calendar  the calendar (<code>null</code> not permitted).
    280         *
    281         * @return The last millisecond of the year.
    282         *
    283         * @throws NullPointerException if <code>calendar</code> is
    284         *     <code>null</code>.
    285         */
    286        public long getLastMillisecond(Calendar calendar) {
    287            calendar.set(this.year, Calendar.DECEMBER, 31, 23, 59, 59);
    288            calendar.set(Calendar.MILLISECOND, 999);
    289            // in the following line, we'd rather call calendar.getTimeInMillis()
    290            // to avoid object creation, but that isn't supported in Java 1.3.1
    291            return calendar.getTime().getTime();
    292        }
    293
    294        /**
    295         * Tests the equality of this <code>Year</code> object to an arbitrary
    296         * object.  Returns <code>true</code> if the target is a <code>Year</code>
    297         * instance representing the same year as this object.  In all other cases,
    298         * returns <code>false</code>.
    299         *
    300         * @param obj  the object (<code>null</code> permitted).
    301         *
    302         * @return <code>true</code> if the year of this and the object are the
    303         *         same.
    304         */
    305        public boolean equals(Object obj) {
    306            if (obj == this) {
    307                return true;
    308            }
    309            if (!(obj instanceof Year)) {
    310                return false;
    311            }
    312            Year that = (Year) obj;
    313            return (this.year == that.year);
    314        }
    315
    316        /**
    317         * Returns a hash code for this object instance.  The approach described by
    318         * Joshua Bloch in "Effective Java" has been used here:
    319         * <p>
    320         * <code>http://developer.java.sun.com/developer/Books/effectivejava
    321         *     /Chapter3.pdf</code>
    322         *
    323         * @return A hash code.
    324         */
    325        public int hashCode() {
    326            int result = 17;
    327            int c = this.year;
    328            result = 37 * result + c;
    329            return result;
    330        }
    331
    332        /**
    333         * Returns an integer indicating the order of this <code>Year</code> object
    334         * relative to the specified object:
    335         *
    336         * negative == before, zero == same, positive == after.
    337         *
    338         * @param o1  the object to compare.
    339         *
    340         * @return negative == before, zero == same, positive == after.
    341         */
    342        public int compareTo(Object o1) {
    343
    344            int result;
    345
    346            // CASE 1 : Comparing to another Year object
    347            // -----------------------------------------
    348            if (o1 instanceof Year) {
    349                Year y = (Year) o1;
    350                result = this.year - y.getYear();
    351            }
    352
    353            // CASE 2 : Comparing to another TimePeriod object
    354            // -----------------------------------------------
    355            else if (o1 instanceof RegularTimePeriod) {
    356                // more difficult case - evaluate later...
    357                result = 0;
    358            }
    359
    360            // CASE 3 : Comparing to a non-TimePeriod object
    361            // ---------------------------------------------
    362            else {
    363                // consider time periods to be ordered after general objects
    364                result = 1;
    365            }
    366
    367            return result;
    368
    369        }
    370
    371        /**
    372         * Returns a string representing the year..
    373         *
    374         * @return A string representing the year.
    375         */
    376        public String toString() {
    377            return Integer.toString(this.year);
    378        }
    379
    380        /**
    381         * Parses the string argument as a year.
    382         * <P>
    383         * The string format is YYYY.
    384         *
    385         * @param s  a string representing the year.
    386         *
    387         * @return <code>null</code> if the string is not parseable, the year
    388         *         otherwise.
    389         */
    390        public static Year parseYear(String s) {
    391
    392            // parse the string...
    393            int y;
    394            try {
    395                y = Integer.parseInt(s.trim());
    396            }
    397            catch (NumberFormatException e) {
    398                throw new TimePeriodFormatException("Cannot parse string.");
    399            }
    400
    401            // create the year...
    402            try {
    403                return new Year(y);
    404            }
    405            catch (IllegalArgumentException e) {
    406                throw new TimePeriodFormatException("Year outside valid range.");
    407            }
    408        }
    409
    410    }