1 /*******************************************************************************
2 * Copyright (c) 2015 Ericsson
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
10 * Alexis Cabana-Loriaux - Initial API and implementation
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.piecharts
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Collections
;
18 import java
.util
.HashMap
;
19 import java
.util
.List
;
21 import java
.util
.Map
.Entry
;
23 import org
.eclipse
.linuxtools
.dataviewers
.piechart
.PieChart
;
24 import org
.eclipse
.swt
.SWT
;
25 import org
.eclipse
.swt
.layout
.FillLayout
;
26 import org
.eclipse
.swt
.widgets
.Composite
;
27 import org
.eclipse
.swt
.widgets
.Listener
;
28 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
29 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.piecharts
.model
.TmfPieChartStatisticsModel
;
32 * Creates a viewer containing 2 pie charts, one for showing information about
33 * the current selection, and the second one for showing information about the
34 * current time-range selection. It follows the MVC pattern, being a view.
36 * This class is closely related with the IPieChartViewerState interface
37 * that acts as a state machine for the general layout of the charts.
39 * @author Alexis Cabana-Loriaux
43 public class TmfPieChartViewer
extends Composite
{
46 * The pie chart containing global information about the trace
48 private PieChart fGlobalPC
;
51 * The name of the piechart containing the statistics about the global trace
53 private String fGlobalPCname
;
56 * The pie chart containing information about the current time-range
59 private PieChart fTimeRangePC
;
62 * The name of the piechart containing the statistics about the current
65 private String fTimeRangePCname
;
68 * The listener added to the charts every time they are created
70 private Listener fMouseListener
;
73 * The name of the slice containing the too little slices
75 private String fOthersSliceName
;
78 * Implementation of the State design pattern to reorder the layout
79 * depending on the selection. This variable holds the current state of the
82 private IPieChartViewerState fCurrentState
;
85 * Represents the minimum percentage a slice of pie must have in order to be
88 private static final float MIN_PRECENTAGE_TO_SHOW_SLICE
= 0.025F
;// 2.5%
91 * Represents the maximum number of slices of the pie charts. WE don't want
92 * to pollute the viewer with too much slice entries.
94 private static final int NB_MAX_SLICES
= 10;
97 * The data that has to be presented by the pie charts
99 private TmfPieChartStatisticsModel fModel
= null;
103 * The parent composite that will hold the viewer
105 public TmfPieChartViewer(Composite parent
) {
106 super(parent
, SWT
.NONE
);
107 fGlobalPCname
= Messages
.TmfStatisticsView_GlobalSelectionPieChartName
;
108 fTimeRangePCname
= Messages
.TmfStatisticsView_TimeRangeSelectionPieChartName
;
109 fOthersSliceName
= Messages
.TmfStatisticsView_PieChartOthersSliceName
;
113 // ------------------------------------------------------------------------
115 // ------------------------------------------------------------------------
118 * Called by this class' constructor. Constructs the basic viewer containing
119 * the charts, as well as their listeners
121 private void initContent() {
122 setLayout(new FillLayout());
127 // Setup listeners for the tooltips
128 fMouseListener
= new Listener() {
130 public void handleEvent(org
.eclipse
.swt
.widgets
.Event event
) {
131 PieChart pc
= (PieChart
) event
.widget
;
132 switch (event
.type
) {
134 int sliceIndex
= pc
.getSliceIndexFromPosition(0, event
.x
, event
.y
);
135 if (sliceIndex
< 0) {
136 // mouse is outside the chart
137 pc
.setToolTipText(null);
140 float percOfSlice
= (float) pc
.getSlicePercent(0, sliceIndex
);
141 String percent
= String
.format("%.1f", percOfSlice
); //$NON-NLS-1$
142 Long nbEvents
= Long
.valueOf((long) pc
.getSeriesSet().getSeries()[sliceIndex
].getXSeries()[0]);
144 String text
= Messages
.TmfStatisticsView_PieChartToolTipTextName
+ " = " + //$NON-NLS-1$
145 pc
.getSeriesSet().getSeries()[sliceIndex
].getId() + "\n"; //$NON-NLS-1$
147 text
+= Messages
.TmfStatisticsView_PieChartToolTipTextEventCount
+ " = "//$NON-NLS-1$
148 + nbEvents
.toString() + " (" + percent
+ "%)"; //$NON-NLS-1$ //$NON-NLS-2$
149 pc
.setToolTipText(text
);
155 // at creation no content is selected
156 setCurrentState(new PieChartViewerStateNoContentSelected(this));
160 public void dispose() {
161 if (fGlobalPC
!= null) {
164 if (fTimeRangePC
!= null) {
165 fTimeRangePC
.dispose();
171 * Updates the data contained in the Global PieChart by using a Map.
172 * Normally, this method is only called by the state machine.
174 synchronized void updateGlobalPieChart() {
175 if (getGlobalPC() == null) {
176 fGlobalPC
= new PieChart(this, SWT
.NONE
);
177 getGlobalPC().getTitle().setText(fGlobalPCname
);
178 getGlobalPC().getAxisSet().getXAxis(0).getTitle().setText(""); //Hide the title over the legend //$NON-NLS-1$
179 getGlobalPC().getLegend().setVisible(true);
180 getGlobalPC().getLegend().setPosition(SWT
.RIGHT
);
181 getGlobalPC().addListener(SWT
.MouseMove
, fMouseListener
);
182 } else if (getGlobalPC().isDisposed() || fModel
== null || fModel
.getPieChartGlobalModel() == null) {
186 Map
<String
, Long
> totalEventCountForChart
= getTotalEventCountForChart(true);
188 if(totalEventCountForChart
== null){
192 updatePieChartWithData(fGlobalPC
, totalEventCountForChart
, MIN_PRECENTAGE_TO_SHOW_SLICE
, fOthersSliceName
);
196 * Updates the data contained in the Time-Range PieChart by using a Map.
197 * Normally, this method is only called by the state machine.
199 synchronized void updateTimeRangeSelectionPieChart() {
200 if (getTimeRangePC() == null) {
201 fTimeRangePC
= new PieChart(this, SWT
.NONE
);
202 getTimeRangePC().getTitle().setText(fTimeRangePCname
);
203 getTimeRangePC().getAxisSet().getXAxis(0).getTitle().setText(""); //Hide the title over the legend //$NON-NLS-1$
204 getTimeRangePC().getLegend().setPosition(SWT
.BOTTOM
);
205 getTimeRangePC().getLegend().setVisible(true);
206 getTimeRangePC().addListener(SWT
.MouseMove
, fMouseListener
);
208 else if (getTimeRangePC().isDisposed()) {
212 Map
<String
, Long
> totalEventCountForChart
= getTotalEventCountForChart(false);
214 if(totalEventCountForChart
== null){
218 updatePieChartWithData(fTimeRangePC
, totalEventCountForChart
, MIN_PRECENTAGE_TO_SHOW_SLICE
, fOthersSliceName
);
221 /* return the chart-friendly map given by the TmfPieChartStatisticsModel */
222 private Map
<String
,Long
> getTotalEventCountForChart(boolean isGlobal
){
226 Map
<ITmfTrace
, Map
<String
, Long
>> chartModel
;
228 chartModel
= fModel
.getPieChartGlobalModel();
230 chartModel
= fModel
.getPieChartSelectionModel();
232 if(chartModel
== null){
236 Map
<String
, Long
> totalEventCountForChart
= new HashMap
<>();
237 for(Entry
<ITmfTrace
, Map
<String
, Long
>> entry
: chartModel
.entrySet()){
238 Map
<String
, Long
> traceEventCount
= entry
.getValue();
239 if(traceEventCount
== null){
242 for(Entry
<String
, Long
> event
: traceEventCount
.entrySet()){
243 if(totalEventCountForChart
.containsKey(event
.getKey())){
244 totalEventCountForChart
.put(event
.getKey(), totalEventCountForChart
.get(event
.getKey()) + event
.getValue());
246 totalEventCountForChart
.put(event
.getKey(), event
.getValue());
251 return totalEventCountForChart
;
255 * Reinitializes the charts to their initial state, without any data
257 synchronized public void reinitializeCharts() {
262 if (getGlobalPC() != null && !getGlobalPC().isDisposed()) {
263 getGlobalPC().dispose();
265 fGlobalPC
= new PieChart(this, SWT
.NONE
);
266 getGlobalPC().getTitle().setText(fGlobalPCname
);
267 getGlobalPC().getAxisSet().getXAxis(0).getTitle().setText(""); //Hide the title over the legend //$NON-NLS-1$
268 if (getTimeRangePC() != null && !getTimeRangePC().isDisposed()) {
269 getTimeRangePC().dispose();
273 setCurrentState(new PieChartViewerStateNoContentSelected(this));
277 * Function used to update or create the slices of a PieChart to match the
278 * content of a Map passed in parameter. It also provides a facade to use
281 private static void updatePieChartWithData(
282 final PieChart chart
,
283 final Map
<String
, Long
> slices
,
284 final float minimumSizeOfSlice
,
285 final String nameOfOthers
) {
287 List
<EventOccurrenceObject
> chartValues
= new ArrayList
<>();
288 Long eventTotal
= 0L;
289 for (Entry
<String
, Long
> entry
: slices
.entrySet()) {
290 eventTotal
+= entry
.getValue();
291 chartValues
.add(new EventOccurrenceObject(entry
.getKey(), entry
.getValue()));
294 // No events in the selection
295 if (eventTotal
== 0) {
296 // clear the chart and show "NO DATA"
302 * filter out the event types taking too little space in the chart and
303 * label the whole group together. The remaining slices will be showing
305 List
<EventOccurrenceObject
> filteredChartValues
= new ArrayList
<>();
306 Long othersEntryCount
= 0L;
308 for (EventOccurrenceObject entry
: chartValues
) {
309 if (entry
.getNbOccurence() / eventTotal
.floatValue() > minimumSizeOfSlice
&& nbSlices
<= NB_MAX_SLICES
) {
310 filteredChartValues
.add(entry
);
313 othersEntryCount
+= entry
.getNbOccurence();
317 Collections
.sort(filteredChartValues
);
319 // Add the "Others" slice in the pie if its not empty
320 if (othersEntryCount
!= 0) {
321 filteredChartValues
.add(new EventOccurrenceObject(nameOfOthers
, othersEntryCount
));
324 // put the entries in the chart and add their percentage
325 double[][] tempValues
= new double[filteredChartValues
.size()][1];
326 String
[] tempNames
= new String
[filteredChartValues
.size()];
328 for (EventOccurrenceObject entry
: filteredChartValues
) {
329 tempValues
[index
][0] = entry
.getNbOccurence();
330 tempNames
[index
] = entry
.getName();
334 chart
.addPieChartSeries(tempNames
, tempValues
);
338 * Refresh this viewer
339 * @param refreshGlobal if we have to refresh the global piechart
340 * @param refreshSelection if we have to refresh the selection piechart
342 public synchronized void refresh(boolean refreshGlobal
, boolean refreshSelection
) {
344 reinitializeCharts();
347 /* will update the global pc */
348 getCurrentState().newGlobalEntries(this);
351 if(refreshSelection
){
352 // Check if the selection is empty
353 int nbEventsType
= 0;
354 Map
<String
, Long
> selectionModel
= getTotalEventCountForChart(false);
355 for (Long l
: selectionModel
.values()) {
361 // Check if the selection is empty or if
362 // there is enough event types to show in the piecharts
363 if (nbEventsType
< 2) {
364 getCurrentState().newEmptySelection(this);
366 getCurrentState().newSelection(this);
372 // ------------------------------------------------------------------------
374 // ------------------------------------------------------------------------
377 * @return the global piechart
379 synchronized PieChart
getGlobalPC() {
384 * @return the time-range selection piechart
386 synchronized PieChart
getTimeRangePC() {
391 * @return the current state of the viewer
393 synchronized IPieChartViewerState
getCurrentState() {
394 return fCurrentState
;
397 // ------------------------------------------------------------------------
399 // ------------------------------------------------------------------------
404 public TmfPieChartStatisticsModel
getModel() {
409 * @param model the model to set
411 public void setInput(TmfPieChartStatisticsModel model
) {
416 * Normally, this method is only called by the state machine
421 public synchronized void setTimeRangePC(PieChart newChart
) {
422 fTimeRangePC
= newChart
;
426 * Setter method for the state.
429 * The new state of the viewer Normally only called by classes
430 * implementing the IPieChartViewerState interface.
432 public synchronized void setCurrentState(final IPieChartViewerState newState
) {
433 fCurrentState
= newState
;
437 * Nested class used to handle and sort more easily the pair (Name, Number
440 * @author Alexis Cabana-Loriaux
442 private static class EventOccurrenceObject
implements Comparable
<EventOccurrenceObject
> {
444 private String fName
;
446 private Long fNbOccurrences
;
448 EventOccurrenceObject(String name
, Long nbOccurences
) {
450 this.fNbOccurrences
= nbOccurences
;
454 public int compareTo(EventOccurrenceObject other
) {
456 return -Long
.compare(this.getNbOccurence(), other
.getNbOccurence());
459 public String
getName() {
463 public Long
getNbOccurence() {
464 return fNbOccurrences
;