/******************************************************************************* * 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 java.util.List; import java.util.Set; 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.module.IXmlStateSystemContainer; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.pattern.stateprovider.XmlPatternStateProvider; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.XmlStateProvider; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; 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.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 State Value in the XML-defined state system, along * with the path to get to the value (either a list of state attributes or an * event field) * * <pre> * Example: * <stateAttribute type="location" value="CurrentThread" /> * <stateAttribute type="constant" value="System_call" /> * <stateValue type="null" /> * </pre> * * @author Florian Wininger */ public abstract class TmfXmlStateValue implements ITmfXmlStateValue { private final TmfXmlStateValueBase fStateValue; /* Path in the State System */ private final List<ITmfXmlStateAttribute> fPath; /* Event field to match with this state value */ private final @Nullable String fEventField; /* Whether this state value is an increment of the previous value */ private final boolean fIncrement; /* Whether to update the current attribute or create a new state */ private final boolean fUpdate; /* Stack value */ private final ValueTypeStack fStackType; /* Forced value type */ private final ITmfStateValue.Type fForcedType; private final IXmlStateSystemContainer fContainer; private final String fMappingGroup; /** * Different behaviors of an attribute that is to be stacked */ protected enum ValueTypeStack { /** Not stacked */ NULL, /** Peek at the value at the top of the stack */ PEEK, /** Take the value at the top of the stack */ POP, /** Push the value on the stack */ PUSH; /** * Get the type stack value corresponding to a string * * @param input * The string to match to a value * @return The ValueTypeStack value */ public static ValueTypeStack getTypeFromString(String input) { switch (input) { case TmfXmlStrings.STACK_PUSH: return PUSH; case TmfXmlStrings.STACK_POP: return POP; case TmfXmlStrings.STACK_PEEK: return PEEK; default: return NULL; } } } /** * Constructor * * @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 * @param attributes * The attributes representing the path to this value */ protected TmfXmlStateValue(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer container, List<ITmfXmlStateAttribute> attributes, @Nullable String eventField) { fPath = attributes; fContainer = container; fEventField = eventField; if (!node.getNodeName().equals(TmfXmlStrings.STATE_VALUE)) { throw new IllegalArgumentException("TmfXmlStateValue constructor: Element is not a stateValue"); //$NON-NLS-1$ } /* Check if there is an increment for the value */ fIncrement = Boolean.parseBoolean(node.getAttribute(TmfXmlStrings.INCREMENT)); /* Check if this value is an update of the ongoing state */ fUpdate = Boolean.parseBoolean(node.getAttribute(TmfXmlStrings.UPDATE)); /* Process the XML Element state value */ fStateValue = initializeStateValue(modelFactory, node); /* * Forced type allows to convert the value to a certain type : For * example, a process's TID in an event field may arrive with a LONG * format but we want to store the data in an INT */ switch (node.getAttribute(TmfXmlStrings.FORCED_TYPE)) { case TmfXmlStrings.TYPE_STRING: fForcedType = ITmfStateValue.Type.STRING; break; case TmfXmlStrings.TYPE_INT: fForcedType = ITmfStateValue.Type.INTEGER; break; case TmfXmlStrings.TYPE_LONG: fForcedType = ITmfStateValue.Type.LONG; break; case TmfXmlStrings.TYPE_DOUBLE: fForcedType = ITmfStateValue.Type.DOUBLE; break; default: fForcedType = ITmfStateValue.Type.NULL; } /* * Stack Actions : allow to define a stack with PUSH/POP/PEEK methods */ String stack = node.getAttribute(TmfXmlStrings.ATTRIBUTE_STACK); fStackType = ValueTypeStack.getTypeFromString(stack); fMappingGroup = node.getAttribute(TmfXmlStrings.MAPPING_GROUP); } /** * Initialize a {@link TmfXmlStateValueBase} object for the type and value * * @param modelFactory * The factory used to create XML model elements * @param node * The state value XML element * @return The internal state value type corresponding to this state value */ protected TmfXmlStateValueBase initializeStateValue(ITmfXmlModelFactory modelFactory, Element node) { return new TmfXmlStateValueNull(); } /** * Return the state system container this class is attached to * * @return The state system container */ protected IXmlStateSystemContainer getSsContainer() { return fContainer; } /** * Get the state system associated with this value's container * * @return The state system associated with the state system container */ protected @Nullable ITmfStateSystem getStateSystem() { return fContainer.getStateSystem(); } /** * Return whether this value is an increment of the previous value * * @return <code>true</code> if the value is an increment */ protected boolean isIncrement() { return fIncrement; } /** * Return whether this value should replace the current value of the * attribute or if a new state should be created. * * @return <code>true</code> if the value is to replace the current one */ protected boolean isUpdate() { return fUpdate; } /** * Get the stack type of this attribute. If the attribute is to be pushed or * popped to a stack. The behavior of the stack attribute will depend on the * implementation of the model. * * @return The stack type of the attribute */ protected ValueTypeStack getStackType() { return fStackType; } /** * Get the forced type of the value. For example, if the value obtained from * the attributes is not in this forced type, it will be converted to this. * * @return The desired type of the value */ protected ITmfStateValue.Type getForcedType() { return fForcedType; } @Override public ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException { return getMappedValue(event, scenarioInfo, fStateValue.getValue(event, scenarioInfo)); } private ITmfStateValue getMappedValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo, ITmfStateValue value) { try { Set<TmfXmlMapEntry> group = null; if (fContainer instanceof XmlPatternStateProvider) { group = ((XmlPatternStateProvider) fContainer).getMappingGroup(fMappingGroup); } else if (fContainer instanceof XmlStateProvider) { group = ((XmlStateProvider) fContainer).getMappingGroup(fMappingGroup); } if (group != null) { for (TmfXmlMapEntry entry : group) { if (entry.getKey().getValue(event, scenarioInfo).equals(value)) { return entry.getValue().getValue(event, scenarioInfo); } } } return value; } catch (AttributeNotFoundException e) { Activator.logError("Unable to map the state value"); //$NON-NLS-1$ // FIXME maybe we should return the raw state value instead of a // null state value return TmfStateValue.nullValue(); } } /** * Get the value of the event field that is the path of this state value * * @param event * The current event * @return the value of the event field */ @Override public ITmfStateValue getEventFieldValue(@NonNull ITmfEvent event) { String eventField = fEventField; if (eventField == null) { throw new IllegalStateException(); } return getEventFieldValue(event, eventField); } /** * Get the value of an event field * * @param event * The current event * @param fieldName * The name of the field of which to get the value * @return The value of the event field */ protected ITmfStateValue getEventFieldValue(ITmfEvent event, String fieldName) { ITmfStateValue value = TmfStateValue.nullValue(); final ITmfEventField field = event.getContent().getField(fieldName); Object fieldValue = null; /* If the field does not exist, see if it's a special case */ if (field == null) { if (fieldName.equalsIgnoreCase(TmfXmlStrings.CPU)) { /* A "CPU" field will return the CPU aspect if available */ Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event); if (cpu != null) { return TmfStateValue.newValueInt(cpu.intValue()); } } else if (fieldName.equalsIgnoreCase(TmfXmlStrings.TIMESTAMP)) { /* * Exception also for "TIMESTAMP", returns the timestamp of this * event */ return TmfStateValue.newValueLong(event.getTimestamp().getValue()); } // This will allow to use any column as input for (ITmfEventAspect<?> aspect : event.getTrace().getEventAspects()) { if (aspect.getName().equals(fieldName)) { fieldValue = aspect.resolve(event); break; } } if (fieldValue == null) { return value; } } else { fieldValue = field.getValue(); } /* * Try to find the right type. The type can be forced by * "forcedType" argument. */ if (fieldValue instanceof String) { String fieldString = (String) fieldValue; switch (fForcedType) { case INTEGER: value = TmfStateValue.newValueInt(Integer.parseInt(fieldString)); break; case LONG: value = TmfStateValue.newValueLong(Long.parseLong(fieldString)); break; case DOUBLE: value = TmfStateValue.newValueDouble(Double.parseDouble(fieldString)); break; case CUSTOM: throw new IllegalStateException("Custom type cannot be forced"); //$NON-NLS-1$ case NULL: case STRING: default: value = TmfStateValue.newValueString(fieldString); break; } } else if (fieldValue instanceof Long) { Long fieldLong = (Long) fieldValue; switch (fForcedType) { case INTEGER: value = TmfStateValue.newValueInt(fieldLong.intValue()); break; case STRING: value = TmfStateValue.newValueString(fieldLong.toString()); break; case DOUBLE: value = TmfStateValue.newValueDouble(fieldLong.doubleValue()); break; case CUSTOM: throw new IllegalStateException("Custom type cannot be forced"); //$NON-NLS-1$ case LONG: case NULL: default: value = TmfStateValue.newValueLong(fieldLong); break; } } else if (fieldValue instanceof Integer) { Integer fieldInteger = (Integer) fieldValue; switch (fForcedType) { case LONG: value = TmfStateValue.newValueLong(fieldInteger.longValue()); break; case STRING: value = TmfStateValue.newValueString(fieldInteger.toString()); break; case DOUBLE: value = TmfStateValue.newValueDouble(fieldInteger.doubleValue()); break; case CUSTOM: throw new IllegalStateException("Custom type cannot be forced"); //$NON-NLS-1$ case INTEGER: case NULL: default: value = TmfStateValue.newValueInt(fieldInteger); break; } } else if (fieldValue instanceof Double) { Double fieldDouble = (Double) fieldValue; switch (fForcedType) { case LONG: value = TmfStateValue.newValueLong(fieldDouble.longValue()); break; case STRING: value = TmfStateValue.newValueString(fieldDouble.toString()); break; case INTEGER: value = TmfStateValue.newValueInt(fieldDouble.intValue()); break; case CUSTOM: throw new IllegalStateException("Custom type cannot be forced"); //$NON-NLS-1$ case DOUBLE: case NULL: default: value = TmfStateValue.newValueDouble(fieldDouble); break; } } return value; } /** * Get the list of state attributes, the path to the state value * * @return the list of Attribute to have the path in the State System */ @Override public List<ITmfXmlStateAttribute> getAttributes() { return fPath; } @Override public void handleEvent(@NonNull ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException, StateValueTypeException, TimeRangeException { int quark = IXmlStateSystemContainer.ROOT_QUARK; for (ITmfXmlStateAttribute attribute : fPath) { quark = attribute.getAttributeQuark(event, quark, scenarioInfo); /* the query is not valid, we stop the state change */ if (quark == IXmlStateSystemContainer.ERROR_QUARK) { Activator.logError("Not found XML attribute " + attribute); //$NON-NLS-1$ return; } } long ts = event.getTimestamp().getValue(); fStateValue.handleEvent(event, quark, ts, scenarioInfo); } @Override public String toString() { StringBuilder builder = new StringBuilder("TmfXmlStateValue: "); //$NON-NLS-1$ if (fEventField != null) { builder.append("Field=").append(fEventField).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ } else if (!fPath.isEmpty()) { builder.append("Path=").append(fPath).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ } builder.append(fStateValue); return builder.toString(); } /** * Base class for all state values. Contain default methods to handle event, * process or increment the value */ protected abstract class TmfXmlStateValueBase { /** * Get the value associated with this state value. * * @param event * The event which can be used to retrieve the value if * necessary. The event can be <code>null</code> if no event * is required. * @param scenarioInfo * The active scenario details. The value should be null if * there no scenario. * @return The state value corresponding to this XML state value * @throws AttributeNotFoundException * Pass through the exception it received */ public abstract ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException; /** * Do something with the state value, possibly using an event * * @param event * The event being handled. If there is no event is * available, use <code>null</code>. * @param quark * The quark for this value * @param timestamp * The timestamp of the event * @param scenarioInfo * The active scenario details. The value should be null if * there no scenario. * @throws StateValueTypeException * Pass through the exception it received * @throws TimeRangeException * Pass through the exception it received * @throws AttributeNotFoundException * Pass through the exception it received */ public void handleEvent(ITmfEvent event, int quark, long timestamp, @Nullable TmfXmlScenarioInfo scenarioInfo) throws StateValueTypeException, TimeRangeException, AttributeNotFoundException { if (fIncrement) { incrementValue(event, quark, timestamp, scenarioInfo); } else { ITmfStateValue value = getValue(event, scenarioInfo); processValue(quark, timestamp, value); } } /** * Set the value of a quark at a given timestamp. * * @param quark * The quark for this value * @param timestamp * The timestamp * @param value * The value of this state value * @throws TimeRangeException * Pass through the exception it received * @throws StateValueTypeException * Pass through the exception it received * @throws AttributeNotFoundException * Pass through the exception it received */ @SuppressWarnings("unused") protected void processValue(int quark, long timestamp, ITmfStateValue value) throws TimeRangeException, StateValueTypeException, AttributeNotFoundException { } /** * Increments the value of the parameter * * @param event * The event being handled * @param quark * The quark for this value * @param timestamp * The timestamp of the event * @param scenarioInfo * The active scenario details. The value should be null if * there no scenario. * @throws StateValueTypeException * Pass through the exception it received * @throws TimeRangeException * Pass through the exception it received * @throws AttributeNotFoundException * Pass through the exception it received */ @SuppressWarnings("unused") protected void incrementValue(ITmfEvent event, int quark, long timestamp, @Nullable TmfXmlScenarioInfo scenarioInfo) throws StateValueTypeException, TimeRangeException, AttributeNotFoundException { } } /* This state value uses a constant value, defined in the XML */ private class TmfXmlStateValueNull extends TmfXmlStateValueBase { @Override public ITmfStateValue getValue(@Nullable ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) throws AttributeNotFoundException { return TmfStateValue.nullValue(); } @Override public String toString() { return "NULL"; //$NON-NLS-1$ } } }