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