| 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2016 Ericsson |
| 3 | * |
| 4 | * All rights reserved. This program and the accompanying materials are |
| 5 | * made available under the terms of the Eclipse Public License v1.0 which |
| 6 | * accompanies this distribution, and is available at |
| 7 | * http://www.eclipse.org/legal/epl-v10.html |
| 8 | ******************************************************************************/ |
| 9 | package org.eclipse.tracecompass.tmf.analysis.xml.core.model; |
| 10 | |
| 11 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| 12 | |
| 13 | import java.util.ArrayList; |
| 14 | import java.util.HashMap; |
| 15 | import java.util.List; |
| 16 | import java.util.Map; |
| 17 | |
| 18 | import org.eclipse.jdt.annotation.Nullable; |
| 19 | import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.Activator; |
| 20 | import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.pattern.stateprovider.XmlPatternStateProvider; |
| 21 | import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; |
| 22 | import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException; |
| 23 | import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; |
| 24 | import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; |
| 25 | import org.eclipse.tracecompass.tmf.analysis.xml.core.module.IXmlStateSystemContainer; |
| 26 | import org.eclipse.tracecompass.tmf.analysis.xml.core.segment.TmfXmlPatternSegment; |
| 27 | import org.eclipse.tracecompass.tmf.analysis.xml.core.stateprovider.TmfXmlStrings; |
| 28 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
| 29 | import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| 30 | import org.w3c.dom.Element; |
| 31 | import org.w3c.dom.NodeList; |
| 32 | |
| 33 | /** |
| 34 | * This class defines a pattern segment builder. It will use the XML description |
| 35 | * of the pattern segment to generate it at runtime. |
| 36 | * |
| 37 | * @author Jean-Christian Kouame |
| 38 | * @since 2.0 |
| 39 | * |
| 40 | */ |
| 41 | public class TmfXmlPatternSegmentBuilder { |
| 42 | |
| 43 | /** |
| 44 | * The string unknown |
| 45 | */ |
| 46 | public static final String UNKNOWN_STRING = "unknown"; //$NON-NLS-1$ |
| 47 | /** |
| 48 | * Prefix for the pattern segment name |
| 49 | */ |
| 50 | public static final String PATTERN_SEGMENT_NAME_PREFIX = "seg_"; //$NON-NLS-1$ |
| 51 | private final ITmfXmlModelFactory fModelFactory; |
| 52 | private final IXmlStateSystemContainer fContainer; |
| 53 | private final List<TmfXmlPatternSegmentField> fFields = new ArrayList<>(); |
| 54 | private final TmfXmlPatternSegmentType fSegmentType; |
| 55 | |
| 56 | /** |
| 57 | * @param modelFactory |
| 58 | * The factory used to create XML model elements |
| 59 | * @param node |
| 60 | * XML element of the pattern segment builder |
| 61 | * @param parent |
| 62 | * The state system container this pattern segment builder |
| 63 | * belongs to |
| 64 | */ |
| 65 | public TmfXmlPatternSegmentBuilder(ITmfXmlModelFactory modelFactory, Element node, IXmlStateSystemContainer parent) { |
| 66 | fModelFactory = modelFactory; |
| 67 | fContainer = parent; |
| 68 | |
| 69 | //Set the XML type of the segment |
| 70 | NodeList nodesSegmentType = node.getElementsByTagName(TmfXmlStrings.SEGMENT_TYPE); |
| 71 | Element element = (Element) nodesSegmentType.item(0); |
| 72 | if (element == null) { |
| 73 | throw new IllegalArgumentException(); |
| 74 | } |
| 75 | fSegmentType = new TmfXmlPatternSegmentType(element); |
| 76 | |
| 77 | //Set the XML content of the segment |
| 78 | NodeList nodesSegmentContent = node.getElementsByTagName(TmfXmlStrings.SEGMENT_CONTENT); |
| 79 | Element fContentElement = (Element) nodesSegmentContent.item(0); |
| 80 | if (fContentElement != null) { |
| 81 | NodeList nodesSegmentField = fContentElement.getElementsByTagName(TmfXmlStrings.SEGMENT_FIELD); |
| 82 | for (int i = 0; i < nodesSegmentField.getLength(); i++) { |
| 83 | fFields.add(new TmfXmlPatternSegmentField(checkNotNull((Element) nodesSegmentField.item(i)))); |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Generate a pattern segment |
| 90 | * |
| 91 | * @param event |
| 92 | * The active event |
| 93 | * @param start |
| 94 | * Start time of the pattern segment to generate |
| 95 | * @param end |
| 96 | * End time of the pattern segment to generate |
| 97 | * @return The pattern segment generated |
| 98 | */ |
| 99 | public TmfXmlPatternSegment generatePatternSegment(ITmfEvent event, ITmfTimestamp start, ITmfTimestamp end) { |
| 100 | int scale = event.getTimestamp().getScale(); |
| 101 | long startValue = start.toNanos(); |
| 102 | long endValue = end.toNanos(); |
| 103 | String segmentName = getPatternSegmentName(event); |
| 104 | Map<String, ITmfStateValue> fields = new HashMap<>(); |
| 105 | setPatternSegmentContent(event, start, end, fields); |
| 106 | TmfXmlPatternSegment segment = new TmfXmlPatternSegment(startValue, endValue, scale, segmentName, fields); |
| 107 | if (fContainer instanceof XmlPatternStateProvider) { |
| 108 | ((XmlPatternStateProvider) fContainer).getListener().onNewSegment(segment); |
| 109 | } |
| 110 | return segment; |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * Get the pattern segment name |
| 115 | * |
| 116 | * @param event |
| 117 | * The active event |
| 118 | * @return The name of the segment |
| 119 | */ |
| 120 | private String getPatternSegmentName(ITmfEvent event) { |
| 121 | return fSegmentType.getName(event); |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Compute all the fields and their values for this pattern segment. The |
| 126 | * fields could be constant values or values queried from the state system. |
| 127 | * |
| 128 | * @param event |
| 129 | * The current event |
| 130 | * @param start |
| 131 | * The start timestamp of this segment |
| 132 | * @param end |
| 133 | * The end timestamp of this segment |
| 134 | * @param fields |
| 135 | * The map that will contained all the fields |
| 136 | */ |
| 137 | private void setPatternSegmentContent(ITmfEvent event, ITmfTimestamp start, ITmfTimestamp end, Map<String, ITmfStateValue> fields) { |
| 138 | for (TmfXmlPatternSegmentField field : fFields) { |
| 139 | fields.put(field.getName(), field.getValue(event)); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | private static ITmfStateValue getStateValueFromConstant(String constantValue, String type) { |
| 144 | switch (type) { |
| 145 | case TmfXmlStrings.TYPE_INT: |
| 146 | return TmfStateValue.newValueInt(Integer.parseInt(constantValue)); |
| 147 | case TmfXmlStrings.TYPE_LONG: |
| 148 | return TmfStateValue.newValueLong(Long.parseLong(constantValue)); |
| 149 | case TmfXmlStrings.TYPE_STRING: |
| 150 | return TmfStateValue.newValueString(constantValue); |
| 151 | case TmfXmlStrings.TYPE_NULL: |
| 152 | return TmfStateValue.nullValue(); |
| 153 | default: |
| 154 | throw new IllegalArgumentException("Invalid type of field : " + type); //$NON-NLS-1$ |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | private static void getNameFromXmlStateValue(ITmfEvent event, StringBuilder builder, ITmfXmlStateValue xmlStateValue) { |
| 159 | try { |
| 160 | ITmfStateValue value = xmlStateValue.getValue(event); |
| 161 | switch (value.getType()) { |
| 162 | case DOUBLE: |
| 163 | builder.append(value.unboxDouble()); |
| 164 | break; |
| 165 | case INTEGER: |
| 166 | builder.append(value.unboxInt()); |
| 167 | break; |
| 168 | case LONG: |
| 169 | builder.append(value.unboxLong()); |
| 170 | break; |
| 171 | case NULL: |
| 172 | builder.append(UNKNOWN_STRING); |
| 173 | break; |
| 174 | case STRING: |
| 175 | builder.append(value.unboxStr()); |
| 176 | break; |
| 177 | default: |
| 178 | throw new StateValueTypeException("Invalid type of state value"); //$NON-NLS-1$ |
| 179 | } |
| 180 | } catch (AttributeNotFoundException e) { |
| 181 | Activator.logInfo("Impossible to get the state value", e); //$NON-NLS-1$ |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * This class represents the segment fields described in the XML. The real |
| 187 | * value of the field will be set at runtime using the active event. |
| 188 | * |
| 189 | * @author Jean-Christian Kouame |
| 190 | * |
| 191 | */ |
| 192 | private class TmfXmlPatternSegmentField { |
| 193 | private final String fName; |
| 194 | private final String fType; |
| 195 | private final @Nullable ITmfStateValue fStateValue; |
| 196 | private final @Nullable ITmfXmlStateValue fXmlStateValue; |
| 197 | |
| 198 | /** |
| 199 | * Constructor |
| 200 | * |
| 201 | * @param element |
| 202 | * The pattern segment field node |
| 203 | */ |
| 204 | public TmfXmlPatternSegmentField(Element element) { |
| 205 | // The name, the type and the value of each field could respectively |
| 206 | // be found from the attributes name, type and value. If the value |
| 207 | // attribute is not available, try to find it from the child state |
| 208 | // value. |
| 209 | fName = element.getAttribute(TmfXmlStrings.NAME); |
| 210 | fType = element.getAttribute(TmfXmlStrings.TYPE); |
| 211 | String constantValue = element.getAttribute(TmfXmlStrings.VALUE); |
| 212 | if (constantValue.isEmpty() && !fType.equals(TmfXmlStrings.TYPE_NULL)) { |
| 213 | fStateValue = null; |
| 214 | Element elementFieldStateValue = (Element) element.getElementsByTagName(TmfXmlStrings.STATE_VALUE).item(0); |
| 215 | if (elementFieldStateValue == null) { |
| 216 | throw new IllegalArgumentException("The value of the field " + fName + " is missing"); //$NON-NLS-1$ //$NON-NLS-2$ |
| 217 | } |
| 218 | fXmlStateValue = fModelFactory.createStateValue(elementFieldStateValue, fContainer, new ArrayList<>()); |
| 219 | } else { |
| 220 | fStateValue = getStateValueFromConstant(constantValue, fType); |
| 221 | fXmlStateValue = null; |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * Get the real value of the XML pattern segment field |
| 227 | * |
| 228 | * @param event |
| 229 | * The active event |
| 230 | * @return The state value representing the value of the XML pattern |
| 231 | * segment field |
| 232 | */ |
| 233 | public ITmfStateValue getValue(ITmfEvent event) { |
| 234 | if (fStateValue != null) { |
| 235 | return fStateValue; |
| 236 | } |
| 237 | try { |
| 238 | return checkNotNull(fXmlStateValue).getValue(event); |
| 239 | } catch (AttributeNotFoundException e) { |
| 240 | Activator.logError("Failed to get the state value", e); //$NON-NLS-1$ |
| 241 | } |
| 242 | throw new IllegalStateException("Failed to get the value for the segment field " + fName); //$NON-NLS-1$ |
| 243 | } |
| 244 | |
| 245 | /** |
| 246 | * Get the name of the XML pattern segment field |
| 247 | * |
| 248 | * @return The name |
| 249 | */ |
| 250 | public String getName() { |
| 251 | return fName; |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * This class represents the segment type described in XML. |
| 257 | * |
| 258 | * @author Jean-Christian Kouame |
| 259 | * |
| 260 | */ |
| 261 | private class TmfXmlPatternSegmentType { |
| 262 | private final String fSegmentNameAttribute; |
| 263 | private final @Nullable ITmfXmlStateValue fNameStateValue; |
| 264 | |
| 265 | /** |
| 266 | * Constructor |
| 267 | * |
| 268 | * @param element |
| 269 | * The pattern segment type node |
| 270 | */ |
| 271 | public TmfXmlPatternSegmentType(Element element) { |
| 272 | // Try to find the segment name from the name attribute. If |
| 273 | // attribute not available, try to find it from the child state value |
| 274 | fSegmentNameAttribute = element.getAttribute(TmfXmlStrings.SEGMENT_NAME); |
| 275 | if (!fSegmentNameAttribute.isEmpty()) { |
| 276 | fNameStateValue = null; |
| 277 | } else { |
| 278 | Element elementSegmentNameStateValue = (Element) element.getElementsByTagName(TmfXmlStrings.STATE_VALUE).item(0); |
| 279 | if (elementSegmentNameStateValue == null) { |
| 280 | throw new IllegalArgumentException("Failed to get the segment name. A state value is needed."); //$NON-NLS-1$ |
| 281 | } |
| 282 | fNameStateValue = fModelFactory.createStateValue(elementSegmentNameStateValue, fContainer, new ArrayList<>()); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | /** |
| 287 | * Get the name of the segment |
| 288 | * |
| 289 | * @param event |
| 290 | * The active event |
| 291 | * @return The segment name |
| 292 | */ |
| 293 | public String getName(ITmfEvent event) { |
| 294 | StringBuilder name = new StringBuilder(PATTERN_SEGMENT_NAME_PREFIX); |
| 295 | if (fNameStateValue != null) { |
| 296 | getNameFromXmlStateValue(event, name, fNameStateValue); |
| 297 | } else { |
| 298 | name.append(fSegmentNameAttribute); |
| 299 | } |
| 300 | return name.toString(); |
| 301 | } |
| 302 | } |
| 303 | } |