00e343d0e48fe7e70a39b1388723c4b07175b163
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.analysis.xml.core / src / org / eclipse / tracecompass / internal / tmf / analysis / xml / core / model / TmfXmlFsm.java
1 /*******************************************************************************
2 * Copyright (c) 2016 Ecole Polytechnique de Montreal, Ericsson
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 package org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model;
10
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.eclipse.tracecompass.common.core.NonNullUtils;
21 import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.TmfXmlScenarioHistoryBuilder.ScenarioStatusType;
22 import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module.IXmlStateSystemContainer;
23 import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
24 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
25 import org.w3c.dom.Element;
26 import org.w3c.dom.NodeList;
27
28 import com.google.common.collect.ImmutableList;
29 import com.google.common.collect.ImmutableMap;
30
31 /**
32 * This Class implements a state machine (FSM) tree in the XML-defined state
33 * system.
34 *
35 * @author Jean-Christian Kouame
36 */
37 public class TmfXmlFsm {
38
39 private final Map<String, TmfXmlState> fStatesMap;
40 private final List<TmfXmlScenario> fActiveScenariosList;
41 private final List<TmfXmlBasicTransition> fPreconditions;
42 private final String fId;
43 private final ITmfXmlModelFactory fModelFactory;
44 private final IXmlStateSystemContainer fContainer;
45 private final String fFinalStateId;
46 private final String fAbandonStateId;
47 private final boolean fInstanceMultipleEnabled;
48 private final String fInitialStateId;
49 private int fTotalScenarios;
50
51 /**
52 * Factory to create a {@link TmfXmlFsm}
53 *
54 * @param modelFactory
55 * The factory used to create XML model elements
56 * @param node
57 * The XML root of this fsm
58 * @param container
59 * The state system container this fsm belongs to
60 * @return The new {@link TmfXmlFsm}
61 */
62 public static TmfXmlFsm create(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container) {
63 String id = node.getAttribute(TmfXmlStrings.ID);
64 boolean instanceMultipleEnabled = node.getAttribute(TmfXmlStrings.MULTIPLE).isEmpty() ? true : Boolean.parseBoolean(node.getAttribute(TmfXmlStrings.MULTIPLE));
65 final List<@NonNull TmfXmlBasicTransition> preconditions = new ArrayList<>();
66
67 // Get the preconditions
68 NodeList nodesPreconditions = node.getElementsByTagName(TmfXmlStrings.PRECONDITION);
69 for (int i = 0; i < nodesPreconditions.getLength(); i++) {
70 preconditions.add(new TmfXmlBasicTransition(((Element) NonNullUtils.checkNotNull(nodesPreconditions.item(i)))));
71 }
72
73 // Get the initial state and the preconditions
74 String initialState = node.getAttribute(TmfXmlStrings.INITIAL);
75 if (initialState.isEmpty()) {
76 NodeList nodesInitialState = node.getElementsByTagName(TmfXmlStrings.INITIAL);
77 if (nodesInitialState.getLength() == 1) {
78 NodeList nodesTransition = ((Element) nodesInitialState.item(0)).getElementsByTagName(TmfXmlStrings.TRANSITION);
79 if (nodesInitialState.getLength() != 1) {
80 throw new IllegalArgumentException("initial state : there should be one and only one initial state."); //$NON-NLS-1$
81 }
82 initialState = ((Element) nodesTransition.item(0)).getAttribute(TmfXmlStrings.TARGET);
83 }
84 }
85
86 Map<@NonNull String, @NonNull TmfXmlState> statesMap = new HashMap<>();
87 // Get the FSM states
88 NodeList nodesState = node.getElementsByTagName(TmfXmlStrings.STATE);
89 for (int i = 0; i < nodesState.getLength(); i++) {
90 Element element = (Element) NonNullUtils.checkNotNull(nodesState.item(i));
91 TmfXmlState state = modelFactory.createState(element, container, null);
92 statesMap.put(state.getId(), state);
93
94 // If the initial state was not already set, we use the first state
95 // declared in the fsm description as initial state
96 if (initialState.isEmpty()) {
97 initialState = state.getId();
98 }
99 }
100
101 if (initialState.isEmpty()) {
102 throw new IllegalStateException("No initial state has been declared in fsm " + id); //$NON-NLS-1$
103 }
104
105 // Get the FSM final state
106 String finalStateId = TmfXmlStrings.NULL;
107 NodeList nodesFinalState = node.getElementsByTagName(TmfXmlStrings.FINAL);
108 if (nodesFinalState.getLength() == 1) {
109 final Element finalElement = NonNullUtils.checkNotNull((Element) nodesFinalState.item(0));
110 finalStateId = finalElement.getAttribute(TmfXmlStrings.ID);
111 if (!finalStateId.isEmpty()) {
112 TmfXmlState finalState = modelFactory.createState(finalElement, container, null);
113 statesMap.put(finalState.getId(), finalState);
114 }
115 }
116
117 // Get the FSM abandon state
118 String abandonStateId = TmfXmlStrings.NULL;
119 NodeList nodesAbandonState = node.getElementsByTagName(TmfXmlStrings.ABANDON_STATE);
120 if (nodesAbandonState.getLength() == 1) {
121 final Element abandonElement = NonNullUtils.checkNotNull((Element) nodesAbandonState.item(0));
122 abandonStateId = abandonElement.getAttribute(TmfXmlStrings.ID);
123 if (!abandonStateId.isEmpty()) {
124 TmfXmlState abandonState = modelFactory.createState(abandonElement, container, null);
125 statesMap.put(abandonState.getId(), abandonState);
126 }
127 }
128 return new TmfXmlFsm(modelFactory, container, id, instanceMultipleEnabled, initialState, finalStateId, abandonStateId, preconditions, statesMap);
129 }
130
131 private TmfXmlFsm(ITmfXmlModelFactory modelFactory, IXmlStateSystemContainer container, String id, boolean multiple,
132 String initialState, String finalState, String abandonState, List<TmfXmlBasicTransition> preconditions,
133 Map<String, TmfXmlState> states) {
134 fModelFactory = modelFactory;
135 fTotalScenarios = 0;
136 fContainer = container;
137 fId = id;
138 fInstanceMultipleEnabled = multiple;
139 fInitialStateId = initialState;
140 fFinalStateId = finalState;
141 fAbandonStateId = abandonState;
142 fPreconditions = ImmutableList.copyOf(preconditions);
143 fStatesMap = ImmutableMap.copyOf(states);
144 fActiveScenariosList = new ArrayList<>();
145 }
146
147 /**
148 * Get the fsm ID
149 *
150 * @return the id of this fsm
151 */
152 public String getId() {
153 return fId;
154 }
155
156 /**
157 * Get the initial state ID of this fsm
158 *
159 * @return the id of the initial state of this finite state machine
160 */
161 public String getInitialStateId() {
162 return fInitialStateId;
163 }
164
165 /**
166 * Get the final state ID of this fsm
167 *
168 * @return the id of the final state of this finite state machine
169 */
170 public String getFinalStateId() {
171 return fFinalStateId;
172 }
173
174 /**
175 * Get the abandon state ID fo this fsm
176 *
177 * @return the id of the abandon state of this finite state machine
178 */
179 public String getAbandonStateId() {
180 return fAbandonStateId;
181 }
182
183 /**
184 * Get the states table of this fsm in map
185 *
186 * @return The map containing all state definition for this fsm
187 */
188 public Map<String, TmfXmlState> getStatesMap() {
189 return Collections.unmodifiableMap(fStatesMap);
190 }
191
192 /**
193 * Process the active event and determine the next step of this fsm
194 *
195 * @param event
196 * The event to process
197 * @param tests
198 * The list of possible transitions of the state machine
199 * @param scenarioInfo
200 * The active scenario details.
201 * @return A pair containing the next state of the state machine and the
202 * actions to execute
203 */
204 public @Nullable TmfXmlStateTransition next(ITmfEvent event, Map<String, TmfXmlTransitionValidator> tests, TmfXmlScenarioInfo scenarioInfo) {
205 boolean matched = false;
206 TmfXmlStateTransition stateTransition = null;
207 TmfXmlState state = NonNullUtils.checkNotNull(fStatesMap.get(scenarioInfo.getActiveState()));
208 for (int i = 0; i < state.getTransitionList().size() && !matched; i++) {
209 stateTransition = state.getTransitionList().get(i);
210 matched = stateTransition.test(event, scenarioInfo, tests);
211 }
212 return matched ? stateTransition : null;
213 }
214
215
216
217 /**
218 * Validate the preconditions of this fsm. If not validate, the fsm will
219 * skip the active event.
220 *
221 * @param event
222 * The current event
223 * @param tests
224 * The transition inputs
225 * @return True if one of the precondition is validated, false otherwise
226 */
227 private boolean validatePreconditions(ITmfEvent event, Map<String, TmfXmlTransitionValidator> tests) {
228 if (fPreconditions.isEmpty()) {
229 return true;
230 }
231 for (TmfXmlBasicTransition precondition : fPreconditions) {
232 if (precondition.test(event, null, tests)) {
233 return true;
234 }
235 }
236 return false;
237 }
238
239 /**
240 * Handle the current event
241 *
242 * @param event
243 * The current event
244 * @param transitionMap
245 * The transitions of the pattern
246 */
247 public void handleEvent(ITmfEvent event, Map<String, TmfXmlTransitionValidator> transitionMap) {
248 if (!validatePreconditions(event, transitionMap)) {
249 return;
250 }
251 for (Iterator<TmfXmlScenario> currentItr = fActiveScenariosList.iterator(); currentItr.hasNext();) {
252 TmfXmlScenario scenario = currentItr.next();
253 // Remove inactive scenarios or handle the active ones.
254 if (!scenario.isActive()) {
255 currentItr.remove();
256 } else {
257 handleScenario(scenario, event);
258 }
259 }
260 }
261
262 /**
263 * Abandon all ongoing scenarios
264 */
265 public void dispose() {
266 for (TmfXmlScenario scenario : fActiveScenariosList) {
267 if (scenario.isActive()) {
268 scenario.cancel();
269 }
270 }
271 }
272
273 private static void handleScenario(TmfXmlScenario scenario, ITmfEvent event) {
274 if (scenario.isActive()) {
275 scenario.handleEvent(event);
276 }
277 }
278
279 /**
280 * Create a new scenario of this fsm
281 *
282 * @param event
283 * The current event, null if not
284 * @param eventHandler
285 * The event handler this fsm belongs
286 * @param force
287 * True to force the creation of the scenario, false otherwise
288 */
289 public synchronized void createScenario(@Nullable ITmfEvent event, TmfXmlPatternEventHandler eventHandler, boolean force) {
290 if (force || isNewScenarioAllowed()) {
291 TmfXmlScenario scenario = new TmfXmlScenario(event, eventHandler, fId, fContainer, fModelFactory);
292 fTotalScenarios++;
293 fActiveScenariosList.add(scenario);
294 }
295 }
296
297 /**
298 * Check if we have the right to create a new scenario. A new scenario could
299 * be created if it is not the first scenario of an FSM and the FSM is not a
300 * singleton and the status of the last created scenario is not PENDING.
301 *
302 * @return True if the start of a new scenario is allowed, false otherwise
303 */
304 public synchronized boolean isNewScenarioAllowed() {
305 return fTotalScenarios > 0
306 && !fActiveScenariosList.get(fActiveScenariosList.size() - 1).getScenarioInfos().getStatus().equals(ScenarioStatusType.PENDING)
307 && fInstanceMultipleEnabled;
308 }
309 }
This page took 0.037351 seconds and 4 git commands to generate.