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