/**
* SPINdle (version 2.2.2)
* Copyright (C) 2009-2012 NICTA Ltd.
*
* This file is part of SPINdle project.
*
* SPINdle is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SPINdle is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPINdle. If not, see <http://www.gnu.org/licenses/>.
*
* @author H.-P. Lam (oleklam@gmail.com), National ICT Australia - Queensland Research Laboratory
*/
package spindle.core.dom;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import spindle.sys.AppConst;
import spindle.sys.AppFeatureConst;
import spindle.sys.Messages;
import spindle.sys.message.ErrorMessage;
/**
* DOM for representing the temporal information in a literal/rule.
* Note that a temporal is a value pairs [s,e) representing an time interval where <i>s</i> (inclusive) and <i>e</i>
* (non-inclusive)
* are the start and time time of the interval, respectively.
* A time instance in this case means that <i>s=e</i>, i.e., the start time of the interval is equal to its end time.
*
* @author H.-P. Lam (oleklam@gmail.com), National ICT Australia - Queensland Research Laboratory
* @since version 2.2.1
* @version Last modified 2012.10.11
* @version 2012.07.11
*/
public class Temporal implements Comparable<Object>, Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private static final Comparator<Temporal> TEMPORAL_COMPARATOR = new TemporalComparator();
private static final Temporal PERSISTENT_TEMPORAL = new Temporal();
private static final String TEMPORAL_START = String.valueOf(DomConst.Literal.TIMESTAMP_START);
private static final String TEMPORAL_END = String.valueOf(DomConst.Literal.TIMESTAMP_END);
private static final String TEMPORAL_SEPARATOR = String.valueOf(DomConst.Literal.LITERAL_SEPARATOR);
private static final String TEMPORAL_POSITIVE_INFINITY = DomConst.Literal.TEMPORAL_POSITIVE_INFINITY;
private static final String TEMPORAL_NEGATIVE_INFINITY = DomConst.Literal.TEMPORAL_NEGATIVE_INFINITY;
public static Temporal getTemporalInstance(long t) {
return new Temporal(t, t);
}
public static Temporal getTemporalFromObject(Object obj) throws TemporalException {
if (obj instanceof Literal) {
Temporal temporal = ((Literal) obj).getTemporal();
return null == temporal ? PERSISTENT_TEMPORAL : temporal;
} else if (obj instanceof Temporal) {
return (Temporal) obj;
} else {
throw new TemporalException(ErrorMessage.TEMPORAL_TEMPORAL_SEGMENTS_INPUT_ERROR, new Object[] { obj });
}
}
public static List<Temporal> getRelatedTemporalSegmentsFromSet(Temporal temporal, Collection<?> objects) throws TemporalException {
Set<Temporal> relatedTemporals = new TreeSet<Temporal>();
Temporal temporalToVerify = null;
for (Object obj : objects) {
temporalToVerify = getTemporalFromObject(obj);
if (null == temporalToVerify) continue;
if (temporal.overlapOrMeet(temporalToVerify)) relatedTemporals.add(temporalToVerify);
}
return getTemporalSegmentsFromSet(relatedTemporals);
}
public static List<Temporal> getTemporalSegmentsFromSet(Collection<?> objects) throws TemporalException {
TreeSet<Long> timeInstances = new TreeSet<Long>();
TreeSet<Long> timeStamps = new TreeSet<Long>();
Temporal temporal = null;
for (Object obj : objects) {
temporal = getTemporalFromObject(obj);
if (null == temporal) continue;
if (temporal.isTimeInstance()) {
timeInstances.add(temporal.startTime);
timeStamps.add(temporal.startTime);
} else {
timeStamps.add(temporal.startTime);
timeStamps.add(temporal.endTime);
}
}
return generateTemporalSegments(timeStamps, timeInstances);
}
private static List<Temporal> generateTemporalSegments(Collection<Long> timestamps, Collection<Long> instances) {
List<Temporal> segments = new ArrayList<Temporal>();
if (null == timestamps) return segments;
if (null == instances) instances = new TreeSet<Long>();
int c = 0;
long timeIntervalStart = 0;
for (Long timestamp : timestamps) {
if (c++ > 0) segments.add(new Temporal(timeIntervalStart, timestamp));
if (instances.contains(timestamp)) segments.add(new Temporal(timestamp, timestamp));
timeIntervalStart = timestamp;
}
return segments;
}
public static Collection<Temporal> consolidateTemporalSegments(Collection<Temporal> temporals) {
if (null == temporals || temporals.size() < 2) return temporals;
Set<Temporal> origTemporals = new TreeSet<Temporal>(temporals);
temporals.clear();
if (origTemporals.contains(PERSISTENT_TEMPORAL)) {
temporals.add(PERSISTENT_TEMPORAL.clone());
return temporals;
}
Iterator<Temporal> it = origTemporals.iterator();
Temporal lastTemporal = it.next();
Temporal temporal = null;
try {
while ((temporal = it.next()) != null) {
if (lastTemporal.overlapOrMeet(temporal)) {
lastTemporal = lastTemporal.join(temporal);
} else {
temporals.add(lastTemporal);
lastTemporal = temporal;
}
}
} catch (Exception e) {
}
temporals.add(lastTemporal);
return temporals;
}
protected long startTime, endTime;
public Temporal() {
this(Long.MIN_VALUE, Long.MAX_VALUE);
}
public Temporal(long startTime) {
this(startTime, Long.MAX_VALUE);
}
public Temporal(long startTime, long endTime) {
setStartTime(startTime);
setEndTime(endTime);
}
public Temporal(Temporal temporal) {
this(temporal.startTime, temporal.endTime);
}
public long getStartTime() {
return startTime;
}
public Temporal getStartTimeAsInstance() {
return new Temporal(startTime, startTime);
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public void removeStartTime() {
startTime = Long.MIN_VALUE;
}
// public void startTimeIncrement() {
// if (startTime < Long.MAX_VALUE) startTime++;
// }
//
// public void startTimeDecrement() {
// if (startTime > Long.MIN_VALUE) startTime--;
// }
public long getEndTime() {
return endTime;
}
public Temporal getEndTimeAsInstance() {
return new Temporal(endTime, endTime);
}
public void setEndTime(long endTime) {
if (startTime > endTime) throw new IllegalArgumentException(Messages.getErrorMessage(ErrorMessage.TEMPORAL_STARTTIME_ENDTIME));
if (startTime == endTime && AppFeatureConst.isIntervalBasedTemporal) this.endTime = Long.MAX_VALUE == startTime ? Long.MAX_VALUE
: startTime + 1;
else this.endTime = endTime;
}
public void removeEndTime() {
endTime = Long.MAX_VALUE;
}
// public void endTimeIncrement() {
// if (endTime < Long.MAX_VALUE) endTime++;
// }
//
// public void endTimeDecrement() {
// if (endTime > Long.MIN_VALUE) endTime--;
// }
public Temporal clone() {
return new Temporal(this);
}
/**
* Check if this temporal represents a time instance (i.e., start time equals end time.)
*
* @return true if it represents an instance of time; false otherwise.
*/
public boolean isTimeInstance() {
return startTime == endTime;
}
public boolean startBefore(Temporal temporal) {
return startTime < temporal.startTime;
}
public boolean startOnOrBefore(Temporal temporal) {
return startTime <= temporal.startTime;
}
public boolean sameStart(Temporal temporal) {
return startTime == temporal.startTime;
}
public boolean startOnOrAfter(Temporal temporal) {
return temporal.startTime <= startTime;
}
public boolean startAfter(Temporal temporal) {
return temporal.startTime < startTime;
}
public boolean endBefore(Temporal temporal) {
return endTime < temporal.endTime;
}
public boolean endOnOrBefore(Temporal temporal) {
return endTime <= temporal.endTime;
}
/**
* Determine if two temporals have the same end time.
* Two temporals are considered having the same end time if:
* (1) they are having the same time end value, and
* (2) either both of them are time instances or both of them are time intervals.
*
* @param temporal Temporal to be checked.
* @return true if the above conditions are satisfied; false otherwise.
*/
public boolean sameEnd(Temporal temporal) {
if (endTime != temporal.endTime) return false;
boolean ins = isTimeInstance();
boolean tins = temporal.isTimeInstance();
if ((ins && tins) || (!ins && !tins)) return true;
return false;
// return sameEndTime(temporal.endTime);
// return endTime == temporal.endTime;
}
public boolean endOnOrAfter(Temporal temporal) {
return temporal.endTime <= endTime;
}
public boolean endAfter(Temporal temporal) {
return temporal.endTime < endTime;
}
/**
* check if the temporal object is empty.
*
* @return true if there is no temporal information; and false otherwise.
*/
public boolean hasTemporalInfo() {
return Long.MIN_VALUE != startTime || Long.MAX_VALUE != endTime;
}
/**
* Check if the two temporals overlap each others.
*
* @param temporal Temporal to be checked.
* @return true if the two temporal intersect; false otherwise.
*/
public boolean overlap(Temporal temporal) {
if (null == temporal) return false;
if (this == temporal || !hasTemporalInfo()) return true;
if (isTimeInstance()) {
if (temporal.isTimeInstance()) {
return startTime == temporal.startTime;
} else {
if (startTime < temporal.startTime || temporal.endTime <= startTime) return false;
}
} else if (temporal.isTimeInstance()) {
if (startTime > temporal.startTime || endTime <= temporal.startTime) return false;
} else {
if (startTime >= temporal.endTime || endTime <= temporal.startTime) return false;
}
return true;
}
/**
* Check if the two temporals meet.
*
* @param temporal Temporal to be checked.
* @return true if the start time of one is equal to the end time of the other; false otherwise.
*/
public boolean meet(Temporal temporal) {
if (null == temporal) return false;
// if (null == temporal || this == temporal) return false;
if (isTimeInstance()) {
if (temporal.isTimeInstance()) return startTime == temporal.startTime;
else return startTime == temporal.endTime || startTime == temporal.startTime;
} else {
if (temporal.isTimeInstance()) return startTime == temporal.startTime || endTime == temporal.startTime;
}
return startTime == temporal.endTime || endTime == temporal.startTime;
}
/**
* Check if the two temporals are overlapped or meet each others.
*
* @param temporal Temporal to be checked.
* @return true if the two temporals are overlapped or meet.
*/
public boolean overlapOrMeet(Temporal temporal) {
if (null == temporal) return false;
if (this == temporal || !hasTemporalInfo()) return true;
if (isTimeInstance()) {
if (temporal.isTimeInstance()) return startTime == temporal.startTime;
else {
if (startTime < temporal.startTime || temporal.endTime < startTime) return false;
}
} else {
if (startTime > temporal.endTime || endTime < temporal.startTime) return false;
}
return true;
}
/**
* Check if the input temporal lies inside this temporal.
*
* @param temporal Temporal to be checked.
* @return true if the input temporal is inside the interval of this temporal; false otherwise.
*/
public boolean contains(Temporal temporal) {
if (null == temporal) return false;
if (this == temporal) return true;
if (isTimeInstance()) {
return temporal.isTimeInstance() ? startTime == temporal.startTime : false;
} else {
if (temporal.isTimeInstance()) return !(startTime > temporal.startTime || temporal.endTime >= endTime);
return !(startTime > temporal.startTime || temporal.endTime > endTime);
}
}
/**
* Check if the temporal is included inside the specified temporal.
*
* @param temporal Temporal to be checked.
* @return true if this temporal is included inside the specified temporal; false otherwise.
*/
public boolean during(Temporal temporal) {
if (null == temporal) return false;
if (this == temporal) return true;
if (isTimeInstance()) {
if (temporal.isTimeInstance()) return startTime == temporal.startTime;
return !(startTime < temporal.startTime || temporal.endTime <= endTime);
} else return !(startTime < temporal.startTime || temporal.endTime < endTime);
}
/**
* Return the union of the two temporal.
*
* @param temporal Temporal to be union with.
* @return Union of the two temporal.
* @throws TemporalException If the two temporal are not intersect with each other.
*/
public Temporal join(Temporal temporal) throws TemporalException {
if (!overlapOrMeet(temporal))
// if (!(overlap(temporal) || meet(temporal)))
throw new TemporalException(ErrorMessage.TEMPORAL_NOT_INTERSECTED, new Object[] { this, temporal });
long st = startTime > temporal.startTime ? temporal.startTime : startTime;
long et = endTime > temporal.endTime ? endTime : temporal.endTime;
return new Temporal(st, et);
}
/**
* Return the intersection of the two temporal.
*
* @param temporal Temporal to be intersected with.
* @return Intersection of the two temporal.
* @throws TemporalException If the two temporal are not intersect with each other.
*/
public Temporal intersect(Temporal temporal) throws TemporalException {
if (!overlap(temporal)) throw new TemporalException(ErrorMessage.TEMPORAL_NOT_INTERSECTED, new Object[] { this, temporal });
long st = startTime > temporal.startTime ? startTime : temporal.startTime;
long et = endTime > temporal.endTime ? temporal.endTime : endTime;
return new Temporal(st, et);
}
/**
* @param temporal Temporal
* @return Time segments of the two temporal.
* @throws TemporalException If the two temporal are not intersect with each other.
*/
public List<Temporal> getTemporalSegments(Temporal temporal) throws TemporalException {
if (null == temporal) throw new TemporalException(ErrorMessage.TEMPORAL_NULL_TEMPORAL);
return getTemporalSegments(new Object[] { temporal });
}
/**
* Return a set of temporal segments that are extracted from a set of temporals or temporal literals, and
* are overlapped with the current temporal.
*
* @param objects A set of temporals or temporal literals.
* @return A set of extracted temporal segments.
*/
public List<Temporal> getTemporalSegments(Collection<?> objects) throws TemporalException {
if (null == objects || objects.size() == 0) throw new TemporalException(ErrorMessage.TEMPORAL_NULL_TEMPORAL);
Object[] objs = new Object[objects.size()];
objects.toArray(objs);
return getTemporalSegments(objs);
}
public List<Temporal> getTemporalSegments(Object[] objects) throws TemporalException {
if (null == objects || objects.length == 0) throw new TemporalException(ErrorMessage.TEMPORAL_NULL_TEMPORAL);
TreeSet<Long> timeInstances = new TreeSet<Long>();
TreeSet<Long> timeStamps = new TreeSet<Long>();
if (isTimeInstance()) {
timeInstances.add(startTime);
timeStamps.add(startTime);
} else {
timeStamps.add(startTime);
timeStamps.add(endTime);
}
Temporal temporal = null;
for (Object obj : objects) {
temporal = getTemporalFromObject(obj);
if (null == temporal) continue;
if (overlapOrMeet(temporal)) {
if (temporal.isTimeInstance()) {
timeInstances.add(temporal.startTime);
timeStamps.add(temporal.startTime);
} else {
timeStamps.add(temporal.startTime);
timeStamps.add(temporal.endTime);
}
} else {
if (!AppConst.isDeploy) {
String msg = Messages.getErrorMessage(ErrorMessage.TEMPORAL_NOT_INTERSECTED, new Object[] { this, temporal });
System.err.println(msg);
}
}
}
return generateTemporalSegments(timeStamps,timeInstances);
}
@Override
public int compareTo(Object o) {
if (this == o) return 0;
if (!(o instanceof Temporal)) return getClass().getName().compareTo(o.getClass().getName());
return TEMPORAL_COMPARATOR.compare(this, (Temporal) o);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (endTime ^ (endTime >>> 32));
result = prime * result + (int) (startTime ^ (startTime >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (null == obj) return false;
if (getClass() != obj.getClass()) return false;
Temporal other = (Temporal) obj;
if (startTime != other.startTime) return false;
if (endTime != other.endTime) return false;
return true;
}
@Override
public String toString() {
// if (!hasTemporalInfo()) return "";
return TEMPORAL_START + (Long.MIN_VALUE == startTime ? TEMPORAL_NEGATIVE_INFINITY : startTime) + TEMPORAL_SEPARATOR
+ (Long.MAX_VALUE == endTime ? TEMPORAL_POSITIVE_INFINITY : endTime) + TEMPORAL_END;
}
}