tmf : Change the order of modifiers of reinitializeCharts() in TmfPieChartViewer
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / internal / tmf / ui / viewers / piecharts / TmfPieChartViewer.java
CommitLineData
cdf994ef
ACL
1/*******************************************************************************
2 * Copyright (c) 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 * Alexis Cabana-Loriaux - Initial API and implementation
11 *
12 *******************************************************************************/
13
1743f395 14package org.eclipse.tracecompass.internal.tmf.ui.viewers.piecharts;
cdf994ef
ACL
15
16import java.util.ArrayList;
17import java.util.Collections;
18import java.util.HashMap;
19import java.util.List;
20import java.util.Map;
21import java.util.Map.Entry;
22
378e6b92 23import org.eclipse.core.runtime.ListenerList;
cdf994ef
ACL
24import org.eclipse.linuxtools.dataviewers.piechart.PieChart;
25import org.eclipse.swt.SWT;
378e6b92
ACL
26import org.eclipse.swt.events.MouseEvent;
27import org.eclipse.swt.events.MouseListener;
cdf994ef
ACL
28import org.eclipse.swt.layout.FillLayout;
29import org.eclipse.swt.widgets.Composite;
378e6b92 30import org.eclipse.swt.widgets.Event;
cdf994ef 31import org.eclipse.swt.widgets.Listener;
378e6b92 32import org.eclipse.tracecompass.internal.tmf.ui.viewers.piecharts.model.TmfPieChartStatisticsModel;
94411c58 33import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
cdf994ef
ACL
34
35/**
36 * Creates a viewer containing 2 pie charts, one for showing information about
37 * the current selection, and the second one for showing information about the
38 * current time-range selection. It follows the MVC pattern, being a view.
39 *
378e6b92
ACL
40 * This class is closely related with the IPieChartViewerState interface that
41 * acts as a state machine for the general layout of the charts.
cdf994ef
ACL
42 *
43 * @author Alexis Cabana-Loriaux
44 * @since 2.0
45 *
46 */
47public class TmfPieChartViewer extends Composite {
48
49 /**
50 * The pie chart containing global information about the trace
51 */
52 private PieChart fGlobalPC;
53
54 /**
55 * The name of the piechart containing the statistics about the global trace
56 */
57 private String fGlobalPCname;
58
59 /**
60 * The pie chart containing information about the current time-range
61 * selection
62 */
63 private PieChart fTimeRangePC;
64
65 /**
66 * The name of the piechart containing the statistics about the current
67 * selection
68 */
69 private String fTimeRangePCname;
70
71 /**
378e6b92
ACL
72 * The listener for the mouse movement event.
73 */
74 private Listener fMouseMoveListener;
75
76 /**
77 * The listener for the mouse right click event.
cdf994ef 78 */
378e6b92
ACL
79 private MouseListener fMouseClickListener;
80
81 /**
82 * The list of listener to notify when an event type is selected
83 */
84 private ListenerList fEventTypeSelectedListeners = new ListenerList(ListenerList.IDENTITY);
cdf994ef
ACL
85
86 /**
87 * The name of the slice containing the too little slices
88 */
89 private String fOthersSliceName;
90
91 /**
92 * Implementation of the State design pattern to reorder the layout
93 * depending on the selection. This variable holds the current state of the
94 * layout.
95 */
96 private IPieChartViewerState fCurrentState;
97
98 /**
99 * Represents the minimum percentage a slice of pie must have in order to be
100 * shown
101 */
102 private static final float MIN_PRECENTAGE_TO_SHOW_SLICE = 0.025F;// 2.5%
103
104 /**
105 * Represents the maximum number of slices of the pie charts. WE don't want
106 * to pollute the viewer with too much slice entries.
107 */
108 private static final int NB_MAX_SLICES = 10;
109
110 /**
111 * The data that has to be presented by the pie charts
112 */
113 private TmfPieChartStatisticsModel fModel = null;
114
115 /**
116 * @param parent
117 * The parent composite that will hold the viewer
118 */
119 public TmfPieChartViewer(Composite parent) {
120 super(parent, SWT.NONE);
121 fGlobalPCname = Messages.TmfStatisticsView_GlobalSelectionPieChartName;
122 fTimeRangePCname = Messages.TmfStatisticsView_TimeRangeSelectionPieChartName;
123 fOthersSliceName = Messages.TmfStatisticsView_PieChartOthersSliceName;
124 initContent();
125 }
126
127 // ------------------------------------------------------------------------
128 // Class methods
129 // ------------------------------------------------------------------------
130
131 /**
132 * Called by this class' constructor. Constructs the basic viewer containing
133 * the charts, as well as their listeners
134 */
135 private void initContent() {
136 setLayout(new FillLayout());
137
138 fGlobalPC = null;
139 fTimeRangePC = null;
140
141 // Setup listeners for the tooltips
378e6b92 142 fMouseMoveListener = new Listener() {
cdf994ef
ACL
143 @Override
144 public void handleEvent(org.eclipse.swt.widgets.Event event) {
145 PieChart pc = (PieChart) event.widget;
146 switch (event.type) {
378e6b92 147 /* Get tooltip information on the slice */
cdf994ef
ACL
148 case SWT.MouseMove:
149 int sliceIndex = pc.getSliceIndexFromPosition(0, event.x, event.y);
150 if (sliceIndex < 0) {
151 // mouse is outside the chart
152 pc.setToolTipText(null);
153 break;
154 }
155 float percOfSlice = (float) pc.getSlicePercent(0, sliceIndex);
156 String percent = String.format("%.1f", percOfSlice); //$NON-NLS-1$
157 Long nbEvents = Long.valueOf((long) pc.getSeriesSet().getSeries()[sliceIndex].getXSeries()[0]);
158
378e6b92
ACL
159 String text = Messages.TmfStatisticsView_PieChartToolTipTextName + " = " + //$NON-NLS-1$
160 pc.getSeriesSet().getSeries()[sliceIndex].getId() + "\n"; //$NON-NLS-1$
cdf994ef 161
378e6b92
ACL
162 text += Messages.TmfStatisticsView_PieChartToolTipTextEventCount + " = "//$NON-NLS-1$
163 + nbEvents.toString() + " (" + percent + "%)"; //$NON-NLS-1$ //$NON-NLS-2$
cdf994ef
ACL
164 pc.setToolTipText(text);
165 return;
166 default:
167 }
168 }
169 };
378e6b92
ACL
170
171 fMouseClickListener = new MouseListener() {
172
173 @Override
174 public void mouseUp(MouseEvent e) {
175 }
176
177 @Override
178 public void mouseDown(MouseEvent e) {
179 PieChart pc = (PieChart) e.widget;
180 int slicenb = pc.getSliceIndexFromPosition(0, e.x, e.y);
181 if (slicenb < 0 || slicenb >= pc.getSeriesSet().getSeries().length) {
182 // mouse is outside the chart
183 return;
184 }
185 Event selectionEvent = new Event();
186 selectionEvent.text = pc.getSeriesSet().getSeries()[slicenb].getId();
187 notifyEventTypeSelectionListener(selectionEvent);
188 }
189
190 @Override
191 public void mouseDoubleClick(MouseEvent e) {
192 }
193 };
194
cdf994ef
ACL
195 // at creation no content is selected
196 setCurrentState(new PieChartViewerStateNoContentSelected(this));
197 }
198
199 @Override
200 public void dispose() {
201 if (fGlobalPC != null) {
202 fGlobalPC.dispose();
203 }
204 if (fTimeRangePC != null) {
205 fTimeRangePC.dispose();
206 }
207 super.dispose();
208 }
209
210 /**
211 * Updates the data contained in the Global PieChart by using a Map.
212 * Normally, this method is only called by the state machine.
213 */
214 synchronized void updateGlobalPieChart() {
215 if (getGlobalPC() == null) {
216 fGlobalPC = new PieChart(this, SWT.NONE);
217 getGlobalPC().getTitle().setText(fGlobalPCname);
218 getGlobalPC().getAxisSet().getXAxis(0).getTitle().setText(""); //Hide the title over the legend //$NON-NLS-1$
219 getGlobalPC().getLegend().setVisible(true);
220 getGlobalPC().getLegend().setPosition(SWT.RIGHT);
378e6b92
ACL
221 getGlobalPC().addListener(SWT.MouseMove, fMouseMoveListener);
222 getGlobalPC().addMouseListener(fMouseClickListener);
cdf994ef
ACL
223 } else if (getGlobalPC().isDisposed() || fModel == null || fModel.getPieChartGlobalModel() == null) {
224 return;
225 }
226
227 Map<String, Long> totalEventCountForChart = getTotalEventCountForChart(true);
228
378e6b92 229 if (totalEventCountForChart == null) {
cdf994ef
ACL
230 return;
231 }
232
233 updatePieChartWithData(fGlobalPC, totalEventCountForChart, MIN_PRECENTAGE_TO_SHOW_SLICE, fOthersSliceName);
234 }
235
236 /**
237 * Updates the data contained in the Time-Range PieChart by using a Map.
238 * Normally, this method is only called by the state machine.
239 */
240 synchronized void updateTimeRangeSelectionPieChart() {
241 if (getTimeRangePC() == null) {
242 fTimeRangePC = new PieChart(this, SWT.NONE);
243 getTimeRangePC().getTitle().setText(fTimeRangePCname);
244 getTimeRangePC().getAxisSet().getXAxis(0).getTitle().setText(""); //Hide the title over the legend //$NON-NLS-1$
245 getTimeRangePC().getLegend().setPosition(SWT.BOTTOM);
246 getTimeRangePC().getLegend().setVisible(true);
378e6b92
ACL
247 getTimeRangePC().addListener(SWT.MouseMove, fMouseMoveListener);
248 getTimeRangePC().addMouseListener(fMouseClickListener);
cdf994ef
ACL
249 }
250 else if (getTimeRangePC().isDisposed()) {
251 return;
252 }
253
254 Map<String, Long> totalEventCountForChart = getTotalEventCountForChart(false);
255
378e6b92 256 if (totalEventCountForChart == null) {
cdf994ef
ACL
257 return;
258 }
259
260 updatePieChartWithData(fTimeRangePC, totalEventCountForChart, MIN_PRECENTAGE_TO_SHOW_SLICE, fOthersSliceName);
261 }
262
263 /* return the chart-friendly map given by the TmfPieChartStatisticsModel */
378e6b92
ACL
264 private Map<String, Long> getTotalEventCountForChart(boolean isGlobal) {
265 if (fModel == null) {
cdf994ef
ACL
266 return null;
267 }
268 Map<ITmfTrace, Map<String, Long>> chartModel;
378e6b92 269 if (isGlobal) {
cdf994ef
ACL
270 chartModel = fModel.getPieChartGlobalModel();
271 } else {
272 chartModel = fModel.getPieChartSelectionModel();
273 }
378e6b92 274 if (chartModel == null) {
cdf994ef
ACL
275 return null;
276 }
277
278 Map<String, Long> totalEventCountForChart = new HashMap<>();
378e6b92 279 for (Entry<ITmfTrace, Map<String, Long>> entry : chartModel.entrySet()) {
cdf994ef 280 Map<String, Long> traceEventCount = entry.getValue();
378e6b92 281 if (traceEventCount == null) {
cdf994ef
ACL
282 continue;
283 }
378e6b92 284 for (Entry<String, Long> event : traceEventCount.entrySet()) {
94411c58
AM
285 final Long value = totalEventCountForChart.get(event.getKey());
286 if (value != null) {
287 totalEventCountForChart.put(event.getKey(), value + event.getValue());
cdf994ef
ACL
288 } else {
289 totalEventCountForChart.put(event.getKey(), event.getValue());
290 }
291 }
292 }
293
294 return totalEventCountForChart;
295 }
296
297 /**
298 * Reinitializes the charts to their initial state, without any data
299 */
f8d97881 300 public synchronized void reinitializeCharts() {
378e6b92 301 if (isDisposed()) {
cdf994ef
ACL
302 return;
303 }
304
305 if (getGlobalPC() != null && !getGlobalPC().isDisposed()) {
306 getGlobalPC().dispose();
307 }
308 fGlobalPC = new PieChart(this, SWT.NONE);
309 getGlobalPC().getTitle().setText(fGlobalPCname);
310 getGlobalPC().getAxisSet().getXAxis(0).getTitle().setText(""); //Hide the title over the legend //$NON-NLS-1$
311 if (getTimeRangePC() != null && !getTimeRangePC().isDisposed()) {
312 getTimeRangePC().dispose();
313 fTimeRangePC = null;
314 }
315 layout();
316 setCurrentState(new PieChartViewerStateNoContentSelected(this));
317 }
318
319 /**
320 * Function used to update or create the slices of a PieChart to match the
321 * content of a Map passed in parameter. It also provides a facade to use
322 * the PieChart API
323 */
324 private static void updatePieChartWithData(
325 final PieChart chart,
326 final Map<String, Long> slices,
327 final float minimumSizeOfSlice,
328 final String nameOfOthers) {
329
330 List<EventOccurrenceObject> chartValues = new ArrayList<>();
331 Long eventTotal = 0L;
332 for (Entry<String, Long> entry : slices.entrySet()) {
333 eventTotal += entry.getValue();
334 chartValues.add(new EventOccurrenceObject(entry.getKey(), entry.getValue()));
335 }
336
337 // No events in the selection
338 if (eventTotal == 0) {
339 // clear the chart and show "NO DATA"
340
341 return;
342 }
343
344 /*
345 * filter out the event types taking too little space in the chart and
346 * label the whole group together. The remaining slices will be showing
347 */
348 List<EventOccurrenceObject> filteredChartValues = new ArrayList<>();
349 Long othersEntryCount = 0L;
350 int nbSlices = 0;
351 for (EventOccurrenceObject entry : chartValues) {
352 if (entry.getNbOccurence() / eventTotal.floatValue() > minimumSizeOfSlice && nbSlices <= NB_MAX_SLICES) {
353 filteredChartValues.add(entry);
354 nbSlices++;
355 } else {
356 othersEntryCount += entry.getNbOccurence();
357 }
358 }
359
360 Collections.sort(filteredChartValues);
361
362 // Add the "Others" slice in the pie if its not empty
363 if (othersEntryCount != 0) {
364 filteredChartValues.add(new EventOccurrenceObject(nameOfOthers, othersEntryCount));
365 }
366
367 // put the entries in the chart and add their percentage
368 double[][] tempValues = new double[filteredChartValues.size()][1];
369 String[] tempNames = new String[filteredChartValues.size()];
370 int index = 0;
371 for (EventOccurrenceObject entry : filteredChartValues) {
372 tempValues[index][0] = entry.getNbOccurence();
373 tempNames[index] = entry.getName();
374 index++;
375 }
376
377 chart.addPieChartSeries(tempNames, tempValues);
378 }
379
380 /**
381 * Refresh this viewer
378e6b92
ACL
382 *
383 * @param refreshGlobal
384 * if we have to refresh the global piechart
385 * @param refreshSelection
386 * if we have to refresh the selection piechart
cdf994ef
ACL
387 */
388 public synchronized void refresh(boolean refreshGlobal, boolean refreshSelection) {
378e6b92 389 if (fModel == null) {
cdf994ef
ACL
390 reinitializeCharts();
391 } else {
378e6b92 392 if (refreshGlobal) {
cdf994ef
ACL
393 /* will update the global pc */
394 getCurrentState().newGlobalEntries(this);
395 }
396
378e6b92 397 if (refreshSelection) {
cdf994ef
ACL
398 // Check if the selection is empty
399 int nbEventsType = 0;
400 Map<String, Long> selectionModel = getTotalEventCountForChart(false);
401 for (Long l : selectionModel.values()) {
378e6b92 402 if (l != 0) {
cdf994ef
ACL
403 nbEventsType++;
404 }
405 }
406
407 // Check if the selection is empty or if
408 // there is enough event types to show in the piecharts
409 if (nbEventsType < 2) {
410 getCurrentState().newEmptySelection(this);
411 } else {
412 getCurrentState().newSelection(this);
413 }
414 }
415 }
416 }
417
378e6b92
ACL
418 /**
419 * @param l
420 * the listener to add
421 */
422 public void addEventTypeSelectionListener(Listener l) {
423 fEventTypeSelectedListeners.add(l);
424 }
425
426 /**
427 * @param l
428 * the listener to remove
429 */
430 public void removeEventTypeSelectionListener(Listener l) {
431 fEventTypeSelectedListeners.remove(l);
432 }
433
434 /* Notify all listeners that an event type has been selected */
435 private void notifyEventTypeSelectionListener(Event e) {
436 for (Object o : fEventTypeSelectedListeners.getListeners()) {
437 ((Listener) o).handleEvent(e);
438 }
439 }
440
cdf994ef
ACL
441 // ------------------------------------------------------------------------
442 // Getters
443 // ------------------------------------------------------------------------
444
445 /**
446 * @return the global piechart
447 */
448 synchronized PieChart getGlobalPC() {
449 return fGlobalPC;
450 }
451
452 /**
453 * @return the time-range selection piechart
454 */
455 synchronized PieChart getTimeRangePC() {
456 return fTimeRangePC;
457 }
458
459 /**
460 * @return the current state of the viewer
461 */
462 synchronized IPieChartViewerState getCurrentState() {
463 return fCurrentState;
464 }
465
466 // ------------------------------------------------------------------------
467 // Setters
468 // ------------------------------------------------------------------------
469
470 /**
471 * @return the model
472 */
473 public TmfPieChartStatisticsModel getModel() {
474 return fModel;
475 }
476
477 /**
378e6b92
ACL
478 * @param model
479 * the model to set
cdf994ef
ACL
480 */
481 public void setInput(TmfPieChartStatisticsModel model) {
482 fModel = model;
483 }
484
485 /**
486 * Normally, this method is only called by the state machine
487 *
488 * @param newChart
489 * the new PieChart
490 */
491 public synchronized void setTimeRangePC(PieChart newChart) {
492 fTimeRangePC = newChart;
493 }
494
495 /**
496 * Setter method for the state.
497 *
498 * @param newState
499 * The new state of the viewer Normally only called by classes
500 * implementing the IPieChartViewerState interface.
501 */
502 public synchronized void setCurrentState(final IPieChartViewerState newState) {
503 fCurrentState = newState;
504 }
505
506 /**
507 * Nested class used to handle and sort more easily the pair (Name, Number
508 * of occurrences)
509 *
510 * @author Alexis Cabana-Loriaux
511 */
512 private static class EventOccurrenceObject implements Comparable<EventOccurrenceObject> {
513
514 private String fName;
515
516 private Long fNbOccurrences;
517
518 EventOccurrenceObject(String name, Long nbOccurences) {
519 this.fName = name;
520 this.fNbOccurrences = nbOccurences;
521 }
522
523 @Override
524 public int compareTo(EventOccurrenceObject other) {
525 // descending order
21c2ae83 526 return Long.compare(other.getNbOccurence(), this.getNbOccurence());
cdf994ef
ACL
527 }
528
529 public String getName() {
530 return fName;
531 }
532
533 public Long getNbOccurence() {
534 return fNbOccurrences;
535 }
536 }
537}
This page took 0.063941 seconds and 5 git commands to generate.