tmf: Move plugins to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.analysis.xml.ui / src / org / eclipse / linuxtools / internal / tmf / analysis / xml / ui / views / xychart / XmlXYViewer.java
1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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 * Contributors:
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.internal.tmf.analysis.xml.ui.views.xychart;
14
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.regex.Pattern;
21
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.eclipse.linuxtools.internal.tmf.analysis.xml.ui.Activator;
26 import org.eclipse.linuxtools.internal.tmf.analysis.xml.ui.TmfXmlUiStrings;
27 import org.eclipse.linuxtools.internal.tmf.analysis.xml.ui.views.XmlViewInfo;
28 import org.eclipse.linuxtools.statesystem.core.ITmfStateSystem;
29 import org.eclipse.linuxtools.statesystem.core.exceptions.AttributeNotFoundException;
30 import org.eclipse.linuxtools.statesystem.core.exceptions.StateSystemDisposedException;
31 import org.eclipse.linuxtools.statesystem.core.exceptions.StateValueTypeException;
32 import org.eclipse.linuxtools.statesystem.core.exceptions.TimeRangeException;
33 import org.eclipse.linuxtools.statesystem.core.statevalue.ITmfStateValue;
34 import org.eclipse.linuxtools.tmf.analysis.xml.core.model.ITmfXmlModelFactory;
35 import org.eclipse.linuxtools.tmf.analysis.xml.core.model.ITmfXmlStateAttribute;
36 import org.eclipse.linuxtools.tmf.analysis.xml.core.model.TmfXmlLocation;
37 import org.eclipse.linuxtools.tmf.analysis.xml.core.model.readonly.TmfXmlReadOnlyModelFactory;
38 import org.eclipse.linuxtools.tmf.analysis.xml.core.module.IXmlStateSystemContainer;
39 import org.eclipse.linuxtools.tmf.analysis.xml.core.module.XmlUtils;
40 import org.eclipse.linuxtools.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
41 import org.eclipse.linuxtools.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
42 import org.eclipse.linuxtools.tmf.core.statesystem.TmfStateSystemAnalysisModule;
43 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
44 import org.eclipse.linuxtools.tmf.ui.viewers.xycharts.linecharts.TmfCommonXLineChartViewer;
45 import org.eclipse.swt.widgets.Composite;
46 import org.w3c.dom.Element;
47
48 /**
49 * Main viewer to display XML-defined xy charts. It uses an XML
50 * {@link TmfXmlUiStrings#XY_VIEW} element from an XML file. This element
51 * defines which entries from the state system will be shown and also gives
52 * additional information on the presentation of the view.
53 *
54 * @author Geneviève Bastien
55 */
56 public class XmlXYViewer extends TmfCommonXLineChartViewer {
57
58 private static final String SPLIT_STRING = "/"; //$NON-NLS-1$
59 /** Timeout between updates in the updateData thread */
60 private static final long BUILD_UPDATE_TIMEOUT = 500;
61
62 @SuppressWarnings("null")
63 private static final @NonNull Pattern WILDCARD_PATTERN = Pattern.compile("\\*"); //$NON-NLS-1$
64
65 private final ITmfXmlModelFactory fFactory = TmfXmlReadOnlyModelFactory.getInstance();
66 private final Map<Integer, SeriesData> fSeriesData = new HashMap<>();
67
68 private final XmlViewInfo fViewInfo;
69
70 /** XML Model elements to use to create the series */
71 private @Nullable ITmfXmlStateAttribute fDisplay;
72 private @Nullable ITmfXmlStateAttribute fSeriesName;
73 private @Nullable XmlXYEntry fEntry;
74
75 private enum DisplayType {
76 ABSOLUTE,
77 DELTA
78 }
79
80 /**
81 * The information related to one series on the chart
82 */
83 private class SeriesData {
84
85 private final double[] fYValues;
86 private final @Nullable double[] fYAbsoluteValues;
87 private final Integer fDisplayQuark;
88 private final String fName;
89 private final DisplayType fType;
90
91 public SeriesData(int length, int attributeQuark, String seriesName, DisplayType type) {
92 fYValues = new double[length];
93 fDisplayQuark = attributeQuark;
94 fName = seriesName;
95 fType = type;
96 switch (fType) {
97 case DELTA:
98 fYAbsoluteValues = new double[length];
99 break;
100 case ABSOLUTE:
101 default:
102 fYAbsoluteValues = null;
103 break;
104 }
105
106 }
107
108 public double[] getYValues() {
109 return fYValues;
110 }
111
112 public Integer getDisplayQuark() {
113 return fDisplayQuark;
114 }
115
116 public String getSeriesName() {
117 return fName;
118 }
119
120 public void setYValue(int i, double yvalue) {
121 switch (fType) {
122 case DELTA:
123 double[] absoluteVals = fYAbsoluteValues;
124 if (absoluteVals == null) {
125 throw new IllegalStateException();
126 }
127 absoluteVals[i] = yvalue;
128 /*
129 * At the first timestamp, the delta value should be 0 since we
130 * do not have the previous values
131 */
132 double prevValue = yvalue;
133 if (i > 0) {
134 prevValue = absoluteVals[i - 1];
135 }
136 fYValues[i] = yvalue - prevValue;
137 break;
138 case ABSOLUTE:
139 default:
140 fYValues[i] = yvalue;
141 break;
142 }
143
144 }
145 }
146
147 private static class XmlXYEntry implements IXmlStateSystemContainer {
148
149 private final ITmfStateSystem fStateSystem;
150 private final String fPath;
151 private final DisplayType fType;
152
153 public XmlXYEntry(ITmfStateSystem stateSystem, String path, Element entryElement) {
154 fStateSystem = stateSystem;
155 fPath = path;
156 switch (entryElement.getAttribute(TmfXmlUiStrings.DISPLAY_TYPE)) {
157 case TmfXmlUiStrings.DISPLAY_TYPE_DELTA:
158 fType = DisplayType.DELTA;
159 break;
160 case TmfXmlUiStrings.DISPLAY_TYPE_ABSOLUTE:
161 default:
162 fType = DisplayType.ABSOLUTE;
163 break;
164 }
165 }
166
167 @Override
168 public @Nullable String getAttributeValue(@Nullable String name) {
169 return name;
170 }
171
172 @Override
173 public ITmfStateSystem getStateSystem() {
174 return fStateSystem;
175 }
176
177 @Override
178 public @Nullable Iterable<TmfXmlLocation> getLocations() {
179 return Collections.EMPTY_SET;
180 }
181
182 public DisplayType getType() {
183 return fType;
184 }
185
186 public List<Integer> getQuarks() {
187 /* Get the list of quarks to process with this path */
188 String[] paths = fPath.split(SPLIT_STRING);
189 @SuppressWarnings("null")
190 @NonNull List<Integer> quarks = Collections.singletonList(IXmlStateSystemContainer.ROOT_QUARK);
191
192 try {
193 for (String path : paths) {
194 List<Integer> subQuarks = new LinkedList<>();
195 /* Replace * by .* to have a regex string */
196 String name = WILDCARD_PATTERN.matcher(path).replaceAll(".*"); //$NON-NLS-1$
197 for (int relativeQuark : quarks) {
198 subQuarks.addAll(fStateSystem.getSubAttributes(relativeQuark, false, name));
199 }
200 quarks = subQuarks;
201 }
202 } catch (AttributeNotFoundException e) {
203 /*
204 * We get all attributes from the state system itself, this
205 * should not happen.
206 */
207 throw new IllegalStateException();
208 }
209 return quarks;
210 }
211 }
212
213 /**
214 * Constructor
215 *
216 * @param parent
217 * parent view
218 * @param viewInfo
219 * The view info object
220 */
221 public XmlXYViewer(@Nullable Composite parent, XmlViewInfo viewInfo) {
222 super(parent, Messages.XmlXYViewer_DefaultViewerTitle, Messages.XmlXYViewer_DefaultXAxis, Messages.XmlXYViewer_DefaultYAxis);
223 fViewInfo = viewInfo;
224 }
225
226 @Override
227 protected void updateData(long start, long end, int nb, @Nullable IProgressMonitor monitor) {
228
229 ITmfXmlStateAttribute display = fDisplay;
230 ITmfXmlStateAttribute seriesNameAttrib = fSeriesName;
231 XmlXYEntry entry = fEntry;
232 if (getTrace() == null || display == null || entry == null) {
233 return;
234 }
235 ITmfStateSystem ss = entry.getStateSystem();
236
237 double[] xvalues = getXAxis(start, end, nb);
238 setXAxis(xvalues);
239
240 boolean complete = false;
241 long currentEnd = start;
242
243 while (!complete && currentEnd < end) {
244 if (monitor != null && monitor.isCanceled()) {
245 return;
246 }
247
248 complete = ss.waitUntilBuilt(BUILD_UPDATE_TIMEOUT);
249 currentEnd = ss.getCurrentEndTime();
250 try {
251 List<Integer> quarks = entry.getQuarks();
252 long traceStart = getStartTime();
253 long traceEnd = getEndTime();
254 long offset = this.getTimeOffset();
255
256 /* Initialize quarks and series names */
257 for (int quark : quarks) {
258 String seriesName = null;
259 if (seriesNameAttrib == null) {
260 seriesName = ss.getAttributeName(quark);
261 } else {
262 int seriesNameQuark = seriesNameAttrib.getAttributeQuark(quark);
263 try {
264 ITmfStateValue seriesNameValue = ss.querySingleState(start, seriesNameQuark).getStateValue();
265 if (!seriesNameValue.isNull()) {
266 seriesName = seriesNameValue.toString();
267 }
268 if (seriesName == null || seriesName.isEmpty()) {
269 seriesName = ss.getAttributeName(quark);
270 }
271 } catch (TimeRangeException e) {
272 /*
273 * The attribute did not exist at this point, simply
274 * use attribute name as series name
275 */
276 seriesName = ss.getAttributeName(quark);
277 }
278 }
279 if (seriesName == null) {
280 throw new IllegalStateException();
281 }
282 fSeriesData.put(quark, new SeriesData(xvalues.length, display.getAttributeQuark(quark), seriesName, entry.getType()));
283 }
284 double yvalue = 0.0;
285 for (int i = 0; i < xvalues.length; i++) {
286 if (monitor != null && monitor.isCanceled()) {
287 return;
288 }
289 double x = xvalues[i];
290 long time = (long) x + offset;
291 // make sure that time is in the trace range after double to
292 // long conversion
293 time = time < traceStart ? traceStart : time;
294 time = time > traceEnd ? traceEnd : time;
295
296 for (int quark : quarks) {
297 try {
298 yvalue = ss.querySingleState(time, fSeriesData.get(quark).getDisplayQuark()).getStateValue().unboxLong();
299 fSeriesData.get(quark).setYValue(i, yvalue);
300 } catch (TimeRangeException e) {
301 fSeriesData.get(quark).setYValue(i, 0);
302 }
303 }
304 }
305 for (int quark : quarks) {
306 setSeries(fSeriesData.get(quark).getSeriesName(), fSeriesData.get(quark).getYValues());
307 }
308 updateDisplay();
309 } catch (AttributeNotFoundException | StateValueTypeException e) {
310 Activator.logError("Error updating the data of XML XY view", e); //$NON-NLS-1$
311 } catch (StateSystemDisposedException e) {
312 return;
313 }
314 }
315
316 }
317
318 @Override
319 protected void initializeDataSource() {
320 super.initializeDataSource();
321
322 ITmfTrace trace = this.getTrace();
323 if (trace == null) {
324 return;
325 }
326
327 Element viewElement = fViewInfo.getViewElement(TmfXmlUiStrings.XY_VIEW);
328 if (viewElement == null) {
329 return;
330 }
331
332 Iterable<String> analysisIds = fViewInfo.getViewAnalysisIds(viewElement);
333
334 List<ITmfAnalysisModuleWithStateSystems> stateSystemModules = new LinkedList<>();
335 if (!analysisIds.iterator().hasNext()) {
336 /*
337 * No analysis specified, take all state system analysis modules
338 */
339 for (ITmfAnalysisModuleWithStateSystems module : trace.getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems.class)) {
340 stateSystemModules.add(module);
341 }
342 } else {
343 for (String moduleId : analysisIds) {
344 @SuppressWarnings("resource")
345 ITmfAnalysisModuleWithStateSystems module = trace.getAnalysisModuleOfClass(ITmfAnalysisModuleWithStateSystems.class, moduleId);
346 if (module != null) {
347 stateSystemModules.add(module);
348 }
349 }
350 }
351
352 /** Initialize the data */
353 fDisplay = null;
354 fSeriesName = null;
355 ITmfStateSystem ss = null;
356 fEntry = null;
357
358 /* Schedule all state systems */
359 for (ITmfAnalysisModuleWithStateSystems module : stateSystemModules) {
360 module.schedule();
361 if (module instanceof TmfStateSystemAnalysisModule) {
362 ((TmfStateSystemAnalysisModule) module).waitForInitialization();
363 }
364 for (ITmfStateSystem ssq : module.getStateSystems()) {
365 if (ssq != null) {
366 ss = ssq;
367 break;
368 }
369 }
370 }
371 if (ss == null) {
372 return;
373 }
374
375 /*
376 * Initialize state attributes. There should be only one entry element
377 * for XY charts.
378 */
379 List<Element> entries = XmlUtils.getChildElements(viewElement, TmfXmlUiStrings.ENTRY_ELEMENT);
380 Element entryElement = entries.get(0);
381 String path = entryElement.getAttribute(TmfXmlUiStrings.PATH);
382 if (path.isEmpty()) {
383 path = TmfXmlStrings.WILDCARD;
384 }
385 XmlXYEntry entry = new XmlXYEntry(ss, path, entryElement);
386 fEntry = entry;
387
388 /* Get the display element to use */
389 List<Element> displayElements = XmlUtils.getChildElements(entryElement, TmfXmlUiStrings.DISPLAY_ELEMENT);
390 if (displayElements.isEmpty()) {
391 Activator.logWarning(String.format("XML view: entry for %s should have a display element", path)); //$NON-NLS-1$
392 return;
393 }
394 Element displayElement = displayElements.get(0);
395 fDisplay = fFactory.createStateAttribute(displayElement, entry);
396
397 /* Get the series name element to use */
398 List<Element> seriesNameElements = XmlUtils.getChildElements(entryElement, TmfXmlUiStrings.NAME_ELEMENT);
399 if (!seriesNameElements.isEmpty()) {
400 Element seriesNameElement = seriesNameElements.get(0);
401 fSeriesName = fFactory.createStateAttribute(seriesNameElement, entry);
402 }
403
404 }
405
406 /**
407 * Tells the viewer that the view info has been updated and the viewer needs
408 * to be reinitialized
409 */
410 public void viewInfoUpdated() {
411 reinitialize();
412 }
413
414 }
This page took 0.043729 seconds and 5 git commands to generate.