📄 segmentedtimeline.java
字号:
*
* @param domainValue domain value to teat as a baseTimeline exception
*/
public void addBaseTimelineException(long domainValue) {
Segment baseSegment = 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 teat 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 = 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()
+ baseTimeline.getSegmentsExcluded() * 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(baseTimeline.getSegmentsGroup());
}
}
/**
* Returns the number of exception segments wholy contained in the
* (fromDomainValue, toDomainValue) interval.
*
* @param fromDomainValue Beginning of the interval
* @param toDomainValue End of interval
* @return Number of exception segments contained in the interval.
*/
public long getExceptionSegmentCount(long fromDomainValue, long toDomainValue) {
if (toDomainValue < fromDomainValue) {
return (0);
}
int n = 0;
for (Iterator iter = exceptionSegments.iterator(); iter.hasNext();) {
Segment segment = (Segment) iter.next();
Segment intersection = segment.intersect(fromDomainValue, toDomainValue);
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 domainValue index into the segment
* @return a Segment that contains index, or the next possible Segment.
*/
public Segment getSegment(long domainValue) {
return (new Segment(domainValue));
}
/**
* 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;
return (segmentSize == other.getSegmentSize()
&& segmentsIncluded == other.getSegmentsIncluded()
&& segmentsExcluded == other.getSegmentsExcluded()
&& startTime == other.getStartTime()
&& equals(exceptionSegments, other.getExceptionSegments()));
}
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 = exceptionSegments.size() - 1;
while (low <= high) {
int mid = (low + high) / 2;
Segment midSegment = (Segment) 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) {
workingCalendar.setTime(date);
workingCalendarNoDST.set(workingCalendar.get(Calendar.YEAR),
workingCalendar.get(Calendar.MONTH),
workingCalendar.get(Calendar.DATE),
workingCalendar.get(Calendar.HOUR_OF_DAY),
workingCalendar.get(Calendar.MINUTE),
workingCalendar.get(Calendar.SECOND));
workingCalendarNoDST.set(Calendar.MILLISECOND, workingCalendar.get(Calendar.MILLISECOND));
return (workingCalendarNoDST.getTime().getTime());
}
/**
* Converts a millisecond value into a {@link Date} object.
*
* @param value the millisecond value.
*
* @return The date.
*/
public Date getDate(long value) {
workingCalendarNoDST.setTime(new Date(value));
return (workingCalendarNoDST.getTime());
}
/**
* Internal class to represent a valid Segment valid for this timeline. A segment
* is valid on a timeline if it is part of its incluced, 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;
/** The index. */
protected long index;
/**
* Protected constructor only used by sub-classes.
*/
protected Segment() {
}
/**
* Creates a segment based on an index inside the segment
*
* @param index index inside the segment
*/
protected Segment(long index) {
this.segmentNumber = getSegmentNumber(index);
this.segmentStart = startTime + segmentNumber * segmentSize;
this.segmentEnd = segmentStart + segmentSize - 1;
this.index = index;
}
/**
* Returns the segment number of an index.
*
* @param index index inside the segment
*
* @return The segment number.
*/
public long getSegmentNumber(long index) {
return ((index - startTime) / segmentSize);
}
/**
* Returns the segment number of this segment. Segments start at 0.
*
* @return The segment number.
*/
public long getSegmentNumber() {
return (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 (segmentStart);
}
/**
* Gets the end of this segment in ms.
*
* @return The segment end.
*/
public long getSegmentEnd() {
return (segmentEnd);
}
/**
* Gets the index in this segment. Index will always be between the segmentStart
* and segmentEnd.
*
* @return The index.
*/
public long getIndex() {
return (index);
}
/**
* Gets a Date that represents the index of this segment.
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -