1/*******************************************************************************
2 * Copyright (c) 2000, 2009 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *     IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.test.internal.performance.results.ui;
12
13import java.util.StringTokenizer;
14
15import org.eclipse.core.runtime.preferences.IEclipsePreferences;
16import org.eclipse.core.runtime.preferences.InstanceScope;
17import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
18import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
19import org.eclipse.jface.action.Action;
20import org.eclipse.jface.action.IAction;
21import org.eclipse.jface.action.IMenuManager;
22import org.eclipse.jface.action.IToolBarManager;
23import org.eclipse.jface.action.MenuManager;
24import org.eclipse.jface.action.Separator;
25import org.eclipse.jface.resource.ImageDescriptor;
26import org.eclipse.jface.resource.JFaceResources;
27import org.eclipse.jface.viewers.ISelectionChangedListener;
28import org.eclipse.jface.viewers.SelectionChangedEvent;
29import org.eclipse.jface.viewers.TreeSelection;
30import org.eclipse.swt.SWT;
31import org.eclipse.swt.custom.CTabFolder;
32import org.eclipse.swt.custom.CTabItem;
33import org.eclipse.swt.widgets.Composite;
34import org.eclipse.swt.widgets.Display;
35import org.eclipse.swt.widgets.Table;
36import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
37import org.eclipse.test.internal.performance.results.model.ComponentResultsElement;
38import org.eclipse.test.internal.performance.results.model.ConfigResultsElement;
39import org.eclipse.test.internal.performance.results.model.DimResultsElement;
40import org.eclipse.test.internal.performance.results.model.PerformanceResultsElement;
41import org.eclipse.test.internal.performance.results.model.ResultsElement;
42import org.eclipse.test.internal.performance.results.model.ScenarioResultsElement;
43import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
44import org.eclipse.test.internal.performance.results.utils.Util;
45import org.eclipse.ui.IActionBars;
46import org.eclipse.ui.IMemento;
47import org.eclipse.ui.IViewSite;
48import org.eclipse.ui.PartInitException;
49import org.eclipse.ui.PlatformUI;
50import org.eclipse.ui.part.ViewPart;
51
52
53/**
54 * This view show for each performance machine the results of all builds
55 * run since the beginning of the current version development.
56 * <p>
57 * Each machine results are show in a separate tab.
58 * </p><p>
59 * There's no real action available action from this view, only the possibility
60 * to change how is displayed the line selection (full or only first column)
61 * and also filter the results:
62 * 	<ul>
63 *	<li>Filter for builds:
64 *		<ul>
65 *		<li>Filter nightly:	hide the nightly builds (starting with 'N')</li>
66 *		<li>Filter non-important builds:	hide all non-important builds, which means non-milestone builds and those after the last milestone</li>
67 *		</ul>
68 *	</li>
69 *	</li>Filter for scenarios:
70 *		<ul>
71 *		<li>Filter non-fingerprints: hide the scenarios which are not in the fingerprints</li>
72 *		</ul>
73 *	</li>
74 *	</ul>
75 * </p><p>
76 * Note that these filters are synchronized with the ones applied in the
77 * {@link ComponentsView Components view}.
78 * </p>
79 *
80 * @see ConfigTab Folder tab containing all the results for a configuration.
81 */
82public class ComponentResultsView extends ViewPart implements ISelectionChangedListener, IPreferenceChangeListener {
83
84	// Constants
85	private static final String ORG_ECLIPSE = "org.eclipse.";
86
87	// SWT resources
88	CTabFolder tabFolder;
89
90	// Model information
91	ConfigTab[] tabs;
92	ComponentResultsElement componentResultsElement;
93
94	// Action
95	Action fullLineSelection;
96	Action filterAdvancedScenarios;
97	Action filterOldBuilds;
98	Action filterNightlyBuilds;
99	ImageDescriptor fullSelectionImageDescriptor;
100
101	// Views
102	IMemento viewState;
103
104	// Eclipse preferences
105	IEclipsePreferences preferences;
106
107/*
108 * Default constructor:
109 * 	- create the image descriptor
110 * 	- register the view as a properties listener
111 */
112public ComponentResultsView() {
113	this.fullSelectionImageDescriptor = ImageDescriptor.createFromFile(getClass(), "icallout_obj.gif");
114	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
115	this.preferences.addPreferenceChangeListener(this);
116	Util.initMilestones(this.preferences);
117}
118
119/*
120 * Contribute the local tools bar and the pull-down menu to the action bars.
121 */
122void contributeToActionBars() {
123	IActionBars bars = getViewSite().getActionBars();
124	fillLocalPullDown(bars.getMenuManager());
125	fillLocalToolBar(bars.getToolBarManager());
126}
127
128/*
129 * (non-Javadoc)
130 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
131 */
132public void createPartControl(Composite parent) {
133
134	// Create the tab folder
135	this.tabFolder = new CTabFolder(parent, SWT.BORDER);
136
137	// Add results view as listener to viewer selection changes
138	Display.getDefault().asyncExec(new Runnable() {
139		public void run() {
140			PerformancesView performancesView = (PerformancesView) PerformancesView.getWorkbenchView("org.eclipse.test.internal.performance.results.ui.ComponentsView");
141			if (performancesView != null) {
142				performancesView.viewer.addSelectionChangedListener(ComponentResultsView.this);
143			}
144		}
145	});
146
147	// Set actions
148	PlatformUI.getWorkbench().getHelpSystem().setHelp(this.tabFolder, "org.eclipse.test.performance.ui.results");
149	makeActions();
150	contributeToActionBars();
151
152	// Restore state
153	restoreState();
154
155	// Create tabs
156	createTabs();
157
158	// Set selections (tab and line)
159	this.tabFolder.setSimple(false);
160}
161
162/*
163 * Create the tab folder pages. There's one tab per performance machine.
164 * The list of these machines is got from the DB_Results contants.
165 */
166void createTabs() {
167	if (this.componentResultsElement == null) return;
168	PerformanceResultsElement performanceResultsElement = (PerformanceResultsElement) this.componentResultsElement.getParent(null);
169	String[] configNames = performanceResultsElement.getConfigs();
170	String[] configDescriptions = performanceResultsElement.getConfigDescriptions();
171	int length = configNames.length;
172	this.tabs = new ConfigTab[length];
173	for (int i=0; i<length; i++) {
174		this.tabs[i] = new ConfigTab(configNames[i], configDescriptions[i]);
175	}
176	for (int i=0; i<this.tabs.length; i++) {
177		CTabItem item = new CTabItem (this.tabFolder, SWT.NONE);
178		item.setText (this.tabs[i].getTabText ());
179		item.setControl (this.tabs[i].createTabFolderPage(this.componentResultsElement, this.tabFolder, this.fullLineSelection.isChecked()));
180		item.setData (this.tabs[i]);
181	}
182	this.tabFolder.setSelection(0);
183}
184
185/*
186 * (non-Javadoc)
187 * @see org.eclipse.ui.part.WorkbenchPart#dispose()
188 */
189public void dispose() {
190	this.tabFolder.dispose();
191	int length = this.tabs==null ? 0 : this.tabs.length;
192	for (int i=0; i<length; i++) {
193		this.tabs[i].dispose();
194	}
195	JFaceResources.getResources().destroyImage(this.fullSelectionImageDescriptor);
196	super.dispose();
197}
198
199/*
200 * Fill the filters drop-down menu with:
201 * 	- filter nightly builds
202 * 	- filter non-milestone builds
203 *	- filter non-fingerprint scenarios
204 */
205void fillFiltersDropDown(IMenuManager manager) {
206	manager.add(this.filterNightlyBuilds);
207	manager.add(this.filterOldBuilds);
208	manager.add(new Separator());
209	manager.add(this.filterAdvancedScenarios);
210}
211
212/*
213 * Fill the local pull down menu.
214 */
215void fillLocalPullDown(IMenuManager manager) {
216	MenuManager filtersManager= new MenuManager("Filters");
217	fillFiltersDropDown(filtersManager);
218	manager.add(filtersManager);
219}
220
221/*
222 * Fill the local tool bar with:
223 * 	- change line selection display
224 */
225void fillLocalToolBar(IToolBarManager manager) {
226	manager.add(this.fullLineSelection);
227}
228
229/*
230 * (non-Javadoc)
231 * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
232 */
233public void init(IViewSite site, IMemento memento) throws PartInitException {
234	super.init(site, memento);
235	this.viewState = memento;
236}
237
238/*
239 * Make the actions of the view:
240 * 	- change table line selection display
241 * 	- filter nightly builds
242 * 	- filter non-milestone builds
243 *	- filter non-fingerprint scenarios
244 */
245void makeActions() {
246
247	// Full line selection action
248	this.fullLineSelection = new Action("", IAction.AS_CHECK_BOX) {
249		public void run() {
250			resetTabFolders(false/*refresh*/);
251		}
252	};
253	this.fullLineSelection.setImageDescriptor(this.fullSelectionImageDescriptor);
254	this.fullLineSelection.setToolTipText("Full line selection");
255//	this.fullLineSelection.setChecked(true);
256
257	// Filter non-fingerprints action
258	this.filterAdvancedScenarios = new Action("Advanced &Scenarios", IAction.AS_CHECK_BOX) {
259		public void run() {
260			ComponentResultsView.this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, isChecked());
261			resetTabFolders(false/*refresh*/);
262        }
263	};
264	this.filterAdvancedScenarios.setChecked(true);
265	this.filterAdvancedScenarios.setToolTipText("Filter advanced scenarios (i.e. not fingerprint ones)");
266
267	// Filter non-important builds action
268	this.filterOldBuilds = new Action("&Old Builds", IAction.AS_CHECK_BOX) {
269		public void run() {
270			ComponentResultsView.this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_OLD_BUILDS, isChecked());
271			resetTabFolders(false/*refresh*/);
272		}
273	};
274	this.filterOldBuilds.setChecked(false);
275	this.filterOldBuilds.setToolTipText("Filter old builds (i.e. before last milestone) but keep all previous milestones)");
276
277	// Filter nightly action
278	this.filterNightlyBuilds = new Action("&Nightly", IAction.AS_CHECK_BOX) {
279		public void run() {
280			ComponentResultsView.this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS, isChecked());
281			resetTabFolders(false/*refresh*/);
282		}
283	};
284	this.filterNightlyBuilds.setToolTipText("Filter nightly builds");
285}
286
287/* (non-Javadoc)
288 * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
289 */
290public void preferenceChange(PreferenceChangeEvent event) {
291	String propertyName = event.getKey();
292	Object newValue = event.getNewValue();
293
294	// Filter non-fingerprints change
295	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS)) {
296		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS : "true".equals(newValue);
297		this.filterAdvancedScenarios.setChecked(checked);
298		resetTabFolders(false/*refresh*/);
299	}
300
301	// Filter non-milestone change
302	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_OLD_BUILDS)) {
303		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS : "true".equals(newValue);
304		this.filterOldBuilds.setChecked(checked);
305		resetTabFolders(false/*refresh*/);
306	}
307
308	// Filter nightly builds change
309	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS)) {
310		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_NIGHTLY_BUILDS : "true".equals(newValue);
311		this.filterNightlyBuilds.setChecked(checked);
312		resetTabFolders(false/*refresh*/);
313	}
314}
315
316/*
317 * Reset the table tab folders by re-create all the pages.
318 * Selections are set onto the first found error if this is the first tab creation (typically on a component change event from the ComponentsView)
319 * or to the previous one if this is just a refresh.
320 */
321void resetTabFolders(boolean init) {
322
323	// Store current indexes
324	int tabIndex = this.tabFolder.getSelectionIndex();
325	int lineIndex = tabIndex<0 ? -1 : this.tabs[tabIndex].table.getSelectionIndex();
326
327	// Create tab folders
328	CTabItem[] tabItems = this.tabFolder.getItems();
329	int length = tabItems.length;
330	if (length == 0) {
331		createTabs();
332	} else {
333		for (int i=0; i<length; i++) {
334			tabItems[i].setControl(this.tabs [i].createTabFolderPage(this.componentResultsElement, this.tabFolder, this.fullLineSelection.isChecked()));
335		}
336	}
337
338	// Set the part name when possible
339	if (this.componentResultsElement != null) {
340		setPartName();
341	}
342
343	// If this is the first display then look for the first error to set the selection on it
344	if (init)  {
345		if (this.componentResultsElement != null) {
346			// If the component has
347			if (this.componentResultsElement.hasError()) {
348				ResultsElement[] children = this.componentResultsElement.getChildren(); // get scenarios
349				int childrenLength = children.length;
350				for (int s=0; s<childrenLength; s++) {
351					if (children[s].hasError()) {
352						children = children[s].getChildren(); // get configs
353						for (int c=0; c<childrenLength; c++) {
354							if (children[c].hasError()) {
355								tabIndex = c;
356								break;
357							}
358						}
359						break;
360					}
361				}
362			}
363		}
364		lineIndex = 0;
365	}
366
367	// Set the selection
368	if (tabIndex >= 0 && lineIndex >= 0) {
369		this.tabFolder.setSelection(tabIndex);
370		Table table = this.tabs[tabIndex].table;
371		table.setSelection(lineIndex);
372	}
373}
374
375/*
376 * Restore the view state from the memento information.
377 */
378void restoreState() {
379
380	// Filter baselines action state
381	if (this.viewState != null) {
382		Boolean state = this.viewState.getBoolean(IPerformancesConstants.PRE_FULL_LINE_SELECTION);
383		boolean fullLine = state == null ? true : state.booleanValue();
384		this.fullLineSelection.setChecked(fullLine);
385	}
386
387	// Filter non fingerprints action state
388	boolean checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
389	this.filterAdvancedScenarios.setChecked(checked);
390
391	// Filter nightly builds action
392	checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS, IPerformancesConstants.DEFAULT_FILTER_NIGHTLY_BUILDS);
393	this.filterNightlyBuilds.setChecked(checked);
394
395	// Filter non important builds action state
396	checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_OLD_BUILDS, IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS);
397	this.filterOldBuilds.setChecked(checked);
398}
399
400/*
401 * (non-Javadoc)
402 * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
403 */
404public void saveState(IMemento memento) {
405	super.saveState(memento);
406	memento.putBoolean(IPerformancesConstants.PRE_FULL_LINE_SELECTION, this.fullLineSelection.isChecked());
407}
408
409/*
410 * (non-Javadoc)
411 * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
412 */
413public void selectionChanged(SelectionChangedEvent event) {
414	ResultsElement selectedElement = (ResultsElement) ((TreeSelection) event.getSelection()).getFirstElement();
415	ComponentResultsElement componentElement = null;
416	ScenarioResultsElement scenarioResultsElement = null;
417	ConfigResultsElement configResultsElement = null;
418	BuildResultsElement buildResultsElement = null;
419	if (selectedElement instanceof ComponentResultsElement) {
420		componentElement = (ComponentResultsElement) selectedElement;
421	} else if (selectedElement instanceof ScenarioResultsElement) {
422		scenarioResultsElement = (ScenarioResultsElement) selectedElement;
423		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
424	} else if (selectedElement instanceof ConfigResultsElement) {
425		configResultsElement = (ConfigResultsElement) selectedElement;
426		scenarioResultsElement = (ScenarioResultsElement) configResultsElement.getParent(null);
427		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
428	} else if (selectedElement instanceof BuildResultsElement) {
429		buildResultsElement = (BuildResultsElement) selectedElement;
430		configResultsElement = (ConfigResultsElement) buildResultsElement.getParent(null);
431		scenarioResultsElement = (ScenarioResultsElement) configResultsElement.getParent(null);
432		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
433	} else if (selectedElement instanceof DimResultsElement) {
434		buildResultsElement = (BuildResultsElement) selectedElement.getParent(null);
435		configResultsElement = (ConfigResultsElement) buildResultsElement.getParent(null);
436		scenarioResultsElement = (ScenarioResultsElement) configResultsElement.getParent(null);
437		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
438	}
439	if (componentElement != this.componentResultsElement) {
440		this.componentResultsElement = componentElement;
441		if (componentElement == null || this.componentResultsElement.getChildren(null).length > 0) {
442			resetTabFolders(true);
443		}
444	}
445	if (configResultsElement != null) {
446		ConfigTab configTab = this.tabs[this.tabFolder.getSelectionIndex()];
447		if (!configResultsElement.getName().equals(configTab.configName)) {
448			int length = this.tabs.length;
449			for (int i=0; i<length; i++) {
450				if (this.tabs[i].configName.equals(configResultsElement.getName())) {
451					this.tabFolder.setSelection(i);
452				}
453			}
454		}
455		if (buildResultsElement != null) {
456			configTab = this.tabs[this.tabFolder.getSelectionIndex()];
457			configTab.select(buildResultsElement);
458		}
459	}
460}
461
462/*
463 * (non-Javadoc)
464 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
465 */
466public void setFocus() {
467	// do nothing
468}
469
470/*
471 * Set the name of the part.
472 * This name is built from the name of the component selected in the Components view.
473 * The rules to build the name are:
474 * 	1. If the component name does not start then the part name is just "'<component name>' results"
475 * 	2. Otherwise, remove "org.eclipse." form the component name and count the tokens separated by a dot ('.')
476 * 		a. if there's only one remaining token, then the part name is "Platform/"
477 * 			+ "<token uppercased>" if token is less than 3 characters,"<token with first char uppercased>" otherwise
478 * 		b. otherwise then the part name is "<first token uppercased>"
479 * 			+ for each followed additional token:
480 * 				"<token uppercased>" if token is less than 3 characters,"<token with first char uppercased>" otherwise
481 * 			+ " results"
482 * E.g.
483 * 	- org.eclipse.ui -> "Platform/UI"
484 * 	- org.eclipse.swt -> "Platform/SWT"
485 * 	- org.eclipse.team -> "Platform/Team"
486 * 	- org.eclipse.jdt.ui -> "JDT/UI"
487 * 	- org.eclipse.jdt.core -> "JDT/Core"
488 * 	- org.eclipse.pde.api.tools -> "PDE/API Tools"
489 */
490protected void setPartName() {
491	String componentName = this.componentResultsElement.getName();
492	String partName;
493	StringBuffer buffer = null;
494	if (componentName.startsWith(ORG_ECLIPSE)) {
495		partName = componentName.substring(ORG_ECLIPSE.length());
496		StringTokenizer tokenizer = new StringTokenizer(partName, ".");
497		while (tokenizer.hasMoreTokens()) {
498			String token = tokenizer.nextToken();
499			if (buffer == null) {
500				if (tokenizer.hasMoreTokens()) {
501					buffer = new StringBuffer("'"+token.toUpperCase());
502					buffer.append('/');
503				} else {
504					buffer = new StringBuffer("'Platform/");
505					if (token.length() > 3) {
506						buffer.append(Character.toUpperCase(token.charAt(0)));
507						buffer.append(token.substring(1));
508					} else {
509						buffer.append(token.toUpperCase());
510					}
511				}
512			} else {
513				if (token.length() > 3) {
514					buffer.append(Character.toUpperCase(token.charAt(0)));
515					buffer.append(token.substring(1));
516				} else {
517					buffer.append(token.toUpperCase());
518				}
519				if (tokenizer.hasMoreTokens()) buffer.append(' ');
520			}
521		}
522	} else {
523		buffer = new StringBuffer("'");
524		buffer.append(componentName);
525		buffer.append("'");
526	}
527	buffer.append("' results");
528	setPartName(buffer.toString());
529}
530
531}
532