org.protempa.proposition.IntervalSectioner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protempa-framework Show documentation
Show all versions of protempa-framework Show documentation
Protempa Framework is the core of Protempa.
/*
* #%L
* Protempa Framework
* %%
* Copyright (C) 2012 - 2013 Emory University
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.protempa.proposition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.protempa.proposition.interval.Interval;
import org.protempa.proposition.interval.IntervalFactory;
import org.protempa.proposition.value.Granularity;
import org.protempa.proposition.value.Value;
/**
* A factory for creating order lists of {@link CompoundValuedInterval}s based
* on input propositions.
*/
public abstract class IntervalSectioner> {
private final static IntervalFactory intervalFactory = new IntervalFactory();
IntervalSectioner() {
}
/**
* Creates a chronologically ordered list of intervals and their associated
* propositions and values. The intervals returned will not necessarily be
* the same as those of the propositions. Multiple propositions could be
* associated to each interval, and a given proposition could overlap
* multiple intervals. The number of returned intervals will be at least as
* big as the number of propositions.
*
* The list will be sorted by {@link Interval}'s natural ordering.
*
* @param propositions
* the list of {@link AbstractParameter}s to put into intervals
* @return a list of {@link CompoundValuedInterval}, sorted chronologically
* based on {@link Interval}'s natural ordering
*/
public List buildIntervalList(List propositions) {
return orderIntervals(propositions);
}
/**
* Creates the required intervals and orders them chronologically. Here, an
* "interval" is actually a {@link CompoundValuedInterval}: a grouping of an
* {@link Interval}, a set of {@link AbstractParameter}s, and the
* {@link Value}s of those abstract parameters. Because we are dealing with
* multiple types of propositions, there may be overlapping intervals, which
* will result in additional intervals being created.
*/
private List orderIntervals(List propositions) {
Granularity startGran = propositions.get(0).getInterval()
.getStartGranularity();
Granularity finishGran = propositions.get(0).getInterval()
.getFinishGranularity();
List result = new ArrayList<>();
Set startBoundsSet = new HashSet<>();
Set finishBoundsSet = new HashSet<>();
SortedSet intervalSet = new TreeSet<>();
// determining the bounds of all of the abstract parameters will allows
// us later to decide which new intervals each parameter overlaps
// note that it doesn't matter whether the bound is a start or finish:
// two start bounds between different parameters that are
// chronologically adjacent will form an interval
for (E p : propositions) {
assert (p.getInterval().getStartGranularity() != null ? p.getInterval().getStartGranularity().equals(startGran) : startGran == null) &&
(p.getInterval().getFinishGranularity() != null ? p.getInterval().getFinishGranularity().equals(finishGran) : finishGran == null) :
"all intervals must have the same start and finish granularities to be combined";
startBoundsSet.add(p.getInterval().getMinStart());
finishBoundsSet.add(p.getInterval().getMaxFinish());
// we must take care to include point intervals where the start and
// finish are the same (ie, timestamps)
// if this special case is ignored, then any abstract parameters
// that are actually just timestamps will be left out of the final
// interval set
if (startBoundsSet.contains(p.getInterval().getMaxFinish())) {
intervalSet.add(intervalFactory
.getInstance(p.getInterval().getMaxFinish(), p
.getInterval().getFinishGranularity()));
}
if (finishBoundsSet.contains(p.getInterval().getMinStart())) {
intervalSet.add(intervalFactory.getInstance(p.getInterval()
.getMinStart(), p.getInterval().getStartGranularity()));
}
}
// the base set of intervals consists the point intervals, which we
// created earlier, and the intervals between adjacent bounds, which we
// create here
// these intervals will be further subdivided when we actually assign
// the propositions
// to intervals below
List intervalBounds = new ArrayList<>(startBoundsSet);
Collections.sort(intervalBounds);
for (int i = 0; i < intervalBounds.size() - 1; i++) {
intervalSet.add(intervalFactory.getInstance(intervalBounds.get(i),
startGran, intervalBounds.get(i + 1), finishGran));
}
SortedMap> fakeIntervals = new TreeMap<>();
// this loop assigns each proposition to the interval(s) it overlaps,
// creating new intervals as needed
for (E p : propositions) {
Long ivalStart = p.getInterval().getMinStart();
// we use getMaximumFinish() instead of getMaxFinish() because for
// point intervals we actually need the "made up" finish time that
// getMaximumFinish() provides, which is one millisecond less than
// the next value in the units of the interval (eg, if
// the timestamp is January 1, 2012 12:00:00.000 PM and the units
// are in minutes, then getMaximumFinish() will return January 1,
// 2012 12:00:00.999 PM as opposed to getMaxFinish(), which will
// return 12:00:00.000)
//
// this is because the overlapping intervals comparison checks that
// the start of proposition interval is before or exactly the start
// of the target interval and that the finish of the proposition
// interval is after the start of the target interval
// if we just used getMaxFinish() then the test would fail on all
// point/timestamp intervals
Long ivalFinish = p.getInterval().getMaximumFinish();
for (Interval interval : intervalSet) {
if ((ivalStart == null || (interval.getMinStart() != null && ivalStart <= interval.getMinStart()))
&& (ivalFinish == null || interval.getMinStart() == null || ivalFinish > interval.getMinStart())) {
// the finish of the new interval should be the finish of
// the target interval if the proposition's interval
// extends beyond it, and it should be the finish of the
// proposition's interval otherwise
Long minMaxFinish;
if (p.getInterval().getMaxFinish() == null) {
minMaxFinish = interval.getMaxFinish();
} else if (interval.getMaxFinish() == null) {
minMaxFinish = p.getInterval().getMaxFinish();
} else {
minMaxFinish = Math.min(p.getInterval().getMaxFinish(),
interval.getMaxFinish());
}
Interval ival = intervalFactory.getInstance(
interval.getMinStart(), startGran,
minMaxFinish, finishGran);
if (!fakeIntervals.containsKey(ival)) {
fakeIntervals.put(ival, new HashSet());
}
fakeIntervals.get(ival).add(p);
}
if ((ivalFinish == null && interval.getMinStart() == null) || (ivalFinish != null && interval.getMinStart() != null && ivalFinish <= interval.getMinStart())) {
break;
}
}
}
for (Entry> e : fakeIntervals.entrySet()) {
Set props = new HashSet<>();
for (E p : e.getValue()) {
props.add(p);
}
result.add(newCompoundInterval(e.getKey(), props));
}
return result;
}
protected abstract K newCompoundInterval(Interval ival, Set props);
}