KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > data > time > TimeSeriesCollection


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
24  * in the United States and other countries.]
25  *
26  * -------------------------
27  * TimeSeriesCollection.java
28  * -------------------------
29  * (C) Copyright 2001-2005, by Object Refinery Limited.
30  *
31  * Original Author: David Gilbert (for Object Refinery Limited);
32  * Contributor(s): -;
33  *
34  * $Id: TimeSeriesCollection.java,v 1.10 2005/05/20 08:20:03 mungady Exp $
35  *
36  * Changes
37  * -------
38  * 11-Oct-2001 : Version 1 (DG);
39  * 18-Oct-2001 : Added implementation of IntervalXYDataSource so that bar plots
40  * (using numerical axes) can be plotted from time series
41  * data (DG);
42  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
43  * 15-Nov-2001 : Added getSeries() method. Changed name from TimeSeriesDataset
44  * to TimeSeriesCollection (DG);
45  * 07-Dec-2001 : TimeSeries --> BasicTimeSeries (DG);
46  * 01-Mar-2002 : Added a time zone offset attribute, to enable fast calculation
47  * of the time period start and end values (DG);
48  * 29-Mar-2002 : The collection now registers itself with all the time series
49  * objects as a SeriesChangeListener. Removed redundant
50  * calculateZoneOffset method (DG);
51  * 06-Jun-2002 : Added a setting to control whether the x-value supplied in the
52  * getXValue() method comes from the START, MIDDLE, or END of the
53  * time period. This is a workaround for JFreeChart, where the
54  * current date axis always labels the start of a time
55  * period (DG);
56  * 24-Jun-2002 : Removed unnecessary import (DG);
57  * 24-Aug-2002 : Implemented DomainInfo interface, and added the
58  * DomainIsPointsInTime flag (DG);
59  * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
60  * 16-Oct-2002 : Added remove methods (DG);
61  * 10-Jan-2003 : Changed method names in RegularTimePeriod class (DG);
62  * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented
63  * Serializable (DG);
64  * 04-Sep-2003 : Added getSeries(String) method (DG);
65  * 15-Sep-2003 : Added a removeAllSeries() method to match
66  * XYSeriesCollection (DG);
67  * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG);
68  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
69  * getYValue() (DG);
70  * 06-Oct-2004 : Updated for changed in DomainInfo interface (DG);
71  * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0
72  * release (DG);
73  * 28-Mar-2005 : Fixed bug in getSeries(int) method (1170825) (DG);
74  *
75  */

76
77 package org.jfree.data.time;
78
79 import java.io.Serializable JavaDoc;
80 import java.util.ArrayList JavaDoc;
81 import java.util.Calendar JavaDoc;
82 import java.util.Collections JavaDoc;
83 import java.util.Iterator JavaDoc;
84 import java.util.List JavaDoc;
85 import java.util.TimeZone JavaDoc;
86
87 import org.jfree.data.DomainInfo;
88 import org.jfree.data.Range;
89 import org.jfree.data.general.DatasetChangeEvent;
90 import org.jfree.data.xy.AbstractIntervalXYDataset;
91 import org.jfree.data.xy.IntervalXYDataset;
92 import org.jfree.data.xy.XYDataset;
93 import org.jfree.util.ObjectUtilities;
94
95 /**
96  * A collection of time series objects. This class implements the
97  * {@link org.jfree.data.xy.XYDataset} interface, as well as the extended
98  * {@link IntervalXYDataset} interface. This makes it a convenient dataset for
99  * use with the {@link org.jfree.chart.plot.XYPlot} class.
100  */

101 public class TimeSeriesCollection extends AbstractIntervalXYDataset
102                                   implements XYDataset,
103                                              IntervalXYDataset,
104                                              DomainInfo,
105                                              Serializable JavaDoc {
106
107     /** For serialization. */
108     private static final long serialVersionUID = 834149929022371137L;
109     
110     /** Storage for the time series. */
111     private List JavaDoc data;
112
113     /** A working calendar (to recycle) */
114     private Calendar JavaDoc workingCalendar;
115     
116     /**
117      * The point within each time period that is used for the X value when this
118      * collection is used as an {@link org.jfree.data.xy.XYDataset}. This can
119      * be the start, middle or end of the time period.
120      */

121     private TimePeriodAnchor xPosition;
122
123     /**
124      * A flag that indicates that the domain is 'points in time'. If this
125      * flag is true, only the x-value is used to determine the range of values
126      * in the domain, the start and end x-values are ignored.
127      */

128     private boolean domainIsPointsInTime;
129
130     /**
131      * Constructs an empty dataset, tied to the default timezone.
132      */

133     public TimeSeriesCollection() {
134         this(null, TimeZone.getDefault());
135     }
136
137     /**
138      * Constructs an empty dataset, tied to a specific timezone.
139      *
140      * @param zone the timezone (<code>null</code> permitted, will use
141      * <code>TimeZone.getDefault()</code> in that case).
142      */

143     public TimeSeriesCollection(TimeZone JavaDoc zone) {
144         this(null, zone);
145     }
146
147     /**
148      * Constructs a dataset containing a single series (more can be added),
149      * tied to the default timezone.
150      *
151      * @param series the series (<code>null</code> permitted).
152      */

153     public TimeSeriesCollection(TimeSeries series) {
154         this(series, TimeZone.getDefault());
155     }
156
157     /**
158      * Constructs a dataset containing a single series (more can be added),
159      * tied to a specific timezone.
160      *
161      * @param series a series to add to the collection (<code>null</code>
162      * permitted).
163      * @param zone the timezone (<code>null</code> permitted, will use
164      * <code>TimeZone.getDefault()</code> in that case).
165      */

166     public TimeSeriesCollection(TimeSeries series, TimeZone JavaDoc zone) {
167
168         if (zone == null) {
169             zone = TimeZone.getDefault();
170         }
171         this.workingCalendar = Calendar.getInstance(zone);
172         this.data = new ArrayList JavaDoc();
173         if (series != null) {
174             this.data.add(series);
175             series.addChangeListener(this);
176         }
177         this.xPosition = TimePeriodAnchor.START;
178         this.domainIsPointsInTime = true;
179
180     }
181     
182     /**
183      * Returns a flag that controls whether the domain is treated as 'points in
184      * time'. This flag is used when determining the max and min values for
185      * the domain. If <code>true</code>, then only the x-values are considered
186      * for the max and min values. If <code>false</code>, then the start and
187      * end x-values will also be taken into consideration.
188      *
189      * @return The flag.
190      */

191     public boolean getDomainIsPointsInTime() {
192         return this.domainIsPointsInTime;
193     }
194
195     /**
196      * Sets a flag that controls whether the domain is treated as 'points in
197      * time', or time periods.
198      *
199      * @param flag the flag.
200      */

201     public void setDomainIsPointsInTime(boolean flag) {
202         this.domainIsPointsInTime = flag;
203         notifyListeners(new DatasetChangeEvent(this, this));
204     }
205     
206     /**
207      * Returns the position within each time period that is used for the X
208      * value when the collection is used as an
209      * {@link org.jfree.data.xy.XYDataset}.
210      *
211      * @return The anchor position (never <code>null</code>).
212      */

213     public TimePeriodAnchor getXPosition() {
214         return this.xPosition;
215     }
216
217     /**
218      * Sets the position within each time period that is used for the X values
219      * when the collection is used as an {@link XYDataset}, then sends a
220      * {@link DatasetChangeEvent} is sent to all registered listeners.
221      *
222      * @param anchor the anchor position (<code>null</code> not permitted).
223      */

224     public void setXPosition(TimePeriodAnchor anchor) {
225         if (anchor == null) {
226             throw new IllegalArgumentException JavaDoc("Null 'anchor' argument.");
227         }
228         this.xPosition = anchor;
229         notifyListeners(new DatasetChangeEvent(this, this));
230     }
231     
232     /**
233      * Returns a list of all the series in the collection.
234      *
235      * @return The list (which is unmodifiable).
236      */

237     public List JavaDoc getSeries() {
238         return Collections.unmodifiableList(this.data);
239     }
240
241     /**
242      * Returns the number of series in the collection.
243      *
244      * @return The series count.
245      */

246     public int getSeriesCount() {
247         return this.data.size();
248     }
249
250     /**
251      * Returns a series.
252      *
253      * @param series the index of the series (zero-based).
254      *
255      * @return The series.
256      */

257     public TimeSeries getSeries(int series) {
258         if ((series < 0) || (series >= getSeriesCount())) {
259             throw new IllegalArgumentException JavaDoc(
260                 "The 'series' argument is out of bounds (" + series + ")."
261             );
262         }
263         return (TimeSeries) this.data.get(series);
264     }
265     
266     /**
267      * Returns the series with the specified key, or <code>null</code> if
268      * there is no such series.
269      *
270      * @param key the series key (<code>null</code> permitted).
271      *
272      * @return The series with the given key.
273      */

274     public TimeSeries getSeries(String JavaDoc key) {
275         TimeSeries result = null;
276         Iterator JavaDoc iterator = this.data.iterator();
277         while (iterator.hasNext()) {
278             TimeSeries series = (TimeSeries) iterator.next();
279             Comparable JavaDoc k = series.getKey();
280             if (k != null && k.equals(key)) {
281                 result = series;
282             }
283         }
284         return result;
285     }
286
287     /**
288      * Returns the key for a series.
289      *
290      * @param series the index of the series (zero-based).
291      *
292      * @return The key for a series.
293      */

294     public Comparable JavaDoc getSeriesKey(int series) {
295         // check arguments...delegated
296
// fetch the series name...
297
return getSeries(series).getKey();
298     }
299
300     /**
301      * Adds a series to the collection and sends a {@link DatasetChangeEvent} to
302      * all registered listeners.
303      *
304      * @param series the series (<code>null</code> not permitted).
305      */

306     public void addSeries(TimeSeries series) {
307         if (series == null) {
308             throw new IllegalArgumentException JavaDoc("Null 'series' argument.");
309         }
310         this.data.add(series);
311         series.addChangeListener(this);
312         fireDatasetChanged();
313     }
314
315     /**
316      * Removes the specified series from the collection and sends a
317      * {@link DatasetChangeEvent} to all registered listeners.
318      *
319      * @param series the series (<code>null</code> not permitted).
320      */

321     public void removeSeries(TimeSeries series) {
322         if (series == null) {
323             throw new IllegalArgumentException JavaDoc("Null 'series' argument.");
324         }
325         this.data.remove(series);
326         series.removeChangeListener(this);
327         fireDatasetChanged();
328     }
329
330     /**
331      * Removes a series from the collection.
332      *
333      * @param index the series index (zero-based).
334      */

335     public void removeSeries(int index) {
336         TimeSeries series = getSeries(index);
337         if (series != null) {
338             removeSeries(series);
339         }
340     }
341
342     /**
343      * Removes all the series from the collection and sends a
344      * {@link DatasetChangeEvent} to all registered listeners.
345      */

346     public void removeAllSeries() {
347
348         // deregister the collection as a change listener to each series in the
349
// collection
350
for (int i = 0; i < this.data.size(); i++) {
351           TimeSeries series = (TimeSeries) this.data.get(i);
352           series.removeChangeListener(this);
353         }
354
355         // remove all the series from the collection and notify listeners.
356
this.data.clear();
357         fireDatasetChanged();
358
359     }
360
361     /**
362      * Returns the number of items in the specified series. This method is
363      * provided for convenience.
364      *
365      * @param series the series index (zero-based).
366      *
367      * @return The item count.
368      */

369     public int getItemCount(int series) {
370         return getSeries(series).getItemCount();
371     }
372     
373     /**
374      * Returns the x-value (as a double primitive) for an item within a series.
375      *
376      * @param series the series (zero-based index).
377      * @param item the item (zero-based index).
378      *
379      * @return The x-value.
380      */

381     public double getXValue(int series, int item) {
382         TimeSeries s = (TimeSeries) this.data.get(series);
383         TimeSeriesDataItem i = s.getDataItem(item);
384         RegularTimePeriod period = i.getPeriod();
385         return getX(period);
386     }
387
388     /**
389      * Returns the x-value for the specified series and item.
390      *
391      * @param series the series (zero-based index).
392      * @param item the item (zero-based index).
393      *
394      * @return The value.
395      */

396     public Number JavaDoc getX(int series, int item) {
397         TimeSeries ts = (TimeSeries) this.data.get(series);
398         TimeSeriesDataItem dp = ts.getDataItem(item);
399         RegularTimePeriod period = dp.getPeriod();
400         return new Long JavaDoc(getX(period));
401     }
402     
403     /**
404      * Returns the x-value for a time period.
405      *
406      * @param period the time period.
407      *
408      * @return The x-value.
409      */

410     protected synchronized long getX(RegularTimePeriod period) {
411
412         long result = 0L;
413         if (this.xPosition == TimePeriodAnchor.START) {
414             result = period.getFirstMillisecond(this.workingCalendar);
415         }
416         else if (this.xPosition == TimePeriodAnchor.MIDDLE) {
417             result = period.getMiddleMillisecond(this.workingCalendar);
418         }
419         else if (this.xPosition == TimePeriodAnchor.END) {
420             result = period.getLastMillisecond(this.workingCalendar);
421         }
422         return result;
423
424     }
425
426     /**
427      * Returns the starting X value for the specified series and item.
428      *
429      * @param series the series (zero-based index).
430      * @param item the item (zero-based index).
431      *
432      * @return The value.
433      */

434     public synchronized Number JavaDoc getStartX(int series, int item) {
435         TimeSeries ts = (TimeSeries) this.data.get(series);
436         TimeSeriesDataItem dp = ts.getDataItem(item);
437         return new Long JavaDoc(dp.getPeriod().getFirstMillisecond(
438             this.workingCalendar)
439         );
440     }
441
442     /**
443      * Returns the ending X value for the specified series and item.
444      *
445      * @param series The series (zero-based index).
446      * @param item The item (zero-based index).
447      *
448      * @return The value.
449      */

450     public synchronized Number JavaDoc getEndX(int series, int item) {
451         TimeSeries ts = (TimeSeries) this.data.get(series);
452         TimeSeriesDataItem dp = ts.getDataItem(item);
453         return new Long JavaDoc(dp.getPeriod().getLastMillisecond(
454             this.workingCalendar)
455         );
456     }
457
458     /**
459      * Returns the y-value for the specified series and item.
460      *
461      * @param series the series (zero-based index).
462      * @param item the item (zero-based index).
463      *
464      * @return The value (possibly <code>null</code>).
465      */

466     public Number JavaDoc getY(int series, int item) {
467         TimeSeries ts = (TimeSeries) this.data.get(series);
468         TimeSeriesDataItem dp = ts.getDataItem(item);
469         return dp.getValue();
470     }
471
472     /**
473      * Returns the starting Y value for the specified series and item.
474      *
475      * @param series the series (zero-based index).
476      * @param item the item (zero-based index).
477      *
478      * @return The value (possibly <code>null</code>).
479      */

480     public Number JavaDoc getStartY(int series, int item) {
481         return getY(series, item);
482     }
483
484     /**
485      * Returns the ending Y value for the specified series and item.
486      *
487      * @param series te series (zero-based index).
488      * @param item the item (zero-based index).
489      *
490      * @return The value (possibly <code>null</code>).
491      */

492     public Number JavaDoc getEndY(int series, int item) {
493         return getY(series, item);
494     }
495
496
497     /**
498      * Returns the indices of the two data items surrounding a particular
499      * millisecond value.
500      *
501      * @param series the series index.
502      * @param milliseconds the time.
503      *
504      * @return An array containing the (two) indices of the items surrounding
505      * the time.
506      */

507     public int[] getSurroundingItems(int series, long milliseconds) {
508         int[] result = new int[] {-1, -1};
509         TimeSeries timeSeries = getSeries(series);
510         for (int i = 0; i < timeSeries.getItemCount(); i++) {
511             Number JavaDoc x = getX(series, i);
512             long m = x.longValue();
513             if (m <= milliseconds) {
514                 result[0] = i;
515             }
516             if (m >= milliseconds) {
517                 result[1] = i;
518                 break;
519             }
520         }
521         return result;
522     }
523     
524     /**
525      * Returns the minimum x-value in the dataset.
526      *
527      * @param includeInterval a flag that determines whether or not the
528      * x-interval is taken into account.
529      *
530      * @return The minimum value.
531      */

532     public double getDomainLowerBound(boolean includeInterval) {
533         double result = Double.NaN;
534         Range r = getDomainBounds(includeInterval);
535         if (r != null) {
536             result = r.getLowerBound();
537         }
538         return result;
539     }
540
541     /**
542      * Returns the maximum x-value in the dataset.
543      *
544      * @param includeInterval a flag that determines whether or not the
545      * x-interval is taken into account.
546      *
547      * @return The maximum value.
548      */

549     public double getDomainUpperBound(boolean includeInterval) {
550         double result = Double.NaN;
551         Range r = getDomainBounds(includeInterval);
552         if (r != null) {
553             result = r.getUpperBound();
554         }
555         return result;
556     }
557
558     /**
559      * Returns the range of the values in this dataset's domain.
560      *
561      * @param includeInterval a flag that determines whether or not the
562      * x-interval is taken into account.
563      *
564      * @return The range.
565      */

566     public Range getDomainBounds(boolean includeInterval) {
567         Range result = null;
568         Iterator JavaDoc iterator = this.data.iterator();
569         while (iterator.hasNext()) {
570             TimeSeries series = (TimeSeries) iterator.next();
571             int count = series.getItemCount();
572             if (count > 0) {
573                 RegularTimePeriod start = series.getTimePeriod(0);
574                 RegularTimePeriod end = series.getTimePeriod(count - 1);
575                 Range temp;
576                 if (!includeInterval || this.domainIsPointsInTime) {
577                     temp = new Range(getX(start), getX(end));
578                 }
579                 else {
580                     temp = new Range(
581                         start.getFirstMillisecond(this.workingCalendar),
582                         end.getLastMillisecond(this.workingCalendar)
583                     );
584                 }
585                 result = Range.combine(result, temp);
586             }
587         }
588         return result;
589     }
590     
591     /**
592      * Tests this time series collection for equality with another object.
593      *
594      * @param obj the other object.
595      *
596      * @return A boolean.
597      */

598     public boolean equals(Object JavaDoc obj) {
599         if (obj == this) {
600             return true;
601         }
602         if (!(obj instanceof TimeSeriesCollection)) {
603             return false;
604         }
605         TimeSeriesCollection that = (TimeSeriesCollection) obj;
606         if (this.xPosition != that.xPosition) {
607             return false;
608         }
609         if (this.domainIsPointsInTime != that.domainIsPointsInTime) {
610             return false;
611         }
612         if (!ObjectUtilities.equal(this.data, that.data)) {
613             return false;
614         }
615         return true;
616     }
617
618     /**
619      * Returns a hash code value for the object.
620      *
621      * @return The hashcode
622      */

623     public int hashCode() {
624         int result;
625         result = this.data.hashCode();
626         result = 29 * result + (this.workingCalendar != null
627                 ? this.workingCalendar.hashCode() : 0);
628         result = 29 * result + (this.xPosition != null
629                 ? this.xPosition.hashCode() : 0);
630         result = 29 * result + (this.domainIsPointsInTime ? 1 : 0);
631         return result;
632     }
633     
634 }
635
Popular Tags