tmf : Add pattern analysis behavior
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.analysis.xml.core / src / org / eclipse / tracecompass / tmf / analysis / xml / core / model / TmfXmlCondition.java
index 51a0d6774fff9ef397de49cb7e8a3414843f4595..573123f6a7fe295830c7a965757a819703a1eb75 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Ecole Polytechnique de Montreal
+ * Copyright (c) 2014, 2015 Ecole Polytechnique de Montreal and others
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -10,6 +10,7 @@
  *   Florian Wininger - Initial API and implementation
  *   Naser Ezzati - Add the comparison operators
  *   Patrick Tasse - Add message to exceptions
+ *   Jean-Christian Kouame - Add comparison between two state values
  ******************************************************************************/
 
 package org.eclipse.tracecompass.tmf.analysis.xml.core.model;
@@ -17,7 +18,10 @@ package org.eclipse.tracecompass.tmf.analysis.xml.core.model;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jdt.annotation.NonNull;
 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.statesystem.core.ITmfStateSystem;
 import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
 import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
@@ -39,25 +43,29 @@ import org.w3c.dom.Element;
  *       <stateValue type="null" />
  *   </condition>
  *   <condition>
+ *      <stateValue type="long" value="2" />
+ *      <stateValue type="long" value="5" />
  *   </condition>
  * </and>
  * </pre>
  *
  * @author Florian Wininger
  */
-public class TmfXmlCondition {
+public class TmfXmlCondition implements ITmfXmlCondition {
 
     private final List<TmfXmlCondition> fConditions = new ArrayList<>();
-    private final @Nullable ITmfXmlStateValue fStateValue;
+    private final List<ITmfXmlStateValue> fStateValues;
     private final LogicalOperator fOperator;
     private final IXmlStateSystemContainer fContainer;
     private final ConditionOperator fConditionOperator;
+    private ConditionType fType;
+    private @Nullable TmfXmlTimestampCondition fTimeCondition;
 
     private enum LogicalOperator {
         NONE,
         NOT,
         AND,
-        OR,
+        OR
     }
 
     private enum ConditionOperator {
@@ -70,8 +78,16 @@ public class TmfXmlCondition {
         LT
     }
 
+    // TODO The XmlCondition needs to be split into several classes of condition
+    // instead of using an enum
+    private enum ConditionType {
+        DATA,
+        TIME,
+        NONE
+    }
+
     /**
-     * Constructor
+     * Factory to create {@link TmfXmlCondition}
      *
      * @param modelFactory
      *            The factory used to create XML model elements
@@ -79,13 +95,13 @@ public class TmfXmlCondition {
      *            The XML root of this condition
      * @param container
      *            The state system container this condition belongs to
+     * @return The new {@link TmfXmlCondition}
+     * @since 2.0
      */
-    public TmfXmlCondition(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container) {
-        fContainer = container;
-
+    public static TmfXmlCondition create(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container) {
         Element rootNode = node;
         /* Process the conditions: in each case, only process Element nodes */
-        List<Element> childElements = XmlUtils.getChildElements(rootNode);
+        List<@Nullable Element> childElements = XmlUtils.getChildElements(rootNode);
 
         /*
          * If the node is an if, take the child as the root condition
@@ -94,160 +110,144 @@ public class TmfXmlCondition {
          */
         if (node.getNodeName().equals(TmfXmlStrings.IF)) {
             if (childElements.isEmpty()) {
-                throw new IllegalArgumentException("TmfXmlCondition constructor: IF node has no child element"); //$NON-NLS-1$
+                throw new IllegalArgumentException("TmfXmlCondition constructor: IF node with no child element"); //$NON-NLS-1$
             }
-            rootNode = childElements.get(0);
+            rootNode = NonNullUtils.checkNotNull(childElements.get(0));
             childElements = XmlUtils.getChildElements(rootNode);
         }
 
+        List<@NonNull TmfXmlCondition> conditions = new ArrayList<>();
         switch (rootNode.getNodeName()) {
         case TmfXmlStrings.CONDITION:
-            fOperator = LogicalOperator.NONE;
-            /* Read comparison type */
-            String equationType = rootNode.getAttribute(TmfXmlStrings.OPERATOR);
-
-            switch (equationType) {
-            case TmfXmlStrings.EQ:
-                fConditionOperator = ConditionOperator.EQ;
-                break;
-            case TmfXmlStrings.NE:
-                fConditionOperator = ConditionOperator.NE;
-                break;
-            case TmfXmlStrings.GE:
-                fConditionOperator = ConditionOperator.GE;
-                break;
-            case TmfXmlStrings.GT:
-                fConditionOperator = ConditionOperator.GT;
-                break;
-            case TmfXmlStrings.LE:
-                fConditionOperator = ConditionOperator.LE;
-                break;
-            case TmfXmlStrings.LT:
-                fConditionOperator = ConditionOperator.LT;
-                break;
-            case TmfXmlStrings.NULL:
-                fConditionOperator = ConditionOperator.EQ;
-                break;
-            default:
-                throw new IllegalArgumentException("TmfXmlCondition: invalid comparison operator."); //$NON-NLS-1$
-            }
-            /* The last element is a state value node */
-            Element stateValueElement = childElements.remove(childElements.size() - 1);
-            if (stateValueElement == null) {
-                throw new IllegalStateException();
-            }
+            return createPatternCondition(modelFactory, container, rootNode, childElements);
+        case TmfXmlStrings.NOT:
+            return createMultipleCondition(modelFactory, container, childElements, LogicalOperator.NOT, conditions);
+        case TmfXmlStrings.AND:
+            return createMultipleCondition(modelFactory, container, childElements, LogicalOperator.AND, conditions);
+        case TmfXmlStrings.OR:
+            return createMultipleCondition(modelFactory, container, childElements, LogicalOperator.OR, conditions);
+        default:
+            throw new IllegalArgumentException("TmfXmlCondition constructor: XML node " + rootNode.getNodeName() + " is of the wrong type"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
 
-            /*
-             * A state value is either preceded by an eventField or a number of
-             * state attributes
-             */
-            if (childElements.size() == 1 && childElements.get(0).getNodeName().equals(TmfXmlStrings.ELEMENT_FIELD)) {
-                String attribute = childElements.get(0).getAttribute(TmfXmlStrings.NAME);
-                if (attribute == null) {
-                    throw new IllegalArgumentException();
-                }
-                fStateValue = modelFactory.createStateValue(stateValueElement, fContainer, attribute);
+    private static TmfXmlCondition createPatternCondition(ITmfXmlModelFactory modelFactory, IXmlStateSystemContainer container, Element rootNode, List<@Nullable Element> childElements) {
+        ArrayList<ITmfXmlStateValue> stateValues;
+        ConditionOperator conditionOperator;
+        TmfXmlTimestampCondition timeCondition = null;
+        int size = rootNode.getElementsByTagName(TmfXmlStrings.STATE_VALUE).getLength();
+        if (size != 0) {
+            stateValues = new ArrayList<>(size);
+            if (size == 1) {
+                conditionOperator = getConditionOperator(rootNode);
+                getStateValuesForXmlCondition(modelFactory, NonNullUtils.checkNotNull(childElements), stateValues, container);
             } else {
-                List<ITmfXmlStateAttribute> attributes = new ArrayList<>();
-                for (Element element : childElements) {
-                    if (!element.getNodeName().equals(TmfXmlStrings.STATE_ATTRIBUTE)) {
-                        throw new IllegalArgumentException("TmfXmlCondition: a condition either has a eventField element or a number of TmfXmlStateAttribute elements before the state value"); //$NON-NLS-1$
-                    }
-                    ITmfXmlStateAttribute attribute = modelFactory.createStateAttribute(element, fContainer);
-                    attributes.add(attribute);
-                }
-                fStateValue = modelFactory.createStateValue(stateValueElement, fContainer, attributes);
+                // No need to test if the childElements size is actually 2.
+                // The XSD validation do this check already.
+                conditionOperator = ConditionOperator.EQ;
+                stateValues.add(modelFactory.createStateValue(NonNullUtils.checkNotNull(childElements.get(0)), container, new ArrayList<ITmfXmlStateAttribute>()));
+                stateValues.add(modelFactory.createStateValue(NonNullUtils.checkNotNull(childElements.get(1)), container, new ArrayList<ITmfXmlStateAttribute>()));
             }
-            break;
-        case TmfXmlStrings.NOT:
-            fOperator = LogicalOperator.NOT;
-            fStateValue = null;
-            fConditionOperator = ConditionOperator.NONE;
-            Element element = childElements.get(0);
-            if (element == null) {
-                throw new IllegalArgumentException();
+            return new TmfXmlCondition(ConditionType.DATA, stateValues, LogicalOperator.NONE, conditionOperator, null, new ArrayList<>(), container);
+        }
+        final Element firstElement = NonNullUtils.checkNotNull(childElements.get(0));
+        timeCondition = modelFactory.createTimestampsCondition(firstElement, container);
+        return new TmfXmlCondition(ConditionType.TIME, new ArrayList<>(), LogicalOperator.NONE, ConditionOperator.EQ, timeCondition, new ArrayList<>(), container);
+    }
+
+    private static TmfXmlCondition createMultipleCondition(ITmfXmlModelFactory modelFactory, IXmlStateSystemContainer container, List<@Nullable Element> childElements, LogicalOperator op,
+            List<@NonNull TmfXmlCondition> conditions) {
+        for (Element condition : childElements) {
+            if (condition == null) {
+                continue;
             }
-            fConditions.add(modelFactory.createCondition(element, fContainer));
-            break;
-        case TmfXmlStrings.AND:
-            fOperator = LogicalOperator.AND;
-            fStateValue = null;
-            fConditionOperator = ConditionOperator.NONE;
-            for (Element condition : childElements) {
-                if (condition == null) {
-                    continue;
+            conditions.add(modelFactory.createCondition(condition, container));
+        }
+        return new TmfXmlCondition(ConditionType.NONE, new ArrayList<>(), op, ConditionOperator.NONE, null, conditions, container);
+    }
+
+    private TmfXmlCondition(ConditionType type, ArrayList<@NonNull ITmfXmlStateValue> stateValues, LogicalOperator operator, ConditionOperator conditionOperator, @Nullable TmfXmlTimestampCondition timeCondition, List<@NonNull TmfXmlCondition> conditions,
+            IXmlStateSystemContainer container) {
+        fType = type;
+        fStateValues = stateValues;
+        fOperator = operator;
+        fTimeCondition = timeCondition;
+        fContainer = container;
+        fConditions.addAll(conditions);
+        fConditionOperator = conditionOperator;
+    }
+
+    private static void getStateValuesForXmlCondition(ITmfXmlModelFactory modelFactory, List<@Nullable Element> childElements, List<ITmfXmlStateValue> stateValues, IXmlStateSystemContainer container) {
+        Element stateValueElement = NonNullUtils.checkNotNull(childElements.remove(childElements.size() - 1));
+        /*
+         * A state value is either preceded by an eventField or a number of
+         * state attributes
+         */
+        final Element firstElement = NonNullUtils.checkNotNull(childElements.get(0));
+        if (childElements.size() == 1 && firstElement.getNodeName().equals(TmfXmlStrings.ELEMENT_FIELD)) {
+            String attribute = firstElement.getAttribute(TmfXmlStrings.NAME);
+            stateValues.add(modelFactory.createStateValue(stateValueElement, container, attribute));
+        } else {
+            List<ITmfXmlStateAttribute> attributes = new ArrayList<>();
+            for (Element element : childElements) {
+                if (element == null) {
+                    throw new NullPointerException("There should be at list one element"); //$NON-NLS-1$
                 }
-                fConditions.add(modelFactory.createCondition(condition, fContainer));
-            }
-            break;
-        case TmfXmlStrings.OR:
-            fOperator = LogicalOperator.OR;
-            fStateValue = null;
-            fConditionOperator = ConditionOperator.NONE;
-            for (Element condition : childElements) {
-                if (condition == null) {
-                    continue;
+                if (!element.getNodeName().equals(TmfXmlStrings.STATE_ATTRIBUTE)) {
+                    throw new IllegalArgumentException("TmfXmlCondition: a condition either has a eventField element or a number of TmfXmlStateAttribute elements before the state value"); //$NON-NLS-1$
                 }
-                fConditions.add(modelFactory.createCondition(condition, fContainer));
+                ITmfXmlStateAttribute attribute = modelFactory.createStateAttribute(element, container);
+                attributes.add(attribute);
             }
-            break;
+            stateValues.add(modelFactory.createStateValue(stateValueElement, container, attributes));
+        }
+    }
+
+    private static ConditionOperator getConditionOperator(Element rootNode) {
+        String equationType = rootNode.getAttribute(TmfXmlStrings.OPERATOR);
+        switch (equationType) {
+        case TmfXmlStrings.EQ:
+            return ConditionOperator.EQ;
+        case TmfXmlStrings.NE:
+            return ConditionOperator.NE;
+        case TmfXmlStrings.GE:
+            return ConditionOperator.GE;
+        case TmfXmlStrings.GT:
+            return ConditionOperator.GT;
+        case TmfXmlStrings.LE:
+            return ConditionOperator.LE;
+        case TmfXmlStrings.LT:
+            return ConditionOperator.LT;
+        case TmfXmlStrings.NULL:
+            return ConditionOperator.EQ;
         default:
-            throw new IllegalArgumentException("TmfXmlCondition constructor: XML node is of the wrong type"); //$NON-NLS-1$
+            throw new IllegalArgumentException("TmfXmlCondition: invalid comparison operator."); //$NON-NLS-1$
         }
     }
 
     /**
-     * Test the result of the condition for an event
-     *
-     * @param event
-     *            The event on which to test the condition
-     * @return Whether the condition is true or not
-     * @throws AttributeNotFoundException
-     *             The state attribute was not found
+     * @since 2.0
      */
-    public boolean testForEvent(ITmfEvent event) throws AttributeNotFoundException {
+    @Override
+    public boolean test(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) {
         ITmfStateSystem ss = fContainer.getStateSystem();
-        /*
-         * The condition is either the equality check of a state value or a
-         * boolean operation on other conditions
-         */
-        if (fStateValue != null) {
-            ITmfXmlStateValue filter = fStateValue;
-            int quark = IXmlStateSystemContainer.ROOT_QUARK;
-            for (ITmfXmlStateAttribute attribute : filter.getAttributes()) {
-                quark = attribute.getAttributeQuark(event, quark);
-                /*
-                 * When verifying a condition, the state attribute must exist,
-                 * if it does not, the query is not valid, we stop the condition
-                 * check
-                 */
-                if (quark == IXmlStateSystemContainer.ERROR_QUARK) {
-                    throw new AttributeNotFoundException(ss.getSSID() + " Attribute:" + attribute); //$NON-NLS-1$
-                }
+        if (fType == ConditionType.DATA) {
+            try {
+                return testForEvent(event, NonNullUtils.checkNotNull(ss), scenarioInfo);
+            } catch (AttributeNotFoundException e) {
+                Activator.logError("Attribute not found", e); //$NON-NLS-1$
+                return false;
             }
-
-            /* Get the value to compare to from the XML file */
-            ITmfStateValue valueXML;
-            valueXML = filter.getValue(event);
-
-            /*
-             * The actual value: it can be either queried in the state system or
-             * found in the event
-             */
-            ITmfStateValue valueState = (quark != IXmlStateSystemContainer.ROOT_QUARK) ? ss.queryOngoingState(quark) :
-                    filter.getEventFieldValue(event);
-            if (valueState == null) {
-                throw new IllegalStateException();
+        } else if (fType == ConditionType.TIME) {
+            if (fTimeCondition != null) {
+                return fTimeCondition.test(event, scenarioInfo);
             }
-
-            return compare(valueState, valueXML, fConditionOperator);
-
         } else if (!fConditions.isEmpty()) {
             /* Verify a condition tree */
             switch (fOperator) {
             case AND:
-                for (TmfXmlCondition childCondition : fConditions) {
-                    if (!childCondition.testForEvent(event)) {
+                for (ITmfXmlCondition childCondition : fConditions) {
+                    if (!childCondition.test(event, scenarioInfo)) {
                         return false;
                     }
                 }
@@ -255,10 +255,10 @@ public class TmfXmlCondition {
             case NONE:
                 break;
             case NOT:
-                return !fConditions.get(0).testForEvent(event);
+                return !fConditions.get(0).test(event, scenarioInfo);
             case OR:
-                for (TmfXmlCondition childCondition : fConditions) {
-                    if (childCondition.testForEvent(event)) {
+                for (ITmfXmlCondition childCondition : fConditions) {
+                    if (childCondition.test(event, scenarioInfo)) {
                         return true;
                     }
                 }
@@ -267,15 +267,60 @@ public class TmfXmlCondition {
                 break;
 
             }
-        } else {
-            throw new IllegalStateException("TmfXmlCondition: the condition should be either a state value or be the result of a condition tree"); //$NON-NLS-1$
         }
         return true;
     }
 
+    private boolean testForEvent(ITmfEvent event, ITmfStateSystem ss, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException {
+        /*
+         * The condition is either the equality check of a state value or a
+         * boolean operation on other conditions
+         */
+        if (fStateValues.size() == 1) {
+            ITmfXmlStateValue filter = fStateValues.get(0);
+            int quark = IXmlStateSystemContainer.ROOT_QUARK;
+            for (ITmfXmlStateAttribute attribute : filter.getAttributes()) {
+                quark = attribute.getAttributeQuark(event, quark, scenarioInfo);
+                /*
+                 * When verifying a condition, the state attribute must exist,
+                 * if it does not, the query is not valid, we stop the condition
+                 * check
+                 */
+                if (quark == IXmlStateSystemContainer.ERROR_QUARK) {
+                    throw new AttributeNotFoundException(ss.getSSID() + " Attribute:" + attribute); //$NON-NLS-1$
+                }
+            }
+
+            /*
+             * The actual value: it can be either queried in the state system or
+             * found in the event
+             */
+            ITmfStateValue valueState = (quark != IXmlStateSystemContainer.ROOT_QUARK) ? ss.queryOngoingState(quark) : filter.getEventFieldValue(event);
+
+            /* Get the value to compare to from the XML file */
+            ITmfStateValue valueXML;
+            valueXML = filter.getValue(event, scenarioInfo);
+            return compare(valueState, valueXML, fConditionOperator);
+        }
+        /* Get the two values needed for the comparison */
+        ITmfStateValue valuesXML1 = fStateValues.get(0).getValue(event, scenarioInfo);
+        ITmfStateValue valuesXML2 = fStateValues.get(1).getValue(event, scenarioInfo);
+        return valuesXML1.equals(valuesXML2);
+    }
+
     @Override
     public String toString() {
-        return "TmfXmlCondition: " + fOperator + " on " + fConditions; //$NON-NLS-1$ //$NON-NLS-2$
+        StringBuilder output = new StringBuilder("TmfXmlCondition: "); //$NON-NLS-1$
+        if (fOperator != LogicalOperator.NONE) {
+            output.append(fOperator).append(" on ").append(fConditions); //$NON-NLS-1$
+        } else {
+            output.append(fConditionOperator).append(" {").append(fStateValues.get(0)); //$NON-NLS-1$
+            if (fStateValues.size() == 2) {
+                output.append(", ").append(fStateValues.get(1)); //$NON-NLS-1$
+            }
+            output.append("}"); //$NON-NLS-1$
+        }
+        return output.toString();
     }
 
     /**
@@ -291,6 +336,7 @@ public class TmfXmlCondition {
      */
     public boolean compare(ITmfStateValue source, ITmfStateValue dest, ConditionOperator comparisonOperator) {
         switch (comparisonOperator) {
+        // TODO The comparison operator should have a compareHelper that calls compare
         case EQ:
             return (source.compareTo(dest) == 0);
         case NE:
@@ -307,7 +353,5 @@ public class TmfXmlCondition {
         default:
             throw new IllegalArgumentException("TmfXmlCondition: invalid comparison operator."); //$NON-NLS-1$
         }
-
     }
-
 }
\ No newline at end of file
This page took 0.031895 seconds and 5 git commands to generate.