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