tmf: Support aspect-based filtering in FilterViewer
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.pcap.ui / src / org / eclipse / tracecompass / internal / tmf / pcap / ui / stream / StreamListView.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 Ericsson
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 * Vincent Perot - Initial API and implementation
11 * Patrick Tasse - Support aspect filters
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.internal.tmf.pcap.ui.stream;
15
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.custom.CTabFolder;
24 import org.eclipse.swt.custom.CTabItem;
25 import org.eclipse.swt.events.SelectionAdapter;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.widgets.Composite;
28 import org.eclipse.swt.widgets.Display;
29 import org.eclipse.swt.widgets.Event;
30 import org.eclipse.swt.widgets.Listener;
31 import org.eclipse.swt.widgets.Menu;
32 import org.eclipse.swt.widgets.MenuItem;
33 import org.eclipse.swt.widgets.Table;
34 import org.eclipse.swt.widgets.TableColumn;
35 import org.eclipse.swt.widgets.TableItem;
36 import org.eclipse.tracecompass.internal.tmf.pcap.core.analysis.StreamListAnalysis;
37 import org.eclipse.tracecompass.internal.tmf.pcap.core.event.TmfPacketStream;
38 import org.eclipse.tracecompass.internal.tmf.pcap.core.event.TmfPacketStreamBuilder;
39 import org.eclipse.tracecompass.internal.tmf.pcap.core.event.aspect.PcapDestinationAspect;
40 import org.eclipse.tracecompass.internal.tmf.pcap.core.event.aspect.PcapSourceAspect;
41 import org.eclipse.tracecompass.internal.tmf.pcap.core.protocol.TmfPcapProtocol;
42 import org.eclipse.tracecompass.internal.tmf.pcap.core.signal.TmfPacketStreamSelectedSignal;
43 import org.eclipse.tracecompass.internal.tmf.pcap.core.trace.PcapTrace;
44 import org.eclipse.tracecompass.internal.tmf.pcap.ui.Activator;
45 import org.eclipse.tracecompass.tmf.core.event.aspect.TmfEventFieldAspect;
46 import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode;
47 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAndNode;
48 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAspectNode;
49 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterContainsNode;
50 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode;
51 import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterOrNode;
52 import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
53 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
54 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
55 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
56 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
57 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
58 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
59 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
60 import org.eclipse.tracecompass.tmf.ui.project.model.TraceUtils;
61 import org.eclipse.tracecompass.tmf.ui.views.TmfView;
62 import org.eclipse.tracecompass.tmf.ui.views.filter.FilterManager;
63 import org.eclipse.tracecompass.tmf.ui.views.filter.FilterView;
64 import org.eclipse.ui.IViewPart;
65 import org.eclipse.ui.IWorkbench;
66 import org.eclipse.ui.IWorkbenchPage;
67 import org.eclipse.ui.PartInitException;
68 import org.eclipse.ui.PlatformUI;
69
70 /**
71 * Class that represents the Stream List View. Such a view lists all the
72 * available streams from the current experiment. <br>
73 * <br>
74 * TODO Switch to TmfUiRefreshHandler once the behavior is fixed
75 *
76 * FIXME analysis is leaking ressource. Someone I will not name told me not to worry about it since
77 * AnalysisModule will not be autocloseable later.
78 *
79 * @author Vincent Perot
80 */
81 public class StreamListView extends TmfView {
82
83 /**
84 * The Stream List View ID.
85 */
86 public static final String ID = "org.eclipse.linuxtools.tmf.pcap.ui.view.stream.list"; //$NON-NLS-1$
87
88 private static final String[] COLUMN_NAMES =
89 { Messages.StreamListView_ID,
90 Messages.StreamListView_EndpointA,
91 Messages.StreamListView_EndpointB,
92 Messages.StreamListView_TotalPackets,
93 Messages.StreamListView_TotalBytes,
94 Messages.StreamListView_PacketsAtoB,
95 Messages.StreamListView_BytesAtoB,
96 Messages.StreamListView_PacketsBtoA,
97 Messages.StreamListView_BytesBtoA,
98 Messages.StreamListView_StartTime,
99 Messages.StreamListView_StopTime,
100 Messages.StreamListView_Duration,
101 Messages.StreamListView_BPSAtoB,
102 Messages.StreamListView_BPSBtoA
103 };
104
105 private static final int[] COLUMN_SIZES =
106 { 75,
107 350,
108 350,
109 110,
110 110,
111 110,
112 110,
113 110,
114 110,
115 180,
116 180,
117 110,
118 110,
119 110 };
120
121 private static final String KEY_PROTOCOL = "$protocol$"; //$NON-NLS-1$
122 private static final String KEY_STREAM = "$stream$"; //$NON-NLS-1$
123
124 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
125
126 private static final long WAIT_TIME = 1000;
127
128 private @Nullable CTabFolder fTabFolder;
129 private @Nullable Map<TmfPcapProtocol, Table> fTableMap;
130
131 private @Nullable TmfPacketStream fCurrentStream;
132 private @Nullable ITmfTrace fCurrentTrace;
133
134 private volatile boolean fStopThread;
135
136 /**
137 * Constructor of the StreamListView class.
138 */
139 public StreamListView() {
140 super(ID);
141 }
142
143 /**
144 * Handler called when an trace is opened.
145 *
146 * @param signal
147 * Contains the information about the selection.
148 */
149 @TmfSignalHandler
150 public void traceOpened(TmfTraceOpenedSignal signal) {
151 fCurrentTrace = signal.getTrace();
152 resetView();
153 queryAnalysis();
154 }
155
156 /**
157 * Handler called when an trace is closed. Checks if the trace is the
158 * current trace and update the view accordingly.
159 *
160 * @param signal
161 * Contains the information about the selection.
162 */
163 @TmfSignalHandler
164 public void traceClosed(TmfTraceClosedSignal signal) {
165 if (fCurrentTrace == signal.getTrace()) {
166 fCurrentTrace = null;
167 resetView();
168 }
169 }
170
171 /**
172 * Handler called when an trace is selected. Checks if the trace has changed
173 * and requests the selected trace if it has not yet been cached.
174 *
175 * @param signal
176 * Contains the information about the selection.
177 */
178 @TmfSignalHandler
179 public void traceSelected(TmfTraceSelectedSignal signal) {
180 if (fCurrentTrace != signal.getTrace()) {
181 fCurrentTrace = signal.getTrace();
182 resetView();
183 queryAnalysis();
184 }
185 }
186
187 private void queryAnalysis() {
188 Thread thread = new Thread(new Runnable() {
189
190 @Override
191 public void run() {
192 ITmfTrace trace = fCurrentTrace;
193 if (trace == null || (!(trace instanceof PcapTrace))) {
194 return;
195 }
196 StreamListAnalysis analysis = TmfTraceUtils.getAnalysisModuleOfClass(trace, StreamListAnalysis.class, StreamListAnalysis.ID);
197 if (analysis == null) {
198 return;
199 }
200 while (!analysis.isFinished() && !fStopThread) {
201 updateUI();
202 try {
203 Thread.sleep(WAIT_TIME);
204 } catch (InterruptedException e) {
205 String message = e.getMessage();
206 if (message == null) {
207 message = EMPTY_STRING;
208 }
209 Activator.logError(message, e);
210 return;
211 }
212 }
213 // Update UI one more time (daft punk)
214 if (!fStopThread) {
215 updateUI();
216 }
217
218 }
219 });
220
221 fStopThread = false;
222 thread.start();
223 }
224
225 private void resetView() {
226
227 // Stop thread if needed
228 fStopThread = true;
229
230 // Remove all content in tables
231 final Display display = Display.getDefault();
232 if (display == null || display.isDisposed()) {
233 return;
234 }
235 display.asyncExec(new Runnable() {
236
237 @Override
238 public void run() {
239 if (display.isDisposed()) {
240 return;
241 }
242 Map<TmfPcapProtocol, Table> tableMap = fTableMap;
243 if (tableMap == null) {
244 return;
245 }
246 for (TmfPcapProtocol protocol : tableMap.keySet()) {
247 if (!(tableMap.get(protocol).isDisposed())) {
248 tableMap.get(protocol).removeAll();
249 }
250 }
251 }
252 });
253 }
254
255 private void updateUI() {
256 final Display display = Display.getDefault();
257 if (display == null || display.isDisposed()) {
258 return;
259 }
260 display.asyncExec(new Runnable() {
261
262 @Override
263 public void run() {
264 if (display.isDisposed()) {
265 return;
266 }
267 ITmfTrace trace = fCurrentTrace;
268 if (trace == null) {
269 return;
270 }
271
272 StreamListAnalysis analysis = TmfTraceUtils.getAnalysisModuleOfClass(trace, StreamListAnalysis.class, StreamListAnalysis.ID);
273 if (analysis == null) {
274 return;
275 }
276
277 Map<TmfPcapProtocol, Table> tables = fTableMap;
278 if (tables == null) {
279 return;
280 }
281 for (TmfPcapProtocol protocol : tables.keySet()) {
282 if (protocol == null) {
283 throw new IllegalStateException();
284 }
285 TmfPacketStreamBuilder builder = analysis.getBuilder(protocol);
286 if (builder != null && !(tables.get(protocol).isDisposed())) {
287 for (TmfPacketStream stream : builder.getStreams()) {
288
289 TableItem item;
290 if (stream.getID() < tables.get(protocol).getItemCount()) {
291 item = tables.get(protocol).getItem(stream.getID());
292 } else {
293 item = new TableItem(tables.get(protocol), SWT.NONE);
294 }
295 item.setText(0, String.valueOf(stream.getID()));
296 item.setText(1, stream.getFirstEndpoint().toString());
297 item.setText(2, stream.getSecondEndpoint().toString());
298 item.setText(3, String.valueOf(stream.getNbPackets()));
299 item.setText(4, String.valueOf(stream.getNbBytes()));
300 item.setText(5, String.valueOf(stream.getNbPacketsAtoB()));
301 item.setText(6, String.valueOf(stream.getNbBytesAtoB()));
302 item.setText(7, String.valueOf(stream.getNbPacketsBtoA()));
303 item.setText(8, String.valueOf(stream.getNbBytesBtoA()));
304 item.setText(9, stream.getStartTime().toString());
305 item.setText(10, stream.getStopTime().toString());
306 item.setText(11, String.format("%.3f", stream.getDuration())); //$NON-NLS-1$
307 item.setText(12, String.format("%.3f", stream.getBPSAtoB())); //$NON-NLS-1$
308 item.setText(13, String.format("%.3f", stream.getBPSBtoA())); //$NON-NLS-1$
309 item.setData(KEY_STREAM, stream);
310 }
311 }
312 }
313 }
314
315 });
316 }
317
318 @Override
319 public void createPartControl(@Nullable Composite parent) {
320 // Initialize
321 fTableMap = new HashMap<>();
322 fCurrentTrace = getActiveTrace();
323 fCurrentStream = null;
324
325 // Add a tab folder
326 fTabFolder = new CTabFolder(parent, SWT.NONE);
327 fTabFolder.addSelectionListener(new SelectionAdapter() {
328
329 @Override
330 public void widgetSelected(@Nullable SelectionEvent e) {
331 Map<TmfPcapProtocol, Table> tables = fTableMap;
332 if (tables == null || e == null) {
333 return;
334 }
335 TmfPcapProtocol protocol = (TmfPcapProtocol) e.item.getData(KEY_PROTOCOL);
336 tables.get(protocol).deselectAll();
337 fCurrentStream = null;
338 }
339
340 });
341
342 // Add items and tables for each protocol
343 for (TmfPcapProtocol protocol : TmfPcapProtocol.values()) {
344 if (protocol.supportsStream()) {
345 CTabItem item = new CTabItem(fTabFolder, SWT.NONE);
346 item.setText(protocol.getName());
347 item.setData(KEY_PROTOCOL, protocol);
348 Table table = new Table(fTabFolder, SWT.NONE);
349 table.setHeaderVisible(true);
350 table.setLinesVisible(true);
351
352 // Add columns to table
353 for (int i = 0; i < COLUMN_NAMES.length || i < COLUMN_SIZES.length; i++) {
354 TableColumn column = new TableColumn(table, SWT.NONE);
355 column.setText(COLUMN_NAMES[i]);
356 column.setWidth(COLUMN_SIZES[i]);
357 }
358 item.setControl(table);
359 table.addSelectionListener(new SelectionAdapter() {
360
361 @Override
362 public void widgetSelected(@Nullable SelectionEvent e) {
363 if (e == null) {
364 return;
365 }
366 fCurrentStream = (TmfPacketStream) e.item.getData(KEY_STREAM);
367 }
368
369 });
370
371 Map<TmfPcapProtocol, Table> tables = fTableMap;
372 if (tables == null) {
373 return;
374 }
375
376 tables.put(protocol, table);
377
378 // Add right click menu
379 Menu menu = new Menu(table);
380 MenuItem menuItem = new MenuItem(menu, SWT.PUSH);
381 menuItem.setText(Messages.StreamListView_FollowStream);
382 menuItem.addListener(SWT.Selection, new Listener() {
383
384 @Override
385 public void handleEvent(@Nullable Event event) {
386 TmfSignal signal = new TmfPacketStreamSelectedSignal(this, 0, fCurrentStream);
387 TmfSignalManager.dispatchSignal(signal);
388 }
389 });
390 menuItem = new MenuItem(menu, SWT.PUSH);
391 menuItem.setText(Messages.StreamListView_Clear);
392 menuItem.addListener(SWT.Selection, new Listener() {
393
394 @Override
395 public void handleEvent(@Nullable Event event) {
396 TmfSignal signal = new TmfPacketStreamSelectedSignal(this, 0, null);
397 TmfSignalManager.dispatchSignal(signal);
398
399 }
400 });
401 menuItem = new MenuItem(menu, SWT.PUSH);
402 menuItem.setText(Messages.StreamListView_ExtractAsFilter);
403 menuItem.addListener(SWT.Selection, new Listener() {
404
405 @Override
406 public void handleEvent(@Nullable Event event) {
407 // Generate filter.
408 ITmfFilterTreeNode filter = generateFilter();
409
410 // Update view and XML
411 updateFilters(filter);
412
413 }
414
415 private void updateFilters(@Nullable ITmfFilterTreeNode filter) {
416 if (filter == null) {
417 return;
418 }
419
420 // Update XML
421 List<ITmfFilterTreeNode> newFilters = new ArrayList<>();
422 ITmfFilterTreeNode[] oldFilters = FilterManager.getSavedFilters();
423 for (int i = 0; i < oldFilters.length; i++) {
424 newFilters.add(oldFilters[i]);
425 }
426 if (!(newFilters.contains(filter))) {
427 newFilters.add(filter);
428 FilterManager.setSavedFilters(newFilters.toArray(new ITmfFilterTreeNode[newFilters.size()]));
429 }
430
431 // Update Filter View
432 try {
433 final IWorkbench wb = PlatformUI.getWorkbench();
434 final IWorkbenchPage activePage = wb.getActiveWorkbenchWindow().getActivePage();
435 IViewPart view = activePage.showView(FilterView.ID);
436 FilterView filterView = (FilterView) view;
437 filterView.addFilter(filter);
438 } catch (final PartInitException e) {
439 TraceUtils.displayErrorMsg(Messages.StreamListView_ExtractAsFilter, "Error opening view " + FilterView.ID + e.getMessage()); //$NON-NLS-1$
440 Activator.logError("Error opening view " + FilterView.ID, e); //$NON-NLS-1$
441 return;
442 }
443
444 }
445
446 private @Nullable ITmfFilterTreeNode generateFilter() {
447 TmfPacketStream stream = fCurrentStream;
448 if (stream == null) {
449 return null;
450 }
451
452 // First stage - root
453 String name = Messages.StreamListView_FilterName_Stream + ' ' + stream.getProtocol().getShortName() + ' ' + stream.getFirstEndpoint()
454 + " <--> " + stream.getSecondEndpoint(); //$NON-NLS-1$
455 TmfFilterNode root = new TmfFilterNode(name);
456
457 // Second stage - and
458 TmfFilterAndNode and = new TmfFilterAndNode(root);
459
460 // Third stage - protocol + or
461 TmfFilterContainsNode protocolFilter = new TmfFilterContainsNode(and);
462 protocolFilter.setEventAspect(new TmfEventFieldAspect(stream.getProtocol().getName(), stream.getProtocol().getName()));
463 protocolFilter.setTraceTypeId(TmfFilterAspectNode.EVENT_FIELD_ASPECT_ID);
464 protocolFilter.setValue(EMPTY_STRING);
465 TmfFilterOrNode or = new TmfFilterOrNode(and);
466
467 // Fourth stage - and
468 TmfFilterAndNode andA = new TmfFilterAndNode(or);
469 TmfFilterAndNode andB = new TmfFilterAndNode(or);
470
471 // Fourth stage - endpoints
472 TmfFilterContainsNode endpointAAndA = new TmfFilterContainsNode(andA);
473 endpointAAndA.setEventAspect(PcapSourceAspect.INSTANCE);
474 endpointAAndA.setTraceTypeId(PcapTrace.TRACE_TYPE_ID);
475 endpointAAndA.setValue(stream.getFirstEndpoint());
476 TmfFilterContainsNode endpointBAndA = new TmfFilterContainsNode(andA);
477 endpointBAndA.setEventAspect(PcapDestinationAspect.INSTANCE);
478 endpointBAndA.setTraceTypeId(PcapTrace.TRACE_TYPE_ID);
479 endpointBAndA.setValue(stream.getSecondEndpoint());
480 TmfFilterContainsNode endpointAAndB = new TmfFilterContainsNode(andB);
481 endpointAAndB.setEventAspect(PcapSourceAspect.INSTANCE);
482 endpointAAndB.setTraceTypeId(PcapTrace.TRACE_TYPE_ID);
483 endpointAAndB.setValue(stream.getSecondEndpoint());
484 TmfFilterContainsNode endpointBAndB = new TmfFilterContainsNode(andB);
485 endpointBAndB.setEventAspect(PcapDestinationAspect.INSTANCE);
486 endpointBAndB.setTraceTypeId(PcapTrace.TRACE_TYPE_ID);
487 endpointBAndB.setValue(stream.getFirstEndpoint());
488
489 return root;
490 }
491 });
492 table.setMenu(menu);
493 }
494 }
495
496 // Ask the analysis for data.
497 queryAnalysis();
498 }
499
500 @Override
501 public void setFocus() {
502 CTabFolder tabFolder = fTabFolder;
503 if (tabFolder != null && !(tabFolder.isDisposed())) {
504 tabFolder.setFocus();
505 }
506 }
507
508 }
This page took 0.04365 seconds and 6 git commands to generate.