/******************************************************************************* * 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 = InstanceScope.INSTANCE.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); } /* * 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() { // 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); 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()); if (sameConnection && sameDB) { // No database preferences has changed do nothing return; } // Update database constants boolean updated = DB_Results.updateDbConstants(connected, eclipseVersion, databaseLocation); 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(); getSiblingView().setTitleToolTip(); // Refresh view if (sameVersion) { // 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(); getSiblingView().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; getSiblingView().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 = !force; 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); } }