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    }