| 1 46 47 package org.jfree.chart.axis; 48 49 import java.io.Serializable ; 50 import java.util.ArrayList ; 51 import java.util.Calendar ; 52 import java.util.Collections ; 53 import java.util.Date ; 54 import java.util.GregorianCalendar ; 55 import java.util.Iterator ; 56 import java.util.List ; 57 import java.util.SimpleTimeZone ; 58 import java.util.TimeZone ; 59 60 164 public class SegmentedTimeline implements Timeline, Cloneable , Serializable { 165 166 167 private static final long serialVersionUID = 1093779862539903110L; 168 169 173 174 public static final long DAY_SEGMENT_SIZE = 24 * 60 * 60 * 1000; 175 176 177 public static final long HOUR_SEGMENT_SIZE = 60 * 60 * 1000; 178 179 180 public static final long FIFTEEN_MINUTE_SEGMENT_SIZE = 15 * 60 * 1000; 181 182 183 public static final long MINUTE_SEGMENT_SIZE = 60 * 1000; 184 185 189 195 public static long FIRST_MONDAY_AFTER_1900; 196 197 202 public static TimeZone NO_DST_TIME_ZONE; 203 204 210 public static TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault(); 211 212 216 private Calendar workingCalendarNoDST 217 = new GregorianCalendar (NO_DST_TIME_ZONE); 218 219 222 private Calendar workingCalendar = Calendar.getInstance(); 223 224 228 229 private long segmentSize; 230 231 232 private int segmentsIncluded; 233 234 235 private int segmentsExcluded; 236 237 238 private int groupSegmentCount; 239 240 244 private long startTime; 245 246 247 private long segmentsIncludedSize; 248 249 250 private long segmentsExcludedSize; 251 252 253 private long segmentsGroupSize; 254 255 259 private List exceptionSegments = new ArrayList (); 260 261 271 private SegmentedTimeline baseTimeline; 272 273 274 private boolean adjustForDaylightSaving = false; 275 276 280 static { 281 int offset = TimeZone.getDefault().getRawOffset(); 283 NO_DST_TIME_ZONE = new SimpleTimeZone (offset, "UTC-" + offset); 284 285 Calendar cal = new GregorianCalendar (NO_DST_TIME_ZONE); 288 cal.set(1900, 0, 1, 0, 0, 0); 289 cal.set(Calendar.MILLISECOND, 0); 290 while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) { 291 cal.add(Calendar.DATE, 1); 292 } 293 FIRST_MONDAY_AFTER_1900 = cal.getTime().getTime(); 296 } 297 298 302 315 public SegmentedTimeline(long segmentSize, 316 int segmentsIncluded, 317 int segmentsExcluded) { 318 319 this.segmentSize = segmentSize; 320 this.segmentsIncluded = segmentsIncluded; 321 this.segmentsExcluded = segmentsExcluded; 322 323 this.groupSegmentCount = this.segmentsIncluded + this.segmentsExcluded; 324 this.segmentsIncludedSize = this.segmentsIncluded * this.segmentSize; 325 this.segmentsExcludedSize = this.segmentsExcluded * this.segmentSize; 326 this.segmentsGroupSize = this.segmentsIncludedSize 327 + this.segmentsExcludedSize; 328 329 } 330 331 339 public static SegmentedTimeline newMondayThroughFridayTimeline() { 340 SegmentedTimeline timeline 341 = new SegmentedTimeline(DAY_SEGMENT_SIZE, 5, 2); 342 timeline.setStartTime(FIRST_MONDAY_AFTER_1900); 343 return timeline; 344 } 345 346 363 public static SegmentedTimeline newFifteenMinuteTimeline() { 364 SegmentedTimeline timeline 365 = new SegmentedTimeline(FIFTEEN_MINUTE_SEGMENT_SIZE, 28, 68); 366 timeline.setStartTime( 367 FIRST_MONDAY_AFTER_1900 + 36 * timeline.getSegmentSize() 368 ); 369 timeline.setBaseTimeline(newMondayThroughFridayTimeline()); 370 return timeline; 371 } 372 373 379 public boolean getAdjustForDaylightSaving() { 380 return this.adjustForDaylightSaving; 381 } 382 383 389 public void setAdjustForDaylightSaving(boolean adjust) { 390 this.adjustForDaylightSaving = adjust; 391 } 392 393 397 403 public void setStartTime(long millisecond) { 404 this.startTime = millisecond; 405 } 406 407 413 public long getStartTime() { 414 return this.startTime; 415 } 416 417 422 public int getSegmentsExcluded() { 423 return this.segmentsExcluded; 424 } 425 426 432 public long getSegmentsExcludedSize() { 433 return this.segmentsExcludedSize; 434 } 435 436 442 public int getGroupSegmentCount() { 443 return this.groupSegmentCount; 444 } 445 446 452 public long getSegmentsGroupSize() { 453 return this.segmentsGroupSize; 454 } 455 456 461 public int getSegmentsIncluded() { 462 return this.segmentsIncluded; 463 } 464 465 470 public long getSegmentsIncludedSize() { 471 return this.segmentsIncludedSize; 472 } 473 474 479 public long getSegmentSize() { 480 return this.segmentSize; 481 } 482 483 489 public List getExceptionSegments() { 490 return Collections.unmodifiableList(this.exceptionSegments); 491 } 492 493 498 public void setExceptionSegments(List exceptionSegments) { 499 this.exceptionSegments = exceptionSegments; 500 } 501 502 507 public SegmentedTimeline getBaseTimeline() { 508 return this.baseTimeline; 509 } 510 511 516 public void setBaseTimeline(SegmentedTimeline baseTimeline) { 517 518 if (baseTimeline != null) { 520 if (baseTimeline.getSegmentSize() < this.segmentSize) { 521 throw new IllegalArgumentException ( 522 "baseTimeline.getSegmentSize() is smaller than segmentSize" 523 ); 524 } 525 else if (baseTimeline.getStartTime() > this.startTime) { 526 throw new IllegalArgumentException ( 527 "baseTimeline.getStartTime() is after startTime" 528 ); 529 } 530 else if ((baseTimeline.getSegmentSize() % this.segmentSize) != 0) { 531 throw new IllegalArgumentException ( 532 "baseTimeline.getSegmentSize() is not multiple of " 533 + "segmentSize" 534 ); 535 } 536 else if (((this.startTime 537 - baseTimeline.getStartTime()) % this.segmentSize) != 0) { 538 throw new IllegalArgumentException ( 539 "baseTimeline is not aligned" 540 ); 541 } 542 } 543 544 this.baseTimeline = baseTimeline; 545 } 546 547 556 public long toTimelineValue(long millisecond) { 557 558 long result; 559 long rawMilliseconds = millisecond - this.startTime; 560 long groupMilliseconds = rawMilliseconds % this.segmentsGroupSize; 561 long groupIndex = rawMilliseconds / this.segmentsGroupSize; 562 563 if (groupMilliseconds >= this.segmentsIncludedSize) { 564 result = toTimelineValue( 565 this.startTime + this.segmentsGroupSize * (groupIndex + 1) 566 ); 567 } 568 else { 569 Segment segment = getSegment(millisecond); 570 if (segment.inExceptionSegments()) { 571 result = toTimelineValue(segment.getSegmentEnd() + 1); 572 } 573 else { 574 long shiftedSegmentedValue = millisecond - this.startTime; 575 long x = shiftedSegmentedValue % this.segmentsGroupSize; 576 long y = shiftedSegmentedValue / this.segmentsGroupSize; 577 578 long wholeExceptionsBeforeDomainValue = 579 getExceptionSegmentCount(this.startTime, millisecond - 1); 580 581 588 if (x < this.segmentsIncludedSize) { 589 result = this.segmentsIncludedSize * y 590 + x - wholeExceptionsBeforeDomainValue 591 * this.segmentSize; 592 } 594 else { 595 result = this.segmentsIncludedSize * (y + 1) 596 - wholeExceptionsBeforeDomainValue 597 * this.segmentSize; 598 } 600 } 601 } 602 603 return result; 604 } 605 606 615 public long toTimelineValue(Date date) { 616 return toTimelineValue(getTime(date)); 617 } 619 620 627 public long toMillisecond(long timelineValue) { 628 629 Segment result = new Segment(this.startTime + timelineValue 631 + (timelineValue / this.segmentsIncludedSize) 632 * this.segmentsExcludedSize); 633 634 long lastIndex = this.startTime; 635 636 while (lastIndex <= result.segmentStart) { 638 639 long exceptionSegmentCount; 641 while ((exceptionSegmentCount = getExceptionSegmentCount( 642 lastIndex, (result.millisecond / this.segmentSize) 643 * this.segmentSize - 1)) > 0 644 ) { 645 lastIndex = result.segmentStart; 646 for (int i = 0; i < exceptionSegmentCount; i++) { 649 do { 650 result.inc(); 651 } 652 while (result.inExcludeSegments()); 653 } 654 } 655 lastIndex = result.segmentStart; 656 657 while (result.inExceptionSegments() || result.inExcludeSegments()) { 659 result.inc(); 660 lastIndex += this.segmentSize; 661 } 662 663 lastIndex++; 664 } 665 666 return getTimeFromLong(result.millisecond); 667 } 668 669 676 public long getTimeFromLong(long date) { 677 long result = date; 678 if (this.adjustForDaylightSaving) { 679 this.workingCalendarNoDST.setTime(new Date (date)); 680 this.workingCalendar.set( 681 this.workingCalendarNoDST.get(Calendar.YEAR), 682 this.workingCalendarNoDST.get(Calendar.MONTH), 683 this.workingCalendarNoDST.get(Calendar.DATE), 684 this.workingCalendarNoDST.get(Calendar.HOUR_OF_DAY), 685 this.workingCalendarNoDST.get(Calendar.MINUTE), 686 this.workingCalendarNoDST.get(Calendar.SECOND) 687 ); 688 this.workingCalendar.set( 689 Calendar.MILLISECOND, 690 this.workingCalendarNoDST.get(Calendar.MILLISECOND) 691 ); 692 result = this.workingCalendar.getTime().getTime(); 695 } 696 return result; 697 } 698 699 706 public boolean containsDomainValue(long millisecond) { 707 Segment segment = getSegment(millisecond); 708 return segment.inIncludeSegments(); 709 } 710 711 718 public boolean containsDomainValue(Date date) { 719 return containsDomainValue(getTime(date)); 720 } 721 722 732 public boolean containsDomainRange(long domainValueStart, 733 long domainValueEnd) { 734 if (domainValueEnd < domainValueStart) { 735 throw new IllegalArgumentException ( 736 "domainValueEnd (" + domainValueEnd 737 + ") < domainValueStart (" + domainValueStart + ")" 738 ); 739 } 740 Segment segment = getSegment(domainValueStart); 741 boolean contains = true; 742 do { 743 contains = (segment.inIncludeSegments()); 744 if (segment.contains(domainValueEnd)) { 745 break; 746 } 747 else { 748 segment.inc(); 749 } 750 } 751 while (contains); 752 return (contains); 753 } 754 755 765 public boolean containsDomainRange(Date dateDomainValueStart, 766 Date dateDomainValueEnd) { 767 return containsDomainRange( 768 getTime(dateDomainValueStart), getTime(dateDomainValueEnd) 769 ); 770 } 771 772 784 public void addException(long millisecond) { 785 addException(new Segment(millisecond)); 786 } 787 788 803 public void addException(long fromDomainValue, long toDomainValue) { 804 addException(new SegmentRange(fromDomainValue, toDomainValue)); 805 } 806 807 818 public void addException(Date exceptionDate) { 819 addException(getTime(exceptionDate)); 820 } 822 823 835 public void addExceptions(List exceptionList) { 836 for (Iterator iter = exceptionList.iterator(); iter.hasNext();) { 837 addException((Date ) iter.next()); 838 } 839 } 840 841 851 private void addException(Segment segment) { 852 if (segment.inIncludeSegments()) { 853 int p = binarySearchExceptionSegments(segment); 854 this.exceptionSegments.add(-(p + 1), segment); 855 } 856 } 857 858 874 public void addBaseTimelineException(long domainValue) { 875 876 Segment baseSegment = this.baseTimeline.getSegment(domainValue); 877 if (baseSegment.inIncludeSegments()) { 878 879 Segment segment = getSegment(baseSegment.getSegmentStart()); 882 while (segment.getSegmentStart() <= baseSegment.getSegmentEnd()) { 883 if (segment.inIncludeSegments()) { 884 885 long fromDomainValue = segment.getSegmentStart(); 887 long toDomainValue; 888 do { 889 toDomainValue = segment.getSegmentEnd(); 890 segment.inc(); 891 } 892 while (segment.inIncludeSegments()); 893 894 addException(fromDomainValue, toDomainValue); 896 897 } 898 else { 899 segment.inc(); 901 } 902 } 903 } 904 } 905 906 918 public void addBaseTimelineException(Date date) { 919 addBaseTimelineException(getTime(date)); 920 } 921 922 931 public void addBaseTimelineExclusions(long fromBaseDomainValue, 932 long toBaseDomainValue) { 933 934 Segment baseSegment = this.baseTimeline.getSegment(fromBaseDomainValue); 936 while (baseSegment.getSegmentStart() <= toBaseDomainValue 937 && !baseSegment.inExcludeSegments()) { 938 939 baseSegment.inc(); 940 941 } 942 943 while (baseSegment.getSegmentStart() <= toBaseDomainValue) { 945 946 long baseExclusionRangeEnd = baseSegment.getSegmentStart() 947 + this.baseTimeline.getSegmentsExcluded() 948 * this.baseTimeline.getSegmentSize() - 1; 949 950 Segment segment = getSegment(baseSegment.getSegmentStart()); 953 while (segment.getSegmentStart() <= baseExclusionRangeEnd) { 954 &nbs
|