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