KickJava   Java API By Example, From Geeks To Geeks.

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


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  * DynamicTimeSeriesCollection.java
28  * --------------------------------
29  * (C) Copyright 2002-2005, by I. H. Thomae and Contributors.
30  *
31  * Original Author: I. H. Thomae (ithomae@ists.dartmouth.edu);
32  * Contributor(s): David Gilbert (for Object Refinery Limited);
33  *
34  * $Id: DynamicTimeSeriesCollection.java,v 1.11 2005/05/20 08:20:03 mungady Exp $
35  *
36  * Changes
37  * -------
38  * 22-Nov-2002 : Initial version completed
39  * Jan 2003 : Optimized advanceTime(), added implemnt'n of RangeInfo intfc
40  * (using cached values for min, max, and range); also added
41  * getOldestIndex() and getNewestIndex() ftns so client classes
42  * can use this class as the master "index authority".
43  * 22-Jan-2003 : Made this class stand on its own, rather than extending
44  * class FastTimeSeriesCollection
45  * 31-Jan-2003 : Changed TimePeriod --> RegularTimePeriod (DG);
46  * 13-Mar-2003 : Moved to com.jrefinery.data.time package (DG);
47  * 29-Apr-2003 : Added small change to appendData method, from Irv Thomae (DG);
48  * 19-Sep-2003 : Added new appendData method, from Irv Thomae (DG);
49  * 05-May-2004 : Now extends AbstractIntervalXYDataset. This also required a
50  * change to the return type of the getY() method - I'm slightly
51  * unsure of the implications of this, so it might require some
52  * further amendment (DG);
53  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
54  * getYValue() (DG);
55  * 11-Jan-2004 : Removed deprecated code in preparation for the 1.0.0
56  * release (DG);
57  *
58  */

59
60 package org.jfree.data.time;
61
62 import java.util.Calendar JavaDoc;
63 import java.util.TimeZone JavaDoc;
64
65 import org.jfree.data.DomainInfo;
66 import org.jfree.data.Range;
67 import org.jfree.data.RangeInfo;
68 import org.jfree.data.general.SeriesChangeEvent;
69 import org.jfree.data.xy.AbstractIntervalXYDataset;
70 import org.jfree.data.xy.IntervalXYDataset;
71
72 /**
73  * A dynamic dataset.
74  * <p>
75  * Like FastTimeSeriesCollection, this class is a functional replacement
76  * for JFreeChart's TimeSeriesCollection _and_ TimeSeries classes.
77  * FastTimeSeriesCollection is appropriate for a fixed time range; for
78  * real-time applications this subclass adds the ability to append new
79  * data and discard the oldest.
80  * In this class, the arrays used in FastTimeSeriesCollection become FIFO's.
81  * NOTE:As presented here, all data is assumed >= 0, an assumption which is
82  * embodied only in methods associated with interface RangeInfo.
83  *
84  * @author Irv Thomae.
85  */

86 public class DynamicTimeSeriesCollection extends AbstractIntervalXYDataset
87                                          implements IntervalXYDataset,
88                                                     DomainInfo,
89                                                     RangeInfo {
90
91     /**
92      * Useful constant for controlling the x-value returned for a time
93      * period.
94      */

95     public static final int START = 0;
96
97     /**
98      * Useful constant for controlling the x-value returned for a time period.
99      */

100     public static final int MIDDLE = 1;
101
102     /**
103      * Useful constant for controlling the x-value returned for a time period.
104      */

105     public static final int END = 2;
106
107     /** The maximum number of items for each series (can be overridden). */
108     private int maximumItemCount = 2000; // an arbitrary safe default value
109

110     /** The history count. */
111     protected int historyCount;
112
113     /** Storage for the series keys. */
114     private Comparable JavaDoc[] seriesKeys;
115
116     /** The time period class - barely used, and could be removed (DG). */
117     private Class JavaDoc timePeriodClass = Minute.class; // default value;
118

119     /** Storage for the x-values. */
120     protected RegularTimePeriod[] pointsInTime;
121
122     /** The number of series. */
123     private int seriesCount;
124
125     /**
126      * A wrapper for a fixed array of float values.
127      */

128     protected class ValueSequence {
129
130         /** Storage for the float values. */
131         float[] dataPoints;
132
133         /**
134          * Default constructor:
135          */

136         public ValueSequence() {
137             this(DynamicTimeSeriesCollection.this.maximumItemCount);
138         }
139
140         /**
141          * Creates a sequence with the specified length.
142          *
143          * @param length the length.
144          */

145         public ValueSequence(int length) {
146             this.dataPoints = new float[length];
147             for (int i = 0; i < length; i++) {
148                 this.dataPoints[i] = 0.0f;
149             }
150         }
151
152         /**
153          * Enters data into the storage array.
154          *
155          * @param index the index.
156          * @param value the value.
157          */

158         public void enterData(int index, float value) {
159             this.dataPoints[index] = value;
160         }
161
162         /**
163          * Returns a value from the storage array.
164          *
165          * @param index the index.
166          *
167          * @return The value.
168          */

169         public float getData(int index) {
170             return this.dataPoints[index];
171         }
172     }
173
174     /** An array for storing the objects that represent each series. */
175     protected ValueSequence[] valueHistory;
176
177     /** A working calendar (to recycle) */
178     protected Calendar JavaDoc workingCalendar;
179
180     /**
181      * The position within a time period to return as the x-value (START,
182      * MIDDLE or END).
183      */

184     private int position;
185
186     /**
187      * A flag that indicates that the domain is 'points in time'. If this flag
188      * is true, only the x-value is used to determine the range of values in
189      * the domain, the start and end x-values are ignored.
190      */

191     private boolean domainIsPointsInTime;
192
193     /** index for mapping: points to the oldest valid time & data. */
194     private int oldestAt; // as a class variable, initializes == 0
195

196     /** Index of the newest data item. */
197     private int newestAt;
198
199     // cached values used for interface DomainInfo:
200

201     /** the # of msec by which time advances. */
202     private long deltaTime;
203
204     /** Cached domain start (for use by DomainInfo). */
205     private Long JavaDoc domainStart;
206
207     /** Cached domain end (for use by DomainInfo). */
208     private Long JavaDoc domainEnd;
209
210     /** Cached domain range (for use by DomainInfo). */
211     private Range domainRange;
212
213     // Cached values used for interface RangeInfo: (note minValue pinned at 0)
214
// A single set of extrema covers the entire SeriesCollection
215

216     /** The minimum value. */
217     private Float JavaDoc minValue = new Float JavaDoc(0.0f);
218
219     /** The maximum value. */
220     private Float JavaDoc maxValue = null;
221
222     /** The value range. */
223     private Range valueRange; // autoinit's to null.
224

225     /**
226      * Constructs a dataset with capacity for N series, tied to default
227      * timezone.
228      *
229      * @param nSeries the number of series to be accommodated.
230      * @param nMoments the number of TimePeriods to be spanned.
231      */

232     public DynamicTimeSeriesCollection(int nSeries, int nMoments) {
233
234         this(nSeries, nMoments, new Millisecond(), TimeZone.getDefault());
235         this.newestAt = nMoments - 1;
236
237     }
238
239     /**
240      * Constructs an empty dataset, tied to a specific timezone.
241      *
242      * @param nSeries the number of series to be accommodated
243      * @param nMoments the number of TimePeriods to be spanned
244      * @param zone the timezone.
245      */

246     public DynamicTimeSeriesCollection(int nSeries, int nMoments,
247                                        TimeZone JavaDoc zone) {
248         this(nSeries, nMoments, new Millisecond(), zone);
249         this.newestAt = nMoments - 1;
250     }
251
252     /**
253      * Creates a new dataset.
254      *
255      * @param nSeries the number of series.
256      * @param nMoments the number of items per series.
257      * @param timeSample a time period sample.
258      */

259     public DynamicTimeSeriesCollection(int nSeries,
260                                        int nMoments,
261                                        RegularTimePeriod timeSample) {
262         this(nSeries, nMoments, timeSample, TimeZone.getDefault());
263     }
264
265     /**
266      * Creates a new dataset.
267      *
268      * @param nSeries the number of series.
269      * @param nMoments the number of items per series.
270      * @param timeSample a time period sample.
271      * @param zone the time zone.
272      */

273     public DynamicTimeSeriesCollection(int nSeries,
274                                        int nMoments,
275                                        RegularTimePeriod timeSample,
276                                        TimeZone JavaDoc zone) {
277
278         // the first initialization must precede creation of the ValueSet array:
279
this.maximumItemCount = nMoments; // establishes length of each array
280
this.historyCount = nMoments;
281         this.seriesKeys = new Comparable JavaDoc[nSeries];
282         // initialize the members of "seriesNames" array so they won't be null:
283
for (int i = 0; i < nSeries; i++) {
284             this.seriesKeys[i] = "";
285         }
286         this.newestAt = nMoments - 1;
287         this.valueHistory = new ValueSequence[nSeries];
288         this.timePeriodClass = timeSample.getClass();
289
290         /// Expand the following for all defined TimePeriods:
291
if (this.timePeriodClass == Second.class) {
292             this.pointsInTime = new Second[nMoments];
293         }
294         else if (this.timePeriodClass == Minute.class) {
295             this.pointsInTime = new Minute[nMoments];
296         }
297         else if (this.timePeriodClass == Hour.class) {
298             this.pointsInTime = new Hour[nMoments];
299         }
300         /// .. etc....
301
this.workingCalendar = Calendar.getInstance(zone);
302         this.position = START;
303         this.domainIsPointsInTime = true;
304     }
305
306     /**
307      * Fill the pointsInTime with times using TimePeriod.next():
308      * Will silently return if the time array was already populated.
309      *
310      * Also computes the data cached for later use by
311      * methods implementing the DomainInfo interface:
312      *
313      * @param start the start.
314      *
315      * @return ??.
316      */

317     public synchronized long setTimeBase(RegularTimePeriod start) {
318
319         if (this.pointsInTime[0] == null) {
320             this.pointsInTime[0] = start;
321             for (int i = 1; i < this.historyCount; i++) {
322                 this.pointsInTime[i] = this.pointsInTime[i - 1].next();
323             }
324         }
325         long oldestL = this.pointsInTime[0].getFirstMillisecond(
326             this.workingCalendar
327         );
328         long nextL = this.pointsInTime[1].getFirstMillisecond(
329             this.workingCalendar
330         );
331         this.deltaTime = nextL - oldestL;
332         this.oldestAt = 0;
333         this.newestAt = this.historyCount - 1;
334         findDomainLimits();
335         return this.deltaTime;
336
337     }
338
339     /**
340      * Finds the domain limits. Note: this doesn't need to be synchronized
341      * because it's called from within another method that already is.
342      */

343     protected void findDomainLimits() {
344
345         long startL = getOldestTime().getFirstMillisecond(this.workingCalendar);
346         long endL;
347         if (this.domainIsPointsInTime) {
348             endL = getNewestTime().getFirstMillisecond(this.workingCalendar);
349         }
350         else {
351             endL = getNewestTime().getLastMillisecond(this.workingCalendar);
352         }
353         this.domainStart = new Long JavaDoc(startL);
354         this.domainEnd = new Long JavaDoc(endL);
355         this.domainRange = new Range(startL, endL);
356
357     }
358
359     /**
360      * Returns the x position type (START, MIDDLE or END).
361      *
362      * @return The x position type.
363      */

364     public int getPosition() {
365         return this.position;
366     }
367
368     /**
369      * Sets the x position type (START, MIDDLE or END).
370      *
371      * @param position The x position type.
372      */

373     public void setPosition(int position) {
374         this.position = position;
375     }
376
377     /**
378      * Adds a series to the dataset. Only the y-values are supplied, the
379      * x-values are specified elsewhere.
380      *
381      * @param values the y-values.
382      * @param seriesNumber the series index (zero-based).
383      * @param seriesKey the series key.
384      *
385      * Use this as-is during setup only, or add the synchronized keyword around
386      * the copy loop.
387      */

388     public void addSeries(float[] values,
389                           int seriesNumber, Comparable JavaDoc seriesKey) {
390
391         invalidateRangeInfo();
392         int i;
393         if (values == null) {
394             throw new IllegalArgumentException JavaDoc("TimeSeriesDataset.addSeries(): "
395                 + "cannot add null array of values.");
396         }
397         if (seriesNumber >= this.valueHistory.length) {
398             throw new IllegalArgumentException JavaDoc("TimeSeriesDataset.addSeries(): "
399                 + "cannot add more series than specified in c'tor");
400         }
401         if (this.valueHistory[seriesNumber] == null) {
402             this.valueHistory[seriesNumber]
403                 = new ValueSequence(this.historyCount);
404             this.seriesCount++;
405         }
406         // But if that series array already exists, just overwrite its contents
407

408         // Avoid IndexOutOfBoundsException:
409
int srcLength = values.length;
410         int copyLength = this.historyCount;
411         boolean fillNeeded = false;
412         if (srcLength < this.historyCount) {
413             fillNeeded = true;
414             copyLength = srcLength;
415         }
416         //{
417
for (i = 0; i < copyLength; i++) { // deep copy from values[], caller
418
// can safely discard that array
419
this.valueHistory[seriesNumber].enterData(i, values[i]);
420         }
421         if (fillNeeded) {
422             for (i = copyLength; i < this.historyCount; i++) {
423                 this.valueHistory[seriesNumber].enterData(i, 0.0f);
424             }
425         }
426       //}
427
if (seriesKey != null) {
428             this.seriesKeys[seriesNumber] = seriesKey;
429         }
430         fireSeriesChanged();
431
432     }
433
434     /**
435      * Sets the name of a series. If planning to add values individually.
436      *
437      * @param seriesNumber the series.
438      * @param key the new key.
439      */

440     public void setSeriesKey(int seriesNumber, Comparable JavaDoc key) {
441         this.seriesKeys[seriesNumber] = key;
442     }
443
444     /**
445      * Adds a value to a series.
446      *
447      * @param seriesNumber the series index.
448      * @param index ??.
449      * @param value the value.
450      */

451     public void addValue(int seriesNumber, int index, float value) {
452
453         invalidateRangeInfo();
454         if (seriesNumber >= this.valueHistory.length) {
455             throw new IllegalArgumentException JavaDoc(
456                 "TimeSeriesDataset.addValue(): series #"
457                 + seriesNumber + "unspecified in c'tor"
458             );
459         }
460         if (this.valueHistory[seriesNumber] == null) {
461             this.valueHistory[seriesNumber]
462                 = new ValueSequence(this.historyCount);
463             this.seriesCount++;
464         }
465         // But if that series array already exists, just overwrite its contents
466
//synchronized(this)
467
//{
468
this.valueHistory[seriesNumber].enterData(index, value);
469         //}
470
fireSeriesChanged();
471     }
472
473     /**
474      * Returns the number of series in the collection.
475      *
476      * @return The series count.
477      */

478     public int getSeriesCount() {
479         return this.seriesCount;
480     }
481
482     /**
483      * Returns the number of items in a series.
484      * <p>
485      * For this implementation, all series have the same number of items.
486      *
487      * @param series the series index (zero-based).
488      *
489      * @return The item count.
490      */

491     public int getItemCount(int series) { // all arrays equal length,
492
// so ignore argument:
493
return this.historyCount;
494     }
495
496     // Methods for managing the FIFO's:
497

498     /**
499      * Re-map an index, for use in retrieving data.
500      *
501      * @param toFetch the index.
502      *
503      * @return The translated index.
504      */

505     protected int translateGet(int toFetch) {
506         if (this.oldestAt == 0) {
507             return toFetch; // no translation needed
508
}
509         // else [implicit here]
510
int newIndex = toFetch + this.oldestAt;
511         if (newIndex >= this.historyCount) {
512             newIndex -= this.historyCount;
513         }
514         return newIndex;
515     }
516
517     /**
518      * Returns the actual index to a time offset by "delta" from newestAt.
519      *
520      * @param delta the delta.
521      *
522      * @return The offset.
523      */

524     public int offsetFromNewest(int delta) {
525         return wrapOffset(this.newestAt + delta);
526     }
527
528     /**
529      * ??
530      *
531      * @param delta ??
532      *
533      * @return The offset.
534      */

535     public int offsetFromOldest(int delta) {
536         return wrapOffset(this.oldestAt + delta);
537     }
538
539     /**
540      * ??
541      *
542      * @param protoIndex the index.
543      *
544      * @return The offset.
545      */

546     protected int wrapOffset(int protoIndex) {
547         int tmp = protoIndex;
548         if (tmp >= this.historyCount) {
549             tmp -= this.historyCount;
550         }
551         else if (tmp < 0) {
552             tmp += this.historyCount;
553         }
554         return tmp;
555     }
556
557     /**
558      * Adjust the array offset as needed when a new time-period is added:
559      * Increments the indices "oldestAt" and "newestAt", mod(array length),
560      * zeroes the series values at newestAt, returns the new TimePeriod.
561      *
562      * @return The new time period.
563      */

564     public synchronized RegularTimePeriod advanceTime() {
565         RegularTimePeriod nextInstant = this.pointsInTime[this.newestAt].next();
566         this.newestAt = this.oldestAt; // newestAt takes value previously held
567
// by oldestAT
568
/***
569          * The next 10 lines or so should be expanded if data can be negative
570          ***/

571         // if the oldest data contained a maximum Y-value, invalidate the stored
572
// Y-max and Y-range data:
573
boolean extremaChanged = false;
574         float oldMax = 0.0f;
575         if (this.maxValue != null) {
576             oldMax = this.maxValue.floatValue();
577         }
578         for (int s = 0; s < getSeriesCount(); s++) {
579             if (this.valueHistory[s].getData(this.oldestAt) == oldMax) {
580                 extremaChanged = true;
581             }
582             if (extremaChanged) {
583                 break;
584             }
585         } /*** If data can be < 0, add code here to check the minimum **/
586         if (extremaChanged) {
587             invalidateRangeInfo();
588         }
589         // wipe the next (about to be used) set of data slots
590
float wiper = (float) 0.0;
591         for (int s = 0; s < getSeriesCount(); s++) {
592             this.valueHistory[s].enterData(this.newestAt, wiper);
593         }
594         // Update the array of TimePeriods:
595
this.pointsInTime[this.newestAt] = nextInstant;
596         // Now advance "oldestAt", wrapping at end of the array
597
this.oldestAt++;
598         if (this.oldestAt >= this.historyCount) {
599             this.oldestAt = 0;
600         }
601         // Update the domain limits:
602
long startL = this.domainStart.longValue(); //(time is kept in msec)
603
this.domainStart = new Long JavaDoc(startL + this.deltaTime);
604         long endL = this.domainEnd.longValue();
605         this.domainEnd = new Long JavaDoc(endL + this.deltaTime);
606         this.domainRange = new Range(startL, endL);
607         fireSeriesChanged();
608         return nextInstant;
609     }
610
611     // If data can be < 0, the next 2 methods should be modified
612

613     /**
614      * Invalidates the range info.
615      */

616     public void invalidateRangeInfo() {
617         this.maxValue = null;
618         this.valueRange = null;
619     }
620
621     /**
622      * Returns the maximum value.
623      *
624      * @return The maximum value.
625      */

626     protected double findMaxValue() {
627         double max = 0.0f;
628         for (int s = 0; s < getSeriesCount(); s++) {
629             for (int i = 0; i < this.historyCount; i++) {
630                 double tmp = getYValue(s, i);
631                 if (tmp > max) {
632                     max = tmp;
633                 }
634             }
635         }
636         return max;
637     }
638
639     /** End, positive-data-only code **/
640
641     /**
642      * Returns the index of the oldest data item.
643      *
644      * @return The index.
645      */

646     public int getOldestIndex() {
647         return this.oldestAt;
648     }
649
650     /**
651      * Returns the index of the newest data item.
652      *
653      * @return The index.
654      */

655     public int getNewestIndex() {
656         return this.newestAt;
657     }
658
659     // appendData() writes new data at the index position given by newestAt/
660
// When adding new data dynamically, use advanceTime(), followed by this:
661
/**
662      * Appends new data.
663      *
664      * @param newData the data.
665      */

666     public void appendData(float[] newData) {
667         int nDataPoints = newData.length;
668         if (nDataPoints > this.valueHistory.length) {
669             throw new IllegalArgumentException JavaDoc(
670                "More data than series to put them in"
671             );
672         }
673         int s; // index to select the "series"
674
for (s = 0; s < nDataPoints; s++) {
675             // check whether the "valueHistory" array member exists; if not,
676
// create them:
677
if (this.valueHistory[s] == null) {
678                 this.valueHistory[s] = new ValueSequence(this.historyCount);
679             }
680             this.valueHistory[s].enterData(this.newestAt, newData[s]);
681         }
682         fireSeriesChanged();
683     }
684
685     /**
686      * Appends data at specified index, for loading up with data from file(s).
687      *
688      * @param newData the data
689      * @param insertionIndex the index value at which to put it
690      * @param refresh value of n in "refresh the display on every nth call"
691      * (ignored if <= 0 )
692      */

693      public void appendData(float[] newData, int insertionIndex, int refresh) {
694          int nDataPoints = newData.length;
695          if (nDataPoints > this.valueHistory.length) {
696              throw new IllegalArgumentException JavaDoc(
697                  "More data than series to put them " + "in"
698              );
699          }
700          for (int s = 0; s < nDataPoints; s++) {
701              if (this.valueHistory[s] == null) {
702                 this.valueHistory[s] = new ValueSequence(this.historyCount);
703              }
704              this.valueHistory[s].enterData(insertionIndex, newData[s]);
705          }
706          if (refresh > 0) {
707              insertionIndex++;
708              if (insertionIndex % refresh == 0) {
709                  fireSeriesChanged();
710              }
711          }
712     }
713
714     /**
715      * Returns the newest time.
716      *
717      * @return The newest time.
718      */

719     public RegularTimePeriod getNewestTime() {
720         return this.pointsInTime[this.newestAt];
721     }
722
723     /**
724      * Returns the oldest time.
725      *
726      * @return The oldest time.
727      */

728     public RegularTimePeriod getOldestTime() {
729         return this.pointsInTime[this.oldestAt];
730     }
731
732     /**
733      * Returns the x-value.
734      *
735      * @param series the series index (zero-based).
736      * @param item the item index (zero-based).
737      *
738      * @return The value.
739      */

740     // getXxx() ftns can ignore the "series" argument:
741
// Don't synchronize this!! Instead, synchronize the loop that calls it.
742
public Number JavaDoc getX(int series, int item) {
743         RegularTimePeriod tp = this.pointsInTime[translateGet(item)];
744         return new Long JavaDoc(getX(tp));
745     }
746
747     /**
748      * Returns the y-value.
749      *
750      * @param series the series index (zero-based).
751      * @param item the item index (zero-based).
752      *
753      * @return The value.
754      */

755     public double getYValue(int series, int item) {
756         // Don't synchronize this!!
757
// Instead, synchronize the loop that calls it.
758
ValueSequence values = this.valueHistory[series];
759         return values.getData(translateGet(item));
760     }
761
762     /**
763      * Returns the y-value.
764      *
765      * @param series the series index (zero-based).
766      * @param item the item index (zero-based).
767      *
768      * @return The value.
769      */

770     public Number JavaDoc getY(int series, int item) {
771         return new Float JavaDoc(getYValue(series, item));
772     }
773
774     /**
775      * Returns the start x-value.
776      *
777      * @param series the series index (zero-based).
778      * @param item the item index (zero-based).
779      *
780      * @return The value.
781      */

782     public Number JavaDoc getStartX(int series, int item) {
783         RegularTimePeriod tp = this.pointsInTime[translateGet(item)];
784         return new Long JavaDoc(tp.getFirstMillisecond(this.workingCalendar));
785     }
786
787     /**
788      * Returns the end x-value.
789      *
790      * @param series the series index (zero-based).
791      * @param item the item index (zero-based).
792      *
793      * @return The value.
794      */

795     public Number JavaDoc getEndX(int series, int item) {
796         RegularTimePeriod tp = this.pointsInTime[translateGet(item)];
797         return new Long JavaDoc(tp.getLastMillisecond(this.workingCalendar));
798     }
799
800     /**
801      * Returns the start y-value.
802      *
803      * @param series the series index (zero-based).
804      * @param item the item index (zero-based).
805      *
806      * @return The value.
807      */

808     public Number JavaDoc getStartY(int series, int item) {
809         return getY(series, item);
810     }
811
812     /**
813      * Returns the end y-value.
814      *
815      * @param series the series index (zero-based).
816      * @param item the item index (zero-based).
817      *
818      * @return The value.
819      */

820     public Number JavaDoc getEndY(int series, int item) {
821         return getY(series, item);
822     }
823
824     /* // "Extras" found useful when analyzing/verifying class behavior:
825     public Number getUntranslatedXValue(int series, int item)
826     {
827       return super.getXValue(series, item);
828     }
829
830     public float getUntranslatedY(int series, int item)
831     {
832       return super.getY(series, item);
833     } */

834
835     /**
836      * Returns the key for a series.
837      *
838      * @param series the series index (zero-based).
839      *
840      * @return The key.
841      */

842     public Comparable JavaDoc getSeriesKey(int series) {
843         return this.seriesKeys[series];
844     }
845
846     /**
847      * Sends a {@link SeriesChangeEvent} to all registered listeners.
848      */

849     protected void fireSeriesChanged() {
850         seriesChanged(new SeriesChangeEvent(this));
851     }
852
853     // The next 3 functions override the base-class implementation of
854
// the DomainInfo interface. Using saved limits (updated by
855
// each updateTime() call), improves performance.
856
//
857

858     /**
859      * Returns the minimum x-value in the dataset.
860      *
861      * @param includeInterval a flag that determines whether or not the
862      * x-interval is taken into account.
863      *
864      * @return The minimum value.
865      */

866     public double getDomainLowerBound(boolean includeInterval) {
867         return this.domainStart.doubleValue();
868         // a Long kept updated by advanceTime()
869
}
870
871     /**
872      * Returns the maximum x-value in the dataset.
873      *
874      * @param includeInterval a flag that determines whether or not the
875      * x-interval is taken into account.
876      *
877      * @return The maximum value.
878      */

879     public double getDomainUpperBound(boolean includeInterval) {
880         return this.domainEnd.doubleValue();
881         // a Long kept updated by advanceTime()
882
}
883
884     /**
885      * Returns the range of the values in this dataset's domain.
886      *
887      * @param includeInterval a flag that determines whether or not the
888      * x-interval is taken into account.
889      *
890      * @return The range.
891      */

892     public Range getDomainBounds(boolean includeInterval) {
893         if (this.domainRange == null) {
894             findDomainLimits();
895         }
896         return this.domainRange;
897     }
898     
899     /**
900      * Returns the x-value for a time period.
901      *
902      * @param period the period.
903      *
904      * @return The x-value.
905      */

906     private long getX(RegularTimePeriod period) {
907         switch (this.position) {
908             case (START) :
909                 return period.getFirstMillisecond(this.workingCalendar);
910             case (MIDDLE) :
911                 return period.getMiddleMillisecond(this.workingCalendar);
912             case (END) :
913                 return period.getLastMillisecond(this.workingCalendar);
914             default:
915                 return period.getMiddleMillisecond(this.workingCalendar);
916         }
917      }
918
919     // The next 3 functions implement the RangeInfo interface.
920
// Using saved limits (updated by each updateTime() call) significantly
921
// improves performance. WARNING: this code makes the simplifying
922
// assumption that data is never negative. Expand as needed for the
923
// general case.
924

925     /**
926      * Returns the minimum range value.
927      *
928      * @param includeInterval a flag that determines whether or not the
929      * y-interval is taken into account.
930      *
931      * @return The minimum range value.
932      */

933     public double getRangeLowerBound(boolean includeInterval) {
934         double result = Double.NaN;
935         if (this.minValue != null) {
936             result = this.minValue.doubleValue();
937         }
938         return result;
939     }
940
941     /**
942      * Returns the maximum range value.
943      *
944      * @param includeInterval a flag that determines whether or not the
945      * y-interval is taken into account.
946      *
947      * @return The maximum range value.
948      */

949     public double getRangeUpperBound(boolean includeInterval) {
950         double result = Double.NaN;
951         if (this.maxValue != null) {
952             result = this.maxValue.doubleValue();
953         }
954         return result;
955     }
956
957     /**
958      * Returns the value range.
959      *
960      * @param includeInterval a flag that determines whether or not the
961      * y-interval is taken into account.
962      *
963      * @return The range.
964      */

965     public Range getRangeBounds(boolean includeInterval) {
966         if (this.valueRange == null) {
967             double max = getRangeUpperBound(includeInterval);
968             this.valueRange = new Range(0.0, max);
969         }
970         return this.valueRange;
971     }
972     
973 }
974
Popular Tags