📄 segmentedtimeline.java
字号:
* to exclude from what would otherwise be considered a valid segment of the timeline. * An exception segment can not be contained inside an already excluded segment. * If so, no action will occur (the proposed exception segment will be discarded). * <p> * The segment is identified by a domainValue into any part of the baseTimeline segment. * * @param domainValue domain value to teat as a baseTimeline exception. */ public void addBaseTimelineException(long domainValue) { Segment baseSegment = this.baseTimeline.getSegment(domainValue); if (baseSegment.inIncludeSegments()) { // cycle through all the segments contained in the BaseTimeline exception segment Segment segment = getSegment(baseSegment.getSegmentStart()); while (segment.getSegmentStart() <= baseSegment.getSegmentEnd()) { if (segment.inIncludeSegments()) { // find all consecutive included segments long fromDomainValue = segment.getSegmentStart(); long toDomainValue; do { toDomainValue = segment.getSegmentEnd(); segment.inc(); } while (segment.inIncludeSegments()); // add the interval as an exception addException(fromDomainValue, toDomainValue); } else { // this is not one of our included segment, skip it segment.inc(); } } } } /** * Adds a segment relative to the baseTimeline as an exception. An exception segment is * defined as a segment to exclude from what would otherwise be considered a valid segment * of the timeline. * An exception segment can not be contained inside an already excluded segment. * If so, no action will occure (the proposed exception segment will be discarted). * <p> * The segment is identified by a domainValue into any part of the segment. * Therefore the segmentStart <= domainValue <= segmentEnd. * * @param date date domain value to treat as a baseTimeline exception */ public void addBaseTimelineException(Date date) { addBaseTimelineException(getTime(date)); } /** * Adds all excluded segments from the BaseTimeline as exceptions to our timeline. This allows * us to combine two timelines for more complex calculations. * * @param fromBaseDomainValue Start of the range where exclusions will be extracted. * @param toBaseDomainValue End of the range to process. */ public void addBaseTimelineExclusions(long fromBaseDomainValue, long toBaseDomainValue) { // find first excluded base segment starting fromDomainValue Segment baseSegment = this.baseTimeline.getSegment(fromBaseDomainValue); while (baseSegment.getSegmentStart() <= toBaseDomainValue && !baseSegment.inExcludeSegments()) { baseSegment.inc(); } // cycle over all the base segments groups in the range while (baseSegment.getSegmentStart() <= toBaseDomainValue) { long baseExclusionRangeEnd = baseSegment.getSegmentStart() + this.baseTimeline.getSegmentsExcluded() * this.baseTimeline.getSegmentSize() - 1; // cycle through all the segments contained in the base exclusion area Segment segment = getSegment(baseSegment.getSegmentStart()); while (segment.getSegmentStart() <= baseExclusionRangeEnd) { if (segment.inIncludeSegments()) { // find all consecutive included segments long fromDomainValue = segment.getSegmentStart(); long toDomainValue; do { toDomainValue = segment.getSegmentEnd(); segment.inc(); } while (segment.inIncludeSegments()); // add the interval as an exception addException(new BaseTimelineSegmentRange(fromDomainValue, toDomainValue)); } else { // this is not one of our included segment, skip it segment.inc(); } } // go to next base segment group baseSegment.inc(this.baseTimeline.getGroupSegmentCount()); } } /** * Returns the number of exception segments wholly contained in the * (fromDomainValue, toDomainValue) interval. * * @param fromMillisecond the beginning of the interval. * @param toMillisecond the end of the interval. * * @return Number of exception segments contained in the interval. */ public long getExceptionSegmentCount(long fromMillisecond, long toMillisecond) { if (toMillisecond < fromMillisecond) { return (0); } int n = 0; for (Iterator iter = this.exceptionSegments.iterator(); iter.hasNext();) { Segment segment = (Segment) iter.next(); Segment intersection = segment.intersect(fromMillisecond, toMillisecond); if (intersection != null) { n += intersection.getSegmentCount(); } } return (n); } /** * Returns a segment that contains a domainValue. If the domainValue is not contained * in the timeline (because it is not contained in the baseTimeline), * a Segment that contains <code>index + segmentSize*m</code> will be returned for the * smallest <code>m</code> possible. * @param millisecond index into the segment * @return a Segment that contains index, or the next possible Segment. */ public Segment getSegment(long millisecond) { return new Segment(millisecond); } /** * Returns a segment that contains a date. For accurate calculations, * the calendar should use TIME_ZONE for its calculation (or any other similar time zone). * * If the date is not contained in the timeline (because it is not contained in the * baseTimeline), a Segment that contains <code>date + segmentSize*m</code> will be returned * for the smallest <code>m</code> possible. * * @param date date into the segment * @return a Segment that contains date, or the next possible Segment. */ public Segment getSegment(Date date) { return (getSegment(getTime(date))); } /** * Convenient method to test equality in two object taking into account nulls. * @param o first object to compare * @param p second object to compare * @return true if both objects are equal or both null, false otherwise. */ private boolean equals(Object o, Object p) { return (o == p || ((o != null) && o.equals(p))); } /** * Returns true if we are equal to the parameter * @param o Object to verify with us * @return true or false */ public boolean equals(Object o) { if (o instanceof SegmentedTimeline) { SegmentedTimeline other = (SegmentedTimeline) o; boolean b0 = (this.segmentSize == other.getSegmentSize()); boolean b1 = (this.segmentsIncluded == other.getSegmentsIncluded()); boolean b2 = (this.segmentsExcluded == other.getSegmentsExcluded()); boolean b3 = (this.startTime == other.getStartTime()); boolean b4 = equals(this.exceptionSegments, other.getExceptionSegments()); return b0 && b1 && b2 && b3 && b4; } else { return (false); } } /** * Preforms a binary serach in the exceptionSegments sorted array. This array can contain * Segments or SegmentRange objects. * * @param segment the key to be searched for. * * @return index of the search segment, if it is contained in the list; * otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. The * <i>insertion point</i> is defined as the point at which the * segment would be inserted into the list: the index of the first * element greater than the key, or <tt>list.size()</tt>, if all * elements in the list are less than the specified segment. Note * that this guarantees that the return value will be >= 0 if * and only if the key is found. */ private int binarySearchExceptionSegments(Segment segment) { int low = 0; int high = this.exceptionSegments.size() - 1; while (low <= high) { int mid = (low + high) / 2; Segment midSegment = (Segment) this.exceptionSegments.get(mid); // first test for equality (contains or contained) if (segment.contains(midSegment) || midSegment.contains(segment)) { return mid; } if (midSegment.before(segment)) { low = mid + 1; } else if (midSegment.after(segment)) { high = mid - 1; } else { throw new IllegalStateException("Invalid condition."); } } return -(low + 1); // key not found } /** * Special method that handles conversion between the Default Time Zone and a UTC time zone * with no DST. This is needed so all days have the same size. This method is the prefered * way of converting a Data into milliseconds for usage in this class. * * @param date Date to convert to long. * * @return The milliseconds. */ public long getTime(Date date) { long result = date.getTime(); if (this.adjustForDaylightSaving) { this.workingCalendar.setTime(date); this.workingCalendarNoDST.set(this.workingCalendar.get(Calendar.YEAR), this.workingCalendar.get(Calendar.MONTH), this.workingCalendar.get(Calendar.DATE), this.workingCalendar.get(Calendar.HOUR_OF_DAY), this.workingCalendar.get(Calendar.MINUTE), this.workingCalendar.get(Calendar.SECOND)); this.workingCalendarNoDST.set(Calendar.MILLISECOND, this.workingCalendar.get(Calendar.MILLISECOND)); Date revisedDate = this.workingCalendarNoDST.getTime(); result = revisedDate.getTime(); } return result; } /** * Converts a millisecond value into a {@link Date} object. * * @param value the millisecond value. * * @return The date. */ public Date getDate(long value) { this.workingCalendarNoDST.setTime(new Date(value)); return (this.workingCalendarNoDST.getTime()); } /** * Returns a clone of the timeline. * * @return A clone. * * @throws CloneNotSupportedException ??. */ public Object clone() throws CloneNotSupportedException { SegmentedTimeline clone = (SegmentedTimeline) super.clone(); return clone; } /** * Internal class to represent a valid segment for this timeline. A segment * is valid on a timeline if it is part of its included, excluded or exception * segments. * <p> * Each segment will know its segment number, segmentStart, segmentEnd and * index inside the segment. */ public class Segment implements Comparable, Cloneable, Serializable { /** The segment number. */ protected long segmentNumber; /** The segment start. */ protected long segmentStart; /** The segment end. */ protected long segmentEnd; /** A reference point within the segment. */ protected long millisecond; /** * Protected constructor only used by sub-classes. */ protected Segment() { // empty } /** * Creates a segment for a given point in time. * * @param millisecond the millisecond (as encoded by java.util.Date). */ protected Segment(long millisecond) { this.segmentNumber = calculateSegmentNumber(millisecond); this.segmentStart = startTime + this.segmentNumber * segmentSize; this.segmentEnd = this.segmentStart + segmentSize - 1; this.millisecond = millisecond; } /** * Calculates the segment number for a given millisecond. * * @param millisecond the millisecond (as encoded by java.util.Date). * * @return The segment number. */ public long calculateSegmentNumber(long millisecond) { if (millisecond >= startTime) { return (millisecond - startTime) / segmentSize; } else { return ((millisecond - startTime) / segmentSize) - 1; } } /** * Returns the segment number of this segment. Segments start at 0. * * @return The segment number. */ public long getSegmentNumber() { return this.segmentNumber; } /** * Returns always one (the number of segments contained in this segment). * * @return The segment count (always 1 for this class). */ public long getSegmentCount() { return 1; } /** * Gets the start of this segment in ms. * * @return The segment start. */ public long getSegmentStart() { return this.segmentStart; } /** * Gets the end of this segment in ms. * * @return The segment end. */ public long getSegmentEnd() { return this.segmentEnd; } /** * Returns the millisecond used to reference this segment (always between the segmentStart * and segmentEnd). * * @return The millisecond. */ public long getMillisecond() { return this.millisecond; } /** * Gets the index in this segment. Index will always be between the segmentStart * and segmentEnd. * * @return The index. * * @deprecated Use getMillisecond(). */ public long getIndex() {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -