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