tmf.xml: Bug 500195. Modify initial state behavior
authorJean-Christian Kouame <jean-christian.kouame@ericsson.com>
Tue, 30 Aug 2016 03:39:10 +0000 (23:39 -0400)
committerBernd Hufmann <bernd.hufmann@ericsson.com>
Wed, 31 Aug 2016 13:32:30 +0000 (09:32 -0400)
Fix Bug 500195 and add a new initialState element. This element acts as
a real state and support conditions and actions. Also, these conditions
are only verified  for the pending scenario and the pending scenario do
will ignore the preconditions.

Change-Id: I520f4129f9c65eee9299d5b08f380e07b2b78d0f
Signed-off-by: Jean-Christian Kouame <jean-christian.kouame@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/79439
Reviewed-by: Hudson CI
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-by: Genevieve Bastien <gbastien+lttng@versatic.net>
Tested-by: Genevieve Bastien <gbastien+lttng@versatic.net>
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/common/org/eclipse/tracecompass/tmf/analysis/xml/core/tests/common/TmfXmlTestFiles.java
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/src/org/eclipse/tracecompass/tmf/analysis/xml/core/tests/model/FsmTest.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/src/org/eclipse/tracecompass/tmf/analysis/xml/core/tests/stateprovider/XmlModuleTestBase.java
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/test_xml_files/test_valid/test_initialState_element1.xml [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/test_xml_files/test_valid/test_initialState_element2.xml [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/model/TmfXmlFsm.java
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/model/TmfXmlScenario.java
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/model/TmfXmlState.java
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/module/xmlPatternStateProvider.xsd
tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/stateprovider/TmfXmlStrings.java

index e2c7ca6fdf3114d9d86bcfe1702b4e8361616483..c0dafe2f554a15efd2507313d9628f458a23c20f 100644 (file)
@@ -49,7 +49,11 @@ public enum TmfXmlTestFiles {
     /** A valid pattern file to test the pattern segment **/
     VALID_PATTERN_SEGMENT("test_xml_files/test_valid/test_pattern_segment.xml"),
     /** A valid file for consuming fsm test */
-    CONSUMING_FSM_TEST("test_xml_files/test_valid/test_consuming_fsm.xml");
+    CONSUMING_FSM_TEST("test_xml_files/test_valid/test_consuming_fsm.xml"),
+    /** A valid pattern file to test the initialState element */
+    INITIAL_STATE_ELEMENT_TEST_FILE_1("test_xml_files/test_valid/test_initialState_element1.xml"),
+    /** A valid pattern file to test the initialState element */
+    INITIAL_STATE_ELEMENT_TEST_FILE_2("test_xml_files/test_valid/test_initialState_element2.xml");
 
     private final String fPath;
 
diff --git a/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/src/org/eclipse/tracecompass/tmf/analysis/xml/core/tests/model/FsmTest.java b/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/src/org/eclipse/tracecompass/tmf/analysis/xml/core/tests/model/FsmTest.java
new file mode 100644 (file)
index 0000000..784ef76
--- /dev/null
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.tmf.analysis.xml.core.tests.model;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.TmfXmlPatternSegmentBuilder;
+import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.pattern.stateprovider.XmlPatternAnalysis;
+import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.segment.TmfXmlPatternSegment;
+import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
+import org.eclipse.tracecompass.segmentstore.core.ISegment;
+import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.analysis.xml.core.tests.common.TmfXmlTestFiles;
+import org.eclipse.tracecompass.tmf.analysis.xml.core.tests.module.XmlUtilsTest;
+import org.eclipse.tracecompass.tmf.analysis.xml.core.tests.stateprovider.XmlModuleTestBase;
+import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * Test the pattern analysis fsm
+ *
+ * @author Jean-Christian Kouame
+ */
+public class FsmTest {
+    private static final int END_TIME = 7;
+    private static final @NonNull String TEST_TRACE = "test_traces/testTrace4.xml";
+    private static final String TEST_SEGMENT_NEW = TmfXmlPatternSegmentBuilder.PATTERN_SEGMENT_NAME_PREFIX + "NEW";
+    private static final TmfXmlTestFiles TEST_FILE_1 = TmfXmlTestFiles.INITIAL_STATE_ELEMENT_TEST_FILE_1;
+    private static final TmfXmlTestFiles TEST_FILE_2 = TmfXmlTestFiles.INITIAL_STATE_ELEMENT_TEST_FILE_2;
+    private static XmlPatternAnalysis fModule;
+    private static XmlPatternAnalysis fModule2;
+    private static ITmfTrace fTrace;
+
+    private static XmlPatternAnalysis createModule(@NonNull Element element, TmfXmlTestFiles file) {
+        XmlPatternAnalysis module = new XmlPatternAnalysis();
+        module.setXmlFile(new Path(file.getFile().getAbsolutePath()));
+        module.setName(XmlModuleTestBase.getName(element));
+        return module;
+    }
+
+    private static XmlPatternAnalysis initModule(TmfXmlTestFiles file) {
+        Document doc = file.getXmlDocument();
+        assertNotNull(doc);
+
+        /* get State Providers modules */
+        NodeList stateproviderNodes = doc.getElementsByTagName(TmfXmlStrings.PATTERN);
+
+        Element node = (Element) stateproviderNodes.item(0);
+        assertNotNull(node);
+
+        XmlPatternAnalysis module = createModule(node, file);
+
+        String moduleId = node.getAttribute(TmfXmlStrings.ID);
+        assertNotNull(moduleId);
+        module.setId(moduleId);
+
+        return module;
+    }
+
+    /**
+     * End the test suite
+     */
+    @AfterClass
+    public static void tearDown() {
+        fModule.dispose();
+        fModule2.dispose();
+        fTrace.dispose();
+    }
+
+    /**
+     * Before the test suite
+     */
+    @BeforeClass
+    public static void before() {
+        ITmfTrace trace = XmlUtilsTest.initializeTrace(TEST_TRACE);
+        //Create first module
+        fModule = initModule(TEST_FILE_1);
+        try {
+            fModule.setTrace(trace);
+            fModule.schedule();
+            assertTrue(fModule.waitForCompletion(new NullProgressMonitor()));
+        } catch (TmfAnalysisException e) {
+            fail("Cannot execute analyses " + e.getMessage());
+        }
+
+        //Create second module
+        fModule2 = initModule(TEST_FILE_2);
+        try {
+            fModule2.setTrace(trace);
+            fModule2.schedule();
+            assertTrue(fModule2.waitForCompletion(new NullProgressMonitor()));
+        } catch (TmfAnalysisException e) {
+            fail("Cannot execute analyses " + e.getMessage());
+        }
+        fTrace = trace;
+    }
+
+    /**
+     * Compare the execution of two state machines that do the same job, one
+     * using the initial element, the second one using the initialState element.
+     * The result should be the same for both state machines
+     */
+    @Test
+    public void testInitialStateDeclaration() {
+        ITmfStateSystem stateSystem = fModule.getStateSystem(fModule.getId());
+        assertNotNull("state system exist", stateSystem);
+        try {
+            int quark = stateSystem.getQuarkAbsolute("fsm1");
+            @NonNull ITmfStateInterval interval = stateSystem.querySingleState(END_TIME, quark);
+            long count1 = interval.getStateValue().unboxLong();
+
+            quark = stateSystem.getQuarkAbsolute("fsm2");
+            interval = stateSystem.querySingleState(END_TIME, quark);
+            long count2 = interval.getStateValue().unboxLong();
+            assertTrue("Test the count value", count1 == count2);
+        } catch (AttributeNotFoundException | StateSystemDisposedException e) {
+            fail("Failed to query the state system");
+        }
+    }
+
+    /**
+     * Compare the execution of two state machines doing the same job, the tid
+     * condition is ignored with the initial element and used with the
+     * initialState element. The result should be different.
+     */
+    @Test
+    public void testInitialStateWithCondition() {
+        ITmfStateSystem stateSystem = fModule.getStateSystem(fModule.getId());
+        assertNotNull("state system exist", stateSystem);
+        try {
+            int quark = stateSystem.getQuarkAbsolute("fsm1");
+            @NonNull ITmfStateInterval interval = stateSystem.querySingleState(END_TIME, quark);
+            long count1 = interval.getStateValue().unboxLong();
+
+            quark = stateSystem.getQuarkAbsolute("fsm3");
+            interval = stateSystem.querySingleState(END_TIME, quark);
+            long count3 = interval.getStateValue().unboxLong();
+            assertTrue("Test the count value", count1 > count3);
+        } catch (AttributeNotFoundException | StateSystemDisposedException e) {
+            fail("Failed to query the state system");
+        }
+    }
+
+    /**
+     * Execute one pattern, with the two types of initial state initialization,
+     * then test that the new behavior is prioritized and that preconditions are
+     * ignored with initialState element
+     */
+    @Test
+    public void testTwoInitialStates() {
+        //Test segment store
+        @Nullable ISegmentStore<@NonNull ISegment> ss = fModule2.getSegmentStore();
+        assertNotNull("segment store exist", ss);
+        assertTrue("Segment store not empty", ss.size() == 1);
+        Object item = ss.toArray()[0];
+        assertTrue(item instanceof TmfXmlPatternSegment);
+        assertTrue(((TmfXmlPatternSegment) item).getName().equals(TEST_SEGMENT_NEW));
+
+        //Test state system
+        ITmfStateSystem stateSystem = fModule2.getStateSystem(fModule2.getId());
+        assertNotNull("state system exist", stateSystem);
+        int quark;
+        try {
+            quark = stateSystem.getQuarkAbsolute("count_new");
+            @NonNull ITmfStateInterval interval = stateSystem.querySingleState(END_TIME, quark);
+            int count = interval.getStateValue().unboxInt();
+            assertTrue("Test the count value", count > 0);
+        } catch (AttributeNotFoundException | StateSystemDisposedException e) {
+            fail("Failed to query the state system");
+        }
+
+        try {
+            quark = stateSystem.getQuarkAbsolute("precond");
+        } catch (AttributeNotFoundException e) {
+            return;
+        }
+        fail();
+    }
+}
index 8aeb9110b236c4a4f0287a414eb62d94392d0a20..d3d4106c2cdfb095b5cdb334c393aa2b43a88491 100644 (file)
@@ -127,7 +127,7 @@ public abstract class XmlModuleTestBase {
      *            The analysis element
      * @return The name
      */
-    public @NonNull String getName(Element element) {
+    public static @NonNull String getName(Element element) {
         String name = null;
         List<Element> head = XmlUtils.getChildElements(element, TmfXmlStrings.HEAD);
         if (head.size() == 1) {
diff --git a/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/test_xml_files/test_valid/test_initialState_element1.xml b/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/test_xml_files/test_valid/test_initialState_element1.xml
new file mode 100644 (file)
index 0000000..0bceb3b
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tmfxml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:noNamespaceSchemaLocation="xmlDefinition.xsd">
+<!-- ***************************************************************************
+* Copyright (c) 2016 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
+*************************************************************************** -->
+<pattern version="0" id="test.analysis.1">
+       <head>
+               <traceType id="org.eclipse.linuxtools.lttng2.kernel.tracetype" />
+               <label value="XML test analysis 1" />
+       </head>
+
+       <patternHandler>
+
+<!--   This condition check if the current running thread PID is 496 -->
+               <test id="curState">
+                       <if>
+                               <condition>
+                                       <field name="curState" />
+                                       <stateValue type="string" value="GOOD" />
+                               </condition>
+                       </if>
+               </test>
+
+               <action id="increment_fsm1_counter">
+                       <stateChange>
+                               <stateAttribute type="constant" value="fsm1" />
+                               <stateValue type="long" value="1" increment="true" />
+                       </stateChange>
+               </action>
+
+               <action id="increment_fsm2_counter">
+                       <stateChange>
+                               <stateAttribute type="constant" value="fsm2" />
+                               <stateValue type="long" value="1" increment="true" />
+                       </stateChange>
+               </action>
+
+               <action id="increment_fsm3_counter">
+                       <stateChange>
+                               <stateAttribute type="constant" value="fsm3" />
+                               <stateValue type="long" value="1" increment="true" />
+                       </stateChange>
+               </action>
+
+               <fsm id="fsm1">
+                       <initial>
+                               <transition cond="curState" target="state1"/>
+                       </initial>
+                       <state id="state1">
+                               <transition event="exit" target="end" action="increment_fsm1_counter" />
+                       </state>
+                       <final id="end" />
+               </fsm>
+
+               <fsm id="fsm2">
+                       <initialState>
+                               <transition event="exit" target="end" action="increment_fsm2_counter" />
+                       </initialState>
+                       <final id="end" />
+               </fsm>
+
+               <fsm id="fsm3">
+                       <initialState>
+                               <transition event="exit" cond="curState" target="end" action="increment_fsm3_counter"/>
+                       </initialState>
+                       <final id="end" />
+               </fsm>
+       </patternHandler>
+</pattern>
+</tmfxml>
\ No newline at end of file
diff --git a/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/test_xml_files/test_valid/test_initialState_element2.xml b/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core.tests/test_xml_files/test_valid/test_initialState_element2.xml
new file mode 100644 (file)
index 0000000..bf5c076
--- /dev/null
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tmfxml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:noNamespaceSchemaLocation="xmlDefinition.xsd">
+<!-- ***************************************************************************
+* Copyright (c) 2016 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
+*************************************************************************** -->
+<pattern version="0" id="test.analysis.2">
+       <head>
+               <traceType id="org.eclipse.linuxtools.lttng2.kernel.tracetype" />
+               <label value="XML test analysis 2" />
+       </head>
+
+       <patternHandler>
+
+               <action id="increment_counter_new">
+                       <stateChange>
+                               <stateAttribute type="constant" value="count_new"/>
+                               <stateValue type="int" value="1" increment="true" />
+                       </stateChange>
+               </action>
+
+               <action id="increment_counter_precond">
+                       <stateChange>
+                               <stateAttribute type="constant" value="precond"/>
+                               <stateValue type="int" value="1" increment="true" />
+                       </stateChange>
+               </action>
+
+               <action id="generate_old_segment">
+                       <segment>
+                               <segType segName="OLD"/>
+                       </segment>
+               </action>
+
+               <action id="generate_new_segment">
+                       <segment>
+                               <segType segName="NEW"/>
+                       </segment>
+               </action>
+
+               <fsm id="test" initial="state_old" multiple="false">
+                       <initial>
+                               <transition target="state_old" />
+                       </initial>
+                       <initialState>
+                               <transition target="state_new" />
+                       </initialState>
+                       <state id="state_old">
+                               <transition target="end" action="generate_old_segment"/>
+                       </state>
+                       <state id="state_new">
+                               <transition target="end" action="generate_new_segment"/>
+                       </state>
+                       <final id="end"/>
+               </fsm>
+
+               <fsm id="test1" multiple="false">
+                       <precondition event="wrong_event" />
+                       <initialState>
+                               <transition target="state_new" action="increment_counter_new"/>
+                       </initialState>
+                       <state id="state_new">
+                               <transition target="state_2" action="increment_counter_precond"/>
+                       </state>
+                       <state id="state_2">
+                               <transition target="end" action="increment_counter_precond"/>
+                       </state>
+
+                       <final id="end"/>
+               </fsm>
+       </patternHandler>
+</pattern>
+</tmfxml>
\ No newline at end of file
index a6ecb57a8338fec59ddefe7e30e942393f267687..9cb13b9f2c3a79685eb8985a965a82dd20a7c31c 100644 (file)
@@ -20,7 +20,6 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.osgi.util.NLS;
 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.model.TmfXmlScenarioHistoryBuilder.ScenarioStatusType;
 import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module.IXmlStateSystemContainer;
 import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
@@ -51,6 +50,7 @@ public class TmfXmlFsm {
     private final boolean fConsuming;
     private boolean fEventConsumed;
     private int fTotalScenarios;
+    private @Nullable TmfXmlScenario fPendingScenario;
 
     /**
      * Factory to create a {@link TmfXmlFsm}
@@ -76,19 +76,31 @@ public class TmfXmlFsm {
         }
 
         // Get the initial state and the preconditions
+        Map<@NonNull String, @NonNull TmfXmlState> statesMap = new HashMap<>();
         String initialState = node.getAttribute(TmfXmlStrings.INITIAL);
-        if (initialState.isEmpty()) {
-            NodeList nodesInitialState = node.getElementsByTagName(TmfXmlStrings.INITIAL);
-            if (nodesInitialState.getLength() == 1) {
-                NodeList nodesTransition = ((Element) nodesInitialState.item(0)).getElementsByTagName(TmfXmlStrings.TRANSITION);
-                if (nodesInitialState.getLength() != 1) {
-                    throw new IllegalArgumentException("initial state : there should be one and only one initial state."); //$NON-NLS-1$
-                }
-                initialState = ((Element) nodesTransition.item(0)).getAttribute(TmfXmlStrings.TARGET);
+        NodeList nodesInitialElement = node.getElementsByTagName(TmfXmlStrings.INITIAL);
+        NodeList nodesInitialStateElement = node.getElementsByTagName(TmfXmlStrings.INITIAL_STATE);
+        if (nodesInitialStateElement.getLength() > 0) {
+            if (!initialState.isEmpty() || nodesInitialElement.getLength() > 0) {
+                Activator.logWarning("Fsm " + id + ": the 'initial' attribute was set or an <initial> element was defined. Only one of the 3 should be used."); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            @NonNull TmfXmlState initial = modelFactory.createState((Element) nodesInitialStateElement.item(0), container, null);
+            statesMap.put(TmfXmlState.INITIAL_STATE_ID, initial);
+            initialState = TmfXmlState.INITIAL_STATE_ID;
+        } else {
+            if (!initialState.isEmpty() && nodesInitialElement.getLength() > 0) {
+                Activator.logWarning("Fsm " + id + " was declared with both 'initial' attribute and <initial> element. Only the 'initial' attribute will be used"); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            if (initialState.isEmpty() && nodesInitialElement.getLength() > 0) {
+                    NodeList nodesTransition = ((Element) nodesInitialElement.item(0)).getElementsByTagName(TmfXmlStrings.TRANSITION);
+                    if (nodesInitialElement.getLength() != 1) {
+                        throw new IllegalArgumentException("initial element : there should be one and only one initial state."); //$NON-NLS-1$
+                    }
+                    initialState = ((Element) nodesTransition.item(0)).getAttribute(TmfXmlStrings.TARGET);
             }
         }
 
-        Map<@NonNull String, @NonNull TmfXmlState> statesMap = new HashMap<>();
+
         // Get the FSM states
         NodeList nodesState = node.getElementsByTagName(TmfXmlStrings.STATE);
         for (int i = 0; i < nodesState.getLength(); i++) {
@@ -266,14 +278,30 @@ public class TmfXmlFsm {
      *
      * @param event
      *            The current event
-     * @param transitionMap
+     * @param testMap
      *            The transitions of the pattern
      */
-    public void handleEvent(ITmfEvent event, Map<String, TmfXmlTransitionValidator> transitionMap) {
+    public void handleEvent(ITmfEvent event, Map<String, TmfXmlTransitionValidator> testMap) {
         setEventConsumed(false);
-        if (!validatePreconditions(event, transitionMap)) {
-            return;
+        boolean isValidInput = handleActiveScenarios(event, testMap);
+        handlePendingScenario(event, isValidInput);
+    }
+
+    /**
+     * Process the active scenario with the ongoing event
+     *
+     * @param event
+     *            The ongoing event
+     * @param testMap
+     *            The map of transition
+     * @return True if the ongoing event validates the preconditions, false otherwise
+     */
+    private boolean handleActiveScenarios(ITmfEvent event, Map<String, TmfXmlTransitionValidator> testMap) {
+        if (!validatePreconditions(event, testMap)) {
+            return false;
         }
+
+        // The event is valid, we can handle the active scenario
         for (Iterator<TmfXmlScenario> currentItr = fActiveScenariosList.iterator(); currentItr.hasNext();) {
             TmfXmlScenario scenario = currentItr.next();
             // Remove inactive scenarios or handle the active ones.
@@ -282,10 +310,35 @@ public class TmfXmlFsm {
             } else {
                 handleScenario(scenario, event);
                 if (fConsuming && isEventConsumed()) {
-                    return;
+                    return true;
                 }
             }
         }
+        // The event is valid but hasn't been consumed. We return true.
+        return true;
+    }
+
+    /**
+     * Handle the pending scenario.
+     *
+     * @param event
+     *            The ongoing event
+     * @param isInputValid
+     *            Either the ongoing event validated the preconditions or not
+     */
+    private void handlePendingScenario(ITmfEvent event, boolean isInputValid) {
+        if (fConsuming && isEventConsumed()) {
+            return;
+        }
+
+        TmfXmlScenario scenario = fPendingScenario;
+        if ((fInitialStateId.equals(TmfXmlState.INITIAL_STATE_ID) || isInputValid) && scenario != null) {
+            handleScenario(scenario, event);
+            if (!scenario.isPending()) {
+                addActiveScenario(scenario);
+                fPendingScenario = null;
+            }
+        }
     }
 
     /**
@@ -300,7 +353,7 @@ public class TmfXmlFsm {
     }
 
     private static void handleScenario(TmfXmlScenario scenario, ITmfEvent event) {
-        if (scenario.isActive()) {
+        if (scenario.isActive() || scenario.isPending()) {
             scenario.handleEvent(event);
         }
     }
@@ -317,12 +370,21 @@ public class TmfXmlFsm {
      */
     public synchronized void createScenario(@Nullable ITmfEvent event, TmfXmlPatternEventHandler eventHandler, boolean force) {
         if (force || isNewScenarioAllowed()) {
-            TmfXmlScenario scenario = new TmfXmlScenario(event, eventHandler, fId, fContainer, fModelFactory);
+            fPendingScenario = new TmfXmlScenario(event, eventHandler, fId, fContainer, fModelFactory);
             fTotalScenarios++;
-            fActiveScenariosList.add(scenario);
         }
     }
 
+    /**
+     * Add a scenario to the active scenario list
+     *
+     * @param scenario
+     *            The scenario
+     */
+    private void addActiveScenario(TmfXmlScenario scenario) {
+        fActiveScenariosList.add(scenario);
+    }
+
     /**
      * Check if we have the right to create a new scenario. A new scenario could
      * be created if it is not the first scenario of an FSM and the FSM is not a
@@ -331,8 +393,7 @@ public class TmfXmlFsm {
      * @return True if the start of a new scenario is allowed, false otherwise
      */
     public synchronized boolean isNewScenarioAllowed() {
-        return fTotalScenarios > 0
-                && !fActiveScenariosList.get(fActiveScenariosList.size() - 1).getScenarioInfos().getStatus().equals(ScenarioStatusType.PENDING)
-                && fInstanceMultipleEnabled;
+        return fTotalScenarios > 0 && fInstanceMultipleEnabled
+                && fPendingScenario == null;
     }
 }
index eeea5781b608e38c15247e554e60b68cfba3bfff..f01664a410682ed12c17bd5230dded520b5bfeba 100644 (file)
@@ -83,7 +83,16 @@ public class TmfXmlScenario {
      * @return True if the scenario is active, false otherwise
      */
     public boolean isActive() {
-        return fScenarioInfo.getStatus().equals(ScenarioStatusType.PENDING) || fScenarioInfo.getStatus().equals(ScenarioStatusType.IN_PROGRESS);
+        return fScenarioInfo.getStatus() == ScenarioStatusType.IN_PROGRESS;
+    }
+
+    /**
+     * Test if the scenario is pending or not
+     *
+     * @return True if the scenario is pending, false otherwise
+     */
+    public boolean isPending() {
+        return fScenarioInfo.getStatus() == ScenarioStatusType.PENDING;
     }
 
     /**
index 12807b4665da06d092cc2b9d99980f0ef22f1e11..55d06f6a5cdfe9e824b858dd12dceda3977f8f79 100644 (file)
@@ -30,6 +30,8 @@ import org.w3c.dom.NodeList;
  */
 public class TmfXmlState {
 
+    /** The initial state ID */
+    public static final String INITIAL_STATE_ID = "#initial"; //$NON-NLS-1$
     private final String fId;
     private final IXmlStateSystemContainer fContainer;
     private final List<TmfXmlStateTransition> fTransitions;
index ee602299db3f8c61e692e61c6bd12666a0a974a8..88a292954b8256478a4d160416b8c144b1427542 100644 (file)
                                        <xs:documentation>Declares a precondition for this fsm. At least one of the preconditions needs to be validated before being able to activate process the fsm. A precondition is a special transition with no target or action. It should contains only conditions that needs to be validated. Only used for fsm.</xs:documentation></xs:annotation></xs:element>
                        <xs:element maxOccurs="1" minOccurs="0" name="initial" type="initialState">
                                <xs:annotation>
-                                       <xs:documentation>Declares the default initial state of this complex state. Must not be specified for an atomic state</xs:documentation></xs:annotation></xs:element>
+                                       <xs:documentation>Declares the default initial state of this complex state. Must not be specified for an atomic state. When define, the scenario will start at the state declared in the target attribute of this initial state's transition.</xs:documentation></xs:annotation></xs:element>
+                       <xs:element maxOccurs="1" minOccurs="0" name="initialState" type="initialState">
+                               <xs:annotation>
+                                       <xs:documentation>Declares the default initial state of this complex state. Must not be specified for an atomic state. When define this initial state is the starting state of the scenario. The scenario will stay at this state until one of its transition is validated.</xs:documentation></xs:annotation></xs:element>
                        <xs:element maxOccurs="unbounded" minOccurs="0" name="state" type="state">
                                <xs:annotation>
                                        <xs:documentation>Declares an state as children of this declared state state.</xs:documentation></xs:annotation></xs:element>
index 3912de93bd4d47c3551cba2222bcc5dae6c34116..94a51744fb0b195fa4fe2084f8c62204937726e1 100644 (file)
@@ -168,4 +168,5 @@ public interface TmfXmlStrings {
     String CONSUMING = "consuming";
     String MAPPING_GROUP = "mappingGroup";
     String ENTRY = "entry";
+    String INITIAL_STATE = "initialState";
 }
\ No newline at end of file
This page took 0.033913 seconds and 5 git commands to generate.