/******************************************************************************* * Copyright (c) 2010, 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 * * Contributors: * Patrick Tasse - Initial API and implementation *******************************************************************************/ package org.eclipse.tracecompass.tmf.core.parsers.custom; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString; import java.text.ParseException; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.tracecompass.internal.tmf.core.parsers.custom.CustomExtraField; import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; import org.eclipse.tracecompass.tmf.core.event.ITmfEventType; import org.eclipse.tracecompass.tmf.core.event.TmfEvent; import org.eclipse.tracecompass.tmf.core.event.TmfEventField; import org.eclipse.tracecompass.tmf.core.event.TmfEventType; import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTraceDefinition.OutputColumn; import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTraceDefinition.Tag; import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat; import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; import com.google.common.collect.Iterables; /** * Base event for custom text parsers. * * @author Patrick Tassé */ public class CustomEvent extends TmfEvent { /** Payload data map key * @since 2.1*/ protected enum Key { /** Timestamp input format */ TIMESTAMP_INPUT_FORMAT } /** Input format key * @deprecated Use {@link Key#TIMESTAMP_INPUT_FORMAT} instead. */ @Deprecated protected static final String TIMESTAMP_INPUT_FORMAT_KEY = "CE_TS_I_F"; //$NON-NLS-1$ /** Empty message */ protected static final String NO_MESSAGE = ""; //$NON-NLS-1$ /** Replacement for the super-class' timestamp field */ private @NonNull ITmfTimestamp customEventTimestamp; /** Replacement for the super-class' content field */ private ITmfEventField customEventContent; /** Replacement for the super-class' type field */ private ITmfEventType customEventType; /** The trace to which this event belongs */ protected CustomTraceDefinition fDefinition; /** * The payload data of this event, where the key is one of: the {@link Tag}, * the field name string if the tag is {@link Tag#OTHER}, or * {@link Key#TIMESTAMP_INPUT_FORMAT}. */ protected Map<Object, String> fData; /** * Basic constructor. * * @param definition * The trace definition to which this event belongs */ public CustomEvent(CustomTraceDefinition definition) { super(null, ITmfContext.UNKNOWN_RANK, null, null, null); fDefinition = definition; fData = new LinkedHashMap<>(); customEventTimestamp = TmfTimestamp.ZERO; } /** * Build a new CustomEvent from an existing TmfEvent. * * @param definition * The trace definition to which this event belongs * @param other * The TmfEvent to copy */ public CustomEvent(CustomTraceDefinition definition, @NonNull TmfEvent other) { super(other); fDefinition = definition; fData = new LinkedHashMap<>(); /* Set our overridden fields */ customEventTimestamp = other.getTimestamp(); customEventContent = other.getContent(); customEventType = other.getType(); } /** * Full constructor * * @param definition * Trace definition of this event * @param parentTrace * Parent trace object * @param timestamp * Timestamp of this event * @param type * Event type */ public CustomEvent(CustomTraceDefinition definition, ITmfTrace parentTrace, ITmfTimestamp timestamp, TmfEventType type) { /* Do not use upstream's fields for stuff we override */ super(parentTrace, ITmfContext.UNKNOWN_RANK, null, null, null); fDefinition = definition; fData = new LinkedHashMap<>(); /* Set our overridden fields */ if (timestamp == null) { customEventTimestamp = TmfTimestamp.ZERO; } else { customEventTimestamp = timestamp; } customEventContent = null; customEventType = type; } // ------------------------------------------------------------------------ // Overridden getters // ------------------------------------------------------------------------ @Override public ITmfTimestamp getTimestamp() { if (fData != null) { processData(); } return customEventTimestamp; } @Override public ITmfEventField getContent() { if (fData != null) { processData(); } return customEventContent; } @Override public ITmfEventType getType() { return customEventType; } @Override public String getName() { if (fData != null) { processData(); } return super.getName(); } // ------------------------------------------------------------------------ // Setters // ------------------------------------------------------------------------ /** * Set this event's timestamp * * @param timestamp * The new timestamp */ protected void setTimestamp(@NonNull ITmfTimestamp timestamp) { customEventTimestamp = timestamp; } /** * Set this event's content * * @param content * The new content */ protected void setContent(ITmfEventField content) { customEventContent = content; } /** * Get this event's content value. * <p> * This does not process the payload data and is therefore safe to call in * the middle of parsing an event. * * @return the event's content value. */ Object getContentValue() { return customEventContent.getValue(); } /** * Set this event's type * * @param type * The new type */ protected void setType(ITmfEventType type) { customEventType = type; } // ------------------------------------------------------------------------ // Other operations // ------------------------------------------------------------------------ /** * Get the contents of an event table cell for this event's row. * * @param index * The ID/index of the field to display. This corresponds to the * index in the event content. * @return The String to display in the cell * @deprecated Use {@link ITmfEventField#getField(String...)} instead. */ @Deprecated public String getEventString(int index) { Collection<? extends ITmfEventField> fields = getContent().getFields(); if (index < 0 || index >= fields.size()) { return ""; //$NON-NLS-1$ } return nullToEmptyString(checkNotNull(Iterables.get(fields, index)).getValue()); } private void processData() { // Remove the values as they are processed, so we can process the extra values at the end String timestampString = fData.remove(Tag.TIMESTAMP); String timestampInputFormat = fData.remove(Key.TIMESTAMP_INPUT_FORMAT); ITmfTimestamp timestamp = null; if (timestampInputFormat != null && timestampString != null) { TmfTimestampFormat timestampFormat = new TmfTimestampFormat(timestampInputFormat); try { long time = timestampFormat.parseValue(timestampString); timestamp = TmfTimestamp.fromNanos(getTrace().getTimestampTransform().transform(time)); setTimestamp(timestamp); } catch (ParseException e) { setTimestamp(TmfTimestamp.ZERO); } } else { setTimestamp(TmfTimestamp.ZERO); } // Update the custom event type of this event if set String eventName = fData.remove(Tag.EVENT_TYPE); ITmfEventType type = getType(); if (eventName != null && type instanceof CustomEventType) { ((CustomEventType) type).setName(eventName); } Map<String, TmfEventField> fieldMap = new LinkedHashMap<>(); for (OutputColumn outputColumn : fDefinition.outputs) { if (outputColumn.tag.equals(Tag.TIMESTAMP)) { if (timestamp != null && fDefinition.timeStampOutputFormat != null && !fDefinition.timeStampOutputFormat.isEmpty()) { TmfTimestampFormat timestampFormat = new TmfTimestampFormat(fDefinition.timeStampOutputFormat); fieldMap.put(outputColumn.name, new TmfEventField(outputColumn.name, timestampFormat.format(timestamp.getValue()), null)); } } else if (outputColumn.tag.equals(Tag.OTHER) || outputColumn.tag.equals(Tag.MESSAGE)) { Object key = (outputColumn.tag.equals(Tag.OTHER) ? outputColumn.name : outputColumn.tag); fieldMap.put(outputColumn.name, new TmfEventField(outputColumn.name, nullToEmptyString(fData.remove(key)), null)); } } // This event contains extra values, we process them now for (Entry<Object, String> entry : fData.entrySet()) { String fieldName = nullToEmptyString(entry.getKey().toString()); // Ignore extra fields if a field of same name is already set if (!fieldMap.containsKey(fieldName)) { fieldMap.put(fieldName, new CustomExtraField(fieldName, nullToEmptyString(entry.getValue()), null)); } } setContent(new CustomEventContent(customEventContent.getName(), customEventContent.getValue(), fieldMap.values().toArray(new ITmfEventField[fieldMap.size()]))); fData = null; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((fDefinition == null) ? 0 : fDefinition.hashCode()); result = prime * result + customEventTimestamp.hashCode(); result = prime * result + ((customEventContent == null) ? 0 : customEventContent.hashCode()); result = prime * result + ((customEventType == null) ? 0 : customEventType.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (!(obj instanceof CustomEvent)) { return false; } CustomEvent other = (CustomEvent) obj; if (!Objects.equals(fDefinition, other.fDefinition)) { return false; } if (!customEventTimestamp.equals(other.customEventTimestamp)) { return false; } if (!Objects.equals(customEventContent, other.customEventContent)) { return false; } if (!Objects.equals(customEventType, other.customEventType)) { return false; } return true; } }