1 59 60 package org.jfree.data.time; 61 62 import java.util.Calendar ; 63 import java.util.TimeZone ; 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 86 public class DynamicTimeSeriesCollection extends AbstractIntervalXYDataset 87 implements IntervalXYDataset, 88 DomainInfo, 89 RangeInfo { 90 91 95 public static final int START = 0; 96 97 100 public static final int MIDDLE = 1; 101 102 105 public static final int END = 2; 106 107 108 private int maximumItemCount = 2000; 110 111 protected int historyCount; 112 113 114 private Comparable [] seriesKeys; 115 116 117 private Class timePeriodClass = Minute.class; 119 120 protected RegularTimePeriod[] pointsInTime; 121 122 123 private int seriesCount; 124 125 128 protected class ValueSequence { 129 130 131 float[] dataPoints; 132 133 136 public ValueSequence() { 137 this(DynamicTimeSeriesCollection.this.maximumItemCount); 138 } 139 140 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 158 public void enterData(int index, float value) { 159 this.dataPoints[index] = value; 160 } 161 162 169 public float getData(int index) { 170 return this.dataPoints[index]; 171 } 172 } 173 174 175 protected ValueSequence[] valueHistory; 176 177 178 protected Calendar workingCalendar; 179 180 184 private int position; 185 186 191 private boolean domainIsPointsInTime; 192 193 194 private int oldestAt; 196 197 private int newestAt; 198 199 201 202 private long deltaTime; 203 204 205 private Long domainStart; 206 207 208 private Long domainEnd; 209 210 211 private Range domainRange; 212 213 216 217 private Float minValue = new Float (0.0f); 218 219 220 private Float maxValue = null; 221 222 223 private Range valueRange; 225 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 246 public DynamicTimeSeriesCollection(int nSeries, int nMoments, 247 TimeZone zone) { 248 this(nSeries, nMoments, new Millisecond(), zone); 249 this.newestAt = nMoments - 1; 250 } 251 252 259 public DynamicTimeSeriesCollection(int nSeries, 260 int nMoments, 261 RegularTimePeriod timeSample) { 262 this(nSeries, nMoments, timeSample, TimeZone.getDefault()); 263 } 264 265 273 public DynamicTimeSeriesCollection(int nSeries, 274 int nMoments, 275 RegularTimePeriod timeSample, 276 TimeZone zone) { 277 278 this.maximumItemCount = nMoments; this.historyCount = nMoments; 281 this.seriesKeys = new Comparable [nSeries]; 282 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 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 this.workingCalendar = Calendar.getInstance(zone); 302 this.position = START; 303 this.domainIsPointsInTime = true; 304 } 305 306 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 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 (startL); 354 this.domainEnd = new Long (endL); 355 this.domainRange = new Range(startL, endL); 356 357 } 358 359 364 public int getPosition() { 365 return this.position; 366 } 367 368 373 public void setPosition(int position) { 374 this.position = position; 375 } 376 377 388 public void addSeries(float[] values, 389 int seriesNumber, Comparable seriesKey) { 390 391 invalidateRangeInfo(); 392 int i; 393 if (values == null) { 394 throw new IllegalArgumentException ("TimeSeriesDataset.addSeries(): " 395 + "cannot add null array of values."); 396 } 397 if (seriesNumber >= this.valueHistory.length) { 398 throw new IllegalArgumentException ("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 408 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 for (i = 0; i < copyLength; i++) { 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 if (seriesKey != null) { 428 this.seriesKeys[seriesNumber] = seriesKey; 429 } 430 fireSeriesChanged(); 431 432 } 433 434 440 public void setSeriesKey(int seriesNumber, Comparable key) { 441 this.seriesKeys[seriesNumber] = key; 442 } 443 444 451 public void addValue(int seriesNumber, int index, float value) { 452 453 invalidateRangeInfo(); 454 if (seriesNumber >= this.valueHistory.length) { 455 throw new IllegalArgumentException ( 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 this.valueHistory[seriesNumber].enterData(index, value); 469 fireSeriesChanged(); 471 } 472 473 478 public int getSeriesCount() { 479 return this.seriesCount; 480 } 481 482 491 public int getItemCount(int series) { return this.historyCount; 494 } 495 496 498 505 protected int translateGet(int toFetch) { 506 if (this.oldestAt == 0) { 507 return toFetch; } 509 int newIndex = toFetch + this.oldestAt; 511 if (newIndex >= this.historyCount) { 512 newIndex -= this.historyCount; 513 } 514 return newIndex; 515 } 516 517 524 public int offsetFromNewest(int delta) { 525 return wrapOffset(this.newestAt + delta); 526 } 527 528 535 public int offsetFromOldest(int delta) { 536 return wrapOffset(this.oldestAt + delta); 537 } 538 539 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 564 public synchronized RegularTimePeriod advanceTime() { 565 RegularTimePeriod nextInstant = this.pointsInTime[this.newestAt].next(); 566 this.newestAt = this.oldestAt; 571 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 } 586 if (extremaChanged) { 587 invalidateRangeInfo(); 588 } 589 float wiper = (float) 0.0; 591 for (int s = 0; s < getSeriesCount(); s++) { 592 this.valueHistory[s].enterData(this.newestAt, wiper); 593 } 594 this.pointsInTime[this.newestAt] = nextInstant; 596 this.oldestAt++; 598 if (this.oldestAt >= this.historyCount) { 599 this.oldestAt = 0; 600 } 601 long startL = this.domainStart.longValue(); this.domainStart = new Long (startL + this.deltaTime); 604 long endL = this.domainEnd.longValue(); 605 this.domainEnd = new Long (endL + this.deltaTime); 606 this.domainRange = new Range(startL, endL); 607 fireSeriesChanged(); 608 return nextInstant; 609 } 610 611 613 616 public void invalidateRangeInfo() { 617 this.maxValue = null; 618 this.valueRange = null; 619 } 620 621 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 640 641 646 public int getOldestIndex() { 647 return this.oldestAt; 648 } 649 650 655 public int getNewestIndex() { 656 return this.newestAt; 657 } 658 659 666 public void appendData(float[] newData) { 667 int nDataPoints = newData.length; 668 if (nDataPoints > this.valueHistory.length) { 669 throw new IllegalArgumentException ( 670 "More data than series to put them in" 671 ); 672 } 673 int s; for (s = 0; s < nDataPoints; s++) { 675 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 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 ( 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 719 public RegularTimePeriod getNewestTime() { 720 return this.pointsInTime[this.newestAt]; 721 } 722 723 728 public RegularTimePeriod getOldestTime() { 729 return this.pointsInTime[this.oldestAt]; 730 } 731 732 740 public Number getX(int series, int item) { 743 RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; 744 return new Long (getX(tp)); 745 } 746 747 755 public double getYValue(int series, int item) { 756 ValueSequence values = this.valueHistory[series]; 759 return values.getData(translateGet(item)); 760 } 761 762 770 public Number getY(int series, int item) { 771 return new Float (getYValue(series, item)); 772 } 773 774 782 public Number getStartX(int series, int item) { 783 RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; 784 return new Long (tp.getFirstMillisecond(this.workingCalendar)); 785 } 786 787 795 public Number getEndX(int series, int item) { 796 RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; 797 return new Long (tp.getLastMillisecond(this.workingCalendar)); 798 } 799 800 808 public Number getStartY(int series, int item) { 809 return getY(series, item); 810 } 811 812 820 public Number getEndY(int series, int item) { 821 return getY(series, item); 822 } 823 824 834 835 842 public Comparable getSeriesKey(int series) { 843 return this.seriesKeys[series]; 844 } 845 846 849 protected void fireSeriesChanged() { 850 seriesChanged(new SeriesChangeEvent(this)); 851 } 852 853 858 866 public double getDomainLowerBound(boolean includeInterval) { 867 return this.domainStart.doubleValue(); 868 } 870 871 879 public double getDomainUpperBound(boolean includeInterval) { 880 return this.domainEnd.doubleValue(); 881 } 883 884 892 public Range getDomainBounds(boolean includeInterval) { 893 if (this.domainRange == null) { 894 findDomainLimits(); 895 } 896 return this.domainRange; 897 } 898 899 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 925 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 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 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 |