/******************************************************************************* * Copyright (c) 2016 Ecole Polytechnique de Montreal, Ericsson * * 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.tmf.analysis.xml.core.model; import java.util.List; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.common.core.NonNullUtils; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.Activator; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module.IXmlStateSystemContainer; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module.XmlUtils; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.pattern.stateprovider.XmlPatternStateProvider; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; import org.w3c.dom.Element; /** * This Class implements a timestamp condition tree in the XML-defined state * system. * * @author Jean-Christian Kouame */ public class TmfXmlTimestampCondition implements ITmfXmlCondition { private enum TimeRangeOperator { IN, OUT, OTHER } private enum ElapsedTimeOperator { LESS, EQUAL, MORE, NONE } private final IXmlTimestampsCondition fTimestampsCondition; private final IXmlStateSystemContainer fParent; /** * Constructor * * @param modelFactory * The factory used to create XML model elements * @param node * The XML root of this timestamp condition * @param container * The state system container this timestamp condition belongs to */ public TmfXmlTimestampCondition(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container) { fParent = container; String type = node.getNodeName(); switch (type) { case TmfXmlStrings.TIME_RANGE: fTimestampsCondition = new TmfXmlTimeRangeCondition(modelFactory, node, fParent); break; case TmfXmlStrings.ELAPSED_TIME: fTimestampsCondition = new TmfXmlElapsedTimeCondition(modelFactory, node, fParent); break; default: throw new IllegalArgumentException("Invalid timestampsChecker declaration in XML : Type should be timeRange or elapsedTime"); //$NON-NLS-1$ } } /** * Normalize the value into a nanosecond time value * * @param timestamp * The timestamp value * @param unit * The initial unit of the timestamp * @return The value of the timestamp in nanoseconds */ public static long valueToNanoseconds(long timestamp, String unit) { switch (unit) { case TmfXmlStrings.NS: return timestamp; case TmfXmlStrings.US: return TmfTimestamp.create(timestamp, ITmfTimestamp.MICROSECOND_SCALE).toNanos(); case TmfXmlStrings.MS: return TmfTimestamp.create(timestamp, ITmfTimestamp.MILLISECOND_SCALE).toNanos(); case TmfXmlStrings.S: return TmfTimestamp.create(timestamp, ITmfTimestamp.SECOND_SCALE).toNanos(); default: throw new IllegalArgumentException("The time unit is not yet supporting."); //$NON-NLS-1$ } } /** * Test if two long value have the same sign * * @param i * The first long * @param j * The second long * @return True if the two long value have the same sign, false otherwise */ private static boolean compareSign(long i, long j) { return (i < 0) ^ (j >= 0); } /** * Validate the event * * @param event * The current event * @param scenarioInfo * The active scenario details. The value should be null if there * is no scenario * @return True if the test succeed, false otherwise */ @Override public boolean test(ITmfEvent event,@Nullable TmfXmlScenarioInfo scenarioInfo) { return fTimestampsCondition.test(event, scenarioInfo); } private interface IXmlTimestampsCondition extends ITmfXmlCondition { } private class TmfXmlTimeRangeCondition implements IXmlTimestampsCondition { private final TimeRangeOperator fType; private final String fUnit; private final String fBegin; private final String fEnd; private final IXmlStateSystemContainer fContainer; /** * Constructor * * @param modelFactory * The factory used to create XML model elements * @param node * The XML root of this time range condition transition * @param container * The state system container this time range condition * belongs to */ public TmfXmlTimeRangeCondition(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container) { fContainer = container; String unit = node.getAttribute(TmfXmlStrings.UNIT); fUnit = unit; List<@Nullable Element> childElements = NonNullUtils.checkNotNull(XmlUtils.getChildElements(node)); if (childElements.size() != 1) { throw new IllegalArgumentException("Invalid timestampsChecker declaration in XML : Only one timing condition is allowed"); //$NON-NLS-1$ } final Element firstElement = NonNullUtils.checkNotNull(childElements.get(0)); String type = firstElement.getNodeName(); switch (type) { case TmfXmlStrings.IN: fType = TimeRangeOperator.IN; break; case TmfXmlStrings.OUT: fType = TimeRangeOperator.OUT; break; default: fType = TimeRangeOperator.OTHER; break; } final String begin = firstElement.getAttribute(TmfXmlStrings.BEGIN); final String end = firstElement.getAttribute(TmfXmlStrings.END); fBegin = begin; fEnd = end; } @Override public boolean test(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) { ITmfStateSystem ss = fContainer.getStateSystem(); long begin; begin = valueToNanoseconds(Long.parseLong(fBegin), fUnit); long end; end = valueToNanoseconds(Long.parseLong(fEnd), fUnit); // swap the value if begin > end if (begin > end) { begin = begin ^ end; end = begin ^ end; begin = begin ^ end; } begin = Math.max(ss.getStartTime(), begin); end = Math.min(ss.getCurrentEndTime(), end); begin = Math.min(begin, end); long ts = event.getTimestamp().toNanos(); switch (fType) { case IN: return intersects(begin, end, ts); case OUT: return !intersects(begin, end, ts); case OTHER: default: return false; } } private boolean intersects(long begin, long end, long ts) { return ts >= begin && ts <= end; } } private class TmfXmlElapsedTimeCondition implements IXmlTimestampsCondition { private final IXmlStateSystemContainer fContainer; private final ElapsedTimeOperator fType; private final String fUnit; private final String fValue; private final String fReferenceState; /** * Constructor * * @param modelFactory * The factory used to create XML model elements * @param node * The XML root of this elapsed time condition * @param container * The state system container this elapsed time condition * belongs to */ public TmfXmlElapsedTimeCondition(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container) { fContainer = container; String unit = node.getAttribute(TmfXmlStrings.UNIT); fUnit = unit; List<@Nullable Element> childElements = XmlUtils.getChildElements(node); if (childElements.size() != 1) { throw new IllegalArgumentException("Invalid timestampsChecker declaration in XML : Only one timing condition is allowed"); //$NON-NLS-1$ } final Element firstElement = NonNullUtils.checkNotNull(childElements.get(0)); String type = firstElement.getNodeName(); switch (type) { case TmfXmlStrings.LESS: fType = ElapsedTimeOperator.LESS; break; case TmfXmlStrings.EQUAL: fType = ElapsedTimeOperator.EQUAL; break; case TmfXmlStrings.MORE: fType = ElapsedTimeOperator.MORE; break; default: fType = ElapsedTimeOperator.NONE; break; } final String reference = firstElement.getAttribute(TmfXmlStrings.SINCE); final String value = firstElement.getAttribute(TmfXmlStrings.VALUE); fReferenceState = reference; fValue = value; } @Override public boolean test(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) { if (scenarioInfo == null) { Activator.logError("Elapse time conditions require scenarioInfos and scenarioInfos is null"); //$NON-NLS-1$ return false; } boolean success; long ts = event.getTimestamp().toNanos(); long referenceTimestamps = ((XmlPatternStateProvider) fContainer).getHistoryBuilder().getSpecificStateStartTime(fContainer, fReferenceState, scenarioInfo, event); if (!compareSign(ts, referenceTimestamps) || ts < referenceTimestamps) { throw new IllegalArgumentException("Timestamp is inferior to reference time"); //$NON-NLS-1$ } switch (fType) { case LESS: success = (ts - referenceTimestamps) < valueToNanoseconds(Long.parseLong(fValue), fUnit); break; case EQUAL: success = (ts - referenceTimestamps) == valueToNanoseconds(Long.parseLong(fValue), fUnit); break; case MORE: success = (ts - referenceTimestamps) > valueToNanoseconds(Long.parseLong(fValue), fUnit); break; case NONE: default: success = false; break; } return success; } } }