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