1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.internal
.tmf
.analysis
.xml
.ui
.views
.xychart
;
15 import java
.util
.Collections
;
16 import java
.util
.HashMap
;
17 import java
.util
.LinkedList
;
18 import java
.util
.List
;
20 import java
.util
.regex
.Pattern
;
22 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
23 import org
.eclipse
.core
.runtime
.IStatus
;
24 import org
.eclipse
.jdt
.annotation
.NonNull
;
25 import org
.eclipse
.jdt
.annotation
.Nullable
;
26 import org
.eclipse
.swt
.widgets
.Composite
;
27 import org
.eclipse
.tracecompass
.internal
.tmf
.analysis
.xml
.ui
.Activator
;
28 import org
.eclipse
.tracecompass
.internal
.tmf
.analysis
.xml
.ui
.TmfXmlUiStrings
;
29 import org
.eclipse
.tracecompass
.internal
.tmf
.analysis
.xml
.ui
.views
.XmlViewInfo
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
36 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.model
.ITmfXmlModelFactory
;
37 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.model
.ITmfXmlStateAttribute
;
38 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.model
.TmfXmlLocation
;
39 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.model
.readonly
.TmfXmlReadOnlyModelFactory
;
40 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.module
.IXmlStateSystemContainer
;
41 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.module
.XmlUtils
;
42 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.stateprovider
.TmfXmlStrings
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.ITmfAnalysisModuleWithStateSystems
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.TmfStateSystemAnalysisModule
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
47 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.linecharts
.TmfCommonXLineChartViewer
;
48 import org
.w3c
.dom
.Element
;
51 * Main viewer to display XML-defined xy charts. It uses an XML
52 * {@link TmfXmlUiStrings#XY_VIEW} element from an XML file. This element
53 * defines which entries from the state system will be shown and also gives
54 * additional information on the presentation of the view.
56 * @author Geneviève Bastien
58 public class XmlXYViewer
extends TmfCommonXLineChartViewer
{
60 private static final String SPLIT_STRING
= "/"; //$NON-NLS-1$
61 /** Timeout between updates in the updateData thread */
62 private static final long BUILD_UPDATE_TIMEOUT
= 500;
64 @SuppressWarnings("null")
65 private static final @NonNull Pattern WILDCARD_PATTERN
= Pattern
.compile("\\*"); //$NON-NLS-1$
67 private final ITmfXmlModelFactory fFactory
= TmfXmlReadOnlyModelFactory
.getInstance();
68 private final Map
<Integer
, SeriesData
> fSeriesData
= new HashMap
<>();
70 private final XmlViewInfo fViewInfo
;
72 /** XML Model elements to use to create the series */
73 private @Nullable ITmfXmlStateAttribute fDisplay
;
74 private @Nullable ITmfXmlStateAttribute fSeriesName
;
75 private @Nullable XmlXYEntry fEntry
;
77 private enum DisplayType
{
83 * The information related to one series on the chart
85 private class SeriesData
{
87 private final double[] fYValues
;
88 private final @Nullable double[] fYAbsoluteValues
;
89 private final Integer fDisplayQuark
;
90 private final String fName
;
91 private final DisplayType fType
;
93 public SeriesData(int length
, int attributeQuark
, String seriesName
, DisplayType type
) {
94 fYValues
= new double[length
];
95 fDisplayQuark
= attributeQuark
;
100 fYAbsoluteValues
= new double[length
];
104 fYAbsoluteValues
= null;
110 public double[] getYValues() {
114 public Integer
getDisplayQuark() {
115 return fDisplayQuark
;
118 public String
getSeriesName() {
122 public void setYValue(int i
, double yvalue
) {
125 double[] absoluteVals
= fYAbsoluteValues
;
126 if (absoluteVals
== null) {
127 throw new IllegalStateException();
129 absoluteVals
[i
] = yvalue
;
131 * At the first timestamp, the delta value should be 0 since we
132 * do not have the previous values
134 double prevValue
= yvalue
;
136 prevValue
= absoluteVals
[i
- 1];
138 fYValues
[i
] = yvalue
- prevValue
;
142 fYValues
[i
] = yvalue
;
149 private static class XmlXYEntry
implements IXmlStateSystemContainer
{
151 private final ITmfStateSystem fStateSystem
;
152 private final String fPath
;
153 private final DisplayType fType
;
155 public XmlXYEntry(ITmfStateSystem stateSystem
, String path
, Element entryElement
) {
156 fStateSystem
= stateSystem
;
158 switch (entryElement
.getAttribute(TmfXmlUiStrings
.DISPLAY_TYPE
)) {
159 case TmfXmlUiStrings
.DISPLAY_TYPE_DELTA
:
160 fType
= DisplayType
.DELTA
;
162 case TmfXmlUiStrings
.DISPLAY_TYPE_ABSOLUTE
:
164 fType
= DisplayType
.ABSOLUTE
;
170 public @Nullable String
getAttributeValue(@Nullable String name
) {
175 public ITmfStateSystem
getStateSystem() {
180 public @Nullable Iterable
<TmfXmlLocation
> getLocations() {
181 return Collections
.EMPTY_SET
;
184 public DisplayType
getType() {
188 public List
<Integer
> getQuarks() {
189 /* Get the list of quarks to process with this path */
190 String
[] paths
= fPath
.split(SPLIT_STRING
);
191 @SuppressWarnings("null")
192 @NonNull List
<Integer
> quarks
= Collections
.singletonList(IXmlStateSystemContainer
.ROOT_QUARK
);
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
));
204 } catch (AttributeNotFoundException e
) {
206 * We get all attributes from the state system itself, this
209 throw new IllegalStateException();
221 * The view info object
223 public XmlXYViewer(@Nullable Composite parent
, XmlViewInfo viewInfo
) {
224 super(parent
, Messages
.XmlXYViewer_DefaultViewerTitle
, Messages
.XmlXYViewer_DefaultXAxis
, Messages
.XmlXYViewer_DefaultYAxis
);
225 fViewInfo
= viewInfo
;
229 protected void updateData(long start
, long end
, int nb
, @Nullable IProgressMonitor monitor
) {
231 ITmfXmlStateAttribute display
= fDisplay
;
232 ITmfXmlStateAttribute seriesNameAttrib
= fSeriesName
;
233 XmlXYEntry entry
= fEntry
;
234 if (getTrace() == null || display
== null || entry
== null) {
237 ITmfStateSystem ss
= entry
.getStateSystem();
239 double[] xvalues
= getXAxis(start
, end
, nb
);
242 boolean complete
= false;
243 long currentEnd
= start
;
245 while (!complete
&& currentEnd
< end
) {
246 if (monitor
!= null && monitor
.isCanceled()) {
250 complete
= ss
.waitUntilBuilt(BUILD_UPDATE_TIMEOUT
);
251 currentEnd
= ss
.getCurrentEndTime();
253 List
<Integer
> quarks
= entry
.getQuarks();
254 long traceStart
= getStartTime();
255 long traceEnd
= getEndTime();
256 long offset
= this.getTimeOffset();
258 /* Initialize quarks and series names */
259 for (int quark
: quarks
) {
260 String seriesName
= null;
261 if (seriesNameAttrib
== null) {
262 seriesName
= ss
.getAttributeName(quark
);
264 int seriesNameQuark
= seriesNameAttrib
.getAttributeQuark(quark
);
266 ITmfStateValue seriesNameValue
= ss
.querySingleState(start
, seriesNameQuark
).getStateValue();
267 if (!seriesNameValue
.isNull()) {
268 seriesName
= seriesNameValue
.toString();
270 if (seriesName
== null || seriesName
.isEmpty()) {
271 seriesName
= ss
.getAttributeName(quark
);
273 } catch (TimeRangeException e
) {
275 * The attribute did not exist at this point, simply
276 * use attribute name as series name
278 seriesName
= ss
.getAttributeName(quark
);
281 if (seriesName
== null) {
282 throw new IllegalStateException();
284 fSeriesData
.put(quark
, new SeriesData(xvalues
.length
, display
.getAttributeQuark(quark
), seriesName
, entry
.getType()));
287 for (int i
= 0; i
< xvalues
.length
; i
++) {
288 if (monitor
!= null && monitor
.isCanceled()) {
291 double x
= xvalues
[i
];
292 long time
= (long) x
+ offset
;
293 // make sure that time is in the trace range after double to
295 time
= time
< traceStart ? traceStart
: time
;
296 time
= time
> traceEnd ? traceEnd
: time
;
298 for (int quark
: quarks
) {
300 yvalue
= ss
.querySingleState(time
, fSeriesData
.get(quark
).getDisplayQuark()).getStateValue().unboxLong();
301 fSeriesData
.get(quark
).setYValue(i
, yvalue
);
302 } catch (TimeRangeException e
) {
303 fSeriesData
.get(quark
).setYValue(i
, 0);
307 for (int quark
: quarks
) {
308 setSeries(fSeriesData
.get(quark
).getSeriesName(), fSeriesData
.get(quark
).getYValues());
311 } catch (AttributeNotFoundException
| StateValueTypeException e
) {
312 Activator
.logError("Error updating the data of XML XY view", e
); //$NON-NLS-1$
313 } catch (StateSystemDisposedException e
) {
321 protected void initializeDataSource() {
322 super.initializeDataSource();
324 ITmfTrace trace
= this.getTrace();
329 Element viewElement
= fViewInfo
.getViewElement(TmfXmlUiStrings
.XY_VIEW
);
330 if (viewElement
== null) {
334 Iterable
<String
> analysisIds
= fViewInfo
.getViewAnalysisIds(viewElement
);
336 List
<ITmfAnalysisModuleWithStateSystems
> stateSystemModules
= new LinkedList
<>();
337 if (!analysisIds
.iterator().hasNext()) {
339 * No analysis specified, take all state system analysis modules
341 for (ITmfAnalysisModuleWithStateSystems module
: TmfTraceUtils
.getAnalysisModulesOfClass(trace
, ITmfAnalysisModuleWithStateSystems
.class)) {
342 stateSystemModules
.add(module
);
345 for (String moduleId
: analysisIds
) {
346 ITmfAnalysisModuleWithStateSystems module
= TmfTraceUtils
.getAnalysisModuleOfClass(trace
, ITmfAnalysisModuleWithStateSystems
.class, moduleId
);
347 if (module
!= null) {
348 stateSystemModules
.add(module
);
353 /** Initialize the data */
356 ITmfStateSystem ss
= null;
359 /* Schedule all state systems */
360 for (ITmfAnalysisModuleWithStateSystems module
: stateSystemModules
) {
361 IStatus status
= module
.schedule();
362 if (!status
.isOK()) {
365 if (module
instanceof TmfStateSystemAnalysisModule
) {
366 ((TmfStateSystemAnalysisModule
) module
).waitForInitialization();
368 for (ITmfStateSystem ssq
: module
.getStateSystems()) {
380 * Initialize state attributes. There should be only one entry element
383 List
<Element
> entries
= XmlUtils
.getChildElements(viewElement
, TmfXmlUiStrings
.ENTRY_ELEMENT
);
384 Element entryElement
= entries
.get(0);
385 String path
= entryElement
.getAttribute(TmfXmlUiStrings
.PATH
);
386 if (path
.isEmpty()) {
387 path
= TmfXmlStrings
.WILDCARD
;
389 XmlXYEntry entry
= new XmlXYEntry(ss
, path
, entryElement
);
392 /* Get the display element to use */
393 List
<Element
> displayElements
= XmlUtils
.getChildElements(entryElement
, TmfXmlUiStrings
.DISPLAY_ELEMENT
);
394 if (displayElements
.isEmpty()) {
395 Activator
.logWarning(String
.format("XML view: entry for %s should have a display element", path
)); //$NON-NLS-1$
398 Element displayElement
= displayElements
.get(0);
399 if (displayElement
== null) {
400 throw new IllegalStateException();
402 fDisplay
= fFactory
.createStateAttribute(displayElement
, entry
);
404 /* Get the series name element to use */
405 List
<Element
> seriesNameElements
= XmlUtils
.getChildElements(entryElement
, TmfXmlUiStrings
.NAME_ELEMENT
);
406 if (!seriesNameElements
.isEmpty()) {
407 Element seriesNameElement
= seriesNameElements
.get(0);
408 if (seriesNameElement
== null) {
409 throw new IllegalStateException();
411 fSeriesName
= fFactory
.createStateAttribute(seriesNameElement
, entry
);
417 * Tells the viewer that the view info has been updated and the viewer needs
418 * to be reinitialized
420 public void viewInfoUpdated() {