blob: 9534b3697f85c274f780698a0d7facffbe1b34a3 [file] [log] [blame]
/*******************************************************************************
* 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 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.analysis.xml.core.fsm.model;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.fsm.model.runtime.DataDrivenScenarioInfo;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.fsm.model.values.DataDrivenValue;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.fsm.module.IAnalysisDataContainer;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
/**
* A data-driven condition.
*
* @author Geneviève Bastien
* @author Florian Wininger
*/
public interface DataDrivenCondition extends IDataDrivenRuntimeObject {
/**
* A condition that returns always true
*/
public static final DataDrivenCondition TRUE_CONDITION = (e, s, c) -> true;
/**
* Condition operators used to compare 2 values together
*/
public enum ConditionOperator implements Predicate<Integer> {
/** equal */
EQ(i -> i == 0),
/** not equal */
NE(i -> i != 0),
/** Greater or equal */
GE(i -> i >= 0),
/** Greater than */
GT(i -> i > 0),
/** Less or equal */
LE(i -> i <= 0),
/** Less than */
LT(i -> i < 0);
private final Function<Integer, Boolean> fCmpFunction;
ConditionOperator(Function<Integer, Boolean> cmpFunction) {
fCmpFunction = cmpFunction;
}
@Override
public boolean test(Integer cmpValue) {
return Objects.requireNonNull(fCmpFunction.apply(cmpValue));
}
}
/**
* Condition operators used to determine the position of a value wrt to a
* given time range. It tests the operator with whether the value intersects
* a time range or not
*/
public enum TimeRangeOperator implements Predicate<Boolean> {
/** value is inside the time range */
IN(i -> i),
/** Value is outside the time range */
OUT(i -> !i);
private final Function<Boolean, Boolean> fResultFunction;
private TimeRangeOperator(Function<Boolean, Boolean> cmpFunction) {
fResultFunction = cmpFunction;
}
@Override
public boolean test(Boolean t) {
return Objects.requireNonNull(fResultFunction.apply(t));
}
}
/**
* A condition comparing 2 values together
*/
public static class DataDrivenComparisonCondition implements DataDrivenCondition {
private final ConditionOperator fOperator;
private final DataDrivenValue fFirstValue;
private final DataDrivenValue fSecondValue;
/**
* Constructor
*
* @param firstValue
* The first value to compare
* @param secondValue
* The second value to compare
* @param operator
* The comparison operator
*/
public DataDrivenComparisonCondition(DataDrivenValue firstValue, DataDrivenValue secondValue, ConditionOperator operator) {
fFirstValue = firstValue;
fSecondValue = secondValue;
fOperator = operator;
}
@Override
public boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container) {
Object firstValue = fFirstValue.getValue(event, ITmfStateSystem.ROOT_ATTRIBUTE, scenarioInfo, container);
Object secondValue = fSecondValue.getValue(event, ITmfStateSystem.ROOT_ATTRIBUTE, scenarioInfo, container);
Integer cmpVal = null;
if ((firstValue instanceof Integer) && (secondValue instanceof Number)) {
cmpVal = ((Integer) firstValue).compareTo(((Number) secondValue).intValue());
} else if ((firstValue instanceof Long) && (secondValue instanceof Number)) {
cmpVal = ((Long) firstValue).compareTo(((Number) secondValue).longValue());
} else {
cmpVal = String.valueOf(firstValue).compareTo(String.valueOf(secondValue));
}
return fOperator.test(cmpVal);
}
@Override
public int hashCode() {
return Objects.hash(fOperator, fFirstValue, fSecondValue);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof DataDrivenComparisonCondition)) {
return false;
}
DataDrivenComparisonCondition other = (DataDrivenComparisonCondition) obj;
return Objects.equals(fOperator, other.fOperator) &&
Objects.equals(fFirstValue, other.fFirstValue) &&
Objects.equals(fSecondValue, other.fSecondValue);
}
@Override
public String toString() {
return fFirstValue.toString() + ' ' + fOperator.toString() + ' ' + fSecondValue.toString();
}
}
/**
* A condition that verifies the relation of a time value with a given time
* range
*/
public static class DataDrivenTimeRangeCondition implements DataDrivenCondition {
private final TimeRangeOperator fOperator;
private final long fBegin;
private final long fEnd;
/**
* Constructor
*
* @param operator
* The operator linking a time value to the time range
* @param begin
* The start of the time range
* @param end
* The end of the time range
*/
public DataDrivenTimeRangeCondition(TimeRangeOperator operator, long begin, long end) {
fOperator = operator;
fBegin = begin;
fEnd = end;
}
@Override
public boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container) {
long ts = event.getTimestamp().toNanos();
return fOperator.test(ts >= fBegin && ts <= fEnd);
}
@Override
public int hashCode() {
return Objects.hash(fOperator, fBegin, fEnd);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof DataDrivenTimeRangeCondition)) {
return false;
}
DataDrivenTimeRangeCondition other = (DataDrivenTimeRangeCondition) obj;
return Objects.equals(fOperator, other.fOperator) &&
fBegin == other.fBegin &&
fEnd == other.fEnd;
}
@Override
public String toString() {
return "Time range conditions: " + fOperator.toString() + ' ' + fBegin + ',' + fEnd; //$NON-NLS-1$
}
}
/**
* A condition that verifies the value of an elapsed time according to an
* operator
*/
public static class DataDrivenElapsedTimeCondition implements DataDrivenCondition {
private final ConditionOperator fOperator;
private final String fReference;
private final long fValue;
/**
* Constructor
*
* @param operator
* The operator to compare the elapsed time with
* @param reference
* The reference state from which to start
* @param value
* The value to compare elapsed time to
*/
public DataDrivenElapsedTimeCondition(ConditionOperator operator, String reference, long value) {
fOperator = operator;
fReference = reference;
fValue = value;
}
@Override
public boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container) {
long ts = event.getTimestamp().toNanos();
long referenceTs = scenarioInfo.getStateStartTime(container, fReference);
if (referenceTs < 0) {
// No elapsed time for the state, return false
return false;
}
return fOperator.test(Long.compare(ts - referenceTs, fValue));
}
@Override
public int hashCode() {
return Objects.hash(fOperator, fReference, fValue);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof DataDrivenElapsedTimeCondition)) {
return false;
}
DataDrivenElapsedTimeCondition other = (DataDrivenElapsedTimeCondition) obj;
return Objects.equals(fOperator, other.fOperator) &&
Objects.equals(fReference, other.fReference) &&
fValue == other.fValue;
}
@Override
public String toString() {
return "Elapsed time condition: " + fOperator.toString() + ' ' + fReference + ',' + fValue; //$NON-NLS-1$
}
}
/**
* A condition negating another condition
*/
public static class DataDrivenNotCondition implements DataDrivenCondition {
private final DataDrivenCondition fCondition;
/**
* Constructor
*
* @param condition
* The condition to verify
*/
public DataDrivenNotCondition(DataDrivenCondition condition) {
fCondition = condition;
}
@Override
public boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container) {
return !fCondition.test(event, scenarioInfo, container);
}
@Override
public int hashCode() {
return Objects.hash(DataDrivenNotCondition.class, fCondition);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof DataDrivenNotCondition)) {
return false;
}
DataDrivenNotCondition other = (DataDrivenNotCondition) obj;
return Objects.equals(fCondition, other.fCondition);
}
@Override
public String toString() {
return "NOT: " + fCondition.toString(); //$NON-NLS-1$
}
}
/**
* A condition verifying if all conditions are true
*/
public static class DataDrivenAndCondition implements DataDrivenCondition {
private final List<DataDrivenCondition> fConditions;
/**
* Constructor
*
* @param conditions
* A list of conditions that must all be true
*/
public DataDrivenAndCondition(List<DataDrivenCondition> conditions) {
fConditions = conditions;
}
@Override
public boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container) {
for (DataDrivenCondition cond : fConditions) {
if (!cond.test(event, scenarioInfo, container)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return Objects.hash(DataDrivenAndCondition.class, fConditions);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof DataDrivenAndCondition)) {
return false;
}
DataDrivenAndCondition other = (DataDrivenAndCondition) obj;
return Objects.equals(fConditions, other.fConditions);
}
@Override
public String toString() {
return "AND: " + fConditions.toString(); //$NON-NLS-1$
}
}
/**
* A condition verifying if any conditions are true
*/
public static class DataDrivenOrCondition implements DataDrivenCondition {
private final List<DataDrivenCondition> fConditions;
/**
* Constructor
*
* @param conditions
* A list of conditions to test
*/
public DataDrivenOrCondition(List<DataDrivenCondition> conditions) {
fConditions = conditions;
}
@Override
public boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container) {
for (DataDrivenCondition cond : fConditions) {
if (cond.test(event, scenarioInfo, container)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(DataDrivenOrCondition.class, fConditions);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof DataDrivenOrCondition)) {
return false;
}
DataDrivenOrCondition other = (DataDrivenOrCondition) obj;
return Objects.equals(fConditions, other.fConditions);
}
@Override
public String toString() {
return "OR: " + fConditions.toString(); //$NON-NLS-1$
}
}
/**
* A condition that validates if a value corresponds to a regex pattern
*/
public static class DataDrivenRegexCondition implements DataDrivenCondition {
private final Pattern fPattern;
private final DataDrivenValue fValue;
/**
* Constructor
*
* @param pattern
* The regex pattern to match
* @param value
* The value to match with the pattern
*/
public DataDrivenRegexCondition(Pattern pattern, DataDrivenValue value) {
fPattern = pattern;
fValue = value;
}
@Override
public boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container) {
Object value = fValue.getValue(event, ITmfStateSystem.ROOT_ATTRIBUTE, scenarioInfo, container);
if (value == null) {
return false;
}
return fPattern.matcher(String.valueOf(value)).matches();
}
@Override
public int hashCode() {
return Objects.hash(String.valueOf(fPattern), fValue);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof DataDrivenRegexCondition)) {
return false;
}
DataDrivenRegexCondition other = (DataDrivenRegexCondition) obj;
// Compare the equality of the pattern's string, as the pattern
// object are not equal if the string is equal
return Objects.equals(String.valueOf(fPattern), String.valueOf(other.fPattern)) &&
Objects.equals(fValue, other.fValue);
}
@Override
public String toString() {
return fValue.toString() + " matches " + fPattern; //$NON-NLS-1$
}
}
/**
* Handle the event, ie execute the actions if the event matches the name
*
* @param event
* The event to handle
* @param scenarioInfo
* The scenario info
* @param container
* The analysis data container
* @return The result of this condition, so <code>true</code> if the
* condition validates, <code>false</code> otherwise
*/
boolean test(ITmfEvent event, DataDrivenScenarioInfo scenarioInfo, IAnalysisDataContainer container);
}