--- /dev/null
+/*******************************************************************************
+ * 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.tmf.analysis.xml.core.model;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.Activator;
+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.analysis.xml.core.module.IXmlStateSystemContainer;
+import org.eclipse.tracecompass.tmf.analysis.xml.core.segment.TmfXmlPatternSegment;
+import org.eclipse.tracecompass.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * This class defines a pattern segment builder. It will use the XML description
+ * of the pattern segment to generate it at runtime.
+ *
+ * @author Jean-Christian Kouame
+ * @since 2.0
+ *
+ */
+public class TmfXmlPatternSegmentBuilder {
+
+ /**
+ * The string unknown
+ */
+ public static final String UNKNOWN_STRING = "unknown"; //$NON-NLS-1$
+ /**
+ * Prefix for the pattern segment name
+ */
+ public static final String PATTERN_SEGMENT_NAME_PREFIX = "seg_"; //$NON-NLS-1$
+ private final ITmfXmlModelFactory fModelFactory;
+ private final IXmlStateSystemContainer fContainer;
+ private final List<TmfXmlPatternSegmentField> fFields = new ArrayList<>();
+ private final TmfXmlPatternSegmentType fSegmentType;
+
+ /**
+ * @param modelFactory
+ * The factory used to create XML model elements
+ * @param node
+ * XML element of the pattern segment builder
+ * @param parent
+ * The state system container this pattern segment builder
+ * belongs to
+ */
+ public TmfXmlPatternSegmentBuilder(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer parent) {
+ fModelFactory = modelFactory;
+ fContainer = parent;
+
+ //Set the XML type of the segment
+ NodeList nodesSegmentType = node.getElementsByTagName(TmfXmlStrings.SEGMENT_TYPE);
+ Element element = (Element) nodesSegmentType.item(0);
+ if (element == null) {
+ throw new IllegalArgumentException();
+ }
+ fSegmentType = new TmfXmlPatternSegmentType(element);
+
+ //Set the XML content of the segment
+ NodeList nodesSegmentContent = node.getElementsByTagName(TmfXmlStrings.SEGMENT_CONTENT);
+ Element fContentElement = (Element) nodesSegmentContent.item(0);
+ if (fContentElement != null) {
+ NodeList nodesSegmentField = fContentElement.getElementsByTagName(TmfXmlStrings.SEGMENT_FIELD);
+ for (int i = 0; i < nodesSegmentField.getLength(); i++) {
+ fFields.add(new TmfXmlPatternSegmentField(checkNotNull((Element) nodesSegmentField.item(i))));
+ }
+ }
+ }
+
+ /**
+ * Generate a pattern segment
+ *
+ * @param event
+ * The active event
+ * @param start
+ * Start time of the pattern segment to generate
+ * @param end
+ * End time of the pattern segment to generate
+ * @return The pattern segment generated
+ */
+ public TmfXmlPatternSegment generatePatternSegment(ITmfEvent event, ITmfTimestamp start, ITmfTimestamp end) {
+ int scale = event.getTimestamp().getScale();
+ long startValue = start.normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
+ long endValue = end.normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
+ String segmentName = getPatternSegmentName(event);
+ Map<String, ITmfStateValue> fields = new HashMap<>();
+ setPatternSegmentContent(event, start, end, fields);
+ return new TmfXmlPatternSegment(startValue, endValue, scale, segmentName, fields);
+ }
+
+ /**
+ * Get the pattern segment name
+ *
+ * @param event
+ * The active event
+ * @return The name of the segment
+ */
+ private String getPatternSegmentName(ITmfEvent event) {
+ return fSegmentType.getName(event);
+ }
+
+ /**
+ * Compute all the fields and their values for this pattern segment. The
+ * fields could be constant values or values queried from the state system.
+ *
+ * @param event
+ * The current event
+ * @param start
+ * The start timestamp of this segment
+ * @param end
+ * The end timestamp of this segment
+ * @param fields
+ * The map that will contained all the fields
+ */
+ private void setPatternSegmentContent(ITmfEvent event, ITmfTimestamp start, ITmfTimestamp end, Map<String, ITmfStateValue> fields) {
+ for (TmfXmlPatternSegmentField field : fFields) {
+ fields.put(field.getName(), field.getValue(event));
+ }
+ }
+
+ private static ITmfStateValue getStateValueFromConstant(String constantValue, String type) {
+ switch (type) {
+ case TmfXmlStrings.TYPE_INT:
+ return TmfStateValue.newValueInt(Integer.parseInt(constantValue));
+ case TmfXmlStrings.TYPE_LONG:
+ return TmfStateValue.newValueLong(Long.parseLong(constantValue));
+ case TmfXmlStrings.TYPE_STRING:
+ return TmfStateValue.newValueString(constantValue);
+ case TmfXmlStrings.TYPE_NULL:
+ return TmfStateValue.nullValue();
+ default:
+ throw new IllegalArgumentException("Invalid type of field : " + type); //$NON-NLS-1$
+ }
+ }
+
+ private static void getNameFromXmlStateValue(ITmfEvent event, StringBuilder builder, ITmfXmlStateValue xmlStateValue) {
+ try {
+ ITmfStateValue value = xmlStateValue.getValue(event);
+ switch (value.getType()) {
+ case DOUBLE:
+ builder.append(value.unboxDouble());
+ break;
+ case INTEGER:
+ builder.append(value.unboxInt());
+ break;
+ case LONG:
+ builder.append(value.unboxLong());
+ break;
+ case NULL:
+ builder.append(UNKNOWN_STRING);
+ break;
+ case STRING:
+ builder.append(value.unboxStr());
+ break;
+ default:
+ throw new StateValueTypeException("Invalid type of state value"); //$NON-NLS-1$
+ }
+ } catch (AttributeNotFoundException e) {
+ Activator.logInfo("Impossible to get the state value", e); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * This class represents the segment fields described in the XML. The real
+ * value of the field will be set at runtime using the active event.
+ *
+ * @author Jean-Christian Kouame
+ *
+ */
+ private class TmfXmlPatternSegmentField {
+ private final String fName;
+ private final String fType;
+ private final @Nullable ITmfStateValue fStateValue;
+ private final @Nullable ITmfXmlStateValue fXmlStateValue;
+
+ /**
+ * Constructor
+ *
+ * @param element
+ * The pattern segment field node
+ */
+ public TmfXmlPatternSegmentField(Element element) {
+ // The name, the type and the value of each field could respectively
+ // be found from the attributes name, type and value. If the value
+ // attribute is not available, try to find it from the child state
+ // value.
+ fName = element.getAttribute(TmfXmlStrings.NAME);
+ fType = element.getAttribute(TmfXmlStrings.TYPE);
+ String constantValue = element.getAttribute(TmfXmlStrings.VALUE);
+ if (constantValue.isEmpty() && !fType.equals(TmfXmlStrings.TYPE_NULL)) {
+ fStateValue = null;
+ Element elementFieldStateValue = (Element) element.getElementsByTagName(TmfXmlStrings.STATE_VALUE).item(0);
+ if (elementFieldStateValue == null) {
+ throw new IllegalArgumentException("The value of the field " + fName + " is missing"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ fXmlStateValue = fModelFactory.createStateValue(elementFieldStateValue, fContainer, new ArrayList<>());
+ } else {
+ fStateValue = getStateValueFromConstant(constantValue, fType);
+ fXmlStateValue = null;
+ }
+ }
+
+ /**
+ * Get the real value of the XML pattern segment field
+ *
+ * @param event
+ * The active event
+ * @return The state value representing the value of the XML pattern
+ * segment field
+ */
+ public ITmfStateValue getValue(ITmfEvent event) {
+ if (fStateValue != null) {
+ return fStateValue;
+ }
+ try {
+ return checkNotNull(fXmlStateValue).getValue(event);
+ } catch (AttributeNotFoundException e) {
+ Activator.logError("Failed to get the state value", e); //$NON-NLS-1$
+ }
+ throw new IllegalStateException("Failed to get the value for the segment field " + fName); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the name of the XML pattern segment field
+ *
+ * @return The name
+ */
+ public String getName() {
+ return fName;
+ }
+ }
+
+ /**
+ * This class represents the segment type described in XML.
+ *
+ * @author Jean-Christian Kouame
+ *
+ */
+ private class TmfXmlPatternSegmentType {
+ private final String fSegmentNameAttribute;
+ private final @Nullable ITmfXmlStateValue fNameStateValue;
+
+ /**
+ * Constructor
+ *
+ * @param element
+ * The pattern segment type node
+ */
+ public TmfXmlPatternSegmentType(Element element) {
+ // Try to find the segment name from the name attribute. If
+ // attribute not available, try to find it from the child state value
+ fSegmentNameAttribute = element.getAttribute(TmfXmlStrings.SEGMENT_NAME);
+ if (!fSegmentNameAttribute.isEmpty()) {
+ fNameStateValue = null;
+ } else {
+ Element elementSegmentNameStateValue = (Element) element.getElementsByTagName(TmfXmlStrings.STATE_VALUE).item(0);
+ if (elementSegmentNameStateValue == null) {
+ throw new IllegalArgumentException("Failed to get the segment name. A state value is needed."); //$NON-NLS-1$
+ }
+ fNameStateValue = fModelFactory.createStateValue(elementSegmentNameStateValue, fContainer, new ArrayList<>());
+ }
+ }
+
+ /**
+ * Get the name of the segment
+ *
+ * @param event
+ * The active event
+ * @return The segment name
+ */
+ public String getName(ITmfEvent event) {
+ StringBuilder name = new StringBuilder(PATTERN_SEGMENT_NAME_PREFIX);
+ if (fNameStateValue != null) {
+ getNameFromXmlStateValue(event, name, fNameStateValue);
+ } else {
+ name.append(fSegmentNameAttribute);
+ }
+ return name.toString();
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ecole Polytechnique de Montreal, 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.tmf.analysis.xml.core.segment;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.segmentstore.core.ISegment;
+import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
+import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
+import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
+
+import com.google.common.collect.Ordering;
+
+/**
+ * This class implements an XML Pattern Segment. This type of segment has
+ * content and a default timestamp, which is the start time of the segment.
+ *
+ * @author Jean-Christian Kouame
+ * @since 2.0
+ *
+ */
+public class TmfXmlPatternSegment implements ISegment {
+
+ /**
+ * The serial version UID
+ */
+ private static final long serialVersionUID = 3556323761465412078L;
+
+ /* 'Byte' equivalent for state values types */
+ private static final byte TYPE_NULL = -1;
+ private static final byte TYPE_INTEGER = 0;
+ private static final byte TYPE_STRING = 1;
+ private static final byte TYPE_LONG = 2;
+
+ private static final @NonNull Comparator<ISegment> COMPARATOR = Ordering
+ .from(SegmentComparators.INTERVAL_START_COMPARATOR)
+ .compound(SegmentComparators.INTERVAL_END_COMPARATOR)
+ /* Kind of lazy, but should work! */
+ .compound(Ordering.usingToString());
+
+ private final int fScale;
+ private final long fStart;
+ private final long fEnd;
+ private final String fSegmentName;
+ private transient Map<@NonNull String, @NonNull ITmfStateValue> fContent;
+
+ /**
+ * Constructs an XML pattern segment
+ *
+ * @param start
+ * Start time of the pattern segment
+ * @param end
+ * End time of the pattern segment
+ * @param scale
+ * Scale of the pattern segment
+ * @param segmentName
+ * Name of the pattern segment
+ * @param fields
+ * Fields of the pattern segment
+ */
+ public TmfXmlPatternSegment(long start, long end, int scale, String segmentName, @NonNull Map<@NonNull String, @NonNull ITmfStateValue> fields) {
+ fStart = start;
+ fEnd = end;
+ fScale = scale;
+ fSegmentName = segmentName;
+ fContent = Collections.unmodifiableMap(fields);
+ }
+
+ /**
+ * Get the start timestamp of the segment
+ *
+ * @return The start timestamp
+ */
+ public @NonNull ITmfTimestamp getTimestampStart() {
+ return new TmfTimestamp(fStart, fScale);
+ }
+
+ /**
+ * Get the end timestamp of this segment
+ *
+ * @return The end timestamp
+ */
+ public @NonNull ITmfTimestamp getTimestampEnd() {
+ return new TmfTimestamp(fEnd, fScale);
+ }
+
+ /**
+ * Get the content of the pattern segment
+ * @return The content
+ */
+ public Map<@NonNull String, @NonNull ITmfStateValue> getContent() {
+ return fContent;
+ }
+
+ /**
+ * Get the name of pattern segment
+ * @return The name
+ */
+ public String getName() {
+ return fSegmentName;
+ }
+
+ /**
+ * Get the timestamp scale of the pattern segment
+ * @return The timestamp scale
+ */
+ public int getScale() {
+ return fScale;
+ }
+
+ @Override
+ public int compareTo(@Nullable ISegment o) {
+ if (o == null) {
+ throw new IllegalArgumentException("Cannot compare to null"); //$NON-NLS-1$
+ }
+ return COMPARATOR.compare(this, o);
+ }
+
+ @Override
+ public long getStart() {
+ return fStart;
+ }
+
+ @Override
+ public long getEnd() {
+ return fEnd;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(getClass().getSimpleName())
+ .append(", [fTimestampStart=").append(getTimestampStart()) //$NON-NLS-1$
+ .append(", fTimestampEnd=").append(getTimestampEnd()) //$NON-NLS-1$
+ .append(", duration= ").append(getLength()) //$NON-NLS-1$
+ .append(", fName=").append(getName()) //$NON-NLS-1$
+ .append(", fContent=").append(getContent()) //$NON-NLS-1$
+ .append("]").toString(); //$NON-NLS-1$
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+
+ // Write the number of fields
+ out.writeInt(fContent.size());
+
+ // Write the fields
+ for (Map.Entry<String, ITmfStateValue> entry : fContent.entrySet()) {
+ out.writeInt(entry.getKey().length());
+ out.writeBytes(entry.getKey());
+ final ITmfStateValue value = entry.getValue();
+ final byte type = getByteFromType(value.getType());
+ out.writeByte(type);
+ switch (type) {
+ case TYPE_NULL:
+ break;
+ case TYPE_INTEGER:
+ out.writeInt(value.unboxInt());
+ break;
+ case TYPE_LONG:
+ out.writeLong(value.unboxLong());
+ break;
+ case TYPE_STRING:
+ final @NonNull String string = value.unboxStr();
+ out.writeInt(string.length());
+ out.writeBytes(string);
+ break;
+ default:
+ throw new IOException("Write object failed : Invalid data"); //$NON-NLS-1$
+ }
+ }
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ int contentSize = in.readInt();
+
+ final Map<@NonNull String, @NonNull ITmfStateValue> content = new HashMap<>();
+ for (int i = 0; i < contentSize; i++) {
+ int length = in.readInt();
+ byte[] bytes = new byte[length];
+ in.read(bytes, 0, length);
+ String name = new String(bytes);
+
+ Byte type = in.readByte();
+ ITmfStateValue value;
+ switch (type) {
+ case TYPE_NULL:
+ value = TmfStateValue.nullValue();
+ break;
+ case TYPE_INTEGER:
+ value = TmfStateValue.newValueInt(in.readInt());
+ break;
+ case TYPE_LONG:
+ value = TmfStateValue.newValueLong(in.readLong());
+ break;
+ case TYPE_STRING:
+ length = in.readInt();
+ bytes = new byte[length];
+ in.read(bytes, 0, length);
+ value = TmfStateValue.newValueString(new String(bytes));
+ break;
+ default:
+ throw new IOException("Read object failed : Invalid data"); //$NON-NLS-1$
+ }
+ content.put(name, value);
+ }
+ fContent = content;
+ }
+
+ /**
+ * Here we determine how state values "types" are written in the 8-bit field
+ * that indicates the value type in the file.
+ */
+ private static byte getByteFromType(ITmfStateValue.Type type) {
+ switch (type) {
+ case NULL:
+ return TYPE_NULL;
+ case INTEGER:
+ return TYPE_INTEGER;
+ case STRING:
+ return TYPE_STRING;
+ case LONG:
+ return TYPE_LONG;
+ case DOUBLE:
+ default:
+ /* Should not happen if the switch is fully covered */
+ throw new IllegalStateException("Data type " + type + " not supported"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+}