1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 Ecole Polytechnique de Montreal and others
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Florian Wininger - Initial API and implementation
11 * Naser Ezzati - Add the comparison operators
12 * Patrick Tasse - Add message to exceptions
13 * Jean-Christian Kouame - Add comparison between two state values
14 ******************************************************************************/
16 package org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.model
;
18 import java
.util
.ArrayList
;
19 import java
.util
.List
;
21 import org
.eclipse
.jdt
.annotation
.NonNull
;
22 import org
.eclipse
.jdt
.annotation
.Nullable
;
23 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
24 import org
.eclipse
.tracecompass
.internal
.tmf
.analysis
.xml
.core
.Activator
;
25 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
26 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
28 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.module
.IXmlStateSystemContainer
;
29 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.module
.XmlUtils
;
30 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.stateprovider
.TmfXmlStrings
;
31 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
32 import org
.w3c
.dom
.Element
;
35 * This Class implement a condition tree in the XML-defined state system.
41 * <stateAttribute type="location" value="CurrentThread" />
42 * <stateAttribute type="constant" value="System_call" />
43 * <stateValue type="null" />
46 * <stateValue type="long" value="2" />
47 * <stateValue type="long" value="5" />
52 * @author Florian Wininger
54 public class TmfXmlCondition
implements ITmfXmlCondition
{
56 private final List
<TmfXmlCondition
> fConditions
= new ArrayList
<>();
57 private final List
<ITmfXmlStateValue
> fStateValues
;
58 private final LogicalOperator fOperator
;
59 private final IXmlStateSystemContainer fContainer
;
60 private final ConditionOperator fConditionOperator
;
61 private ConditionType fType
;
62 private @Nullable TmfXmlTimestampCondition fTimeCondition
;
64 private enum LogicalOperator
{
71 private enum ConditionOperator
{
81 // TODO The XmlCondition needs to be split into several classes of condition
82 // instead of using an enum
83 private enum ConditionType
{
90 * Factory to create {@link TmfXmlCondition}
93 * The factory used to create XML model elements
95 * The XML root of this condition
97 * The state system container this condition belongs to
98 * @return The new {@link TmfXmlCondition}
101 public static TmfXmlCondition
create(ITmfXmlModelFactory modelFactory
, Element node
, IXmlStateSystemContainer container
) {
102 Element rootNode
= node
;
103 /* Process the conditions: in each case, only process Element nodes */
104 List
<@Nullable Element
> childElements
= XmlUtils
.getChildElements(rootNode
);
107 * If the node is an if, take the child as the root condition
109 * FIXME: Maybe the caller should do this instead.
111 if (node
.getNodeName().equals(TmfXmlStrings
.IF
)) {
112 if (childElements
.isEmpty()) {
113 throw new IllegalArgumentException("TmfXmlCondition constructor: IF node with no child element"); //$NON-NLS-1$
115 rootNode
= NonNullUtils
.checkNotNull(childElements
.get(0));
116 childElements
= XmlUtils
.getChildElements(rootNode
);
119 List
<@NonNull TmfXmlCondition
> conditions
= new ArrayList
<>();
120 switch (rootNode
.getNodeName()) {
121 case TmfXmlStrings
.CONDITION
:
122 return createPatternCondition(modelFactory
, container
, rootNode
, childElements
);
123 case TmfXmlStrings
.NOT
:
124 return createMultipleCondition(modelFactory
, container
, childElements
, LogicalOperator
.NOT
, conditions
);
125 case TmfXmlStrings
.AND
:
126 return createMultipleCondition(modelFactory
, container
, childElements
, LogicalOperator
.AND
, conditions
);
127 case TmfXmlStrings
.OR
:
128 return createMultipleCondition(modelFactory
, container
, childElements
, LogicalOperator
.OR
, conditions
);
130 throw new IllegalArgumentException("TmfXmlCondition constructor: XML node " + rootNode
.getNodeName() + " is of the wrong type"); //$NON-NLS-1$ //$NON-NLS-2$
134 private static TmfXmlCondition
createPatternCondition(ITmfXmlModelFactory modelFactory
, IXmlStateSystemContainer container
, Element rootNode
, List
<@Nullable Element
> childElements
) {
135 ArrayList
<ITmfXmlStateValue
> stateValues
;
136 ConditionOperator conditionOperator
;
137 TmfXmlTimestampCondition timeCondition
= null;
138 int size
= rootNode
.getElementsByTagName(TmfXmlStrings
.STATE_VALUE
).getLength();
140 stateValues
= new ArrayList
<>(size
);
142 conditionOperator
= getConditionOperator(rootNode
);
143 getStateValuesForXmlCondition(modelFactory
, NonNullUtils
.checkNotNull(childElements
), stateValues
, container
);
145 // No need to test if the childElements size is actually 2.
146 // The XSD validation do this check already.
147 conditionOperator
= ConditionOperator
.EQ
;
148 stateValues
.add(modelFactory
.createStateValue(NonNullUtils
.checkNotNull(childElements
.get(0)), container
, new ArrayList
<ITmfXmlStateAttribute
>()));
149 stateValues
.add(modelFactory
.createStateValue(NonNullUtils
.checkNotNull(childElements
.get(1)), container
, new ArrayList
<ITmfXmlStateAttribute
>()));
151 return new TmfXmlCondition(ConditionType
.DATA
, stateValues
, LogicalOperator
.NONE
, conditionOperator
, null, new ArrayList
<>(), container
);
153 final Element firstElement
= NonNullUtils
.checkNotNull(childElements
.get(0));
154 timeCondition
= modelFactory
.createTimestampsCondition(firstElement
, container
);
155 return new TmfXmlCondition(ConditionType
.TIME
, new ArrayList
<>(), LogicalOperator
.NONE
, ConditionOperator
.EQ
, timeCondition
, new ArrayList
<>(), container
);
158 private static TmfXmlCondition
createMultipleCondition(ITmfXmlModelFactory modelFactory
, IXmlStateSystemContainer container
, List
<@Nullable Element
> childElements
, LogicalOperator op
,
159 List
<@NonNull TmfXmlCondition
> conditions
) {
160 for (Element condition
: childElements
) {
161 if (condition
== null) {
164 conditions
.add(modelFactory
.createCondition(condition
, container
));
166 return new TmfXmlCondition(ConditionType
.NONE
, new ArrayList
<>(), op
, ConditionOperator
.NONE
, null, conditions
, container
);
169 private TmfXmlCondition(ConditionType type
, ArrayList
<@NonNull ITmfXmlStateValue
> stateValues
, LogicalOperator operator
, ConditionOperator conditionOperator
, @Nullable TmfXmlTimestampCondition timeCondition
, List
<@NonNull TmfXmlCondition
> conditions
,
170 IXmlStateSystemContainer container
) {
172 fStateValues
= stateValues
;
173 fOperator
= operator
;
174 fTimeCondition
= timeCondition
;
175 fContainer
= container
;
176 fConditions
.addAll(conditions
);
177 fConditionOperator
= conditionOperator
;
180 private static void getStateValuesForXmlCondition(ITmfXmlModelFactory modelFactory
, List
<@Nullable Element
> childElements
, List
<ITmfXmlStateValue
> stateValues
, IXmlStateSystemContainer container
) {
181 Element stateValueElement
= NonNullUtils
.checkNotNull(childElements
.remove(childElements
.size() - 1));
183 * A state value is either preceded by an eventField or a number of
186 final Element firstElement
= NonNullUtils
.checkNotNull(childElements
.get(0));
187 if (childElements
.size() == 1 && firstElement
.getNodeName().equals(TmfXmlStrings
.ELEMENT_FIELD
)) {
188 String attribute
= firstElement
.getAttribute(TmfXmlStrings
.NAME
);
189 stateValues
.add(modelFactory
.createStateValue(stateValueElement
, container
, attribute
));
191 List
<ITmfXmlStateAttribute
> attributes
= new ArrayList
<>();
192 for (Element element
: childElements
) {
193 if (element
== null) {
194 throw new NullPointerException("There should be at list one element"); //$NON-NLS-1$
196 if (!element
.getNodeName().equals(TmfXmlStrings
.STATE_ATTRIBUTE
)) {
197 throw new IllegalArgumentException("TmfXmlCondition: a condition either has a eventField element or a number of TmfXmlStateAttribute elements before the state value"); //$NON-NLS-1$
199 ITmfXmlStateAttribute attribute
= modelFactory
.createStateAttribute(element
, container
);
200 attributes
.add(attribute
);
202 stateValues
.add(modelFactory
.createStateValue(stateValueElement
, container
, attributes
));
206 private static ConditionOperator
getConditionOperator(Element rootNode
) {
207 String equationType
= rootNode
.getAttribute(TmfXmlStrings
.OPERATOR
);
208 switch (equationType
) {
209 case TmfXmlStrings
.EQ
:
210 return ConditionOperator
.EQ
;
211 case TmfXmlStrings
.NE
:
212 return ConditionOperator
.NE
;
213 case TmfXmlStrings
.GE
:
214 return ConditionOperator
.GE
;
215 case TmfXmlStrings
.GT
:
216 return ConditionOperator
.GT
;
217 case TmfXmlStrings
.LE
:
218 return ConditionOperator
.LE
;
219 case TmfXmlStrings
.LT
:
220 return ConditionOperator
.LT
;
221 case TmfXmlStrings
.NULL
:
222 return ConditionOperator
.EQ
;
224 throw new IllegalArgumentException("TmfXmlCondition: invalid comparison operator."); //$NON-NLS-1$
232 public boolean test(ITmfEvent event
, @Nullable TmfXmlScenarioInfo scenarioInfo
) {
233 ITmfStateSystem ss
= fContainer
.getStateSystem();
234 if (fType
== ConditionType
.DATA
) {
236 return testForEvent(event
, NonNullUtils
.checkNotNull(ss
), scenarioInfo
);
237 } catch (AttributeNotFoundException e
) {
238 Activator
.logError("Attribute not found", e
); //$NON-NLS-1$
241 } else if (fType
== ConditionType
.TIME
) {
242 if (fTimeCondition
!= null) {
243 return fTimeCondition
.test(event
, scenarioInfo
);
245 } else if (!fConditions
.isEmpty()) {
246 /* Verify a condition tree */
249 for (ITmfXmlCondition childCondition
: fConditions
) {
250 if (!childCondition
.test(event
, scenarioInfo
)) {
258 return !fConditions
.get(0).test(event
, scenarioInfo
);
260 for (ITmfXmlCondition childCondition
: fConditions
) {
261 if (childCondition
.test(event
, scenarioInfo
)) {
274 private boolean testForEvent(ITmfEvent event
, ITmfStateSystem ss
, @Nullable TmfXmlScenarioInfo scenarioInfo
) throws AttributeNotFoundException
{
276 * The condition is either the equality check of a state value or a
277 * boolean operation on other conditions
279 if (fStateValues
.size() == 1) {
280 ITmfXmlStateValue filter
= fStateValues
.get(0);
281 int quark
= IXmlStateSystemContainer
.ROOT_QUARK
;
282 for (ITmfXmlStateAttribute attribute
: filter
.getAttributes()) {
283 quark
= attribute
.getAttributeQuark(event
, quark
, scenarioInfo
);
285 * When verifying a condition, the state attribute must exist,
286 * if it does not, the query is not valid, we stop the condition
289 if (quark
== IXmlStateSystemContainer
.ERROR_QUARK
) {
290 throw new AttributeNotFoundException(ss
.getSSID() + " Attribute:" + attribute
); //$NON-NLS-1$
295 * The actual value: it can be either queried in the state system or
298 ITmfStateValue valueState
= (quark
!= IXmlStateSystemContainer
.ROOT_QUARK
) ? ss
.queryOngoingState(quark
) : filter
.getEventFieldValue(event
);
300 /* Get the value to compare to from the XML file */
301 ITmfStateValue valueXML
;
302 valueXML
= filter
.getValue(event
, scenarioInfo
);
303 return compare(valueState
, valueXML
, fConditionOperator
);
305 /* Get the two values needed for the comparison */
306 ITmfStateValue valuesXML1
= fStateValues
.get(0).getValue(event
, scenarioInfo
);
307 ITmfStateValue valuesXML2
= fStateValues
.get(1).getValue(event
, scenarioInfo
);
308 return valuesXML1
.equals(valuesXML2
);
312 public String
toString() {
313 StringBuilder output
= new StringBuilder("TmfXmlCondition: "); //$NON-NLS-1$
314 if (fOperator
!= LogicalOperator
.NONE
) {
315 output
.append(fOperator
).append(" on ").append(fConditions
); //$NON-NLS-1$
317 output
.append(fConditionOperator
).append(" {").append(fStateValues
.get(0)); //$NON-NLS-1$
318 if (fStateValues
.size() == 2) {
319 output
.append(", ").append(fStateValues
.get(1)); //$NON-NLS-1$
321 output
.append("}"); //$NON-NLS-1$
323 return output
.toString();
327 * Compare two ITmfStateValues based on the given comparison operator
330 * the state value to compare to
332 * the state value to be compared with
333 * @param comparisonOperator
334 * the operator to compare the inputs
335 * @return the boolean result of the comparison
337 public boolean compare(ITmfStateValue source
, ITmfStateValue dest
, ConditionOperator comparisonOperator
) {
338 switch (comparisonOperator
) {
339 // TODO The comparison operator should have a compareHelper that calls compare
341 return (source
.compareTo(dest
) == 0);
343 return (source
.compareTo(dest
) != 0);
345 return (source
.compareTo(dest
) >= 0);
347 return (source
.compareTo(dest
) > 0);
349 return (source
.compareTo(dest
) <= 0);
351 return (source
.compareTo(dest
) < 0);
354 throw new IllegalArgumentException("TmfXmlCondition: invalid comparison operator."); //$NON-NLS-1$