blob: 3dabb41610fdf0641bced708e4fdc15827926ed0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.test.internal.performance.results.ui;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.Comparator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.test.internal.performance.results.db.DB_Results;
import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
import org.eclipse.test.internal.performance.results.model.ResultsElement;
import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
import org.eclipse.test.internal.performance.results.utils.Util;
import org.eclipse.test.performance.ui.GenerateResults;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
/**
* View to see all the builds which have performance results stored in the database.
* <p>
* Typical actions from this view are update local data files with builds results
* and generated the HTML pages.
* </p>
*/
public class BuildsView extends PerformancesView {
/**
* Action to generate results.
*/
final class GenerateAction extends Action {
IStatus status;
public void run() {
// Ask for output directory
String resultGenerationDir = BuildsView.this.preferences.get(IPerformancesConstants.PRE_RESULTS_GENERATION_DIR, "");
String pathFilter = (BuildsView.this.outputDir == null) ? resultGenerationDir : BuildsView.this.outputDir.getPath();
File dir = changeDir(pathFilter, "Select directory to write comparison files");
if (dir == null) {
return;
}
BuildsView.this.outputDir = dir;
BuildsView.this.preferences.put(IPerformancesConstants.PRE_RESULTS_GENERATION_DIR, dir.getAbsolutePath());
// Select the reference
String[] baselines = BuildsView.this.results.getBaselines();
int bLength = baselines.length;
String selectedBaseline;
switch (bLength) {
case 0:
// no baseline, nothing to do...
selectedBaseline = BuildsView.this.results.getPerformanceResults().getBaselineName();
break;
case 1:
// only one baseline, no selection to do
selectedBaseline = baselines[0];
break;
default:
// select the baseline from list
ElementListSelectionDialog dialog = new ElementListSelectionDialog(getSite().getShell(), new LabelProvider());
dialog.setTitle(getTitleToolTip());
dialog.setMessage("Select the baseline to use while generating results:");
String[] defaultBaseline = new String[] { baselines[baselines.length - 1] };
dialog.setInitialSelections(defaultBaseline);
dialog.setElements(baselines);
dialog.open();
Object[] selected = dialog.getResult();
if (selected == null)
return;
selectedBaseline = (String) selected[0];
break;
}
final String baselineName = selectedBaseline;
BuildsView.this.results.getPerformanceResults().setBaselineName(baselineName);
// Ask for fingerprints
final boolean fingerprints = MessageDialog.openQuestion(BuildsView.this.shell, getTitleToolTip(), "Generate only fingerprints?");
// Generate all selected builds
int length = BuildsView.this.buildsResults.length;
for (int i = 0; i < length; i++) {
generate(i, baselineName, fingerprints);
}
}
/*
* Generate the HTML pages.
*/
private void generate(int i, final String baselineName, final boolean fingerprints) {
// Create output directory
final String buildName = BuildsView.this.buildsResults[i].getName();
final File genDir = new File(BuildsView.this.outputDir, buildName);
if (!genDir.exists() && !genDir.mkdir()) {
MessageDialog.openError(BuildsView.this.shell, getTitleToolTip(), "Cannot create " + genDir.getPath() + " to generate results!");
return;
}
// Create runnable
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
monitor.beginTask("Generate performance results", 10000);
GenerateResults generation = new GenerateResults(BuildsView.this.results.getPerformanceResults(),
buildName,
baselineName,
fingerprints,
BuildsView.this.dataDir,
genDir);
GenerateAction.this.status = generation.run(monitor);
monitor.done();
} catch (Exception e) {
e.printStackTrace();
}
}
};
// Run with progress monitor
ProgressMonitorDialog readProgress = new ProgressMonitorDialog(getSite().getShell());
try {
readProgress.run(true, true, runnable);
} catch (InvocationTargetException e) {
// skip
} catch (InterruptedException e) {
// skip
}
// Results
if (!this.status.isOK()) {
StringWriter swriter = new StringWriter();
PrintWriter pwriter = new PrintWriter(swriter);
swriter.write(this.status.getMessage());
Throwable ex = this.status.getException();
if (ex != null) {
swriter.write(": ");
swriter.write(ex.getMessage());
swriter.write('\n');
ex.printStackTrace(pwriter);
}
MessageDialog.open(this.status.getSeverity(),
BuildsView.this.shell,
getTitleToolTip(),
swriter.toString(),
SWT.NONE);
}
}
}
/**
* Action to update local data files with the performance results of a build.
*
* This may be done lazily (i.e. not done if the local data already knows
* the build) or forced (i.e. done whatever the local data files contain).
*/
class UpdateBuildAction extends Action {
boolean force;
UpdateBuildAction(boolean force) {
super();
this.force = force;
}
public void run() {
// Verify that directories are set
if (BuildsView.this.dataDir == null) {
if (changeDataDir() == null) {
if (!MessageDialog.openConfirm(BuildsView.this.shell, getTitleToolTip(), "No local files directory is set, hence the update could not be written! OK to continue?")) {
return;
}
}
}
// Progress dialog
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
updateBuilds(monitor);
} catch (Exception e) {
e.printStackTrace();
}
}
};
ProgressMonitorDialog readProgress = new ProgressMonitorDialog(getSite().getShell());
try {
readProgress.run(true, true, runnable);
} catch (InvocationTargetException e) {
return;
} catch (InterruptedException e) {
return;
}
// Reset Components and Builds views input
refreshInput();
getSiblingView().refreshInput();
}
void updateBuilds(IProgressMonitor monitor) {
BuildsView.this.updateBuilds(monitor, this.force);
}
}
/**
* Action to update local data files with the performance results of all builds.
*
* This may be done lazily (i.e. not done if the local data already knows
* the build) or forced (i.e. done whatever the local data files contain).
*/
class UpdateAllBuildsAction extends UpdateBuildAction {
UpdateAllBuildsAction(boolean force) {
super(force);
}
//
// public boolean isEnabled() {
// String[] elements = buildsToUpdate();
// return elements != null;
// }
void updateBuilds(IProgressMonitor monitor) {
BuildsView.this.updateAllBuilds(monitor, this.force);
}
}
/**
* Class to compare builds regarding their date instead of their name.
*
* @see Util#getBuildDate(String)
*/
class BuildDateComparator implements Comparator {
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
return Util.getBuildDate(s1).compareTo(Util.getBuildDate(s2));
}
}
// Views
PerformancesView componentsView;
// Results model
BuildResultsElement[] buildsResults;
// Generation info
File outputDir;
// Actions
Action generate;
UpdateBuildAction updateBuild, updateAllBuilds;
// UpdateBuildAction forceUpdateBuild, forceUpdateAllBuilds;
// SWT resources
Font italicFont;
/*
* Default constructor.
*/
public BuildsView() {
this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
this.preferences.addPreferenceChangeListener(this);
}
/*
* Compute the list of builds to update based on their status.
*/
String[] buildsToUpdate() {
Object[] elements = this.results.getBuilds();
int length = elements.length;
String[] buildsToUpdate = new String[length];
int count = 0;
for (int i=0; i<length; i++) {
BuildResultsElement element = (BuildResultsElement) elements[i];
if (element.getStatus() == 0) {
buildsToUpdate[count++] = element.getName();
}
}
if (count == 0) return null;
if (count < length) {
System.arraycopy(buildsToUpdate, 0, buildsToUpdate = new String[count], 0, count);
}
return buildsToUpdate;
}
/* (non-Javadoc)
* @see org.eclipse.test.internal.performance.results.ui.PerformancesView#createPartControl(org.eclipse.swt.widgets.Composite)
*/
public void createPartControl(Composite parent) {
super.createPartControl(parent);
// Create the viewer
this.viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
// Set the content provider: first level is builds list
WorkbenchContentProvider contentProvider = new WorkbenchContentProvider() {
public Object[] getElements(Object o) {
return getBuilds();
}
};
this.viewer.setContentProvider(contentProvider);
// Set the label provider
WorkbenchLabelProvider labelProvider = new WorkbenchLabelProvider() {
// Set an italic font when no local data have been read
public Font getFont(Object element) {
Font font = super.getFont(element);
if (element instanceof BuildResultsElement) {
if (((BuildResultsElement) element).isUnknown()) {
if (BuildsView.this.italicFont == null) {
FontData[] defaultFont = JFaceResources.getDefaultFont().getFontData();
FontData italicFontData = new FontData(defaultFont[0].getName(), defaultFont[0].getHeight(), SWT.ITALIC);
BuildsView.this.italicFont = new Font(DEFAULT_DISPLAY, italicFontData);
}
return BuildsView.this.italicFont;
}
}
return font;
}
// Set font in gray when no local data is available (i.e. local data needs to be updated)
public Color getForeground(Object element) {
Color color = super.getForeground(element);
if (element instanceof BuildResultsElement) {
if (!((BuildResultsElement) element).isRead()) {
color = DARK_GRAY;
}
}
return color;
}
};
this.viewer.setLabelProvider(labelProvider);
// Set the children sorter
ViewerSorter nameSorter = new ViewerSorter() {
// Sort children using specific comparison (see the implementation
// of the #compareTo(Object) in the ResultsElement hierarchy
public int compare(Viewer view, Object e1, Object e2) {
if (e2 instanceof ResultsElement) {
return ((ResultsElement) e2).compareTo(e1);
}
return super.compare(view, e1, e2);
}
};
this.viewer.setSorter(nameSorter);
// Finalize viewer initialization
PlatformUI.getWorkbench().getHelpSystem().setHelp(this.viewer.getControl(), "org.eclipse.test.performance.ui.builds");
finalizeViewerCreation();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#dispose()
*/
public void dispose() {
if (this.italicFont != null) {
this.italicFont.dispose();
}
super.dispose();
}
/*
* (non-Javadoc)
* @see org.eclipse.test.internal.performance.results.ui.PerformancesView#fillContextMenu(org.eclipse.jface.action.IMenuManager)
*/
void fillContextMenu(IMenuManager manager) {
super.fillContextMenu(manager);
manager.add(this.generate);
manager.add(this.updateBuild);
// manager.add(this.forceUpdateBuild);
}
/*
* (non-Javadoc)
* @see org.eclipse.test.internal.performance.results.ui.PerformancesView#fillLocalPullDown(org.eclipse.jface.action.IMenuManager)
*/
void fillFiltersDropDown(IMenuManager manager) {
super.fillFiltersDropDown(manager);
manager.add(this.filterLastBuilds);
}
/*
* Fill the local data drop-down menu
*/
void fillLocalDataDropDown(IMenuManager manager) {
super.fillLocalDataDropDown(manager);
manager.add(new Separator());
manager.add(this.updateAllBuilds);
// manager.add(this.forceUpdateAllBuilds);
}
/*
* Get all builds from the model.
*/
Object[] getBuilds() {
if (this.results == null) {
initResults();
}
return this.results.getBuilds();
}
/*
* Return the components view.
*/
PerformancesView getSiblingView() {
if (this.componentsView == null) {
this.componentsView = (PerformancesView) getWorkbenchView("org.eclipse.test.internal.performance.results.ui.ComponentsView");
}
return this.componentsView;
}
/*
* (non-Javadoc)
* @see org.eclipse.test.internal.performance.results.ui.PerformancesView#makeActions()
*/
void makeActions() {
super.makeActions();
// Generate action
this.generate = new GenerateAction();
this.generate.setText("&Generate");
// Update build actions
boolean connected = this.preferences.getBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
this.updateBuild = new UpdateBuildAction(false);
this.updateBuild.setText("&Update from DB");
this.updateBuild.setEnabled(connected);
// this.forceUpdateBuild = new UpdateBuildAction(true);
// this.forceUpdateBuild.setText("Force Update");
// Update build action
this.updateAllBuilds = new UpdateAllBuildsAction(false);
this.updateAllBuilds.setText("&Update from DB (all)");
this.updateAllBuilds.setEnabled(connected);
// this.forceUpdateAllBuilds = new UpdateAllBuildsAction(true);
// this.forceUpdateAllBuilds.setText("Force Update all");
// Set filters default
this.filterBaselineBuilds.setChecked(false);
this.filterNightlyBuilds.setChecked(false);
}
/**
* Reset the views.
*/
public void resetView() {
boolean debug = true;
// Look whether database constants has changed or not
int eclipseVersion = this.preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
boolean connected = this.preferences.getBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
String databaseLocation = this.preferences.get(IPerformancesConstants.PRE_DATABASE_LOCATION, IPerformancesConstants.NETWORK_DATABASE_LOCATION);
String lastBuild = this.preferences.get(IPerformancesConstants.PRE_LAST_BUILD, null);
boolean noLastBuild = lastBuild.length() == 0;
if (debug) {
System.out.println("Reset View:");
System.out.println(" - eclispe version = "+eclipseVersion);
System.out.println(" - connected = "+connected);
System.out.println(" - db location = "+databaseLocation);
System.out.println(" - last build = "+(noLastBuild?"<none>":lastBuild));
}
final boolean sameVersion = DB_Results.getDbVersion().endsWith(Integer.toString(eclipseVersion));
final boolean sameConnection = connected == DB_Results.DB_CONNECTION;
final boolean sameDB = sameVersion && databaseLocation.equals(DB_Results.getDbLocation());
boolean sameLastBuild = (noLastBuild && LAST_BUILD == null) || lastBuild.equals(LAST_BUILD);
if (debug) {
System.out.println(" - same version: "+sameVersion);
System.out.println(" - same connection: "+sameConnection);
System.out.println(" - same DB: "+sameDB);
System.out.println(" - same last build: "+sameLastBuild);
}
final PerformancesView siblingView = getSiblingView();
if (sameConnection && sameDB) {
if (!sameLastBuild) {
// Set last build
LAST_BUILD = noLastBuild ? null : lastBuild;
this.results.setLastBuildName(LAST_BUILD);
siblingView.results.setLastBuildName(LAST_BUILD);
// Reset views content
resetInput();
siblingView.resetInput();
// May be read local data now
File newDataDir = changeDataDir();
if (newDataDir == null) {
this.dataDir = null;
siblingView.dataDir = null;
}
}
// No database preferences has changed do nothing
return;
}
// Update database constants
boolean updated = DB_Results.updateDbConstants(connected, eclipseVersion, databaseLocation);
if (debug) {
System.out.println(" - updated: "+updated);
}
if (!connected) {
if (!updated) {
MessageDialog.openError(this.shell, getTitleToolTip(), "Error while updating database results constants!\nOpen error log to see more details on this error");
}
} else if (updated) {
StringBuffer message = new StringBuffer("Database connection has been correctly ");
message.append( connected ? "opened." : "closed.");
MessageDialog.openInformation(this.shell, getTitleToolTip(), message.toString());
} else {
MessageDialog.openError(this.shell, getTitleToolTip(), "The database connection cannot be established!\nOpen error log to see more details on this error");
DB_Results.updateDbConstants(false, eclipseVersion, databaseLocation);
}
setTitleToolTip();
siblingView.setTitleToolTip();
// Refresh view
if (sameVersion && sameLastBuild) {
// Refresh only builds view as the sibling view (Components) contents is based on local data files contents
this.results.resetBuildNames();
refreshInput();
} else {
// Reset views content
resetInput();
siblingView.resetInput();
// May be read local data now
if (MessageDialog.openQuestion(this.shell, getTitleToolTip(), "Do you want to read local data right now?")) {
changeDataDir();
} else {
this.dataDir = null;
siblingView.dataDir = null;
}
}
// Update actions
this.updateBuild.setEnabled(connected);
this.updateAllBuilds.setEnabled(connected);
}
/*
* (non-Javadoc)
* @see org.eclipse.test.internal.performance.results.ui.PerformancesView#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
super.selectionChanged(event);
// Update selected element
Object selection = this.viewer.getSelection();
int length = 0;
if (selection instanceof IStructuredSelection) {
Object[] elements = ((IStructuredSelection)selection).toArray();
length = elements == null ? 0 : elements.length;
this.buildsResults = new BuildResultsElement[length];
if (length == 0) {
this.updateAllBuilds.setText("&Update from DB (all)");
return;
}
for (int i=0; i<length; i++) {
this.buildsResults[i] = (BuildResultsElement) elements[i];
}
} else {
return;
}
// Update update build action
// boolean enableUpdateBuild = true;
// boolean enableGenerate = true;
int readBuilds = 0;
for (int i=0; i<length; i++) {
if (this.buildsResults[i].isRead()) {
// enableUpdateBuild = false;
readBuilds++;
} else {
// enableGenerate = false;
}
}
// this.updateBuild.setEnabled(enableUpdateBuild);
// this.forceUpdateBuild.setEnabled(!enableUpdateBuild);
final boolean force = readBuilds < length;
this.updateBuild.force = force;
this.updateAllBuilds.force = force;
this.updateAllBuilds.setText("&Update from DB");
// Update generate action
boolean enableGenerate = true;
if (enableGenerate) {
for (int i=0; i<length; i++) {
if (this.buildsResults[i].getName().startsWith(DB_Results.getDbBaselinePrefix())) {
enableGenerate = false;
break;
}
}
}
this.generate.setEnabled(enableGenerate);
}
void updateAllBuilds(IProgressMonitor monitor, boolean force) {
if (this.dataDir == null) {
changeDataDir();
}
String[] builds = buildsToUpdate();
if (builds == null) {
this.results.updateBuild(null, true, this.dataDir, monitor);
} else {
this.results.updateBuilds(builds, force, this.dataDir, monitor);
}
}
void updateBuilds(IProgressMonitor monitor, boolean force) {
if (this.dataDir == null) {
changeDataDir();
}
int length = this.buildsResults.length;
String[] builds = new String[length];
for (int i = 0; i < length; i++) {
builds[i] = this.buildsResults[i].getName();
}
this.results.updateBuilds(builds, force, this.dataDir, monitor);
}
}