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