View Javadoc

1   /*
2    * This file is a part of CAST project.
3    * (c) Copyright 2007, AGH University of Science & Technology
4    * https://caribou.iisg.agh.edu.pl/trac/cast
5    *
6    * Licensed under the Eclipse Public License, Version 1.0 (the "License").
7    * You may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * http://www.eclipse.org/legal/epl-v10.html
10   */
11  /*
12   * File: StatisticsDialog.java
13   * Created: 2007-00-00
14   * Author: amiskowiec, apohllo, entrop
15   * $Id: StatisticsDialog.java 3128 2009-08-05 15:04:07Z czerwin $
16   */
17  
18  package pl.edu.agh.cast.ui.dialogs;
19  
20  import java.lang.reflect.InvocationTargetException;
21  import java.util.ArrayList;
22  import java.util.LinkedList;
23  import java.util.List;
24  
25  import org.eclipse.core.runtime.CoreException;
26  import org.eclipse.core.runtime.IConfigurationElement;
27  import org.eclipse.core.runtime.IProgressMonitor;
28  import org.eclipse.core.runtime.NullProgressMonitor;
29  import org.eclipse.jface.operation.IRunnableWithProgress;
30  import org.eclipse.jface.viewers.ILabelProviderListener;
31  import org.eclipse.jface.viewers.IStructuredContentProvider;
32  import org.eclipse.jface.viewers.ITableLabelProvider;
33  import org.eclipse.jface.viewers.TableViewer;
34  import org.eclipse.jface.viewers.Viewer;
35  import org.eclipse.jface.viewers.ViewerSorter;
36  import org.eclipse.swt.SWT;
37  import org.eclipse.swt.dnd.TextTransfer;
38  import org.eclipse.swt.dnd.Transfer;
39  import org.eclipse.swt.events.MouseAdapter;
40  import org.eclipse.swt.events.MouseEvent;
41  import org.eclipse.swt.events.SelectionEvent;
42  import org.eclipse.swt.events.SelectionListener;
43  import org.eclipse.swt.graphics.Color;
44  import org.eclipse.swt.layout.GridData;
45  import org.eclipse.swt.layout.GridLayout;
46  import org.eclipse.swt.widgets.Button;
47  import org.eclipse.swt.widgets.Combo;
48  import org.eclipse.swt.widgets.Display;
49  import org.eclipse.swt.widgets.Event;
50  import org.eclipse.swt.widgets.Label;
51  import org.eclipse.swt.widgets.Listener;
52  import org.eclipse.swt.widgets.Shell;
53  import org.eclipse.swt.widgets.Table;
54  import org.eclipse.swt.widgets.TableColumn;
55  import org.eclipse.swt.widgets.TableItem;
56  import org.eclipse.swt.widgets.Text;
57  
58  import pl.edu.agh.cast.Activator;
59  import pl.edu.agh.cast.CastApplication;
60  import pl.edu.agh.cast.backward.editor.EditorUtilBackward;
61  import pl.edu.agh.cast.model.DefaultStatisticsProvider;
62  import pl.edu.agh.cast.model.IStatisticsProvider;
63  import pl.edu.agh.cast.model.base.IDataProvider;
64  import pl.edu.agh.cast.model.visual.backward.Statistic;
65  import pl.edu.agh.cast.util.IExceptionHandler;
66  import pl.edu.agh.cast.util.Messages;
67  
68  /**
69   * Dialog used for displaying data statistics.
70   *
71   * @author AGH CAST Team
72   */
73  public class StatisticsDialog extends org.eclipse.swt.widgets.Dialog {
74  
75  	private static final IStatisticsProvider DEFAULT_STATISTICS_PROVIDER = new DefaultStatisticsProvider();
76  
77  	private static final String ROW_SEPARATOR = "\n"; //$NON-NLS-1$
78  
79  	private static final String COLUMN_SEPARATOR = "\t"; //$NON-NLS-1$
80  
81  	private static final String ID = "id"; //$NON-NLS-1$
82  
83  	private Shell dialogShell;
84  
85  	private Table table;
86  
87  	private TableViewer viewer;
88  
89  	private TableColumn sourceColumn;
90  
91  	private TableColumn targetColumn;
92  
93  	private TableColumn nodeNameColumn;
94  
95  	private TableColumn totalColumn;
96  
97  	private Combo statisticsCombo;
98  
99  	private List<IConfigurationElement> statisticsEvaluators = new LinkedList<IConfigurationElement>();
100 
101 	private IDataProvider dataProvider;
102 
103 	private Button copyToClipboardButton;
104 
105 	private Label statisticsLabel;
106 
107 	private Text description;
108 
109 	private Label descriptionLabel;
110 
111 	private List<Statistic> currentStatistics;
112 
113 	private Button hideZerosRows;
114 
115 	/**
116 	 * Creates new statistics dialog for given data.
117 	 *
118 	 * @param diagram
119 	 *            data provider to display statistics for
120 	 * @param parent
121 	 *            parent {@link Shell}
122 	 * @param style
123 	 *            SWT window style
124 	 */
125 	public StatisticsDialog(IDataProvider diagram, Shell parent, int style) {
126 		super(parent, style);
127 		dataProvider = diagram;
128 	}
129 
130 	/**
131 	 * Opens the dialog.
132 	 */
133 	public void open() {
134 		try {
135 			Shell parent = getParent();
136 			dialogShell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.APPLICATION_MODAL);
137 
138 			dialogShell.setText(Messages.ShowStatisticsDialog_1);
139 
140 			GridLayout dialogShellLayout = new GridLayout();
141 			dialogShellLayout.numColumns = 3;
142 			dialogShell.setLayout(dialogShellLayout);
143 			dialogShell.layout();
144 			dialogShell.pack();
145 			dialogShell.setSize(584, 442);
146 
147 			table = new Table(dialogShell, SWT.NONE);
148 			table.setLinesVisible(true);
149 			GridData tableLData = new GridData();
150 			tableLData.grabExcessHorizontalSpace = true;
151 			tableLData.grabExcessVerticalSpace = true;
152 			tableLData.verticalAlignment = GridData.FILL;
153 			tableLData.horizontalAlignment = GridData.FILL;
154 			tableLData.horizontalSpan = 3;
155 			table.setLayoutData(tableLData);
156 			table.setHeaderVisible(true);
157 
158 			statisticsLabel = new Label(dialogShell, SWT.NONE);
159 			statisticsLabel.setText(Messages.StatisticsDialog_1);
160 			statisticsLabel.setSize(-1, -1);
161 
162 			statisticsCombo = new Combo(dialogShell, SWT.READ_ONLY);
163 			statisticsCombo.setSize(-1, -1);
164 
165 			copyToClipboardButton = new Button(dialogShell, SWT.PUSH | SWT.CENTER);
166 			copyToClipboardButton.setText(Messages.StatisticsDialog_0);
167 			GridData copyToClipboardButtonLData = new GridData();
168 			copyToClipboardButtonLData.verticalAlignment = GridData.FILL;
169 			copyToClipboardButtonLData.horizontalAlignment = GridData.END;
170 			copyToClipboardButton.setLayoutData(copyToClipboardButtonLData);
171 			copyToClipboardButton.setSize(-1, -1);
172 			copyToClipboardButton.addMouseListener(new MouseAdapter() {
173 				@Override
174 				public void mouseUp(MouseEvent evt) {
175 					copyToClipboard(evt);
176 				}
177 			});
178 
179 			descriptionLabel = new Label(dialogShell, SWT.NONE);
180 			descriptionLabel.setText(Messages.StatisticsDialog_2);
181 			GridData descriptionLabelData = new GridData();
182 			descriptionLabelData.verticalAlignment = GridData.BEGINNING;
183 			descriptionLabel.setLayoutData(descriptionLabelData);
184 
185 			description = new Text(dialogShell, SWT.MULTI | SWT.READ_ONLY);
186 			description.setText(Messages.StatisticsDialog_3);
187 			description.setEnabled(false);
188 			GridData descriptionData = new GridData();
189 			descriptionData.horizontalSpan = 2;
190 			descriptionData.horizontalAlignment = GridData.FILL;
191 			descriptionData.grabExcessHorizontalSpace = true;
192 			description.setLayoutData(descriptionData);
193 
194 			new Label(dialogShell, SWT.NONE);
195 
196 			hideZerosRows = new Button(dialogShell, SWT.CHECK);
197 			hideZerosRows.setText(Messages.StatisticsDialog_HideZeros);
198 			hideZerosRows.addListener(SWT.Selection, new Listener() {
199 				public void handleEvent(Event arg0) {
200 					fillData();
201 				}
202 
203 			});
204 
205 			nodeNameColumn = new TableColumn(table, SWT.NONE);
206 			nodeNameColumn.setText(Messages.EntityHeader);
207 			nodeNameColumn.setWidth(120);
208 			nodeNameColumn.setResizable(true);
209 			nodeNameColumn.setData(ID, Messages.EntityHeader);
210 
211 			targetColumn = new TableColumn(table, SWT.NONE);
212 			targetColumn.setText(Messages.IncomingCallsHeader);
213 			targetColumn.setWidth(150);
214 			targetColumn.setResizable(true);
215 			targetColumn.setData(ID, Messages.IncomingCallsHeader);
216 
217 			sourceColumn = new TableColumn(table, SWT.NONE);
218 			sourceColumn.setText(Messages.OutgoingCallsHeader);
219 			sourceColumn.setWidth(150);
220 			sourceColumn.setResizable(true);
221 			sourceColumn.setData(ID, Messages.OutgoingCallsHeader);
222 
223 			totalColumn = new TableColumn(table, SWT.NONE);
224 			totalColumn.setText(Messages.TotalHeader);
225 			totalColumn.setWidth(150);
226 			totalColumn.setResizable(true);
227 			totalColumn.setData(ID, Messages.TotalHeader);
228 
229 			dialogShell.setLocation(getParent().toDisplay(100, 100));
230 			setupViewer();
231 			setData(DEFAULT_STATISTICS_PROVIDER.statistics(dataProvider, new NullProgressMonitor()));
232 			fillStatistics();
233 			dialogShell.open();
234 			Display display = dialogShell.getDisplay();
235 			while (!dialogShell.isDisposed()) {
236 				if (!display.readAndDispatch()) {
237 					display.sleep();
238 				}
239 			}
240 		} catch (Exception e) {
241 			e.printStackTrace();
242 		}
243 	}
244 
245 	private void fillStatistics() {
246 		for (IConfigurationElement contribution : Activator.getStatisticsConfigurations()) {
247 			statisticsCombo.add(Messages.AllRelations);
248 			statisticsEvaluators.add(null);
249 			for (IConfigurationElement stat : contribution.getChildren()) {
250 				statisticsCombo.add(stat.getAttribute(Activator.STATISTIC_NAME));
251 				statisticsEvaluators.add(stat);
252 			}
253 			statisticsCombo.select(0);
254 		}
255 		statisticsCombo.addSelectionListener(new SelectionListener() {
256 
257 			public void widgetDefaultSelected(SelectionEvent e) {
258 			}
259 
260 			public void widgetSelected(SelectionEvent e) {
261 				final IConfigurationElement stat = statisticsEvaluators.get(statisticsCombo.getSelectionIndex());
262 
263 				final IStatisticsProvider provider;
264 				if (stat == null) {
265 					provider = null;
266 				} else {
267 					try {
268 						provider = (IStatisticsProvider)stat.createExecutableExtension(Activator.STATISTIC_CLASS);
269 					} catch (CoreException e1) {
270 						Activator.getLogger().error("Error while initializing statistics", e1); //$NON-NLS-1$
271 						return;
272 					}
273 				}
274 
275 				EditorUtilBackward.runWithProgressMonitorInUIThread(new IRunnableWithProgress() {
276 					public void run(IProgressMonitor progress) throws InvocationTargetException, InterruptedException {
277 						doCalculateStatistics(stat, provider, progress);
278 					}
279 				}, new IExceptionHandler() {
280 					public void handleException(Exception e) {
281 						Activator.getLogger().error("Error calculating statistics", e); //$NON-NLS-1$
282 					};
283 				});
284 			}
285 		});
286 		statisticsCombo.addSelectionListener(new SelectionListener() {
287 
288 			public void widgetDefaultSelected(SelectionEvent e) {
289 			}
290 
291 			public void widgetSelected(SelectionEvent e) {
292 				IConfigurationElement stat = statisticsEvaluators.get(statisticsCombo.getSelectionIndex());
293 				if (stat == null) {
294 					description.setText(Messages.StatisticsDialog_3);
295 				} else {
296 					description.setText(stat.getAttribute(Activator.STATISTIC_DESCRIPTION));
297 				}
298 			}
299 
300 		});
301 
302 	}
303 
304 	private void doCalculateStatistics(final IConfigurationElement stat, IStatisticsProvider provider,
305 	        IProgressMonitor progress) {
306 		if (stat == null) {
307 			// default statistics
308 			setData(DEFAULT_STATISTICS_PROVIDER.statistics(dataProvider, progress));
309 			targetColumn.setText(Messages.IncomingCallsHeader);
310 			sourceColumn.setText(Messages.OutgoingCallsHeader);
311 		} else {
312 			setData(provider.statistics(dataProvider, progress));
313 			targetColumn.setText(stat.getAttribute(Activator.STATISTIC_TARGET_LABEL));
314 			sourceColumn.setText(stat.getAttribute(Activator.STATISTIC_SOURCE_LABEL));
315 		}
316 	}
317 
318 	private void setupViewer() {
319 		viewer = new TableViewer(table);
320 
321 		attachContentProvider();
322 		attachLabelProvider();
323 		attachSortListeners();
324 	}
325 
326 	private void attachSortListeners() {
327 		Listener sortListener = new Listener() {
328 			public void handleEvent(Event e) {
329 				// determine new sort column and direction
330 				TableColumn sortColumn = viewer.getTable().getSortColumn();
331 				TableColumn currentColumn = (TableColumn)e.widget;
332 				int dir = viewer.getTable().getSortDirection();
333 				if (sortColumn == currentColumn) {
334 					dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
335 				} else {
336 					viewer.getTable().setSortColumn(currentColumn);
337 					dir = SWT.UP;
338 				}
339 				// sort the data based on column and direction
340 				String sortIdentifier = null;
341 
342 				if (currentColumn.getData(ID).equals(Sorter.ENTITY)) {
343 					sortIdentifier = Sorter.ENTITY;
344 				} else if (currentColumn.getData(ID).equals(Sorter.TARGET)) {
345 					sortIdentifier = Sorter.TARGET;
346 				} else if (currentColumn.getData(ID).equals(Sorter.SOURCE)) {
347 					sortIdentifier = Sorter.SOURCE;
348 				} else if (currentColumn.getData(ID).equals(Sorter.TOTAL)) {
349 					sortIdentifier = Sorter.TOTAL;
350 				}
351 
352 				viewer.getTable().setSortDirection(dir);
353 				viewer.setSorter(new Sorter(sortIdentifier, dir));
354 				fillData();
355 			}
356 		};
357 
358 		nodeNameColumn.addListener(SWT.Selection, sortListener);
359 		sourceColumn.addListener(SWT.Selection, sortListener);
360 		targetColumn.addListener(SWT.Selection, sortListener);
361 		totalColumn.addListener(SWT.Selection, sortListener);
362 
363 	}
364 
365 	private void setData(List<Statistic> statistics) {
366 		currentStatistics = statistics;
367 		fillData();
368 	}
369 
370 	private void fillData() {
371 		for (int i = 0; i < viewer.getTable().getItemCount(); i++) {
372 			viewer.clear(i);
373 		}
374 		viewer.setItemCount(0);
375 
376 		List<Statistic> data = new ArrayList<Statistic>();
377 		boolean hideZeros = hideZerosRows.getSelection();
378 		for (Statistic statistic : currentStatistics) {
379 			if (hideZeros && statistic.getTotal() == 0) {
380 				continue;
381 			}
382 			data.add(statistic);
383 		}
384 
385 		viewer.add(data.toArray());
386 		double sourceSummary = 0;
387 		double targetSummary = 0;
388 		for (Statistic stat : data) {
389 			sourceSummary += stat.getSource();
390 			targetSummary += stat.getTarget();
391 		}
392 		Statistic summaryStatistics = new Statistic(data.size() + "", //$NON-NLS-1$
393 		        sourceSummary, targetSummary);
394 		summaryStatistics.setSummary(true);
395 		viewer.add(summaryStatistics);
396 		TableItem ti = viewer.getTable().getItem(viewer.getTable().getItemCount() - 1);
397 		Color blue = new Color(table.getDisplay(), 0, 200, 255);
398 		// highlight the summary row
399 		for (int i = 0; i < 5; i++) {
400 			ti.setBackground(i, blue);
401 		}
402 	}
403 
404 	private void attachLabelProvider() {
405 		viewer.setLabelProvider(new ITableLabelProvider() {
406 			public org.eclipse.swt.graphics.Image getColumnImage(Object element, int columnIndex) {
407 				return null;
408 			}
409 
410 			public String getColumnText(Object element, int columnIndex) {
411 				String text = null;
412 				switch (columnIndex) {
413 					case 0: {
414 						text = ((Statistic)element).getNodeName();
415 						break;
416 					}
417 					case 1: {
418 						text = ((Statistic)element).getTargetLabel();
419 						break;
420 					}
421 					case 2: {
422 						text = ((Statistic)element).getSourceLabel();
423 						break;
424 					}
425 					case 3: {
426 						text = ((Statistic)element).getTotalLabel();
427 						break;
428 					}
429 					default: {
430 						throw new IllegalArgumentException("Invalid column index " //$NON-NLS-1$
431 						        + columnIndex);
432 					}
433 				}
434 				return text;
435 			}
436 
437 			public void addListener(ILabelProviderListener listener) {
438 			}
439 
440 			public void dispose() {
441 			}
442 
443 			public boolean isLabelProperty(Object element, String property) {
444 				return false;
445 			}
446 
447 			public void removeListener(ILabelProviderListener lpl) {
448 			}
449 		});
450 	}
451 
452 	private void attachContentProvider() {
453 		viewer.setContentProvider(new IStructuredContentProvider() {
454 			public Object[] getElements(Object inputElement) {
455 				return (Object[])inputElement;
456 			}
457 
458 			public void dispose() {
459 			}
460 
461 			public void inputChanged(Viewer view, Object oldInput, Object newInput) {
462 			}
463 
464 		});
465 	}
466 
467 	private void copyToClipboard(MouseEvent evt) {
468 		StringBuilder builder = new StringBuilder();
469 		int columnsCount = viewer.getTable().getColumnCount();
470 		for (TableItem item : viewer.getTable().getItems()) {
471 			boolean first = true;
472 			for (int i = 0; i < columnsCount; i++) {
473 				if (first) {
474 					first = false;
475 				} else {
476 					builder.append(COLUMN_SEPARATOR);
477 				}
478 				builder.append(item.getText(i));
479 			}
480 			builder.append(ROW_SEPARATOR);
481 		}
482 		CastApplication.getClipboard().setContents(new Object[] { builder.toString() },
483 		        new Transfer[] { TextTransfer.getInstance() });
484 	}
485 
486 	/**
487 	 * Statistics view sorter.
488 	 *
489 	 * @author AGH CAST Team
490 	 */
491 	private static class Sorter extends ViewerSorter {
492 
493 		public static final String ENTITY = Messages.EntityHeader;
494 
495 		public static final String TARGET = Messages.IncomingCallsHeader;
496 
497 		public static final String SOURCE = Messages.OutgoingCallsHeader;
498 
499 		public static final String TOTAL = Messages.TotalHeader;
500 
501 		private String column;
502 
503 		private int dir;
504 
505 		public Sorter(String column, int dir) {
506 			super();
507 			this.column = column;
508 			this.dir = dir;
509 		}
510 
511 		/**
512 		 * {@inheritDoc}
513 		 *
514 		 * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object,
515 		 *      java.lang.Object)
516 		 */
517 		@Override
518 		public int compare(Viewer viewer, Object e1, Object e2) {
519 			int returnValue = 0;
520 
521 			Statistic s1 = (Statistic)e1;
522 			Statistic s2 = (Statistic)e2;
523 			if (s1.isSummary()) {
524 				return 1;
525 			}
526 			if (s2.isSummary()) {
527 				return -1;
528 			}
529 			if (this.column == ENTITY) {
530 				returnValue = s1.getNodeName().compareTo(s2.getNodeName());
531 			} else if (this.column == TARGET) {
532 				returnValue = s1.getTarget() - s2.getTarget() > 0 ? 1 : -1;
533 			} else if (this.column == SOURCE) {
534 				returnValue = s1.getSource() - s2.getSource() > 0 ? 1 : -1;
535 			} else if (this.column == TOTAL) {
536 				returnValue = s1.getSource() + s1.getTarget() - s2.getSource() - s2.getTarget() > 0 ? 1 : -1;
537 			}
538 
539 			if (this.dir == SWT.DOWN) {
540 				returnValue = returnValue * -1;
541 			}
542 			return returnValue;
543 		}
544 	}
545 
546 }