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 * OHLCSeriesCollection.java
029 * -------------------------
030 * (C) Copyright 2006, 2007, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes
036 * -------
037 * 04-Dec-2006 : Version 1 (DG);
038 * 10-Jul-2008 : Added accessor methods for xPosition attribute (DG);
039 */
040
041package org.jfree.data.time.ohlc;
042
043import java.io.Serializable;
044import java.util.List;
045
046import org.jfree.data.general.DatasetChangeEvent;
047import org.jfree.data.time.RegularTimePeriod;
048import org.jfree.data.time.TimePeriodAnchor;
049import org.jfree.data.xy.AbstractXYDataset;
050import org.jfree.data.xy.OHLCDataset;
051import org.jfree.data.xy.XYDataset;
052import org.jfree.util.ObjectUtilities;
053
054/**
055 * A collection of {@link OHLCSeries} objects.
056 *
057 * @since 1.0.4
058 *
059 * @see OHLCSeries
060 */
061public class OHLCSeriesCollection extends AbstractXYDataset
062                                implements OHLCDataset, Serializable {
063
064    /** Storage for the data series. */
065    private List data;
066
067    private TimePeriodAnchor xPosition = TimePeriodAnchor.MIDDLE;
068
069    /**
070     * Creates a new instance of <code>OHLCSeriesCollection</code>.
071     */
072    public OHLCSeriesCollection() {
073        this.data = new java.util.ArrayList();
074    }
075
076    /**
077     * Returns the position within each time period that is used for the X
078     * value when the collection is used as an {@link XYDataset}.
079     *
080     * @return The anchor position (never <code>null</code>).
081     *
082     * @since 1.0.11
083     */
084    public TimePeriodAnchor getXPosition() {
085        return this.xPosition;
086    }
087
088    /**
089     * Sets the position within each time period that is used for the X values
090     * when the collection is used as an {@link XYDataset}, then sends a
091     * {@link DatasetChangeEvent} is sent to all registered listeners.
092     *
093     * @param anchor  the anchor position (<code>null</code> not permitted).
094     *
095     * @since 1.0.11
096     */
097    public void setXPosition(TimePeriodAnchor anchor) {
098        if (anchor == null) {
099            throw new IllegalArgumentException("Null 'anchor' argument.");
100        }
101        this.xPosition = anchor;
102        notifyListeners(new DatasetChangeEvent(this, this));
103    }
104
105    /**
106     * Adds a series to the collection and sends a {@link DatasetChangeEvent}
107     * to all registered listeners.
108     *
109     * @param series  the series (<code>null</code> not permitted).
110     */
111    public void addSeries(OHLCSeries series) {
112        if (series == null) {
113            throw new IllegalArgumentException("Null 'series' argument.");
114        }
115        this.data.add(series);
116        series.addChangeListener(this);
117        fireDatasetChanged();
118    }
119
120    /**
121     * Returns the number of series in the collection.
122     *
123     * @return The series count.
124     */
125    public int getSeriesCount() {
126        return this.data.size();
127    }
128
129    /**
130     * Returns a series from the collection.
131     *
132     * @param series  the series index (zero-based).
133     *
134     * @return The series.
135     *
136     * @throws IllegalArgumentException if <code>series</code> is not in the
137     *     range <code>0</code> to <code>getSeriesCount() - 1</code>.
138     */
139    public OHLCSeries getSeries(int series) {
140        if ((series < 0) || (series >= getSeriesCount())) {
141            throw new IllegalArgumentException("Series index out of bounds");
142        }
143        return (OHLCSeries) this.data.get(series);
144    }
145
146    /**
147     * Returns the key for a series.
148     *
149     * @param series  the series index (in the range <code>0</code> to
150     *     <code>getSeriesCount() - 1</code>).
151     *
152     * @return The key for a series.
153     *
154     * @throws IllegalArgumentException if <code>series</code> is not in the
155     *     specified range.
156     */
157    public Comparable getSeriesKey(int series) {
158        // defer argument checking
159        return getSeries(series).getKey();
160    }
161
162    /**
163     * Returns the number of items in the specified series.
164     *
165     * @param series  the series (zero-based index).
166     *
167     * @return The item count.
168     *
169     * @throws IllegalArgumentException if <code>series</code> is not in the
170     *     range <code>0</code> to <code>getSeriesCount() - 1</code>.
171     */
172    public int getItemCount(int series) {
173        // defer argument checking
174        return getSeries(series).getItemCount();
175    }
176
177    /**
178     * Returns the x-value for a time period.
179     *
180     * @param period  the time period (<code>null</code> not permitted).
181     *
182     * @return The x-value.
183     */
184    protected synchronized long getX(RegularTimePeriod period) {
185        long result = 0L;
186        if (this.xPosition == TimePeriodAnchor.START) {
187            result = period.getFirstMillisecond();
188        }
189        else if (this.xPosition == TimePeriodAnchor.MIDDLE) {
190            result = period.getMiddleMillisecond();
191        }
192        else if (this.xPosition == TimePeriodAnchor.END) {
193            result = period.getLastMillisecond();
194        }
195        return result;
196    }
197
198    /**
199     * Returns the x-value for an item within a series.
200     *
201     * @param series  the series index.
202     * @param item  the item index.
203     *
204     * @return The x-value.
205     */
206    public double getXValue(int series, int item) {
207        OHLCSeries s = (OHLCSeries) this.data.get(series);
208        OHLCItem di = (OHLCItem) s.getDataItem(item);
209        RegularTimePeriod period = di.getPeriod();
210        return getX(period);
211    }
212
213    /**
214     * Returns the x-value for an item within a series.
215     *
216     * @param series  the series index.
217     * @param item  the item index.
218     *
219     * @return The x-value.
220     */
221    public Number getX(int series, int item) {
222        return new Double(getXValue(series, item));
223    }
224
225    /**
226     * Returns the y-value for an item within a series.
227     *
228     * @param series  the series index.
229     * @param item  the item index.
230     *
231     * @return The y-value.
232     */
233    public Number getY(int series, int item) {
234        OHLCSeries s = (OHLCSeries) this.data.get(series);
235        OHLCItem di = (OHLCItem) s.getDataItem(item);
236        return new Double(di.getYValue());
237    }
238
239    /**
240     * Returns the open-value for an item within a series.
241     *
242     * @param series  the series index.
243     * @param item  the item index.
244     *
245     * @return The open-value.
246     */
247    public double getOpenValue(int series, int item) {
248        OHLCSeries s = (OHLCSeries) this.data.get(series);
249        OHLCItem di = (OHLCItem) s.getDataItem(item);
250        return di.getOpenValue();
251    }
252
253    /**
254     * Returns the open-value for an item within a series.
255     *
256     * @param series  the series index.
257     * @param item  the item index.
258     *
259     * @return The open-value.
260     */
261    public Number getOpen(int series, int item) {
262        return new Double(getOpenValue(series, item));
263    }
264
265    /**
266     * Returns the close-value for an item within a series.
267     *
268     * @param series  the series index.
269     * @param item  the item index.
270     *
271     * @return The close-value.
272     */
273    public double getCloseValue(int series, int item) {
274        OHLCSeries s = (OHLCSeries) this.data.get(series);
275        OHLCItem di = (OHLCItem) s.getDataItem(item);
276        return di.getCloseValue();
277    }
278
279    /**
280     * Returns the close-value for an item within a series.
281     *
282     * @param series  the series index.
283     * @param item  the item index.
284     *
285     * @return The close-value.
286     */
287    public Number getClose(int series, int item) {
288        return new Double(getCloseValue(series, item));
289    }
290
291    /**
292     * Returns the high-value for an item within a series.
293     *
294     * @param series  the series index.
295     * @param item  the item index.
296     *
297     * @return The high-value.
298     */
299    public double getHighValue(int series, int item) {
300        OHLCSeries s = (OHLCSeries) this.data.get(series);
301        OHLCItem di = (OHLCItem) s.getDataItem(item);
302        return di.getHighValue();
303    }
304
305    /**
306     * Returns the high-value for an item within a series.
307     *
308     * @param series  the series index.
309     * @param item  the item index.
310     *
311     * @return The high-value.
312     */
313    public Number getHigh(int series, int item) {
314        return new Double(getHighValue(series, item));
315    }
316
317    /**
318     * Returns the low-value for an item within a series.
319     *
320     * @param series  the series index.
321     * @param item  the item index.
322     *
323     * @return The low-value.
324     */
325    public double getLowValue(int series, int item) {
326        OHLCSeries s = (OHLCSeries) this.data.get(series);
327        OHLCItem di = (OHLCItem) s.getDataItem(item);
328        return di.getLowValue();
329    }
330
331    /**
332     * Returns the low-value for an item within a series.
333     *
334     * @param series  the series index.
335     * @param item  the item index.
336     *
337     * @return The low-value.
338     */
339    public Number getLow(int series, int item) {
340        return new Double(getLowValue(series, item));
341    }
342
343    /**
344     * Returns <code>null</code> always, because this dataset doesn't record
345     * any volume data.
346     *
347     * @param series  the series index (ignored).
348     * @param item  the item index (ignored).
349     *
350     * @return <code>null</code>.
351     */
352    public Number getVolume(int series, int item) {
353        return null;
354    }
355
356    /**
357     * Returns <code>Double.NaN</code> always, because this dataset doesn't
358     * record any volume data.
359     *
360     * @param series  the series index (ignored).
361     * @param item  the item index (ignored).
362     *
363     * @return <code>Double.NaN</code>.
364     */
365    public double getVolumeValue(int series, int item) {
366        return Double.NaN;
367    }
368
369    /**
370     * Tests this instance for equality with an arbitrary object.
371     *
372     * @param obj  the object (<code>null</code> permitted).
373     *
374     * @return A boolean.
375     */
376    public boolean equals(Object obj) {
377        if (obj == this) {
378            return true;
379        }
380        if (!(obj instanceof OHLCSeriesCollection)) {
381            return false;
382        }
383        OHLCSeriesCollection that = (OHLCSeriesCollection) obj;
384        if (!this.xPosition.equals(that.xPosition)) {
385            return false;
386        }
387        return ObjectUtilities.equal(this.data, that.data);
388    }
389
390    /**
391     * Returns a clone of this instance.
392     *
393     * @return A clone.
394     *
395     * @throws CloneNotSupportedException if there is a problem.
396     */
397    public Object clone() throws CloneNotSupportedException {
398        OHLCSeriesCollection clone
399                = (OHLCSeriesCollection) super.clone();
400        clone.data = (List) ObjectUtilities.deepClone(this.data);
401        return clone;
402    }
403
404}