573123f6a7fe295830c7a965757a819703a1eb75
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.analysis.xml.core / src / org / eclipse / tracecompass / tmf / analysis / xml / core / model / TmfXmlCondition.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 Ecole Polytechnique de Montreal and others
3 *
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
8 *
9 * Contributors:
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 ******************************************************************************/
15
16 package org.eclipse.tracecompass.tmf.analysis.xml.core.model;
17
18 import java.util.ArrayList;
19 import java.util.List;
20
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;
33
34 /**
35 * This Class implement a condition tree in the XML-defined state system.
36 *
37 * <pre>
38 * example:
39 * <and>
40 * <condition>
41 * <stateAttribute type="location" value="CurrentThread" />
42 * <stateAttribute type="constant" value="System_call" />
43 * <stateValue type="null" />
44 * </condition>
45 * <condition>
46 * <stateValue type="long" value="2" />
47 * <stateValue type="long" value="5" />
48 * </condition>
49 * </and>
50 * </pre>
51 *
52 * @author Florian Wininger
53 */
54 public class TmfXmlCondition implements ITmfXmlCondition {
55
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;
63
64 private enum LogicalOperator {
65 NONE,
66 NOT,
67 AND,
68 OR
69 }
70
71 private enum ConditionOperator {
72 NONE,
73 EQ,
74 NE,
75 GE,
76 GT,
77 LE,
78 LT
79 }
80
81 // TODO The XmlCondition needs to be split into several classes of condition
82 // instead of using an enum
83 private enum ConditionType {
84 DATA,
85 TIME,
86 NONE
87 }
88
89 /**
90 * Factory to create {@link TmfXmlCondition}
91 *
92 * @param modelFactory
93 * The factory used to create XML model elements
94 * @param node
95 * The XML root of this condition
96 * @param container
97 * The state system container this condition belongs to
98 * @return The new {@link TmfXmlCondition}
99 * @since 2.0
100 */
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);
105
106 /*
107 * If the node is an if, take the child as the root condition
108 *
109 * FIXME: Maybe the caller should do this instead.
110 */
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$
114 }
115 rootNode = NonNullUtils.checkNotNull(childElements.get(0));
116 childElements = XmlUtils.getChildElements(rootNode);
117 }
118
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);
129 default:
130 throw new IllegalArgumentException("TmfXmlCondition constructor: XML node " + rootNode.getNodeName() + " is of the wrong type"); //$NON-NLS-1$ //$NON-NLS-2$
131 }
132 }
133
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();
139 if (size != 0) {
140 stateValues = new ArrayList<>(size);
141 if (size == 1) {
142 conditionOperator = getConditionOperator(rootNode);
143 getStateValuesForXmlCondition(modelFactory, NonNullUtils.checkNotNull(childElements), stateValues, container);
144 } else {
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>()));
150 }
151 return new TmfXmlCondition(ConditionType.DATA, stateValues, LogicalOperator.NONE, conditionOperator, null, new ArrayList<>(), container);
152 }
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);
156 }
157
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) {
162 continue;
163 }
164 conditions.add(modelFactory.createCondition(condition, container));
165 }
166 return new TmfXmlCondition(ConditionType.NONE, new ArrayList<>(), op, ConditionOperator.NONE, null, conditions, container);
167 }
168
169 private TmfXmlCondition(ConditionType type, ArrayList<@NonNull ITmfXmlStateValue> stateValues, LogicalOperator operator, ConditionOperator conditionOperator, @Nullable TmfXmlTimestampCondition timeCondition, List<@NonNull TmfXmlCondition> conditions,
170 IXmlStateSystemContainer container) {
171 fType = type;
172 fStateValues = stateValues;
173 fOperator = operator;
174 fTimeCondition = timeCondition;
175 fContainer = container;
176 fConditions.addAll(conditions);
177 fConditionOperator = conditionOperator;
178 }
179
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));
182 /*
183 * A state value is either preceded by an eventField or a number of
184 * state attributes
185 */
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));
190 } else {
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$
195 }
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$
198 }
199 ITmfXmlStateAttribute attribute = modelFactory.createStateAttribute(element, container);
200 attributes.add(attribute);
201 }
202 stateValues.add(modelFactory.createStateValue(stateValueElement, container, attributes));
203 }
204 }
205
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;
223 default:
224 throw new IllegalArgumentException("TmfXmlCondition: invalid comparison operator."); //$NON-NLS-1$
225 }
226 }
227
228 /**
229 * @since 2.0
230 */
231 @Override
232 public boolean test(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) {
233 ITmfStateSystem ss = fContainer.getStateSystem();
234 if (fType == ConditionType.DATA) {
235 try {
236 return testForEvent(event, NonNullUtils.checkNotNull(ss), scenarioInfo);
237 } catch (AttributeNotFoundException e) {
238 Activator.logError("Attribute not found", e); //$NON-NLS-1$
239 return false;
240 }
241 } else if (fType == ConditionType.TIME) {
242 if (fTimeCondition != null) {
243 return fTimeCondition.test(event, scenarioInfo);
244 }
245 } else if (!fConditions.isEmpty()) {
246 /* Verify a condition tree */
247 switch (fOperator) {
248 case AND:
249 for (ITmfXmlCondition childCondition : fConditions) {
250 if (!childCondition.test(event, scenarioInfo)) {
251 return false;
252 }
253 }
254 return true;
255 case NONE:
256 break;
257 case NOT:
258 return !fConditions.get(0).test(event, scenarioInfo);
259 case OR:
260 for (ITmfXmlCondition childCondition : fConditions) {
261 if (childCondition.test(event, scenarioInfo)) {
262 return true;
263 }
264 }
265 return false;
266 default:
267 break;
268
269 }
270 }
271 return true;
272 }
273
274 private boolean testForEvent(ITmfEvent event, ITmfStateSystem ss, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException {
275 /*
276 * The condition is either the equality check of a state value or a
277 * boolean operation on other conditions
278 */
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);
284 /*
285 * When verifying a condition, the state attribute must exist,
286 * if it does not, the query is not valid, we stop the condition
287 * check
288 */
289 if (quark == IXmlStateSystemContainer.ERROR_QUARK) {
290 throw new AttributeNotFoundException(ss.getSSID() + " Attribute:" + attribute); //$NON-NLS-1$
291 }
292 }
293
294 /*
295 * The actual value: it can be either queried in the state system or
296 * found in the event
297 */
298 ITmfStateValue valueState = (quark != IXmlStateSystemContainer.ROOT_QUARK) ? ss.queryOngoingState(quark) : filter.getEventFieldValue(event);
299
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);
304 }
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);
309 }
310
311 @Override
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$
316 } else {
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$
320 }
321 output.append("}"); //$NON-NLS-1$
322 }
323 return output.toString();
324 }
325
326 /**
327 * Compare two ITmfStateValues based on the given comparison operator
328 *
329 * @param source
330 * the state value to compare to
331 * @param dest
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
336 */
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
340 case EQ:
341 return (source.compareTo(dest) == 0);
342 case NE:
343 return (source.compareTo(dest) != 0);
344 case GE:
345 return (source.compareTo(dest) >= 0);
346 case GT:
347 return (source.compareTo(dest) > 0);
348 case LE:
349 return (source.compareTo(dest) <= 0);
350 case LT:
351 return (source.compareTo(dest) < 0);
352 case NONE:
353 default:
354 throw new IllegalArgumentException("TmfXmlCondition: invalid comparison operator."); //$NON-NLS-1$
355 }
356 }
357 }
This page took 0.051615 seconds and 5 git commands to generate.