/*******************************************************************************
* Copyright (c) 2014 Ecole Polytechnique de Montreal
*
* 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
*
* Contributors:
* Florian Wininger - Initial API and implementation
******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module.IXmlStateSystemContainer;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module.XmlUtils;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* This Class implement a State Change in the XML-defined state system
*
* <pre>
* example 1: Simple state change
* <stateChange>
* <stateAttribute type="location" value="CurrentThread" />
* <stateAttribute type="constant" value="System_call" />
* <stateValue type="null" />
* </stateChange>
*
* example 2: Conditional state change
* <stateChange>
* <if>
* <condition>
* <stateAttribute type="location" value="CurrentThread" />
* <stateAttribute type="constant" value="System_call" />
* <stateValue type="null" />
* </condition>
* </if>
* <then>
* <stateAttribute type="location" value="CurrentThread" />
* <stateAttribute type="constant" value="Status" />
* <stateValue int="$PROCESS_STATUS_RUN_USERMODE"/>
* </then>
* <else>
* <stateAttribute type="location" value="CurrentThread" />
* <stateAttribute type="constant" value="Status" />
* <stateValue int="$PROCESS_STATUS_RUN_SYSCALL"/>
* </else>
* </stateChange>
* </pre>
*
* @author Florian Wininger
*/
public class TmfXmlStateChange {
private final IXmlStateChange fChange;
private final IXmlStateSystemContainer fContainer;
/**
* Constructor
*
* @param modelFactory
* The factory used to create XML model elements
* @param statechange
* XML node root of this state change
* @param container
* The state system container this state change belongs to
*/
public TmfXmlStateChange(ITmfXmlModelFactory modelFactory, Element statechange, IXmlStateSystemContainer container) {
fContainer = container;
/*
* child nodes is either a list of TmfXmlStateAttributes and
* TmfXmlStateValues, or an if-then-else series of nodes.
*/
Node ifNode = statechange.getElementsByTagName(TmfXmlStrings.IF).item(0);
if (ifNode != null) {
/* the state change has a condition */
fChange = new XmlConditionalChange(modelFactory, statechange);
} else {
/* the state change does not have a condition */
fChange = new XmlStateValueChange(modelFactory, statechange);
}
}
/**
* Execute the state change for an event. If necessary, it validates the
* condition and executes the required change.
*
* @param event
* The event to process
* @param scenarioInfo
* The active scenario details. The value should be null if there
* no scenario.
* @throws AttributeNotFoundException
* Pass through the exception it received
* @throws TimeRangeException
* Pass through the exception it received
* @throws StateValueTypeException
* Pass through the exception it received
*/
public void handleEvent(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException, StateValueTypeException, TimeRangeException {
fChange.handleEvent(event, scenarioInfo);
}
@Override
public String toString() {
return "TmfXmlStateChange: " + fChange; //$NON-NLS-1$
}
/* Interface for both private classes to handle the event */
private interface IXmlStateChange {
void handleEvent(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException, StateValueTypeException, TimeRangeException;
}
/**
* Conditional state change with a condition to verify
*/
private class XmlConditionalChange implements IXmlStateChange {
private final ITmfXmlCondition fCondition;
private final TmfXmlStateChange fThenChange;
private final @Nullable TmfXmlStateChange fElseChange;
public XmlConditionalChange(ITmfXmlModelFactory modelFactory, Element statechange) {
/*
* The if node exists, it has been verified before calling this
*/
Node ifNode = statechange.getElementsByTagName(TmfXmlStrings.IF).item(0);
if (ifNode == null) {
throw new IllegalArgumentException();
}
fCondition = modelFactory.createCondition((Element) ifNode, fContainer);
Node thenNode = statechange.getElementsByTagName(TmfXmlStrings.THEN).item(0);
if (thenNode == null) {
throw new IllegalArgumentException("Conditional state change: there should be a then clause."); //$NON-NLS-1$
}
fThenChange = modelFactory.createStateChange((Element) thenNode, fContainer);
Node elseNode = statechange.getElementsByTagName(TmfXmlStrings.ELSE).item(0);
if (elseNode != null) {
fElseChange = modelFactory.createStateChange((Element) elseNode, fContainer);
} else {
fElseChange = null;
}
}
@Override
public void handleEvent(@NonNull ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException, StateValueTypeException, TimeRangeException {
TmfXmlStateChange toExecute = fThenChange;
if (!fCondition.test(event, scenarioInfo)) {
toExecute = fElseChange;
}
if (toExecute == null) {
return;
}
toExecute.handleEvent(event, scenarioInfo);
}
@Override
public String toString() {
return "Condition: " + fCondition; //$NON-NLS-1$
}
}
/**
* State change with no condition
*/
private class XmlStateValueChange implements IXmlStateChange {
private final ITmfXmlStateValue fValue;
public XmlStateValueChange(ITmfXmlModelFactory modelFactory, Element statechange) {
List<@Nullable Element> childElements = XmlUtils.getChildElements(statechange);
/*
* Last child element is the state value, the others are attributes
* to reach to value to set
*/
Element stateValueElement = childElements.remove(childElements.size() - 1);
if (stateValueElement == null) {
throw new IllegalStateException();
}
List<ITmfXmlStateAttribute> attributes = new ArrayList<>();
for (Element element : childElements) {
if (element == null || !element.getNodeName().equals(TmfXmlStrings.STATE_ATTRIBUTE)) {
throw new IllegalArgumentException("TmfXmlStateChange: a state change must have only TmfXmlStateAttribute elements before the state value"); //$NON-NLS-1$
}
ITmfXmlStateAttribute attribute = modelFactory.createStateAttribute(element, fContainer);
attributes.add(attribute);
}
if (attributes.isEmpty()) {
throw new IllegalArgumentException("TmfXmlStateChange: a state change must have at least one TmfXmlStateAttribute element before the state value"); //$NON-NLS-1$
}
fValue = modelFactory.createStateValue(stateValueElement, fContainer, attributes);
}
@Override
public void handleEvent(@NonNull ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException, StateValueTypeException, TimeRangeException {
fValue.handleEvent(event, scenarioInfo);
}
@Override
public String toString() {
return "Value: " + fValue; //$NON-NLS-1$
}
}
}