| /******************************************************************************* |
| * Copyright (c) 2011, 2014 Ericsson, É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 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Matthew Khouzam - Initial API and implementation |
| * Alexandre Montplaisir - Initial API and implementation, extend TmfEventField |
| * Bernd Hufmann - Add Enum field handling |
| * Geneviève Bastien - Add Struct and Variant field handling |
| * Jean-Christian Kouame - Correct handling of unsigned integer fields |
| * François Doray - Add generic array field type |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.tmf.ctf.core.event; |
| |
| import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.tracecompass.ctf.core.event.types.AbstractArrayDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.CompoundDeclaration; |
| import org.eclipse.tracecompass.ctf.core.event.types.Definition; |
| import org.eclipse.tracecompass.ctf.core.event.types.EnumDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.FloatDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.ICompositeDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration; |
| import org.eclipse.tracecompass.ctf.core.event.types.IDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.IntegerDeclaration; |
| import org.eclipse.tracecompass.ctf.core.event.types.IntegerDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.StringDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.VariantDefinition; |
| import org.eclipse.tracecompass.internal.ctf.core.event.types.ByteArrayDefinition; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; |
| import org.eclipse.tracecompass.tmf.core.event.TmfEventField; |
| import org.eclipse.tracecompass.tmf.ctf.core.CtfEnumPair; |
| |
| /** |
| * The CTF implementation of the TMF event field model |
| * |
| * @version 2.0 |
| * @author Matthew Khouzam |
| * @author Alexandre Montplaisir |
| */ |
| public abstract class CtfTmfEventField extends TmfEventField { |
| |
| /** |
| * Value that can be used in the {@link #getField(String...)} for variants. |
| * Using this field value means that the selected field will be returned |
| * whatever the selected choice for the event |
| * |
| * @since 2.1 |
| */ |
| public static final @NonNull String FIELD_VARIANT_SELECTED = "Any"; //$NON-NLS-1$ |
| |
| // ------------------------------------------------------------------------ |
| // Constructor |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Standard constructor. Only to be used internally, call parseField() to |
| * generate a new field object. |
| * |
| * @param name |
| * The name of this field |
| * @param value |
| * The value of this field. Its type should match the field type. |
| * @param fields |
| * The children fields. Useful for composite fields |
| */ |
| protected CtfTmfEventField(@NonNull String name, Object value, ITmfEventField[] fields) { |
| super(/* Strip the underscore from the field name if there is one */ |
| name.startsWith("_") ? name.substring(1) : name, //$NON-NLS-1$ |
| value, |
| fields); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Operations |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Factory method to instantiate CtfTmfEventField objects. |
| * |
| * @param fieldDef |
| * The CTF Definition of this event field |
| * @param fieldName |
| * String The name to assign to this field |
| * @return The resulting CtfTmfEventField object |
| */ |
| public static @NonNull CtfTmfEventField parseField(IDefinition fieldDef, |
| @NonNull String fieldName) { |
| CtfTmfEventField field = null; |
| |
| /* Determine the Definition type */ |
| if (fieldDef instanceof IntegerDefinition) { |
| IntegerDefinition intDef = (IntegerDefinition) fieldDef; |
| int base = intDef.getDeclaration().getBase(); |
| field = new CTFIntegerField(fieldName, intDef.getValue(), base, intDef.getDeclaration().isSigned()); |
| |
| } else if (fieldDef instanceof EnumDefinition) { |
| EnumDefinition enumDef = (EnumDefinition) fieldDef; |
| field = new CTFEnumField(fieldName, new CtfEnumPair(enumDef.getValue(), enumDef.getIntegerValue())); |
| |
| } else if (fieldDef instanceof StringDefinition) { |
| field = new CTFStringField(fieldName, ((StringDefinition) fieldDef).getValue()); |
| |
| } else if (fieldDef instanceof FloatDefinition) { |
| FloatDefinition floatDef = (FloatDefinition) fieldDef; |
| field = new CTFFloatField(fieldName, floatDef.getValue()); |
| |
| } else if (fieldDef instanceof AbstractArrayDefinition) { |
| AbstractArrayDefinition arrayDef = (AbstractArrayDefinition) fieldDef; |
| IDeclaration decl = arrayDef.getDeclaration(); |
| if (!(decl instanceof CompoundDeclaration)) { |
| throw new IllegalArgumentException("Array definitions should only come from sequence or array declarations"); //$NON-NLS-1$ |
| } |
| CompoundDeclaration arrDecl = (CompoundDeclaration) decl; |
| IDeclaration elemType = null; |
| elemType = arrDecl.getElementType(); |
| if (elemType instanceof IntegerDeclaration) { |
| /* |
| * Array of integers => CTFIntegerArrayField, unless it's a |
| * CTFStringField |
| */ |
| IntegerDeclaration elemIntType = (IntegerDeclaration) elemType; |
| /* Are the integers characters and encoded? */ |
| if (elemIntType.isCharacter()) { |
| /* it's a CTFStringField */ |
| field = new CTFStringField(fieldName, arrayDef.toString()); |
| } else if (arrayDef instanceof ByteArrayDefinition) { // unsigned byte array |
| ByteArrayDefinition byteArrayDefinition = (ByteArrayDefinition) arrayDef; |
| /* it's a CTFIntegerArrayField */ |
| int size = arrayDef.getLength(); |
| long[] values = new long[size]; |
| for (int i = 0; i < size; i++) { |
| values[i] = Byte.toUnsignedLong(byteArrayDefinition.getByte(i)); |
| } |
| field = new CTFIntegerArrayField(fieldName, values, |
| elemIntType.getBase(), |
| elemIntType.isSigned()); |
| |
| } else { |
| /* it's a CTFIntegerArrayField */ |
| int size = arrayDef.getLength(); |
| long[] values = new long[size]; |
| for (int i = 0; i < size; i++) { |
| IDefinition elem = arrayDef.getDefinitions().get(i); |
| if (elem == null) { |
| break; |
| } |
| values[i] = ((IntegerDefinition) elem).getValue(); |
| } |
| field = new CTFIntegerArrayField(fieldName, values, |
| elemIntType.getBase(), |
| elemIntType.isSigned()); |
| } |
| } else { |
| /* Arrays of elements of any other type */ |
| CtfTmfEventField[] elements = new CtfTmfEventField[arrayDef.getLength()]; |
| /* Parse the elements of the array. */ |
| int i = 0; |
| List<Definition> definitions = arrayDef.getDefinitions(); |
| for (IDefinition definition : definitions) { |
| CtfTmfEventField curField = CtfTmfEventField.parseField( |
| definition, fieldName + '[' + i + ']'); |
| elements[i] = curField; |
| i++; |
| } |
| |
| field = new CTFArrayField(fieldName, elements); |
| } |
| } else if (fieldDef instanceof ICompositeDefinition) { |
| ICompositeDefinition strDef = (ICompositeDefinition) fieldDef; |
| |
| List<ITmfEventField> list = new ArrayList<>(); |
| /* Recursively parse the fields */ |
| for (String fn : strDef.getFieldNames()) { |
| list.add(CtfTmfEventField.parseField(strDef.getDefinition(fn), fn)); |
| } |
| field = new CTFStructField(fieldName, list.toArray(new CtfTmfEventField[list.size()])); |
| |
| } else if (fieldDef instanceof VariantDefinition) { |
| VariantDefinition varDef = (VariantDefinition) fieldDef; |
| |
| String curFieldName = checkNotNull(varDef.getCurrentFieldName()); |
| IDefinition curFieldDef = varDef.getCurrentField(); |
| if (curFieldDef != null) { |
| CtfTmfEventField subField = CtfTmfEventField.parseField(curFieldDef, curFieldName); |
| field = new CTFVariantField(fieldName, subField); |
| } else { |
| /* A safe-guard, but curFieldDef should never be null */ |
| field = new CTFStringField(curFieldName, ""); //$NON-NLS-1$ |
| } |
| |
| } else { |
| /* |
| * Safe-guard, to avoid null exceptions later, field is expected not |
| * to be null |
| */ |
| field = new CTFStringField(fieldName, Messages.CtfTmfEventField_UnsupportedType + fieldDef.getClass().toString()); |
| } |
| return field; |
| } |
| |
| @Override |
| public String toString() { |
| return getName() + '=' + getFormattedValue(); |
| } |
| |
| } |
| |
| /** |
| * The CTF field implementation for integer fields. |
| * |
| * @author alexmont |
| */ |
| final class CTFIntegerField extends CtfTmfEventField { |
| |
| private final int fBase; |
| private final boolean fSigned; |
| |
| /** |
| * A CTF "IntegerDefinition" can be an integer of any byte size, so in the |
| * Java parser this is interpreted as a long. |
| * |
| * @param name |
| * The name of this field |
| * @param longValue |
| * The integer value of this field |
| * @param signed |
| * Is the value signed or not |
| */ |
| CTFIntegerField(@NonNull String name, long longValue, int base, boolean signed) { |
| super(name, Long.valueOf(longValue), null); |
| fSigned = signed; |
| fBase = base; |
| } |
| |
| @Override |
| public Long getValue() { |
| return (Long) super.getValue(); |
| } |
| |
| @Override |
| public String getFormattedValue() { |
| return IntegerDefinition.formatNumber(getValue(), fBase, fSigned); |
| } |
| |
| } |
| |
| /** |
| * The CTF field implementation for string fields |
| * |
| * @author alexmont |
| */ |
| final class CTFStringField extends CtfTmfEventField { |
| |
| /** |
| * Constructor for CTFStringField. |
| * |
| * @param strValue |
| * The string value of this field |
| * @param name |
| * The name of this field |
| */ |
| CTFStringField(@NonNull String name, String strValue) { |
| super(name, strValue, null); |
| } |
| |
| @Override |
| public String getValue() { |
| return (String) super.getValue(); |
| } |
| } |
| |
| /** |
| * CTF field implementation for arrays of integers. |
| * |
| * @author alexmont |
| */ |
| final class CTFIntegerArrayField extends CtfTmfEventField { |
| |
| private final int fBase; |
| private final boolean fSigned; |
| private String fFormattedValue = null; |
| |
| /** |
| * Constructor for CTFIntegerArrayField. |
| * |
| * @param name |
| * The name of this field |
| * @param longValues |
| * The array of integers (as longs) that compose this field's |
| * value |
| * @param signed |
| * Are the values in the array signed or not |
| */ |
| CTFIntegerArrayField(@NonNull String name, long[] longValues, int base, boolean signed) { |
| super(name, longValues, null); |
| fBase = base; |
| fSigned = signed; |
| } |
| |
| @Override |
| public long[] getValue() { |
| return (long[]) super.getValue(); |
| } |
| |
| @Override |
| public synchronized String getFormattedValue() { |
| if (fFormattedValue == null) { |
| List<String> strings = new ArrayList<>(); |
| for (long value : getValue()) { |
| strings.add(IntegerDefinition.formatNumber(value, fBase, fSigned)); |
| } |
| fFormattedValue = strings.toString(); |
| } |
| return fFormattedValue; |
| } |
| |
| } |
| |
| /** |
| * CTF field implementation for arrays of arbitrary types. |
| * |
| * @author fdoray |
| */ |
| final class CTFArrayField extends CtfTmfEventField { |
| |
| private String fFormattedValue = null; |
| |
| /** |
| * Constructor for CTFArrayField. |
| * |
| * @param name |
| * The name of this field |
| * @param elements |
| * The array elements of this field |
| */ |
| CTFArrayField(@NonNull String name, CtfTmfEventField[] elements) { |
| super(name, elements, elements); |
| } |
| |
| @Override |
| public CtfTmfEventField[] getValue() { |
| return (CtfTmfEventField[]) super.getValue(); |
| } |
| |
| @Override |
| public synchronized String getFormattedValue() { |
| if (fFormattedValue == null) { |
| List<String> strings = new ArrayList<>(); |
| for (CtfTmfEventField element : getValue()) { |
| strings.add(element.getFormattedValue()); |
| } |
| fFormattedValue = strings.toString(); |
| } |
| return fFormattedValue; |
| } |
| } |
| |
| /** |
| * CTF field implementation for floats. |
| * |
| * @author emathko |
| */ |
| final class CTFFloatField extends CtfTmfEventField { |
| |
| /** |
| * Constructor for CTFFloatField. |
| * |
| * @param value |
| * The float value (actually a double) of this field |
| * @param name |
| * The name of this field |
| */ |
| protected CTFFloatField(@NonNull String name, double value) { |
| super(name, value, null); |
| } |
| |
| @Override |
| public Double getValue() { |
| return (Double) super.getValue(); |
| } |
| } |
| |
| /** |
| * The CTF field implementation for Enum fields |
| * |
| * @author Bernd Hufmann |
| */ |
| final class CTFEnumField extends CtfTmfEventField { |
| |
| /** |
| * Constructor for CTFEnumField. |
| * |
| * @param enumValue |
| * The Enum value consisting of a pair of Enum value name and its |
| * long value |
| * @param name |
| * The name of this field |
| */ |
| CTFEnumField(@NonNull String name, CtfEnumPair enumValue) { |
| super(name, new CtfEnumPair(enumValue.getFirst(), |
| enumValue.getSecond()), null); |
| } |
| |
| @Override |
| public CtfEnumPair getValue() { |
| return (CtfEnumPair) super.getValue(); |
| } |
| } |
| |
| /** |
| * The CTF field implementation for struct fields with sub-fields |
| * |
| * @author gbastien |
| */ |
| final class CTFStructField extends CtfTmfEventField { |
| |
| /** |
| * Constructor for CTFStructField. |
| * |
| * @param fields |
| * The children of this field |
| * @param name |
| * The name of this field |
| */ |
| CTFStructField(@NonNull String name, CtfTmfEventField[] fields) { |
| super(name, fields, fields); |
| } |
| |
| @Override |
| public CtfTmfEventField[] getValue() { |
| return (CtfTmfEventField[]) super.getValue(); |
| } |
| |
| @Override |
| public String getFormattedValue() { |
| return Arrays.toString(getValue()); |
| } |
| |
| } |
| |
| /** |
| * The CTF field implementation for variant fields its child |
| * |
| * @author gbastien |
| */ |
| final class CTFVariantField extends CtfTmfEventField { |
| |
| /** |
| * Constructor for CTFVariantField. |
| * |
| * @param field |
| * The field selected for this variant |
| * @param name |
| * The name of this field |
| */ |
| CTFVariantField(@NonNull String name, CtfTmfEventField field) { |
| super(name, field, new CtfTmfEventField[] { field }); |
| } |
| |
| @Override |
| public CtfTmfEventField getValue() { |
| return (CtfTmfEventField) super.getValue(); |
| } |
| |
| @Override |
| public ITmfEventField getField(final String... path) { |
| /* |
| * We use the == to make sure that this constant was used, otherwise, it could |
| * conflict with a field with the same name |
| */ |
| if (path.length == 1 && path[0] == FIELD_VARIANT_SELECTED) { |
| Iterator<ITmfEventField> it = getFields().iterator(); |
| return it.hasNext() ? it.next() : null; |
| } |
| return super.getField(path); |
| } |
| |
| } |
| |
| /* Implement other possible fields types here... */ |