/*******************************************************************************
* 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.internal.tmf.analysis.xml.core.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.module.IXmlStateSystemContainer;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* This class implements a state tree described in XML-defined pattern
*
* @author Jean-Christian Kouame
*/
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;
private @Nullable TmfXmlState fparent;
private List<String> fOnEntryActions;
private List<String> fOnExitActions;
//TODO Sub-state are not yet supported.
private Map<String, TmfXmlState> fChildren;
private @Nullable TmfXmlStateTransition fInitialTransition;
private @Nullable String fInitialStateId;
private @Nullable String fFinalStateId;
private Type fType;
/**
* Enum for the type of state
*/
public enum Type {
/**
* Final state type
*/
FINAL,
/**
* Initial state type
*/
INITIAL,
/**
* Fail state type, the pattern has failed to match
*/
FAIL,
/**
* This is the normal state type, for states that are not the first,
* final or failing state
*/
DEFAULT
}
private TmfXmlState(IXmlStateSystemContainer container, Type type, String id, @Nullable TmfXmlState parent, List<@NonNull TmfXmlStateTransition> transitions, Map<@NonNull String, @NonNull TmfXmlState> children, List<String> onentryActions, List<String> onexitActions) {
fContainer = container;
fType = type;
fId = id;
fparent = parent;
fTransitions = transitions;
fChildren = children;
fOnEntryActions = onentryActions;
fOnExitActions = onexitActions;
}
/**
* Constructor
*
* @param modelFactory
* The factory used to create XML model elements
* @param node
* The XML root of this state
* @param container
* The state system container this state definition belongs to
* @param parent
* The parent state of this state
* @return The new {@link TmfXmlState}
*/
public static TmfXmlState create(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container, @Nullable TmfXmlState parent) {
Type type = getStateType(node);
String id = node.getAttribute(TmfXmlStrings.ID);
List<TmfXmlStateTransition> transitions = getTransitions(modelFactory, container, node);
NodeList nodesOnentry = node.getElementsByTagName(TmfXmlStrings.ONENTRY);
List<String> onentryActions = nodesOnentry.getLength() > 0 ? Arrays.asList(((Element) nodesOnentry.item(0)).getAttribute(TmfXmlStrings.ACTION).split(TmfXmlStrings.AND_SEPARATOR)) : Collections.EMPTY_LIST;
NodeList nodesOnexit = node.getElementsByTagName(TmfXmlStrings.ONEXIT);
List<String> onexitActions = nodesOnexit.getLength() > 0 ? Arrays.asList(((Element) nodesOnexit.item(0)).getAttribute(TmfXmlStrings.ACTION).split(TmfXmlStrings.AND_SEPARATOR)) : Collections.EMPTY_LIST;
TmfXmlState state = new TmfXmlState(container, type, id, parent, transitions, new HashMap<>(), onentryActions, onexitActions);
initState(state, modelFactory, container, node);
return state;
}
private static void getFinalState(TmfXmlState parentState, ITmfXmlModelFactory modelFactory, IXmlStateSystemContainer container, Element node) {
NodeList nodesFinal = node.getElementsByTagName(TmfXmlStrings.FINAL);
String finalStateId = null;
if (nodesFinal.getLength() > 0) {
final Element finalElement = NonNullUtils.checkNotNull((Element) nodesFinal.item(0));
finalStateId = nodesFinal.getLength() > 0 ? finalElement.getAttribute(TmfXmlStrings.ID) : null;
TmfXmlState finalState = modelFactory.createState(finalElement, container, parentState);
parentState.getChildren().put(finalState.getId(), finalState);
}
parentState.fFinalStateId = finalStateId;
}
private static void getSubStates(TmfXmlState parentState, ITmfXmlModelFactory modelFactory, IXmlStateSystemContainer container, Element node) {
String initial = node.getAttribute(TmfXmlStrings.INITIAL);
TmfXmlStateTransition initialTransition = null;
if (initial.isEmpty()) {
NodeList nodesInitial = node.getElementsByTagName(TmfXmlStrings.INITIAL);
if (nodesInitial.getLength() == 1) {
final @NonNull Element transitionElement = NonNullUtils.checkNotNull((Element) ((Element) nodesInitial.item(0)).getElementsByTagName(TmfXmlStrings.TRANSITION).item(0));
initialTransition = modelFactory.createStateTransition(transitionElement, container);
initial = initialTransition.getTarget();
}
}
NodeList nodesState = node.getElementsByTagName(TmfXmlStrings.STATE);
for (int i = 0; i < nodesState.getLength(); i++) {
TmfXmlState child = modelFactory.createState(NonNullUtils.checkNotNull((Element) nodesState.item(i)), container, parentState);
parentState.getChildren().put(child.getId(), child);
if (i == 0 && initial.isEmpty()) {
initial = child.getId();
}
}
parentState.fInitialStateId = initial.isEmpty() ? null : initial;
parentState.fInitialTransition = initialTransition;
}
private static void initState(TmfXmlState state, ITmfXmlModelFactory modelFactory, IXmlStateSystemContainer container, Element node) {
getSubStates(state, modelFactory, container, node);
getFinalState(state, modelFactory, container, node);
}
/**
* Get the List of transitions for this state
*
* @param modelFactory
* The factory used to create XML model elements
* @param node
* The XML root of this state definition
* @return The list of transitions
*/
private static List<@NonNull TmfXmlStateTransition> getTransitions(ITmfXmlModelFactory modelFactory, IXmlStateSystemContainer container, Element node) {
List<@NonNull TmfXmlStateTransition> transitions = new ArrayList<>();
NodeList nodesTransition = node.getElementsByTagName(TmfXmlStrings.TRANSITION);
for (int i = 0; i < nodesTransition.getLength(); i++) {
final Element element = (Element) nodesTransition.item(i);
if (element == null) {
throw new IllegalArgumentException();
}
TmfXmlStateTransition transition = modelFactory.createStateTransition(element, container);
transitions.add(transition);
}
return transitions;
}
/**
* Get the state type from its XML definition
* @param node
* The XML definition of the state
* @return The state type
*/
private static Type getStateType(Element node) {
switch (node.getNodeName()) {
case TmfXmlStrings.FINAL:
return Type.FINAL;
case TmfXmlStrings.INITIAL:
return Type.INITIAL;
case TmfXmlStrings.ABANDON:
return Type.FAIL;
case TmfXmlStrings.STATE:
default:
return Type.DEFAULT;
}
}
/**
* Get the state id
*
* @return The state id
*/
public String getId() {
return fId;
}
/**
* Get the container
*
* @return The container
*/
public IXmlStateSystemContainer getContainer() {
return fContainer;
}
/**
* The list of transitions of this state
*
* @return The list of transitions
*/
public List<TmfXmlStateTransition> getTransitionList() {
return fTransitions;
}
/**
* Get the actions to execute when entering this state, in an array
*
* @return The array of actions
*/
public List<String> getOnEntryActions() {
return fOnEntryActions;
}
/**
* Get the actions to execute when leaving this state, in an array
*
* @return The array of actions
*/
public List<String> getOnExitActions() {
return fOnExitActions;
}
/**
* Get children states of this state into a map
*
* @return The map of children state
*/
public Map<String, TmfXmlState> getChildren() {
return fChildren;
}
/**
* Get the initial transition of this state
*
* @return The initial transition
*/
public @Nullable TmfXmlStateTransition getInitialTransition() {
return fInitialTransition;
}
/**
* Get the initial state ID
*
* @return The initial state ID
*/
public @Nullable String getInitialStateId() {
return fInitialStateId;
}
/**
* Get the final state ID
*
* @return The final state ID
*/
public @Nullable String getFinalStateId() {
return fFinalStateId;
}
/**
* Get the parent state
*
* @return The parent state
*/
public @Nullable TmfXmlState getParent() {
return fparent;
}
/**
* Get the type of this state
*
* @return The type of the state
*/
public Type getType() {
return fType;
}
}