tmf: Fix legend image leak in Histogram view
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.analysis.xml.ui / src / org / eclipse / tracecompass / tmf / analysis / xml / ui / views / timegraph / XmlTimeGraphView.java
CommitLineData
1a23419e
FW
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 * Florian Wininger - Initial API and implementation
11 * Geneviève Bastien - Review of the initial implementation
12 *******************************************************************************/
13
2bdf0193 14package org.eclipse.tracecompass.tmf.analysis.xml.ui.views.timegraph;
1a23419e
FW
15
16import java.util.ArrayList;
17import java.util.Collections;
18import java.util.Comparator;
19import java.util.HashMap;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.TreeSet;
25
26import org.eclipse.core.runtime.IProgressMonitor;
e1c415b3 27import org.eclipse.core.runtime.IStatus;
1a23419e 28import org.eclipse.jdt.annotation.NonNull;
1a23419e
FW
29import org.eclipse.jface.util.IPropertyChangeListener;
30import org.eclipse.jface.util.PropertyChangeEvent;
1a23419e 31import org.eclipse.swt.widgets.Display;
2bdf0193
AM
32import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.Activator;
33import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.TmfXmlUiStrings;
34import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.views.XmlViewInfo;
e894a508 35import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
1dd75589 36import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
e894a508
AM
37import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
38import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
39import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
40import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
41import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
2bdf0193
AM
42import org.eclipse.tracecompass.tmf.analysis.xml.core.model.ITmfXmlModelFactory;
43import org.eclipse.tracecompass.tmf.analysis.xml.core.model.ITmfXmlStateAttribute;
44import org.eclipse.tracecompass.tmf.analysis.xml.core.model.readonly.TmfXmlReadOnlyModelFactory;
45import org.eclipse.tracecompass.tmf.analysis.xml.core.module.IXmlStateSystemContainer;
46import org.eclipse.tracecompass.tmf.analysis.xml.core.module.XmlUtils;
47import org.eclipse.tracecompass.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
48import org.eclipse.tracecompass.tmf.analysis.xml.ui.views.timegraph.XmlEntry.EntryDisplayType;
49import org.eclipse.tracecompass.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
50import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
51import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
52import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
53import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView;
54import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2;
55import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
56import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
57import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
58import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
59import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
60import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
1a23419e
FW
61import org.w3c.dom.Element;
62
63/**
64 * This view displays state system data in a time graph view. It uses an XML
65 * {@link TmfXmlUiStrings#TIME_GRAPH_VIEW} element from an XML file. This
66 * element defines which entries from the state system will be shown and also
67 * gives additional information on the presentation of the view (states, colors,
68 * etc)
69 *
70 * @author Florian Wininger
71 */
72public class XmlTimeGraphView extends AbstractTimeGraphView {
73
74 /** View ID. */
35423ee0 75 public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.analysis.xml.ui.views.timegraph"; //$NON-NLS-1$
1a23419e
FW
76
77 private static final String[] DEFAULT_COLUMN_NAMES = new String[] {
78 Messages.XmlTimeGraphView_ColumnName,
79 Messages.XmlTimeGraphView_ColumnId,
80 Messages.XmlTimeGraphView_ColumnParentId,
81 };
82
83 private static final String[] DEFAULT_FILTER_COLUMN_NAMES = new String[] {
84 Messages.XmlTimeGraphView_ColumnName,
85 Messages.XmlTimeGraphView_ColumnId
86 };
87
88 /** The relative weight of the sash */
89 private static final int[] fWeight = { 1, 2 };
90
91 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
92 private static final String SPLIT_STRING = "/"; //$NON-NLS-1$
93
35423ee0 94 private final @NonNull XmlViewInfo fViewInfo = new XmlViewInfo(ID);
1a23419e
FW
95 private final ITmfXmlModelFactory fFactory;
96
97 // ------------------------------------------------------------------------
98 // Constructors
99 // ------------------------------------------------------------------------
100
101 /**
102 * Default constructor
103 */
104 public XmlTimeGraphView() {
105 super(ID, new XmlPresentationProvider());
106 setWeight(fWeight);
107 setTreeColumns(DEFAULT_COLUMN_NAMES);
108 setTreeLabelProvider(new XmlTreeLabelProvider());
109 setFilterColumns(DEFAULT_FILTER_COLUMN_NAMES);
110 setFilterLabelProvider(new XmlTreeLabelProvider());
111 setEntryComparator(new XmlEntryComparator());
112 this.addPartPropertyListener(new IPropertyChangeListener() {
113 @Override
114 public void propertyChange(PropertyChangeEvent event) {
115 if (event.getProperty().equals(TmfXmlUiStrings.XML_OUTPUT_DATA)) {
35423ee0
GB
116 Object newValue = event.getNewValue();
117 if (newValue instanceof String) {
118 String data = (String) newValue;
119 fViewInfo.setViewData(data);
120 loadNewXmlView();
121 }
1a23419e
FW
122 }
123 }
124 });
1a23419e 125
1a23419e
FW
126 fFactory = TmfXmlReadOnlyModelFactory.getInstance();
127 }
128
1a23419e
FW
129 private void loadNewXmlView() {
130 rebuild();
131 }
132
133 private void setViewTitle(final String title) {
134 Display.getDefault().asyncExec(new Runnable() {
135 @Override
136 public void run() {
137 setPartName(title);
138 }
139 });
140
141 }
142
143 @Override
144 protected String getNextText() {
145 return Messages.XmlTimeGraphView_NextText;
146 }
147
148 @Override
149 protected String getNextTooltip() {
150 return Messages.XmlTimeGraphView_NextTooltip;
151 }
152
153 @Override
154 protected String getPrevText() {
155 return Messages.XmlTimeGraphView_PreviousText;
156 }
157
158 @Override
159 protected String getPrevTooltip() {
160 return Messages.XmlTimeGraphView_PreviousInterval;
161 }
162
163 /**
164 * Default label provider, it shows name, id and parent columns
165 *
166 * TODO: There should be a way to define columns in the XML
167 * */
168 private static class XmlTreeLabelProvider extends TreeLabelProvider {
169
170 @Override
171 public String getColumnText(Object element, int columnIndex) {
172 XmlEntry entry = (XmlEntry) element;
173
174 if (DEFAULT_COLUMN_NAMES[columnIndex].equals(Messages.XmlTimeGraphView_ColumnName)) {
175 return entry.getName();
176 } else if (DEFAULT_COLUMN_NAMES[columnIndex].equals(Messages.XmlTimeGraphView_ColumnId)) {
177 return entry.getId();
178 } else if (DEFAULT_COLUMN_NAMES[columnIndex].equals(Messages.XmlTimeGraphView_ColumnParentId)) {
179 return entry.getParentId();
180 }
181 return EMPTY_STRING;
182 }
183
184 }
185
186 private static class XmlEntryComparator implements Comparator<ITimeGraphEntry> {
187
188 @Override
189 public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
190
191 int result = 0;
192
193 if ((o1 instanceof XmlEntry) && (o2 instanceof XmlEntry)) {
194 XmlEntry entry1 = (XmlEntry) o1;
195 XmlEntry entry2 = (XmlEntry) o2;
196 result = entry1.getTrace().getStartTime().compareTo(entry2.getTrace().getStartTime());
197 if (result == 0) {
198 result = entry1.getTrace().getName().compareTo(entry2.getTrace().getName());
199 }
200 if (result == 0) {
201 result = entry1.getName().compareTo(entry2.getName());
202 }
203 }
204
205 if (result == 0) {
206 result = o1.getStartTime() < o2.getStartTime() ? -1 : o1.getStartTime() > o2.getStartTime() ? 1 : 0;
207 }
208
209 return result;
210 }
211 }
212
213 // ------------------------------------------------------------------------
214 // Internal
215 // ------------------------------------------------------------------------
216
217 @Override
218 protected void buildEventList(ITmfTrace trace, ITmfTrace parentTrace, IProgressMonitor monitor) {
219
220 /*
221 * Get the view element from the XML file. If the element can't be
222 * found, return.
223 */
35423ee0 224 Element viewElement = fViewInfo.getViewElement(TmfXmlUiStrings.TIME_GRAPH_VIEW);
1a23419e
FW
225 if (viewElement == null) {
226 return;
227 }
228 ITimeGraphPresentationProvider2 pres = this.getPresentationProvider();
229 if (pres instanceof XmlPresentationProvider) {
230 /*
231 * TODO: Each entry of a line could have their own states/color.
232 * That will require an update to the presentation provider
233 */
234 ((XmlPresentationProvider) pres).loadNewStates(viewElement);
235 }
236
35423ee0
GB
237 String title = fViewInfo.getViewTitle(viewElement);
238 if (title == null) {
239 title = Messages.XmlTimeGraphView_DefaultTitle;
1a23419e 240 }
35423ee0
GB
241 setViewTitle(title);
242 Set<String> analysisIds = fViewInfo.getViewAnalysisIds(viewElement);
243
1a23419e
FW
244 List<Element> entries = XmlUtils.getChildElements(viewElement, TmfXmlUiStrings.ENTRY_ELEMENT);
245 Set<XmlEntry> entryList = new TreeSet<>(getEntryComparator());
246 for (ITmfTrace aTrace : TmfTraceManager.getTraceSet(trace)) {
247 if (monitor.isCanceled()) {
248 return;
249 }
250
251 List<ITmfAnalysisModuleWithStateSystems> stateSystemModules = new LinkedList<>();
252 if (analysisIds.isEmpty()) {
253 /*
254 * No analysis specified, take all state system analysis modules
255 */
256 for (ITmfAnalysisModuleWithStateSystems module : aTrace.getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems.class)) {
257 stateSystemModules.add(module);
258 }
259 } else {
260 for (String moduleId : analysisIds) {
261 ITmfAnalysisModuleWithStateSystems module = aTrace.getAnalysisModuleOfClass(ITmfAnalysisModuleWithStateSystems.class, moduleId);
262 if (module != null) {
263 stateSystemModules.add(module);
264 }
265 }
266 }
267
268 for (ITmfAnalysisModuleWithStateSystems module : stateSystemModules) {
e1c415b3
BH
269 IStatus status = module.schedule();
270 if (!status.isOK()) {
271 return;
272 }
1a23419e
FW
273 if (module instanceof TmfStateSystemAnalysisModule) {
274 ((TmfStateSystemAnalysisModule) module).waitForInitialization();
275 }
276 for (ITmfStateSystem ssq : module.getStateSystems()) {
277 if (ssq == null) {
278 return;
279 }
280 ssq.waitUntilBuilt();
281
282 long startTime = ssq.getStartTime();
283 long endTime = ssq.getCurrentEndTime();
284 XmlEntry groupEntry = new XmlEntry(-1, aTrace, aTrace.getName(), ssq);
285 entryList.add(groupEntry);
286 setStartTime(Math.min(getStartTime(), startTime));
287 setEndTime(Math.max(getEndTime(), endTime));
288
289 /* Add children entry of this entry for each line */
290 for (Element entry : entries) {
291 buildEntry(entry, groupEntry, -1);
292 }
293 }
294 }
295 }
296 putEntryList(trace, new ArrayList<TimeGraphEntry>(entryList));
297
298 if (trace.equals(getTrace())) {
299 refresh();
300 }
301 for (XmlEntry traceEntry : entryList) {
302 if (monitor.isCanceled()) {
303 return;
304 }
305 long startTime = traceEntry.getStateSystem().getStartTime();
306 long endTime = traceEntry.getStateSystem().getCurrentEndTime() + 1;
307 buildStatusEvent(traceEntry, monitor, startTime, endTime);
308 }
309 }
310
311 private void buildEntry(Element entryElement, XmlEntry parentEntry, int baseQuark) {
312 /* Get the attribute string to display */
313 String path = entryElement.getAttribute(TmfXmlUiStrings.PATH);
314 if (path.isEmpty()) {
315 path = TmfXmlStrings.WILDCARD;
316 }
317
318 /*
319 * Make sure the XML element has either a display attribute or entries,
320 * otherwise issue a warning
321 */
322
323 List<Element> displayElements = XmlUtils.getChildElements(entryElement, TmfXmlUiStrings.DISPLAY_ELEMENT);
324 List<Element> entryElements = XmlUtils.getChildElements(entryElement, TmfXmlUiStrings.ENTRY_ELEMENT);
325
326 if (displayElements.isEmpty() && entryElements.isEmpty()) {
327 Activator.logWarning(String.format("XML view: entry for %s should have either a display element or entry elements", path)); //$NON-NLS-1$
328 return;
329 }
330
331 ITmfStateSystem ss = parentEntry.getStateSystem();
332
333 /* Get the list of quarks to process with this path */
334 String[] paths = path.split(SPLIT_STRING);
335 int i = 0;
336 List<Integer> quarks = Collections.singletonList(baseQuark);
337
338 try {
339 while (i < paths.length) {
340 List<Integer> subQuarks = new LinkedList<>();
341 /* Replace * by .* to have a regex string */
342 String name = paths[i].replaceAll("\\*", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
343 for (int relativeQuark : quarks) {
344 for (int quark : ss.getSubAttributes(relativeQuark, false, name)) {
345 subQuarks.add(quark);
346 }
347 }
348 quarks = subQuarks;
349 i++;
350 }
351
352 /* Process each quark */
353 XmlEntry currentEntry = parentEntry;
354 Element displayElement = null;
355 Map<String, XmlEntry> entryMap = new HashMap<>();
356 if (!displayElements.isEmpty()) {
357 displayElement = displayElements.get(0);
358 }
359 for (int quark : quarks) {
360 currentEntry = parentEntry;
361 /* Process the current entry, if specified */
362 if (displayElement != null) {
363 currentEntry = processEntry(entryElement, displayElement, parentEntry, quark, ss);
364 entryMap.put(currentEntry.getId(), currentEntry);
365 }
366 /* Process the children entry of this entry */
367 for (Element subEntryEl : entryElements) {
368 buildEntry(subEntryEl, currentEntry, quark);
369 }
370 }
371 if (!entryMap.isEmpty()) {
372 buildTree(entryMap, parentEntry);
373 }
374 } catch (AttributeNotFoundException e) {
375 }
376 }
377
378 private XmlEntry processEntry(@NonNull Element entryElement, @NonNull Element displayEl,
379 XmlEntry parentEntry, int quark, ITmfStateSystem ss) {
380 /*
381 * Get the start time and end time of this entry from the display
382 * attribute
383 */
384 ITmfXmlStateAttribute display = fFactory.createStateAttribute(displayEl, parentEntry);
385 int displayQuark = display.getAttributeQuark(quark);
386 if (displayQuark == IXmlStateSystemContainer.ERROR_QUARK) {
387 return new XmlEntry(quark, parentEntry.getTrace(),
388 String.format("Unknown display quark for %s", ss.getAttributeName(quark)), ss); //$NON-NLS-1$
389 }
390
391 long entryStart = ss.getStartTime();
392 long entryEnd = ss.getCurrentEndTime();
fadf52eb 393
1a23419e 394 try {
1a23419e 395
fadf52eb 396 ITmfStateInterval oneInterval = ss.querySingleState(entryStart, displayQuark);
1a23419e 397
fadf52eb
FR
398 /* The entry start is the first non-null interval */
399 while (oneInterval.getStateValue().isNull()) {
400 long ts = oneInterval.getEndTime() + 1;
401 if (ts > ss.getCurrentEndTime()) {
402 break;
1a23419e 403 }
fadf52eb 404 oneInterval = ss.querySingleState(ts, displayQuark);
1a23419e 405 }
fadf52eb
FR
406 entryStart = oneInterval.getStartTime();
407
408 /* The entry end is the last non-null interval */
409 oneInterval = ss.querySingleState(entryEnd, displayQuark);
410 while (oneInterval.getStateValue().isNull()) {
411 long ts = oneInterval.getStartTime() - 1;
412 if (ts < ss.getStartTime()) {
413 break;
414 }
415 oneInterval = ss.querySingleState(ts, displayQuark);
416 }
417 entryEnd = oneInterval.getEndTime();
418
1a23419e
FW
419 } catch (AttributeNotFoundException | StateSystemDisposedException e) {
420 }
421
422 return new XmlEntry(quark, displayQuark, parentEntry.getTrace(), ss.getAttributeName(quark),
423 entryStart, entryEnd, EntryDisplayType.DISPLAY, ss, entryElement);
424 }
425
426 private void buildStatusEvent(XmlEntry traceEntry, IProgressMonitor monitor, long start, long end) {
427 long resolution = (end - start) / getDisplayWidth();
428 long startTime = Math.max(start, traceEntry.getStartTime());
429 long endTime = Math.min(end + 1, traceEntry.getEndTime());
430 List<ITimeEvent> eventList = getEventList(traceEntry, startTime, endTime, resolution, monitor);
431 if (monitor.isCanceled()) {
432 return;
433 }
434 traceEntry.setEventList(eventList);
435 redraw();
436
a3188982 437 for (ITimeGraphEntry entry : traceEntry.getChildren()) {
1a23419e
FW
438 if (monitor.isCanceled()) {
439 return;
440 }
441 XmlEntry xmlEntry = (XmlEntry) entry;
442 buildStatusEvent(xmlEntry, monitor, start, end);
443 }
444 }
445
446 /** Build a tree using getParentId() and getId() */
447 private static void buildTree(Map<String, XmlEntry> entryMap, XmlEntry rootEntry) {
448 for (XmlEntry entry : entryMap.values()) {
449 boolean root = true;
450 if (!entry.getParentId().isEmpty()) {
451 XmlEntry parent = entryMap.get(entry.getParentId());
452 if (parent != null &&
453 entry.getStartTime() >= parent.getStartTime() &&
454 entry.getStartTime() <= parent.getEndTime()) {
455 parent.addChild(entry);
456 root = false;
457 }
458 }
459 if (root) {
460 rootEntry.addChild(entry);
461 }
462 }
463 }
464
465 @Override
466 protected List<ITimeEvent> getEventList(TimeGraphEntry entry, long startTime, long endTime, long resolution, IProgressMonitor monitor) {
467 if (!(entry instanceof XmlEntry)) {
468 return Collections.EMPTY_LIST;
469 }
470 XmlEntry xmlEntry = (XmlEntry) entry;
471 ITmfStateSystem ssq = xmlEntry.getStateSystem();
472 final long realStart = Math.max(startTime, entry.getStartTime());
473 final long realEnd = Math.min(endTime, entry.getEndTime());
474 if (realEnd <= realStart) {
475 return null;
476 }
477 List<ITimeEvent> eventList = null;
478 int quark = xmlEntry.getDisplayQuark();
479
480 try {
481 if (xmlEntry.getType() == EntryDisplayType.DISPLAY) {
482
1dd75589 483 List<ITmfStateInterval> statusIntervals = StateSystemUtils.queryHistoryRange(ssq, quark, realStart, realEnd - 1, resolution, monitor);
1a23419e
FW
484 eventList = new ArrayList<>(statusIntervals.size());
485 long lastEndTime = -1;
486 for (ITmfStateInterval statusInterval : statusIntervals) {
487 if (monitor.isCanceled()) {
488 return null;
489 }
490 int status = statusInterval.getStateValue().unboxInt();
491 long time = statusInterval.getStartTime();
492 long duration = statusInterval.getEndTime() - time + 1;
493 if (!statusInterval.getStateValue().isNull()) {
494 if (lastEndTime != time && lastEndTime != -1) {
495 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
496 }
497 eventList.add(new TimeEvent(entry, time, duration, status));
498 } else if (lastEndTime == -1 || time + duration >= endTime) {
499 // add null event if it intersects the start or end time
500 eventList.add(new NullTimeEvent(entry, time, duration));
501 }
502 lastEndTime = time + duration;
503 }
504 }
505 } catch (AttributeNotFoundException | TimeRangeException | StateValueTypeException | StateSystemDisposedException e) {
506 /* Ignored */
507 }
508 return eventList;
509 }
510
511 @Override
512 protected List<ILinkEvent> getLinkList(long startTime, long endTime, long resolution, IProgressMonitor monitor) {
513 /* TODO: not implemented yet, need XML to go along */
514 return Collections.EMPTY_LIST;
515 }
516
517}
This page took 0.066157 seconds and 5 git commands to generate.