/*******************************************************************************
* Copyright (c) 2014 École Polytechnique de Montréal
*
* 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:
* Geneviève Bastien - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.readwrite;
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.Activator;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.ITmfXmlModelFactory;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.ITmfXmlStateAttribute;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.TmfXmlScenarioInfo;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.TmfXmlStateValue;
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.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.statesystem.core.StateSystemBuilderUtils;
import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.w3c.dom.Element;
/**
* Implements a state value in a read write mode. See {@link TmfXmlStateValue}
* for the syntax of the state value.
*
* In read/write mode, a state value can be considered as an assignation where
* the state value is assigned to the quark represented by the state attributes
*
* @author Geneviève Bastien
*/
public class TmfXmlReadWriteStateValue extends TmfXmlStateValue {
private static final String ILLEGAL_STATE_EXCEPTION_MESSAGE = "The state system hasn't been initialized yet"; //$NON-NLS-1$
/**
* Constructor where the path to the value is a list of state attributes
*
* @param modelFactory
* The factory used to create XML model elements
* @param node
* The state value XML element
* @param container
* The state system container this state value belongs to
* @param attributes
* The attributes representing the path to this value
*/
public TmfXmlReadWriteStateValue(TmfXmlReadWriteModelFactory modelFactory, Element node, IXmlStateSystemContainer container, List<ITmfXmlStateAttribute> attributes) {
this(modelFactory, node, container, attributes, null);
}
/**
* Constructor where the path to the value is an event field
*
* @param modelFactory
* The factory used to create XML model elements
* @param node
* The state value XML element
* @param container
* The state system container this state value belongs to
* @param eventField
* The event field where to get the value
*/
public TmfXmlReadWriteStateValue(TmfXmlReadWriteModelFactory modelFactory, Element node, IXmlStateSystemContainer container, String eventField) {
this(modelFactory, node, container, new ArrayList<ITmfXmlStateAttribute>(), eventField);
}
private TmfXmlReadWriteStateValue(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container, List<ITmfXmlStateAttribute> attributes, @Nullable String eventField) {
super(modelFactory, node, container, attributes, eventField);
}
@Override
protected @Nullable ITmfStateSystemBuilder getStateSystem() {
return (ITmfStateSystemBuilder) super.getStateSystem();
}
@Override
protected TmfXmlStateValueBase initializeStateValue(ITmfXmlModelFactory modelFactory, Element node) {
TmfXmlStateValueBase stateValueType = null;
/* Process the XML Element state value */
String type = node.getAttribute(TmfXmlStrings.TYPE);
String value = getSsContainer().getAttributeValue(node.getAttribute(TmfXmlStrings.VALUE));
if (value == null && getStackType().equals(ValueTypeStack.NULL)) {
throw new IllegalStateException();
}
List<@Nullable Element> children = XmlUtils.getChildElements(node);
List<ITmfXmlStateAttribute> childAttributes = new ArrayList<>();
for (Element child : children) {
if (child == null) {
continue;
}
ITmfXmlStateAttribute queryAttribute = modelFactory.createStateAttribute(child, getSsContainer());
childAttributes.add(queryAttribute);
}
switch (type) {
case TmfXmlStrings.TYPE_INT: {
/* Integer value */
ITmfStateValue stateValue = value != null && !value.isEmpty() ?
TmfStateValue.newValueInt(Integer.parseInt(value)) : TmfStateValue.nullValue();
stateValueType = new TmfXmlStateValueTmf(stateValue, childAttributes);
break;
}
case TmfXmlStrings.TYPE_LONG: {
/* Long value */
ITmfStateValue stateValue = value != null && !value.isEmpty() ?
TmfStateValue.newValueLong(Long.parseLong(value)) : TmfStateValue.nullValue();
stateValueType = new TmfXmlStateValueTmf(stateValue, childAttributes);
break;
}
case TmfXmlStrings.TYPE_STRING: {
/* String value */
ITmfStateValue stateValue = value != null ?
TmfStateValue.newValueString(value) : TmfStateValue.nullValue();
stateValueType = new TmfXmlStateValueTmf(stateValue, childAttributes);
break;
}
case TmfXmlStrings.TYPE_NULL: {
/* Null value */
ITmfStateValue stateValue = TmfStateValue.nullValue();
stateValueType = new TmfXmlStateValueTmf(stateValue, childAttributes);
break;
}
case TmfXmlStrings.EVENT_FIELD:
/* Event field */
if (value == null) {
throw new IllegalStateException("Event field name cannot be null"); //$NON-NLS-1$
}
stateValueType = new TmfXmlStateValueEventField(value);
break;
case TmfXmlStrings.TYPE_EVENT_NAME:
/* The value is the event name */
stateValueType = new TmfXmlStateValueEventName();
break;
case TmfXmlStrings.TYPE_DELETE:
/* Deletes the value of an attribute */
stateValueType = new TmfXmlStateValueDelete();
break;
case TmfXmlStrings.TYPE_QUERY:
/* Value is the result of a query */
stateValueType = new TmfXmlStateValueQuery(childAttributes);
break;
default:
throw new IllegalArgumentException(String.format("TmfXmlStateValue constructor: unexpected element %s for stateValue type", type)); //$NON-NLS-1$
}
return stateValueType;
}
// ----------------------------------------------------------
// Internal state value classes for the different types
// ----------------------------------------------------------
/**
* Base class for all state value. Contain default methods to handle event,
* process or increment the value
*/
protected abstract class TmfXmlStateValueTypeReadWrite extends TmfXmlStateValueBase {
@Override
public final void handleEvent(ITmfEvent event, int quark, long timestamp, @Nullable TmfXmlScenarioInfo scenarioInfo) throws StateValueTypeException, TimeRangeException, AttributeNotFoundException {
if (isIncrement()) {
incrementValue(event, quark, timestamp, scenarioInfo);
} else {
ITmfStateValue value = getValue(event, scenarioInfo);
processValue(quark, timestamp, value);
}
}
@Override
protected void processValue(int quark, long timestamp, ITmfStateValue value) throws AttributeNotFoundException, TimeRangeException, StateValueTypeException {
ITmfStateSystemBuilder ss = getStateSystem();
if (ss == null) {
throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_MESSAGE);
}
switch (getStackType()) {
case POP:
ss.popAttribute(timestamp, quark);
break;
case PUSH:
ss.pushAttribute(timestamp, value, quark);
break;
case NULL:
case PEEK:
default:
if (isUpdate()) {
ss.updateOngoingState(value, quark);
} else {
ss.modifyAttribute(timestamp, value, quark);
}
break;
}
}
@Override
protected void incrementValue(ITmfEvent event, int quark, long timestamp, @Nullable TmfXmlScenarioInfo scenarioInfo) throws StateValueTypeException, TimeRangeException, AttributeNotFoundException {
ITmfStateSystemBuilder ss = getStateSystem();
if (ss == null) {
throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_MESSAGE);
}
StateSystemBuilderUtils.incrementAttributeInt(ss, timestamp, quark, 1);
}
}
private static @Nullable ITmfStateValue incrementByType(int quark, ITmfStateSystem ss, ITmfStateValue stateValue) {
ITmfStateValue value = null;
switch (stateValue.getType()) {
case LONG: {
long incrementLong = stateValue.unboxLong();
ITmfStateValue currentState = ss.queryOngoingState(quark);
long currentValue = (currentState.isNull() ? 0 : currentState.unboxLong());
value = TmfStateValue.newValueLong(incrementLong + currentValue);
return value;
}
case INTEGER: {
int increment = stateValue.unboxInt();
ITmfStateValue currentState = ss.queryOngoingState(quark);
int currentValue = (currentState.isNull() ? 0 : currentState.unboxInt());
value = TmfStateValue.newValueInt(increment + currentValue);
return value;
}
case DOUBLE:
case NULL:
case STRING:
case CUSTOM:
default:
}
return value;
}
/* This state value uses a constant value, defined in the XML */
private class TmfXmlStateValueTmf extends TmfXmlStateValueTypeReadWrite {
private final ITmfStateValue fValue;
private final List<ITmfXmlStateAttribute> fAttributesValue;
public TmfXmlStateValueTmf(ITmfStateValue value, List<ITmfXmlStateAttribute> attributes) {
fValue = value;
fAttributesValue = attributes;
}
@Override
public ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) {
try {
switch (getStackType()) {
case PEEK:
return peek(event, scenarioInfo);
case PUSH:
case NULL:
case POP:
default:
return fValue;
}
} catch (AttributeNotFoundException | StateSystemDisposedException e) {
Activator.logError("Query stack failed"); //$NON-NLS-1$
return TmfStateValue.nullValue();
}
}
/**
* @param event
* The ongoing event
* @param scenarioInfo
* The active scenario details. The value should be null if
* there no scenario.
* @return The value value at the top of the stack without removing it
* @throws AttributeNotFoundException
* If the do not exist
* @throws StateSystemDisposedException
* If the state system is disposed
*/
private ITmfStateValue peek(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException, StateSystemDisposedException {
int quarkQuery = IXmlStateSystemContainer.ROOT_QUARK;
ITmfStateSystemBuilder ss = getStateSystem();
if (ss == null) {
throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_MESSAGE);
}
if (event == null) {
throw new IllegalStateException("The event should not be null at this point."); //$NON-NLS-1$
}
for (ITmfXmlStateAttribute attribute : fAttributesValue) {
quarkQuery = attribute.getAttributeQuark(event, quarkQuery, scenarioInfo);
if (quarkQuery == IXmlStateSystemContainer.ERROR_QUARK) {
/*
* the query is not valid, we stop the state change
*/
return TmfStateValue.nullValue();
}
}
final long ts = event.getTimestamp().toNanos();
@Nullable ITmfStateInterval stackTopInterval = StateSystemUtils.querySingleStackTop(ss, ts, quarkQuery);
final ITmfStateValue value = stackTopInterval != null ? stackTopInterval.getStateValue() : null;
return value != null ? value : TmfStateValue.nullValue();
}
@Override
public void incrementValue(ITmfEvent event, int quark, long timestamp, @Nullable TmfXmlScenarioInfo scenarioInfo) throws StateValueTypeException, TimeRangeException, AttributeNotFoundException {
ITmfStateSystem ss = getStateSystem();
if (ss == null) {
throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_MESSAGE);
}
ITmfStateValue value = incrementByType(quark, ss, fValue);
if (value != null) {
processValue(quark, timestamp, value);
} else {
Activator.logWarning("TmfXmlStateValue: The increment value is not a number type"); //$NON-NLS-1$
}
}
@Override
public String toString() {
return "Value=" + fValue; //$NON-NLS-1$
}
}
/* The state value uses the value of an event field */
private class TmfXmlStateValueEventField extends TmfXmlStateValueTypeReadWrite {
private final String fFieldName;
public TmfXmlStateValueEventField(String field) {
fFieldName = field;
}
@Override
public ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) {
if (event == null) {
Activator.logWarning("XML State value: requested an event field, but event is null"); //$NON-NLS-1$
return TmfStateValue.nullValue();
}
return getEventFieldValue(event, fFieldName);
}
@Override
public void incrementValue(ITmfEvent event, int quark, long timestamp, @Nullable TmfXmlScenarioInfo scenarioInfo) throws StateValueTypeException, TimeRangeException, AttributeNotFoundException {
ITmfStateSystem ss = getSsContainer().getStateSystem();
if (ss == null) {
throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_MESSAGE);
}
ITmfStateValue incrementValue = getValue(event, scenarioInfo);
ITmfStateValue value = incrementByType(quark, ss, incrementValue);
if (value != null) {
processValue(quark, timestamp, value);
} else {
Activator.logWarning(String.format("TmfXmlStateValue: The event field increment %s is not a number type but a %s", fFieldName, incrementValue.getType())); //$NON-NLS-1$
}
}
@Override
public String toString() {
return "Event Field=" + fFieldName; //$NON-NLS-1$
}
}
/* The state value is the event name */
private class TmfXmlStateValueEventName extends TmfXmlStateValueTypeReadWrite {
@Override
public @NonNull ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException {
if (event == null) {
Activator.logWarning("XML State value: request event name, but event is null"); //$NON-NLS-1$
return TmfStateValue.nullValue();
}
return TmfStateValue.newValueString(event.getName());
}
@Override
public String toString() {
return "Event name"; //$NON-NLS-1$
}
}
/* The state value deletes an attribute */
private class TmfXmlStateValueDelete extends TmfXmlStateValueTypeReadWrite {
@Override
public @NonNull ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException {
return TmfStateValue.nullValue();
}
@Override
protected void processValue(int quark, long timestamp, ITmfStateValue value) throws TimeRangeException, AttributeNotFoundException {
ITmfStateSystem ss = getStateSystem();
if (!(ss instanceof ITmfStateSystemBuilder)) {
throw new IllegalStateException("incrementValue should never be called when not building the state system"); //$NON-NLS-1$
}
ITmfStateSystemBuilder builder = (ITmfStateSystemBuilder) ss;
builder.removeAttribute(timestamp, quark);
}
@Override
public String toString() {
return "Delete"; //$NON-NLS-1$
}
}
/* The state value uses the result of a query */
private class TmfXmlStateValueQuery extends TmfXmlStateValueTypeReadWrite {
private final List<ITmfXmlStateAttribute> fQueryValue;
public TmfXmlStateValueQuery(List<ITmfXmlStateAttribute> childAttributes) {
fQueryValue = childAttributes;
}
@Override
public ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException {
/* Query the state system for the value */
ITmfStateValue value = TmfStateValue.nullValue();
int quarkQuery = IXmlStateSystemContainer.ROOT_QUARK;
ITmfStateSystem ss = getStateSystem();
if (ss == null) {
throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_MESSAGE);
}
for (ITmfXmlStateAttribute attribute : fQueryValue) {
quarkQuery = attribute.getAttributeQuark(event, quarkQuery, scenarioInfo);
if (quarkQuery == IXmlStateSystemContainer.ERROR_QUARK) {
/* the query is not valid, we stop the state change */
break;
}
}
/*
* the query can fail : for example, if a value is requested but has
* not been set yet
*/
if (quarkQuery != IXmlStateSystemContainer.ERROR_QUARK) {
value = ss.queryOngoingState(quarkQuery);
}
return value;
}
@Override
public void incrementValue(ITmfEvent event, int quark, long timestamp, @Nullable TmfXmlScenarioInfo scenarioInfo) throws StateValueTypeException, TimeRangeException, AttributeNotFoundException {
ITmfStateSystem ss = getStateSystem();
if (ss == null) {
throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_MESSAGE);
}
ITmfStateValue incrementValue = getValue(event, scenarioInfo);
ITmfStateValue value = incrementByType(quark, ss, incrementValue);
if (value != null) {
processValue(quark, timestamp, value);
} else {
Activator.logWarning("TmfXmlStateValue: The query result increment is not a number type"); //$NON-NLS-1$
}
}
@Override
public String toString() {
return "Query=" + fQueryValue; //$NON-NLS-1$
}
}
}