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
041 package org.jfree.data.time.ohlc;
042
043 import java.io.Serializable;
044 import java.util.List;
045
046 import org.jfree.data.general.DatasetChangeEvent;
047 import org.jfree.data.time.RegularTimePeriod;
048 import org.jfree.data.time.TimePeriodAnchor;
049 import org.jfree.data.xy.AbstractXYDataset;
050 import org.jfree.data.xy.OHLCDataset;
051 import org.jfree.data.xy.XYDataset;
052 import org.jfree.util.ObjectUtilities;
053
054 /**
055 * A collection of {@link OHLCSeries} objects.
056 *
057 * @since 1.0.4
058 *
059 * @see OHLCSeries
060 */
061 public 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 }