/*******************************************************************************
* Copyright (c) 2014, 2015 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 static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
import java.util.LinkedList;
import java.util.List;
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.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.exceptions.AttributeNotFoundException;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
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.eclipse.tracecompass.tmf.core.event.ITmfEventField;
import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
import org.w3c.dom.Element;
/**
* This Class implements a single attribute value in the XML-defined state
* system.
*
* <pre>
* Examples:
* <stateAttribute type="constant" value="Threads" />
* <stateAttribute type="query" />
* <stateAttribute type="constant" value="CPUs" />
* <stateAttribute type="eventField" value="cpu" />
* <stateAttribute type="constant" value="Current_thread" />
* </attribute>
* </pre>
*
* @author Florian Wininger
*/
public abstract class TmfXmlStateAttribute implements ITmfXmlStateAttribute {
private enum StateAttributeType {
NONE,
CONSTANT,
EVENTFIELD,
QUERY,
LOCATION,
SELF,
EVENTNAME
}
private final String CURRENT_STATE = "#currentState"; //$NON-NLS-1$
private final String CURRENT_SCENARIO = "#CurrentScenario"; //$NON-NLS-1$
/** Type of attribute */
private final StateAttributeType fType;
/** Attribute's name */
private final @Nullable String fName;
/** List of attributes for a query */
private final List<ITmfXmlStateAttribute> fQueryList = new LinkedList<>();
private final IXmlStateSystemContainer fContainer;
/**
* Constructor
*
* @param modelFactory
* The factory used to create XML model elements
* @param attribute
* XML element of the attribute
* @param container
* The state system container this state attribute belongs to
*/
protected TmfXmlStateAttribute(ITmfXmlModelFactory modelFactory, Element attribute, IXmlStateSystemContainer container) {
fContainer = container;
switch (attribute.getAttribute(TmfXmlStrings.TYPE)) {
case TmfXmlStrings.TYPE_CONSTANT:
fType = StateAttributeType.CONSTANT;
fName = getAttributeName(attribute);
break;
case TmfXmlStrings.EVENT_FIELD:
fType = StateAttributeType.EVENTFIELD;
fName = getAttributeName(attribute);
break;
case TmfXmlStrings.TYPE_LOCATION:
fType = StateAttributeType.LOCATION;
fName = getAttributeName(attribute);
break;
case TmfXmlStrings.TYPE_QUERY:
List<@Nullable Element> childElements = XmlUtils.getChildElements(attribute);
for (Element subAttributeNode : childElements) {
if (subAttributeNode == null) {
continue;
}
ITmfXmlStateAttribute subAttribute = modelFactory.createStateAttribute(subAttributeNode, fContainer);
fQueryList.add(subAttribute);
}
fType = StateAttributeType.QUERY;
fName = null;
break;
case TmfXmlStrings.TYPE_EVENT_NAME:
fType = StateAttributeType.EVENTNAME;
fName = getAttributeName(attribute);
break;
case TmfXmlStrings.NULL:
fType = StateAttributeType.NONE;
fName = null;
break;
case TmfXmlStrings.TYPE_SELF:
fType = StateAttributeType.SELF;
fName = null;
break;
default:
throw new IllegalArgumentException("TmfXmlStateAttribute constructor: The XML element is not of the right type"); //$NON-NLS-1$
}
}
private String getAttributeName(Element attribute) {
return fContainer.getAttributeValue(attribute.getAttribute(TmfXmlStrings.VALUE)).intern();
}
@Override
public int getAttributeQuark(int startQuark, @Nullable TmfXmlScenarioInfo scenarioInfo) {
return getAttributeQuark(null, startQuark, scenarioInfo);
}
/**
* Basic quark-retrieving method. Pass an attribute in parameter as an array
* of strings, the matching quark will be returned. If the attribute does
* not exist, it will add the quark to the state system if the context
* allows it.
*
* See {@link ITmfStateSystemBuilder#getQuarkAbsoluteAndAdd(String...)}
*
* @param path
* Full path to the attribute
* @return The quark for this attribute
* @throws AttributeNotFoundException
* The attribute does not exist and cannot be added
*/
protected abstract int getQuarkAbsoluteAndAdd(String... path) throws AttributeNotFoundException;
/**
* Quark-retrieving method, but the attribute is queried starting from the
* startNodeQuark. If the attribute does not exist, it will add it to the
* state system if the context allows it.
*
* See {@link ITmfStateSystemBuilder#getQuarkRelativeAndAdd(int, String...)}
*
* @param startNodeQuark
* The quark of the attribute from which 'path' originates.
* @param path
* Relative path to the attribute
* @return The quark for this attribute
* @throws AttributeNotFoundException
* The attribute does not exist and cannot be added
*/
protected abstract int getQuarkRelativeAndAdd(int startNodeQuark, String... path) throws AttributeNotFoundException;
/**
* Get the state system associated with this attribute's container
*
* @return The state system associated with this state attribute
*/
protected @Nullable ITmfStateSystem getStateSystem() {
return fContainer.getStateSystem();
}
@Override
public int getAttributeQuark(@Nullable ITmfEvent event, int startQuark, @Nullable TmfXmlScenarioInfo scenarioInfo) {
ITmfStateSystem ss = getStateSystem();
if (ss == null) {
throw new IllegalStateException("The state system hasn't been initialized yet"); //$NON-NLS-1$
}
String name = nullToEmptyString(fName);
if (name.length() > 0 && name.charAt(0) == '#' && scenarioInfo == null) {
throw new IllegalStateException("XML Attribute needs " + fName + " but the data is not available."); //$NON-NLS-1$//$NON-NLS-2$
}
name = name.equals(CURRENT_STATE) ? checkNotNull(scenarioInfo).getActiveState()
: fName;
try {
switch (fType) {
case CONSTANT: {
int quark;
if (name == null) {
throw new IllegalStateException("Invalid attribute name"); //$NON-NLS-1$
}
if (name.equals(CURRENT_SCENARIO)) {
return checkNotNull(scenarioInfo).getQuark();
}
if (startQuark == IXmlStateSystemContainer.ROOT_QUARK) {
quark = getQuarkAbsoluteAndAdd(name);
} else {
quark = getQuarkRelativeAndAdd(startQuark, name);
}
return quark;
}
case EVENTFIELD: {
int quark = IXmlStateSystemContainer.ERROR_QUARK;
if (event == null) {
Activator.logWarning("XML State attribute: looking for an event field, but event is null"); //$NON-NLS-1$
return quark;
}
if (name == null) {
throw new IllegalStateException("Invalid attribute name"); //$NON-NLS-1$
}
Object fieldValue = null;
/* First, look for a field with the given name */
ITmfEventField field = event.getContent().getField(name);
/* Field not found, see if it is a special case field */
if (field == null) {
if (name.equalsIgnoreCase(TmfXmlStrings.CPU)) {
/* See if the event advertises a CPU aspect */
Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(
event.getTrace(), TmfCpuAspect.class, event);
if (cpu != null) {
return getQuarkRelativeAndAdd(startQuark, cpu.toString());
}
return IXmlStateSystemContainer.ERROR_QUARK;
}
/* Search between the trace event aspects */
for (ITmfEventAspect<?> aspect : event.getTrace().getEventAspects()) {
if (aspect.getName().equals(fName)) {
fieldValue = aspect.resolve(event);
break;
}
}
} else {
fieldValue = field.getValue();
}
if (fieldValue instanceof String) {
String fieldString = (String) fieldValue;
quark = getQuarkRelativeAndAdd(startQuark, fieldString);
} else if (fieldValue instanceof Long) {
Long fieldLong = (Long) fieldValue;
quark = getQuarkRelativeAndAdd(startQuark, fieldLong.toString());
} else if (fieldValue instanceof Integer) {
Integer fieldInterger = (Integer) fieldValue;
quark = getQuarkRelativeAndAdd(startQuark, fieldInterger.toString());
}
return quark;
}
case QUERY: {
int quark;
ITmfStateValue value = TmfStateValue.nullValue();
int quarkQuery = IXmlStateSystemContainer.ROOT_QUARK;
for (ITmfXmlStateAttribute attrib : fQueryList) {
quarkQuery = attrib.getAttributeQuark(event, quarkQuery, scenarioInfo);
if (quarkQuery == IXmlStateSystemContainer.ERROR_QUARK) {
break;
}
}
// the query may fail: for example CurrentThread if there
// has not been a sched_switch event
if (quarkQuery != IXmlStateSystemContainer.ERROR_QUARK) {
value = ss.queryOngoingState(quarkQuery);
}
switch (value.getType()) {
case INTEGER: {
int result = value.unboxInt();
quark = getQuarkRelativeAndAdd(startQuark, String.valueOf(result));
break;
}
case LONG: {
long result = value.unboxLong();
quark = getQuarkRelativeAndAdd(startQuark, String.valueOf(result));
break;
}
case STRING: {
String result = value.unboxStr();
quark = getQuarkRelativeAndAdd(startQuark, result);
break;
}
case DOUBLE:
case NULL:
case CUSTOM:
default:
quark = IXmlStateSystemContainer.ERROR_QUARK; // error
break;
}
return quark;
}
case LOCATION: {
int quark = startQuark;
String idLocation = name;
/* TODO: Add a fContainer.getLocation(id) method */
for (TmfXmlLocation location : fContainer.getLocations()) {
if (location.getId().equals(idLocation)) {
quark = location.getLocationQuark(event, quark, scenarioInfo);
if (quark == IXmlStateSystemContainer.ERROR_QUARK) {
break;
}
}
}
return quark;
}
case EVENTNAME: {
int quark = IXmlStateSystemContainer.ERROR_QUARK;
if (event == null) {
Activator.logWarning("XML State attribute: looking for an eventname, but event is null"); //$NON-NLS-1$
return quark;
}
quark = getQuarkRelativeAndAdd(startQuark, event.getName());
return quark;
}
case SELF:
return startQuark;
case NONE:
default:
return startQuark;
}
} catch (AttributeNotFoundException ae) {
/*
* This can be happen before the creation of the node for a query in
* the state system. Example : current thread before a sched_switch
*/
return IXmlStateSystemContainer.ERROR_QUARK;
} catch (StateValueTypeException e) {
/*
* This would happen if we were trying to push/pop attributes not of
* type integer. Which, once again, should never happen.
*/
Activator.logError("StateValueTypeException", e); //$NON-NLS-1$
return IXmlStateSystemContainer.ERROR_QUARK;
}
}
@Override
public String toString() {
return "TmfXmlStateAttribute " + fType + ": " + fName; //$NON-NLS-1$ //$NON-NLS-2$
}
}