    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     * DefaultHighLowDataset.java
    029     * --------------------------
    030     * (C) Copyright 2002-2008, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes
    036     * -------
    037     * 21-Mar-2002 : Version 1 (DG);
    038     * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    039     * 06-May-2004 : Now extends AbstractXYDataset and added new methods from
    040     *               HighLowDataset (DG);
    041     * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
    042     *               getYValue() (DG);
    043     * ------------- JFREECHART 1.0.x ---------------------------------------------
    044     * 28-Nov-2006 : Added equals() method override (DG);
    045     * 22-Apr-2008 : Implemented PublicCloneable (DG);
    046     *
    047     */
    048
    049    package org.jfree.data.xy;
    050
    051    import java.util.Arrays;
    052    import java.util.Date;
    053
    054    import org.jfree.util.PublicCloneable;
    055
    056    /**
    057     * A simple implementation of the {@link OHLCDataset} interface.  See also
    058     * the {@link DefaultOHLCDataset} class, which provides another implementation
    059     * that is very similar.
    060     */
    061    public class DefaultHighLowDataset extends AbstractXYDataset
    062            implements OHLCDataset, PublicCloneable {
    063
    064        /** The series key. */
    065        private Comparable seriesKey;
    066
    067        /** Storage for the dates. */
    068        private Date[] date;
    069
    070        /** Storage for the high values. */
    071        private Number[] high;
    072
    073        /** Storage for the low values. */
    074        private Number[] low;
    075
    076        /** Storage for the open values. */
    077        private Number[] open;
    078
    079        /** Storage for the close values. */
    080        private Number[] close;
    081
    082        /** Storage for the volume values. */
    083        private Number[] volume;
    084
    085        /**
    086         * Constructs a new high/low/open/close dataset.
    087         * <p>
    088         * The current implementation allows only one series in the dataset.
    089         * This may be extended in a future version.
    090         *
    091         * @param seriesKey  the key for the series (<code>null</code> not
    092         *     permitted).
    093         * @param date  the dates (<code>null</code> not permitted).
    094         * @param high  the high values (<code>null</code> not permitted).
    095         * @param low  the low values (<code>null</code> not permitted).
    096         * @param open  the open values (<code>null</code> not permitted).
    097         * @param close  the close values (<code>null</code> not permitted).
    098         * @param volume  the volume values (<code>null</code> not permitted).
    099         */
    100        public DefaultHighLowDataset(Comparable seriesKey, Date[] date,
    101                double[] high, double[] low, double[] open, double[] close,
    102                double[] volume) {
    103
    104            if (seriesKey == null) {
    105                throw new IllegalArgumentException("Null 'series' argument.");
    106            }
    107            if (date == null) {
    108                throw new IllegalArgumentException("Null 'date' argument.");
    109            }
    110            this.seriesKey = seriesKey;
    111            this.date = date;
    112            this.high = createNumberArray(high);
    113            this.low = createNumberArray(low);
    114            this.open = createNumberArray(open);
    115            this.close = createNumberArray(close);
    116            this.volume = createNumberArray(volume);
    117
    118        }
    119
    120        /**
    121         * Returns the key for the series stored in this dataset.
    122         *
    123         * @param series  the index of the series (ignored, this dataset supports
    124         *     only one series and this method always returns the key for series 0).
    125         *
    126         * @return The series key (never <code>null</code>).
    127         */
    128        public Comparable getSeriesKey(int series) {
    129            return this.seriesKey;
    130        }
    131
    132        /**
    133         * Returns the x-value for one item in a series.  The value returned is a
    134         * <code>Long</code> instance generated from the underlying
    135         * <code>Date</code> object.  To avoid generating a new object instance,
    136         * you might prefer to call {@link #getXValue(int, int)}.
    137         *
    138         * @param series  the series (zero-based index).
    139         * @param item  the item (zero-based index).
    140         *
    141         * @return The x-value.
    142         *
    143         * @see #getXValue(int, int)
    144         * @see #getXDate(int, int)
    145         */
    146        public Number getX(int series, int item) {
    147            return new Long(this.date[item].getTime());
    148        }
    149
    150        /**
    151         * Returns the x-value for one item in a series, as a Date.
    152         * <p>
    153         * This method is provided for convenience only.
    154         *
    155         * @param series  the series (zero-based index).
    156         * @param item  the item (zero-based index).
    157         *
    158         * @return The x-value as a Date.
    159         *
    160         * @see #getX(int, int)
    161         */
    162        public Date getXDate(int series, int item) {
    163            return this.date[item];
    164        }
    165
    166        /**
    167         * Returns the y-value for one item in a series.
    168         * <p>
    169         * This method (from the {@link XYDataset} interface) is mapped to the
    170         * {@link #getCloseValue(int, int)} method.
    171         *
    172         * @param series  the series (zero-based index).
    173         * @param item  the item (zero-based index).
    174         *
    175         * @return The y-value.
    176         *
    177         * @see #getYValue(int, int)
    178         */
    179        public Number getY(int series, int item) {
    180            return getClose(series, item);
    181        }
    182
    183        /**
    184         * Returns the high-value for one item in a series.
    185         *
    186         * @param series  the series (zero-based index).
    187         * @param item  the item (zero-based index).
    188         *
    189         * @return The high-value.
    190         *
    191         * @see #getHighValue(int, int)
    192         */
    193        public Number getHigh(int series, int item) {
    194            return this.high[item];
    195        }
    196
    197        /**
    198         * Returns the high-value (as a double primitive) for an item within a
    199         * series.
    200         *
    201         * @param series  the series (zero-based index).
    202         * @param item  the item (zero-based index).
    203         *
    204         * @return The high-value.
    205         *
    206         * @see #getHigh(int, int)
    207         */
    208        public double getHighValue(int series, int item) {
    209            double result = Double.NaN;
    210            Number high = getHigh(series, item);
    211            if (high != null) {
    212                result = high.doubleValue();
    213            }
    214            return result;
    215        }
    216
    217        /**
    218         * Returns the low-value for one item in a series.
    219         *
    220         * @param series  the series (zero-based index).
    221         * @param item  the item (zero-based index).
    222         *
    223         * @return The low-value.
    224         *
    225         * @see #getLowValue(int, int)
    226         */
    227        public Number getLow(int series, int item) {
    228            return this.low[item];
    229        }
    230
    231        /**
    232         * Returns the low-value (as a double primitive) for an item within a
    233         * series.
    234         *
    235         * @param series  the series (zero-based index).
    236         * @param item  the item (zero-based index).
    237         *
    238         * @return The low-value.
    239         *
    240         * @see #getLow(int, int)
    241         */
    242        public double getLowValue(int series, int item) {
    243            double result = Double.NaN;
    244            Number low = getLow(series, item);
    245            if (low != null) {
    246                result = low.doubleValue();
    247            }
    248            return result;
    249        }
    250
    251        /**
    252         * Returns the open-value for one item in a series.
    253         *
    254         * @param series  the series (zero-based index).
    255         * @param item  the item (zero-based index).
    256         *
    257         * @return The open-value.
    258         *
    259         * @see #getOpenValue(int, int)
    260         */
    261        public Number getOpen(int series, int item) {
    262            return this.open[item];
    263        }
    264
    265        /**
    266         * Returns the open-value (as a double primitive) for an item within a
    267         * series.
    268         *
    269         * @param series  the series (zero-based index).
    270         * @param item  the item (zero-based index).
    271         *
    272         * @return The open-value.
    273         *
    274         * @see #getOpen(int, int)
    275         */
    276        public double getOpenValue(int series, int item) {
    277            double result = Double.NaN;
    278            Number open = getOpen(series, item);
    279            if (open != null) {
    280                result = open.doubleValue();
    281            }
    282            return result;
    283        }
    284
    285        /**
    286         * Returns the close-value for one item in a series.
    287         *
    288         * @param series  the series (zero-based index).
    289         * @param item  the item (zero-based index).
    290         *
    291         * @return The close-value.
    292         *
    293         * @see #getCloseValue(int, int)
    294         */
    295        public Number getClose(int series, int item) {
    296            return this.close[item];
    297        }
    298
    299        /**
    300         * Returns the close-value (as a double primitive) for an item within a
    301         * series.
    302         *
    303         * @param series  the series (zero-based index).
    304         * @param item  the item (zero-based index).
    305         *
    306         * @return The close-value.
    307         *
    308         * @see #getClose(int, int)
    309         */
    310        public double getCloseValue(int series, int item) {
    311            double result = Double.NaN;
    312            Number close = getClose(series, item);
    313            if (close != null) {
    314                result = close.doubleValue();
    315            }
    316            return result;
    317        }
    318
    319        /**
    320         * Returns the volume-value for one item in a series.
    321         *
    322         * @param series  the series (zero-based index).
    323         * @param item  the item (zero-based index).
    324         *
    325         * @return The volume-value.
    326         *
    327         * @see #getVolumeValue(int, int)
    328         */
    329        public Number getVolume(int series, int item) {
    330            return this.volume[item];
    331        }
    332
    333        /**
    334         * Returns the volume-value (as a double primitive) for an item within a
    335         * series.
    336         *
    337         * @param series  the series (zero-based index).
    338         * @param item  the item (zero-based index).
    339         *
    340         * @return The volume-value.
    341         *
    342         * @see #getVolume(int, int)
    343         */
    344        public double getVolumeValue(int series, int item) {
    345            double result = Double.NaN;
    346            Number volume = getVolume(series, item);
    347            if (volume != null) {
    348                result = volume.doubleValue();
    349            }
    350            return result;
    351        }
    352
    353        /**
    354         * Returns the number of series in the dataset.
    355         * <p>
    356         * This implementation only allows one series.
    357         *
    358         * @return The number of series.
    359         */
    360        public int getSeriesCount() {
    361            return 1;
    362        }
    363
    364        /**
    365         * Returns the number of items in the specified series.
    366         *
    367         * @param series  the index (zero-based) of the series.
    368         *
    369         * @return The number of items in the specified series.
    370         */
    371        public int getItemCount(int series) {
    372            return this.date.length;
    373        }
    374
    375        /**
    376         * Tests this dataset for equality with an arbitrary instance.
    377         *
    378         * @param obj  the object (<code>null</code> permitted).
    379         *
    380         * @return A boolean.
    381         */
    382        public boolean equals(Object obj) {
    383            if (obj == this) {
    384                return true;
    385            }
    386            if (!(obj instanceof DefaultHighLowDataset)) {
    387                return false;
    388            }
    389            DefaultHighLowDataset that = (DefaultHighLowDataset) obj;
    390            if (!this.seriesKey.equals(that.seriesKey)) {
    391                return false;
    392            }
    393            if (!Arrays.equals(this.date, that.date)) {
    394                return false;
    395            }
    396            if (!Arrays.equals(this.open, that.open)) {
    397                return false;
    398            }
    399            if (!Arrays.equals(this.high, that.high)) {
    400                return false;
    401            }
    402            if (!Arrays.equals(this.low, that.low)) {
    403                return false;
    404            }
    405            if (!Arrays.equals(this.close, that.close)) {
    406                return false;
    407            }
    408            if (!Arrays.equals(this.volume, that.volume)) {
    409                return false;
    410            }
    411            return true;
    412        }
    413
    414        /**
    415         * Constructs an array of Number objects from an array of doubles.
    416         *
    417         * @param data  the double values to convert (<code>null</code> not
    418         *     permitted).
    419         *
    420         * @return The data as an array of Number objects.
    421         */
    422        public static Number[] createNumberArray(double[] data) {
    423            Number[] result = new Number[data.length];
    424            for (int i = 0; i < data.length; i++) {
    425                result[i] = new Double(data[i]);
    426            }
    427            return result;
    428        }
    429
    430    }