001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2009, 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 * CombinedDataset.java
029 * --------------------
030 * (C) Copyright 2001-2009, by Bill Kelemen and Contributors.
031 *
032 * Original Author: Bill Kelemen;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 *
035 * Changes
036 * -------
037 * 06-Dec-2001 : Version 1 (BK);
038 * 27-Dec-2001 : Fixed bug in getChildPosition method (BK);
039 * 29-Dec-2001 : Fixed bug in getChildPosition method with complex
040 * CombinePlot (BK);
041 * 05-Feb-2002 : Small addition to the interface HighLowDataset, as requested
042 * by Sylvain Vieujot (DG);
043 * 14-Feb-2002 : Added bug fix for IntervalXYDataset methods, submitted by
044 * Gyula Kun-Szabo (DG);
045 * 11-Jun-2002 : Updated for change in event constructor (DG);
046 * 04-Oct-2002 : Fixed errors reported by Checkstyle (DG);
047 * 06-May-2004 : Now extends AbstractIntervalXYDataset and added other methods
048 * that return double primitives (DG);
049 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
050 * getYValue() (DG);
051 * ------------- JFREECHART 1.0.x ---------------------------------------------
052 * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
053 * 04-Feb-2009 : Deprecated the class (DG);
054 *
055 */
056
057 package org.jfree.data.general;
058
059 import java.util.List;
060
061 import org.jfree.data.xy.AbstractIntervalXYDataset;
062 import org.jfree.data.xy.IntervalXYDataset;
063 import org.jfree.data.xy.OHLCDataset;
064 import org.jfree.data.xy.XYDataset;
065
066 /**
067 * This class can combine instances of {@link XYDataset}, {@link OHLCDataset}
068 * and {@link IntervalXYDataset} together exposing the union of all the series
069 * under one dataset.
070 *
071 * @deprecated As of version 1.0.13. This class will be removed from
072 * JFreeChart 1.2.0 onwards. Anyone needing this facility will need to
073 * maintain it outside of JFreeChart.
074 */
075 public class CombinedDataset extends AbstractIntervalXYDataset
076 implements XYDataset, OHLCDataset, IntervalXYDataset,
077 CombinationDataset {
078
079 /** Storage for the datasets we combine. */
080 private List datasetInfo = new java.util.ArrayList();
081
082 /**
083 * Default constructor for an empty combination.
084 */
085 public CombinedDataset() {
086 super();
087 }
088
089 /**
090 * Creates a CombinedDataset initialized with an array of SeriesDatasets.
091 *
092 * @param data array of SeriesDataset that contains the SeriesDatasets to
093 * combine.
094 */
095 public CombinedDataset(SeriesDataset[] data) {
096 add(data);
097 }
098
099 /**
100 * Adds one SeriesDataset to the combination. Listeners are notified of the
101 * change.
102 *
103 * @param data the SeriesDataset to add.
104 */
105 public void add(SeriesDataset data) {
106 fastAdd(data);
107 DatasetChangeEvent event = new DatasetChangeEvent(this, this);
108 notifyListeners(event);
109 }
110
111 /**
112 * Adds an array of SeriesDataset's to the combination. Listeners are
113 * notified of the change.
114 *
115 * @param data array of SeriesDataset to add
116 */
117 public void add(SeriesDataset[] data) {
118
119 for (int i = 0; i < data.length; i++) {
120 fastAdd(data[i]);
121 }
122 DatasetChangeEvent event = new DatasetChangeEvent(this, this);
123 notifyListeners(event);
124
125 }
126
127 /**
128 * Adds one series from a SeriesDataset to the combination. Listeners are
129 * notified of the change.
130 *
131 * @param data the SeriesDataset where series is contained
132 * @param series series to add
133 */
134 public void add(SeriesDataset data, int series) {
135 add(new SubSeriesDataset(data, series));
136 }
137
138 /**
139 * Fast add of a SeriesDataset. Does not notify listeners of the change.
140 *
141 * @param data SeriesDataset to add
142 */
143 private void fastAdd(SeriesDataset data) {
144 for (int i = 0; i < data.getSeriesCount(); i++) {
145 this.datasetInfo.add(new DatasetInfo(data, i));
146 }
147 }
148
149 ///////////////////////////////////////////////////////////////////////////
150 // From SeriesDataset
151 ///////////////////////////////////////////////////////////////////////////
152
153 /**
154 * Returns the number of series in the dataset.
155 *
156 * @return The number of series in the dataset.
157 */
158 public int getSeriesCount() {
159 return this.datasetInfo.size();
160 }
161
162 /**
163 * Returns the key for a series.
164 *
165 * @param series the series (zero-based index).
166 *
167 * @return The key for a series.
168 */
169 public Comparable getSeriesKey(int series) {
170 DatasetInfo di = getDatasetInfo(series);
171 return di.data.getSeriesKey(di.series);
172 }
173
174 ///////////////////////////////////////////////////////////////////////////
175 // From XYDataset
176 ///////////////////////////////////////////////////////////////////////////
177
178 /**
179 * Returns the X-value for the specified series and item.
180 * <P>
181 * Note: throws <code>ClassCastException</code> if the series is not from
182 * a {@link XYDataset}.
183 *
184 * @param series the index of the series of interest (zero-based).
185 * @param item the index of the item of interest (zero-based).
186 *
187 * @return The X-value for the specified series and item.
188 */
189 public Number getX(int series, int item) {
190 DatasetInfo di = getDatasetInfo(series);
191 return ((XYDataset) di.data).getX(di.series, item);
192 }
193
194 /**
195 * Returns the Y-value for the specified series and item.
196 * <P>
197 * Note: throws <code>ClassCastException</code> if the series is not from
198 * a {@link XYDataset}.
199 *
200 * @param series the index of the series of interest (zero-based).
201 * @param item the index of the item of interest (zero-based).
202 *
203 * @return The Y-value for the specified series and item.
204 */
205 public Number getY(int series, int item) {
206 DatasetInfo di = getDatasetInfo(series);
207 return ((XYDataset) di.data).getY(di.series, item);
208 }
209
210 /**
211 * Returns the number of items in a series.
212 * <P>
213 * Note: throws <code>ClassCastException</code> if the series is not from
214 * a {@link XYDataset}.
215 *
216 * @param series the index of the series of interest (zero-based).
217 *
218 * @return The number of items in a series.
219 */
220 public int getItemCount(int series) {
221 DatasetInfo di = getDatasetInfo(series);
222 return ((XYDataset) di.data).getItemCount(di.series);
223 }
224
225 ///////////////////////////////////////////////////////////////////////////
226 // From HighLowDataset
227 ///////////////////////////////////////////////////////////////////////////
228
229 /**
230 * Returns the high-value for the specified series and item.
231 * <P>
232 * Note: throws <code>ClassCastException</code> if the series is not from a
233 * {@link OHLCDataset}.
234 *
235 * @param series the index of the series of interest (zero-based).
236 * @param item the index of the item of interest (zero-based).
237 *
238 * @return The high-value for the specified series and item.
239 */
240 public Number getHigh(int series, int item) {
241 DatasetInfo di = getDatasetInfo(series);
242 return ((OHLCDataset) di.data).getHigh(di.series, item);
243 }
244
245 /**
246 * Returns the high-value (as a double primitive) for an item within a
247 * series.
248 *
249 * @param series the series (zero-based index).
250 * @param item the item (zero-based index).
251 *
252 * @return The high-value.
253 */
254 public double getHighValue(int series, int item) {
255 double result = Double.NaN;
256 Number high = getHigh(series, item);
257 if (high != null) {
258 result = high.doubleValue();
259 }
260 return result;
261 }
262
263 /**
264 * Returns the low-value for the specified series and item.
265 * <P>
266 * Note: throws <code>ClassCastException</code> if the series is not from a
267 * {@link OHLCDataset}.
268 *
269 * @param series the index of the series of interest (zero-based).
270 * @param item the index of the item of interest (zero-based).
271 *
272 * @return The low-value for the specified series and item.
273 */
274 public Number getLow(int series, int item) {
275 DatasetInfo di = getDatasetInfo(series);
276 return ((OHLCDataset) di.data).getLow(di.series, item);
277 }
278
279 /**
280 * Returns the low-value (as a double primitive) for an item within a
281 * series.
282 *
283 * @param series the series (zero-based index).
284 * @param item the item (zero-based index).
285 *
286 * @return The low-value.
287 */
288 public double getLowValue(int series, int item) {
289 double result = Double.NaN;
290 Number low = getLow(series, item);
291 if (low != null) {
292 result = low.doubleValue();
293 }
294 return result;
295 }
296
297 /**
298 * Returns the open-value for the specified series and item.
299 * <P>
300 * Note: throws <code>ClassCastException</code> if the series is not from a
301 * {@link OHLCDataset}.
302 *
303 * @param series the index of the series of interest (zero-based).
304 * @param item the index of the item of interest (zero-based).
305 *
306 * @return The open-value for the specified series and item.
307 */
308 public Number getOpen(int series, int item) {
309 DatasetInfo di = getDatasetInfo(series);
310 return ((OHLCDataset) di.data).getOpen(di.series, item);
311 }
312
313 /**
314 * Returns the open-value (as a double primitive) for an item within a
315 * series.
316 *
317 * @param series the series (zero-based index).
318 * @param item the item (zero-based index).
319 *
320 * @return The open-value.
321 */
322 public double getOpenValue(int series, int item) {
323 double result = Double.NaN;
324 Number open = getOpen(series, item);
325 if (open != null) {
326 result = open.doubleValue();
327 }
328 return result;
329 }
330
331 /**
332 * Returns the close-value for the specified series and item.
333 * <P>
334 * Note: throws <code>ClassCastException</code> if the series is not from a
335 * {@link OHLCDataset}.
336 *
337 * @param series the index of the series of interest (zero-based).
338 * @param item the index of the item of interest (zero-based).
339 *
340 * @return The close-value for the specified series and item.
341 */
342 public Number getClose(int series, int item) {
343 DatasetInfo di = getDatasetInfo(series);
344 return ((OHLCDataset) di.data).getClose(di.series, item);
345 }
346
347 /**
348 * Returns the close-value (as a double primitive) for an item within a
349 * series.
350 *
351 * @param series the series (zero-based index).
352 * @param item the item (zero-based index).
353 *
354 * @return The close-value.
355 */
356 public double getCloseValue(int series, int item) {
357 double result = Double.NaN;
358 Number close = getClose(series, item);
359 if (close != null) {
360 result = close.doubleValue();
361 }
362 return result;
363 }
364
365 /**
366 * Returns the volume value for the specified series and item.
367 * <P>
368 * Note: throws <code>ClassCastException</code> if the series is not from a
369 * {@link OHLCDataset}.
370 *
371 * @param series the index of the series of interest (zero-based).
372 * @param item the index of the item of interest (zero-based).
373 *
374 * @return The volume value for the specified series and item.
375 */
376 public Number getVolume(int series, int item) {
377 DatasetInfo di = getDatasetInfo(series);
378 return ((OHLCDataset) di.data).getVolume(di.series, item);
379 }
380
381 /**
382 * Returns the volume-value (as a double primitive) for an item within a
383 * series.
384 *
385 * @param series the series (zero-based index).
386 * @param item the item (zero-based index).
387 *
388 * @return The volume-value.
389 */
390 public double getVolumeValue(int series, int item) {
391 double result = Double.NaN;
392 Number volume = getVolume(series, item);
393 if (volume != null) {
394 result = volume.doubleValue();
395 }
396 return result;
397 }
398
399 ///////////////////////////////////////////////////////////////////////////
400 // From IntervalXYDataset
401 ///////////////////////////////////////////////////////////////////////////
402
403 /**
404 * Returns the starting X value for the specified series and item.
405 *
406 * @param series the index of the series of interest (zero-based).
407 * @param item the index of the item of interest (zero-based).
408 *
409 * @return The value.
410 */
411 public Number getStartX(int series, int item) {
412 DatasetInfo di = getDatasetInfo(series);
413 if (di.data instanceof IntervalXYDataset) {
414 return ((IntervalXYDataset) di.data).getStartX(di.series, item);
415 }
416 else {
417 return getX(series, item);
418 }
419 }
420
421 /**
422 * Returns the ending X value for the specified series and item.
423 *
424 * @param series the index of the series of interest (zero-based).
425 * @param item the index of the item of interest (zero-based).
426 *
427 * @return The value.
428 */
429 public Number getEndX(int series, int item) {
430 DatasetInfo di = getDatasetInfo(series);
431 if (di.data instanceof IntervalXYDataset) {
432 return ((IntervalXYDataset) di.data).getEndX(di.series, item);
433 }
434 else {
435 return getX(series, item);
436 }
437 }
438
439 /**
440 * Returns the starting Y value for the specified series and item.
441 *
442 * @param series the index of the series of interest (zero-based).
443 * @param item the index of the item of interest (zero-based).
444 *
445 * @return The starting Y value for the specified series and item.
446 */
447 public Number getStartY(int series, int item) {
448 DatasetInfo di = getDatasetInfo(series);
449 if (di.data instanceof IntervalXYDataset) {
450 return ((IntervalXYDataset) di.data).getStartY(di.series, item);
451 }
452 else {
453 return getY(series, item);
454 }
455 }
456
457 /**
458 * Returns the ending Y value for the specified series and item.
459 *
460 * @param series the index of the series of interest (zero-based).
461 * @param item the index of the item of interest (zero-based).
462 *
463 * @return The ending Y value for the specified series and item.
464 */
465 public Number getEndY(int series, int item) {
466 DatasetInfo di = getDatasetInfo(series);
467 if (di.data instanceof IntervalXYDataset) {
468 return ((IntervalXYDataset) di.data).getEndY(di.series, item);
469 }
470 else {
471 return getY(series, item);
472 }
473 }
474
475 ///////////////////////////////////////////////////////////////////////////
476 // New methods from CombinationDataset
477 ///////////////////////////////////////////////////////////////////////////
478
479 /**
480 * Returns the parent Dataset of this combination. If there is more than
481 * one parent, or a child is found that is not a CombinationDataset, then
482 * returns <code>null</code>.
483 *
484 * @return The parent Dataset of this combination or <code>null</code>.
485 */
486 public SeriesDataset getParent() {
487
488 SeriesDataset parent = null;
489 for (int i = 0; i < this.datasetInfo.size(); i++) {
490 SeriesDataset child = getDatasetInfo(i).data;
491 if (child instanceof CombinationDataset) {
492 SeriesDataset childParent
493 = ((CombinationDataset) child).getParent();
494 if (parent == null) {
495 parent = childParent;
496 }
497 else if (parent != childParent) {
498 return null;
499 }
500 }
501 else {
502 return null;
503 }
504 }
505 return parent;
506
507 }
508
509 /**
510 * Returns a map or indirect indexing form our series into parent's series.
511 * Prior to calling this method, the client should check getParent() to make
512 * sure the CombinationDataset uses the same parent. If not, the map
513 * returned by this method will be invalid or null.
514 *
515 * @return A map or indirect indexing form our series into parent's series.
516 *
517 * @see #getParent()
518 */
519 public int[] getMap() {
520
521 int[] map = null;
522 for (int i = 0; i < this.datasetInfo.size(); i++) {
523 SeriesDataset child = getDatasetInfo(i).data;
524 if (child instanceof CombinationDataset) {
525 int[] childMap = ((CombinationDataset) child).getMap();
526 if (childMap == null) {
527 return null;
528 }
529 map = joinMap(map, childMap);
530 }
531 else {
532 return null;
533 }
534 }
535 return map;
536 }
537
538 ///////////////////////////////////////////////////////////////////////////
539 // New Methods
540 ///////////////////////////////////////////////////////////////////////////
541
542 /**
543 * Returns the child position.
544 *
545 * @param child the child dataset.
546 *
547 * @return The position.
548 */
549 public int getChildPosition(Dataset child) {
550
551 int n = 0;
552 for (int i = 0; i < this.datasetInfo.size(); i++) {
553 SeriesDataset childDataset = getDatasetInfo(i).data;
554 if (childDataset instanceof CombinedDataset) {
555 int m = ((CombinedDataset) childDataset)
556 .getChildPosition(child);
557 if (m >= 0) {
558 return n + m;
559 }
560 n++;
561 }
562 else {
563 if (child == childDataset) {
564 return n;
565 }
566 n++;
567 }
568 }
569 return -1;
570 }
571
572 ///////////////////////////////////////////////////////////////////////////
573 // Private
574 ///////////////////////////////////////////////////////////////////////////
575
576 /**
577 * Returns the DatasetInfo object associated with the series.
578 *
579 * @param series the index of the series.
580 *
581 * @return The DatasetInfo object associated with the series.
582 */
583 private DatasetInfo getDatasetInfo(int series) {
584 return (DatasetInfo) this.datasetInfo.get(series);
585 }
586
587 /**
588 * Joins two map arrays (int[]) together.
589 *
590 * @param a the first array.
591 * @param b the second array.
592 *
593 * @return A copy of { a[], b[] }.
594 */
595 private int[] joinMap(int[] a, int[] b) {
596 if (a == null) {
597 return b;
598 }
599 if (b == null) {
600 return a;
601 }
602 int[] result = new int[a.length + b.length];
603 System.arraycopy(a, 0, result, 0, a.length);
604 System.arraycopy(b, 0, result, a.length, b.length);
605 return result;
606 }
607
608 /**
609 * Private class to store as pairs (SeriesDataset, series) for all combined
610 * series.
611 */
612 private class DatasetInfo {
613
614 /** The dataset. */
615 private SeriesDataset data;
616
617 /** The series. */
618 private int series;
619
620 /**
621 * Creates a new dataset info record.
622 *
623 * @param data the dataset.
624 * @param series the series.
625 */
626 DatasetInfo(SeriesDataset data, int series) {
627 this.data = data;
628 this.series = series;
629 }
630 }
631
632 }