001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * --------------------------
028 * DefaultHighLowDataset.java
029 * --------------------------
030 * (C) Copyright 2002-present, by David Gilbert.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.data.xy;
038
039import java.util.Arrays;
040import java.util.Date;
041import java.util.Objects;
042import org.jfree.chart.util.Args;
043import org.jfree.chart.util.PublicCloneable;
044
045/**
046 * A simple implementation of the {@link OHLCDataset} interface.  See also
047 * the {@link DefaultOHLCDataset} class, which provides another implementation
048 * that is very similar.
049 */
050public class DefaultHighLowDataset extends AbstractXYDataset
051        implements OHLCDataset, PublicCloneable {
052
053    /** The series key. */
054    private final Comparable seriesKey;
055
056    /** Storage for the dates. */
057    private Date[] date;
058
059    /** Storage for the high values. */
060    private Number[] high;
061
062    /** Storage for the low values. */
063    private Number[] low;
064
065    /** Storage for the open values. */
066    private Number[] open;
067
068    /** Storage for the close values. */
069    private Number[] close;
070
071    /** Storage for the volume values. */
072    private Number[] volume;
073
074    /**
075     * Constructs a new high/low/open/close dataset.
076     * <p>
077     * The current implementation allows only one series in the dataset.
078     * This may be extended in a future version.
079     *
080     * @param seriesKey  the key for the series ({@code null} not
081     *     permitted).
082     * @param date  the dates ({@code null} not permitted).
083     * @param high  the high values ({@code null} not permitted).
084     * @param low  the low values ({@code null} not permitted).
085     * @param open  the open values ({@code null} not permitted).
086     * @param close  the close values ({@code null} not permitted).
087     * @param volume  the volume values ({@code null} not permitted).
088     */
089    public DefaultHighLowDataset(Comparable seriesKey, Date[] date,
090            double[] high, double[] low, double[] open, double[] close,
091            double[] volume) {
092
093        Args.nullNotPermitted(seriesKey, "seriesKey");
094        Args.nullNotPermitted(date, "date");
095        this.seriesKey = seriesKey;
096        this.date = date;
097        this.high = createNumberArray(high);
098        this.low = createNumberArray(low);
099        this.open = createNumberArray(open);
100        this.close = createNumberArray(close);
101        this.volume = createNumberArray(volume);
102    }
103
104    /**
105     * Returns the key for the series stored in this dataset.
106     *
107     * @param series  the index of the series (ignored, this dataset supports
108     *     only one series and this method always returns the key for series 0).
109     *
110     * @return The series key (never {@code null}).
111     */
112    @Override
113    public Comparable getSeriesKey(int series) {
114        return this.seriesKey;
115    }
116
117    /**
118     * Returns the x-value for one item in a series.  The value returned is a
119     * {@code Long} instance generated from the underlying
120     * {@code Date} object.  To avoid generating a new object instance,
121     * you might prefer to call {@link #getXValue(int, int)}.
122     *
123     * @param series  the series (zero-based index).
124     * @param item  the item (zero-based index).
125     *
126     * @return The x-value.
127     *
128     * @see #getXValue(int, int)
129     * @see #getXDate(int, int)
130     */
131    @Override
132    public Number getX(int series, int item) {
133        return this.date[item].getTime();
134    }
135
136    /**
137     * Returns the x-value for one item in a series, as a Date.
138     * <p>
139     * This method is provided for convenience only.
140     *
141     * @param series  the series (zero-based index).
142     * @param item  the item (zero-based index).
143     *
144     * @return The x-value as a Date.
145     *
146     * @see #getX(int, int)
147     */
148    public Date getXDate(int series, int item) {
149        return this.date[item];
150    }
151
152    /**
153     * Returns the y-value for one item in a series.
154     * <p>
155     * This method (from the {@link XYDataset} interface) is mapped to the
156     * {@link #getCloseValue(int, int)} method.
157     *
158     * @param series  the series (zero-based index).
159     * @param item  the item (zero-based index).
160     *
161     * @return The y-value.
162     *
163     * @see #getYValue(int, int)
164     */
165    @Override
166    public Number getY(int series, int item) {
167        return getClose(series, item);
168    }
169
170    /**
171     * Returns the high-value for one item in a series.
172     *
173     * @param series  the series (zero-based index).
174     * @param item  the item (zero-based index).
175     *
176     * @return The high-value.
177     *
178     * @see #getHighValue(int, int)
179     */
180    @Override
181    public Number getHigh(int series, int item) {
182        return this.high[item];
183    }
184
185    /**
186     * Returns the high-value (as a double primitive) for an item within a
187     * series.
188     *
189     * @param series  the series (zero-based index).
190     * @param item  the item (zero-based index).
191     *
192     * @return The high-value.
193     *
194     * @see #getHigh(int, int)
195     */
196    @Override
197    public double getHighValue(int series, int item) {
198        double result = Double.NaN;
199        Number h = getHigh(series, item);
200        if (h != null) {
201            result = h.doubleValue();
202        }
203        return result;
204    }
205
206    /**
207     * Returns the low-value for one item in a series.
208     *
209     * @param series  the series (zero-based index).
210     * @param item  the item (zero-based index).
211     *
212     * @return The low-value.
213     *
214     * @see #getLowValue(int, int)
215     */
216    @Override
217    public Number getLow(int series, int item) {
218        return this.low[item];
219    }
220
221    /**
222     * Returns the low-value (as a double primitive) for an item within a
223     * series.
224     *
225     * @param series  the series (zero-based index).
226     * @param item  the item (zero-based index).
227     *
228     * @return The low-value.
229     *
230     * @see #getLow(int, int)
231     */
232    @Override
233    public double getLowValue(int series, int item) {
234        double result = Double.NaN;
235        Number l = getLow(series, item);
236        if (l != null) {
237            result = l.doubleValue();
238        }
239        return result;
240    }
241
242    /**
243     * Returns the open-value for one item in a series.
244     *
245     * @param series  the series (zero-based index).
246     * @param item  the item (zero-based index).
247     *
248     * @return The open-value.
249     *
250     * @see #getOpenValue(int, int)
251     */
252    @Override
253    public Number getOpen(int series, int item) {
254        return this.open[item];
255    }
256
257    /**
258     * Returns the open-value (as a double primitive) for an item within a
259     * series.
260     *
261     * @param series  the series (zero-based index).
262     * @param item  the item (zero-based index).
263     *
264     * @return The open-value.
265     *
266     * @see #getOpen(int, int)
267     */
268    @Override
269    public double getOpenValue(int series, int item) {
270        double result = Double.NaN;
271        Number open = getOpen(series, item);
272        if (open != null) {
273            result = open.doubleValue();
274        }
275        return result;
276    }
277
278    /**
279     * Returns the close-value for one item in a series.
280     *
281     * @param series  the series (zero-based index).
282     * @param item  the item (zero-based index).
283     *
284     * @return The close-value.
285     *
286     * @see #getCloseValue(int, int)
287     */
288    @Override
289    public Number getClose(int series, int item) {
290        return this.close[item];
291    }
292
293    /**
294     * Returns the close-value (as a double primitive) for an item within a
295     * series.
296     *
297     * @param series  the series (zero-based index).
298     * @param item  the item (zero-based index).
299     *
300     * @return The close-value.
301     *
302     * @see #getClose(int, int)
303     */
304    @Override
305    public double getCloseValue(int series, int item) {
306        double result = Double.NaN;
307        Number c = getClose(series, item);
308        if (c != null) {
309            result = c.doubleValue();
310        }
311        return result;
312    }
313
314    /**
315     * Returns the volume-value for one item in a series.
316     *
317     * @param series  the series (zero-based index).
318     * @param item  the item (zero-based index).
319     *
320     * @return The volume-value.
321     *
322     * @see #getVolumeValue(int, int)
323     */
324    @Override
325    public Number getVolume(int series, int item) {
326        return this.volume[item];
327    }
328
329    /**
330     * Returns the volume-value (as a double primitive) for an item within a
331     * series.
332     *
333     * @param series  the series (zero-based index).
334     * @param item  the item (zero-based index).
335     *
336     * @return The volume-value.
337     *
338     * @see #getVolume(int, int)
339     */
340    @Override
341    public double getVolumeValue(int series, int item) {
342        double result = Double.NaN;
343        Number v = getVolume(series, item);
344        if (v != null) {
345            result = v.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    @Override
358    public int getSeriesCount() {
359        return 1;
360    }
361
362    /**
363     * Returns the number of items in the specified series.
364     *
365     * @param series  the index (zero-based) of the series.
366     *
367     * @return The number of items in the specified series.
368     */
369    @Override
370    public int getItemCount(int series) {
371        return this.date.length;
372    }
373
374    /**
375     * Tests this dataset for equality with an arbitrary instance.
376     *
377     * @param obj  the object ({@code null} permitted).
378     *
379     * @return A boolean.
380     */
381    @Override
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    @Override
415    public int hashCode() {
416        int hash = 5;
417        hash = 67 * hash + Objects.hashCode(this.seriesKey);
418        return hash;
419    }
420
421    /**
422     * Constructs an array of Number objects from an array of doubles.
423     *
424     * @param data  the double values to convert ({@code null} not
425     *     permitted).
426     *
427     * @return The data as an array of Number objects.
428     */
429    public static Number[] createNumberArray(double[] data) {
430        Number[] result = new Number[data.length];
431        for (int i = 0; i < data.length; i++) {
432            result[i] = data[i];
433        }
434        return result;
435    }
436
437}