/*******************************************************************************
* 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.internal.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 java.util.Map.Entry;
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.segment.TmfXmlPatternSegment;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
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.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
*/
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
* @param scenarioInfo
* The active scenario details. Or <code>null</code> if there is
* no scenario.
* @return The pattern segment generated
*/
public TmfXmlPatternSegment generatePatternSegment(ITmfEvent event, ITmfTimestamp start, ITmfTimestamp end, @Nullable TmfXmlScenarioInfo scenarioInfo) {
int scale = event.getTimestamp().getScale();
long startValue = start.toNanos();
long endValue = end.toNanos();
String segmentName = getPatternSegmentName(event, scenarioInfo);
Map<String, ITmfStateValue> fields = new HashMap<>();
setPatternSegmentContent(event, fields, scenarioInfo);
TmfXmlPatternSegment segment = new TmfXmlPatternSegment(startValue, endValue, scale, segmentName, fields);
if (fContainer instanceof XmlPatternStateProvider) {
((XmlPatternStateProvider) fContainer).getListener().onNewSegment(segment);
}
return segment;
}
/**
* Get the pattern segment name
*
* @param event
* The active event
* @param scenarioInfo
* The active scenario details. Or <code>null</code> if there is
* no scenario.
* @return The name of the segment
*/
private String getPatternSegmentName(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) {
return fSegmentType.getName(event, scenarioInfo);
}
/**
* 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 fields
* The map that will contained all the fields
* @param scenarioInfo
* The active scenario details. Or <code>null</code> if there is
* no scenario.
*/
private void setPatternSegmentContent(ITmfEvent event, Map<String, ITmfStateValue> fields, @Nullable TmfXmlScenarioInfo scenarioInfo) {
for (TmfXmlPatternSegmentField field : fFields) {
fields.put(field.getName().intern(), field.getValue(event, scenarioInfo));
}
if (scenarioInfo != null) {
addStoredFieldsContent(event, fields, scenarioInfo);
}
}
/**
* Query the stored fields path and add them to the content of the pattern
* segment. This is specific to pattern analysis.
*
* @param event
* The active event
* @param fields
* The segment fields
* @param info
* The active scenario details
*/
protected void addStoredFieldsContent(ITmfEvent event, Map<String, ITmfStateValue> fields, final TmfXmlScenarioInfo info) {
if (fContainer instanceof XmlPatternStateProvider) {
for (Entry<String, String> entry : ((XmlPatternStateProvider) fContainer).getStoredFields().entrySet()) {
ITmfStateValue value = ((XmlPatternStateProvider) fContainer).getHistoryBuilder().getStoredFieldValue(fContainer, entry.getValue(), info, event);
if (!value.isNull()) {
fields.put(entry.getValue().intern(), value);
}
}
}
}
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, @Nullable TmfXmlScenarioInfo scenarioInfo) {
try {
ITmfStateValue value = xmlStateValue.getValue(event, scenarioInfo);
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;
case CUSTOM:
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
* @param scenarioInfo
* The active scenario details. Or <code>null</code> if there
* is no scenario.
*/
public ITmfStateValue getValue(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) {
if (fStateValue != null) {
return fStateValue;
}
try {
return checkNotNull(fXmlStateValue).getValue(event, scenarioInfo);
} 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
* @param scenarioInfo
* The active scenario details. Or <code>null</code> if there
* is no scenario.
* @return The segment name
*/
public String getName(ITmfEvent event, @Nullable TmfXmlScenarioInfo scenarioInfo) {
StringBuilder name = new StringBuilder(PATTERN_SEGMENT_NAME_PREFIX);
if (fNameStateValue != null) {
getNameFromXmlStateValue(event, name, fNameStateValue, scenarioInfo);
} else {
name.append(fSegmentNameAttribute);
}
return name.toString().intern();
}
}
}