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