/*******************************************************************************
* Copyright (c) 2017 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
package org.eclipse.tracecompass.internal.provisional.datastore.core.condition;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.datastore.core.condition.ContinuousRangeCondition;
import org.eclipse.tracecompass.internal.datastore.core.condition.DiscreteRangeCondition;
/**
* Describe conditions such as ranges or sets. These conditions are used for
* example to abstract queries on history trees.
*
* New RangeCondition objects can be generated by using the
* {@link #forContinuousRange} and {@link #fromCollection} factory methods.
*
* @author Loïc Prieur-Drevon
*
* @param <E>
* Comparable type that extends Number, typically an Integer or a
* Long
* @noimplement Accessor interface
*/
public interface RangeCondition<@NonNull E extends Comparable<E>> extends Predicate<E> {
/**
* Get the lower bound of this range
*
* @return the lowest acceptable value for this condition.
*/
@NonNull E min();
/**
* Get the upper bound of this range
*
* @return the highest acceptable value for this condition.
*/
@NonNull E max();
/**
* Test whether a value is within this specific range boundaries. If the
* range is continuous, it will return <code>true</code> if the value is
* between the lower and upper bounds. If the range is discrete, it will
* return <code>true</code> if the requested element is one of the elements
* in the discrete range.
*
* @param element
* value that we want to test
* @return true if element is contained in this condition's set or range
*/
@Override
boolean test(@NonNull E element);
/**
* Determine if the current range intersects a ranged bounded by the values
* in parameter
*
* @param low
* interval's lower bound
* @param high
* interval's upper bound
* @return true if this element intersects the range's condition or any of
* the set's elements
*/
boolean intersects(@NonNull E low, @NonNull E high);
/**
* Reduce the Condition to elements or the range within bounds from and to.
* <code>null</code> is returned if the resulting condition is empty.
*
* @param from
* lower bound for the condition reduction.
* @param to
* upper bound for the condition reduction.
* @return the reduced condition or <code>null</code> if the reduced
* condition does not contain any element
*/
@Nullable RangeCondition<E> subCondition(@NonNull E from, @NonNull E to);
/**
* Get a condition of a single element.
*
* @param elem The single element
* @return The corresponding range condition
*/
static <E extends Comparable<E>> RangeCondition<E> singleton(E elem) {
return new DiscreteRangeCondition<>(Collections.singleton(elem));
}
/**
* Get a range condition representing a continuous time range.
*
* @param bound1
* The first bound
* @param bound2
* The second bound. It's fine for bound2 to be > or < than
* bound1.
* @return The corresponding range condition
*/
static <E extends Comparable<E>> RangeCondition<E> forContinuousRange(
E bound1, E bound2) {
return new ContinuousRangeCondition<>(bound1, bound2);
}
/**
* Get a range condition representing distinct elements within a range.
*
* @param collection
* A collection of individual elements
* @return The corresponding range condition
*/
static <E extends Comparable<E>> RangeCondition<E> fromCollection(
Collection<E> collection) {
return new DiscreteRangeCondition<>(collection);
}
/**
* Utility method to generate a range condition of discrete elements, by
* providing a min, a max, and a "step" between each elements. Note that
* "max" will be included in the condition.
*
* For example, specifying (min=1, max=10, step=2) will return a condition
* representing [1, 3, 5, 7, 9, 10].
*
* @param min
* The minimum value
* @param max
* The maximum value. Should be greater than or equal to min
* @param step
* The step between each element
* @return The corresponding range condition
*/
static RangeCondition<Long> forDiscreteRange(long min, long max, long step) {
if (max < min || step < 0) {
throw new IllegalArgumentException("Invalid range: max should be < min and step >= 0 (max: " + max + ", min:" + min + ", step: " + step + ')'); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
/*
* If resolution is 0, adjust increment to return consecutive number
*/
long increment = Math.max(step, 1L);
Set<Long> times = new TreeSet<>();
for (long t = min; t < max; t += increment) {
times.add(t);
}
times.add(max);
return fromCollection(times);
}
}