3.8M3 bundles
diff --git a/bundles/org.eclipse.build.tools/.classpath b/bundles/org.eclipse.build.tools/.classpath
index f64cf12..a3194db 100644
--- a/bundles/org.eclipse.build.tools/.classpath
+++ b/bundles/org.eclipse.build.tools/.classpath
@@ -5,7 +5,7 @@
 	<classpathentry kind="src" path="src_bugzilla"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-	<classpathentry kind="var" path="ECLIPSE_HOME/plugins/org.eclipse.osgi_3.8.0.v20110908-1857.jar"/>
+	<classpathentry kind="var" path="ECLIPSE_HOME/plugins/org.eclipse.osgi_3.8.0.v20111025-1330.jar"/>
 	<classpathentry kind="var" path="ECLIPSE_HOME/plugins/org.apache.ant_1.8.2.v20110505-1300/lib/ant.jar"/>
 	<classpathentry kind="var" path="ECLIPSE_HOME/plugins/org.apache.ant_1.8.2.v20110505-1300/lib/ant-launcher.jar"/>
 	<classpathentry kind="output" path="bin"/>
diff --git a/bundles/org.eclipse.test.performance.ui/.classpath b/bundles/org.eclipse.test.performance.ui/.classpath
new file mode 100644
index 0000000..4c3abc5
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="src"/>
+</classpath>
diff --git a/bundles/org.eclipse.test.performance.ui/.project b/bundles/org.eclipse.test.performance.ui/.project
new file mode 100644
index 0000000..47525f5
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.test.performance.ui</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.test.performance.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.test.performance.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..ce5257a
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,27 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: org.eclipse.test.performance.ui; singleton:=true
+Bundle-Version: 3.7.0.qualifier
+Bundle-ClassPath: performanceui.jar
+Bundle-Activator: org.eclipse.test.performance.ui.UiPlugin
+Bundle-ActivationPolicy: lazy
+Bundle-Vendor: %Bundle-Vendor
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.swt,
+ org.apache.derby;resolution:=optional,
+ Cloudscape;resolution:=optional,
+ org.eclipse.test.performance.derby;bundle-version="10.4.2";resolution:=optional,
+ org.junit,
+ org.eclipse.ui.views;bundle-version="3.4.0";resolution:=optional,
+ org.eclipse.test.performance;bundle-version="3.6.0",
+ org.eclipse.ui;bundle-version="3.5.0";resolution:=optional,
+ org.eclipse.ui.ide;bundle-version="3.5.0";resolution:=optional,
+ org.eclipse.core.resources;bundle-version="3.5.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Export-Package: org.eclipse.test.internal.performance.results.db,
+ org.eclipse.test.internal.performance.results.model,
+ org.eclipse.test.internal.performance.results.ui,
+ org.eclipse.test.internal.performance.results.utils,
+ org.eclipse.test.performance.ui
diff --git a/bundles/org.eclipse.test.performance.ui/about.html b/bundles/org.eclipse.test.performance.ui/about.html
new file mode 100644
index 0000000..e336131
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/about.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<meta name="copyright" content="Copyright (c) IBM Corporation and others 2000, 2005. This page is made available under license. For full details see the LEGAL in the documentation book that contains this page." >
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>February 24, 2005</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/build.properties b/bundles/org.eclipse.test.performance.ui/build.properties
new file mode 100644
index 0000000..07c50a4
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/build.properties
@@ -0,0 +1,26 @@
+###############################################################################
+# Copyright (c) 2003, 2006 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
+###############################################################################
+
+bin.includes = about.html,\
+               META-INF/,\
+               images/,\
+               plugin.xml,\
+               readme.html,\
+               scripts/,\
+               icons/,\
+               html/,\
+               performanceui.jar,\
+               toc.xml,\
+               contexts.xml,\
+               doc/
+source.performanceui.jar = src/
+jars.compile.order = performanceui.jar
+src.includes = src/
diff --git a/bundles/org.eclipse.test.performance.ui/contexts.xml b/bundles/org.eclipse.test.performance.ui/contexts.xml
new file mode 100644
index 0000000..c205f96
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/contexts.xml
@@ -0,0 +1,30 @@
+<contexts>
+ <context id="components" title="Components View">
+    <description>This view shows the performance results in a hierarchical tree:
+Component
+   + Scenario
+      + Test machine
+         + Build
+            + Performance numbers
+
+Typically, the Eclipse builder runs performance tests for each component after the build is made on several performance test machines. Each component defines one or several specific performance test suites made of several test (aka scenario). Several performance numbers (e.g. Elapsed Process Time and CPU Time) are stored for each scenario and all build results are available in the performance results database.
+
+This structure is roughly the same than the one used to generate performance results, hence make it easy to match the numbers in the corresponding page HTML page.</description>
+ </context>
+ <context id="builds" title="Builds view">
+    <description>This view shows the list of all the builds contained in the connected database or in the local data files if no database is connected.
+
+Different fonts are used to display build names depending on the level of knowledge about the corresponding build:
+ - gray+italic: connected to a database but no available local data
+ - gray: connected to a database and local data do not contain any information for this build
+ - black: local data contain information about this build</description>
+ </context>
+ <context id="results" title="Component Results view">
+    <description>This view show for each of the performance machine the entire results since the beginning.
+
+Each tab of this view represent a performance machine (also named config).
+The name of the machine is the name of the tab. The results are displayed in
+a table where each column represents a scenario and each line a build.
+The lines are ordered in descending order (from the most recent to the oldest build).</description>
+ </context>
+</contexts>
diff --git a/bundles/org.eclipse.test.performance.ui/doc/help.html b/bundles/org.eclipse.test.performance.ui/doc/help.html
new file mode 100644
index 0000000..74a75e6
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/help.html
@@ -0,0 +1,108 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<style type="text/css">p, table, td, th {  font-family: arial, helvetica, geneva; font-size: 10pt}
+pre {  font-family: "Courier New", Courier, mono; font-size: 10pt}
+h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
+code {  font-family: "Courier New", Courier, mono; font-size: 10pt}
+sup {  font-family: arial,helvetica,geneva; font-size: 10px}
+h3 {  font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
+li {  font-family: arial, helvetica, geneva; font-size: 10pt}
+h1 {  font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
+body {  font-family: arial, helvetica, geneva; font-size: 10pt; clip:   rect(   ); margin-top: 5mm; margin-left: 3mm}
+.indextop { font-size: x-large;; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold}
+.indexsub { font-size: xx-small;; font-family: Arial, Helvetica, sans-serif; color: #8080FF}
+</style>
+<body>
+<p>
+<h1>Table of contents</h1>
+<ul>
+<li><a href="#scales">Scales</a>
+<ul>
+<li><a href="#percent">Percentage</a></li>
+<li><a href="#time">Time linear</a></li>
+<li><a href="#time">Time logarithmic</a></li>
+</ul>
+</li>
+<li><a href="#tips">Tips for time scales</a></li>
+</ul>
+</p>
+<h1><a name="scales">Scales</h1>
+Since 3.5, it's possible to see results in fingerprints with three different
+kinds of scale.
+<h3><a name="percent">Percentage scale</h3>
+<p>The X axis represents percentage of the variation vs. the given baseline</p>
+<p>This is the way fingerprints were displayed since the beginning:
+<p><img src="images/percentage.png">
+<ul>
+<li>Red bar means a regression, even if it's less than the 10% threshold.</li>
+<li>Green bar means an improvement</li>
+<li>Gray bar means an 'explained' regression.</li>
+</ul>
+</p>
+<h3><a name="time">Linear and logarithmic time scales</h3>
+<p>For these scales, the X axis represents the duration time of the test.<br>
+The colors meanings are the same than for the percentage scale.<br>
+These kind of graphs give a better idea of time duration for each test.</p>
+<p>Typically use linear scale if you want to see the tests relativeness for all the component tests:</p>
+<p><img src="images/linear.png">
+<p>But the logarithmic scale is more appropriate when there are a strong duration differences between tests, hence makes short duration tests easier to survey:</p>
+<p><img src="images/log.png">
+<p>Each test have two bars: the former is white and shows the baseline result, the latter is colored (red, green or gray) and shows the current build result.<br>
+The variation between the baseline and the build is displayed as a percentage on top of both bars.</p>
+<h1><a name="tips">Tips for time scales</h1>
+<p>Tips are almost the same for linear and logarithmic scales:</p>
+<table border="0">
+  <tr>
+    <td valign="top"><img src="../images/light.gif"></td>
+    <td><b>Flying over a bar displays its time value<b>:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_time_baseline.png"></td>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_time_current.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="../images/light.gif"></td>
+    <td><b>For <u>linear scale only</u>, when the error on the time result is noticeable,
+		then the measurement uncertainty is shown in yellow at the end of the bar<b>:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_time_error.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="../images/light.gif"></td>
+    <td><b>A performance regression may sometimes have a known good reason</b>.<br>
+    In this case, the current build bar is grayed and flying over it also shows the given explanation:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_explained_regression.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="../images/light.gif"></td>
+    <td><b>Test result may have big error which can make the test result not fully reliable</b>.<br>
+  In this case, a warning icon is shown after the variation value and flying over it gives the offending error value:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_error_warning.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="../images/light.gif"></td>
+    <td><b>Test may have no result for the used baseline, hence the first available build is used as a reference</b>.<br>
+  In this case, a warning icon is shown after the scenario title and flying over it gives the build ID used to compute the variation:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_no_baseline.png"></td>
+  </tr>
+</table>
+</body>
+</html>
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_error_warning.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_error_warning.png
new file mode 100644
index 0000000..399acaf
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_error_warning.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_explained_regression.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_explained_regression.png
new file mode 100644
index 0000000..a7b7c84
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_explained_regression.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_no_baseline.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_no_baseline.png
new file mode 100644
index 0000000..f06762e
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_no_baseline.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_time_baseline.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_baseline.png
new file mode 100644
index 0000000..594fdf2
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_baseline.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_time_current.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_current.png
new file mode 100644
index 0000000..5e913cb
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_current.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_time_error.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_error.png
new file mode 100644
index 0000000..2ce272d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_error.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/linear.png b/bundles/org.eclipse.test.performance.ui/doc/images/linear.png
new file mode 100644
index 0000000..cd276a9
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/linear.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/log.png b/bundles/org.eclipse.test.performance.ui/doc/images/log.png
new file mode 100644
index 0000000..7f2e84a
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/log.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/percentage.png b/bundles/org.eclipse.test.performance.ui/doc/images/percentage.png
new file mode 100644
index 0000000..6f965da
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/percentage.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/builds-comparison.html b/bundles/org.eclipse.test.performance.ui/html/builds-comparison.html
new file mode 100644
index 0000000..3bded21
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/builds-comparison.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Builds Comparison view</title>
+</head>
+
+<body>
+<h1>Builds Comparison view</h1>
+<p>
+This view shows the scenarios status table of the difference between two builds
+selected in the <a href="builds.html">Builds view</a>.
+</p><p>
+The comparison between the two builds is shown for each component in a
+separated tab which contains a table made of all scenarios (lines) and all
+configuration (columns).<br>
+<i>Note that this format is similar than the one used to generate performance
+results HTML pages...</i>
+</p>
+
+<h2>Filters</h2>
+<p>
+
+<h3>Scenarios filter</h3>
+<ul>
+<li>Advanced scenarios: hide the scenarios which are not in the fingerprints</li>
+</ul>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/builds.html b/bundles/org.eclipse.test.performance.ui/html/builds.html
new file mode 100644
index 0000000..8e71a63
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/builds.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Builds view</title>
+</head>
+
+<body>
+<h1>Builds view</h1>
+<p>
+This view shows the list of all the builds contained in the connected database
+or in the local data files if no database is connected.
+</p>
+
+<h2>Information status</h2>
+<p>
+When starting the tool for the first time, this view is empty as no data has been
+populated, neither from the <a href="local_data.html">local data</a> files
+nor from the performance results database.
+</p><p>
+The typical usage of the performance tools is to read local data files copied
+from the server (see <a href="local_data.html">local data</a>). When this has
+been done, build names are displayed in this view using normal black color.
+</p>
+Some or all build names of the list may be displayed in a different font when
+a performance results database is connected:
+<ul>
+<li>
+<i><font color="#888888">gray and italic</font></i>: no local data files
+were read
+<p><img src="images/builds-nolocaldata.png" alt="Builds view with no local data"/></p>
+</li>
+<li>
+<font color="#888888">gray</font></i>: the local data files were read but contain
+no information about this build (typically new performance tests ran but the local
+data files were not copied since then):
+<p><img src="images/builds-missinglocaldata.png" alt="Builds view with missing local data"/></p>
+In this case it's possible to update those builds using the popup-menu:
+<p><img src="images/builds-update.png" alt="Update local data builds"/></p>
+It's also possible to rewrite the local data files from the database contents
+in one shot using the toolbar pull-up menu:
+<p><img src="images/builds-updateall.png" alt="Update all local data"/></p>
+</li>
+</ul>
+
+<h2>Generate</h2>
+<p>
+From this view, it is also possible to generate HTML pages using the generate
+command accessible from the popup-menu:
+<p><img src="images/builds-generate-menu.png" alt="Generate results: menu item"/></p>
+<p>
+After having selected the directory where to put the generated files, it's possible
+to choose the baseline to compare with:
+<p><img src="images/builds-generate-baseline.png" alt="Generate results: baseline selection"/></p>
+<p>
+And also whether you only want to generate the fingerprints or all the data:
+<p><img src="images/builds-generate-fingerprints.png" alt="Generate results: fingerprints"/></p>
+<p>
+After the generation ends, there should be in the specified directory similar
+files than the one generated on eclipsebuildserv.
+</p><p>
+<i>Note that a PHP server is neeeded to be able to read these files as it's done on
+eclipsebuildserv or fullmoon...</i>
+</p>
+
+<h2><a name="writecomparison">Write comparison</h2>
+<p>
+From this view, it is also possible to write the comparison between two 
+selected builds using the <b>Write comparison</b> item of the View menu:
+</p>
+<p><img src="images/write-comparison-menu.png" alt="Write comparison menu item"/></p>
+<p>
+The written HTML file will contain the Scenario Status Table as it would
+have been generated but comparing the two selected build instead of comparing
+a build vs a baseline.
+</p><p>
+This is helpful to see if a regression occurs after having made a change with 
+possible impact performance...
+</p>
+<p>Note that this comparison can also be shown in the
+<a href="builds-comparison.html">Builds Comparison view</a>...
+</p>
+
+<h2>Filters</h2>
+<p>
+There are several possible filters in this view.
+ 
+<h3>Builds filters</h3>
+<ul>
+<li>Baseline: hide the baselines (starting with R-3.x)</li>
+<li>Nightly: hide the nightly builds (starting with 'N')</li>
+<li>Old: hide all builds before last milestone except earlier milestones</li>
+</ul>
+<p>
+In this view baselines filter is not activated by default. It's because it may
+be interesting to know which baselines have results in the DB.
+Note that this filter is not synchronized with the one in Components view.
+</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/components.html b/bundles/org.eclipse.test.performance.ui/html/components.html
new file mode 100644
index 0000000..6c9b714
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/components.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Components view</title>
+</head>
+
+<body>
+<h1>Components view</h1>
+<p>
+This view shows the performance results in similar way than the one used to generate
+the performance results, hence make it easy to match the numbers in the corresponding
+page HTML page.
+</p><p>
+When starting the tool for the first time, this view is empty as no data has been
+populated, neither from the <a href="local_data.html">local data</a> files
+nor from the performance results database.
+</p>
+
+
+<h2>Hierarchical tree</h2>
+<p>
+Typically, the Eclipse builder runs performance tests for each component after
+the build is made on several performance test machines. Each component defines one
+or several specific performance test suites made of several test (aka scenario).
+</p><p>
+Several performance numbers (e.g. Elapsed Process Time and CPU Time) are stored
+for each scenario and all build results are available in the performance results
+database.
+</p><p>
+Hence the tree structure is made as follow:
+<pre>
+Component
+   + Scenario
+      + Test machine
+         + Build
+            + Performance numbers
+</pre>
+and may look as follow:
+<p><img src="images/components.png" alt="Components view"/></p>
+
+<h2>Icons</h2>
+<p>
+Several icons are displayed on tree element, here are their meaning.
+</p><p>
+The red cross means that there's at least one scenario on one machine for
+the last build with a failure (i.e. a regression over 10%).
+</p><p>
+The warning icon means that some warnings occur for some results.
+The current possible warning are:
+<ul>
+<li>error over the 3% threshold on test(s)</li>
+<li>unreliable test(s): the deviation through the test(s) history is over 20%</li>
+<li>unstable test(s): the deviation through the test(s) history is between 10 and 20%</li>
+<li>no baseline for test(s)</li>
+<li>only one run on test(s)</li>
+</ul>
+</p><p>
+The information icon gives some other interesting information:
+<ul>
+<li>the Student T-test fails on test(s)</li>
+<li>the test(s) value or its delta is less than 100ms</li>
+</ul>
+</p><p>
+Note that for component and scenario level, the status is the aggregation of
+the children status. That means that as soon as one scenario is in error then
+the component is also flagged in error. And of course the higher severity is
+displayed masking lower possible icons.
+</p>
+
+<h2>Filters</h2>
+<p>
+There are several possible filters in this view:
+
+<h3>Builds filters</h3>
+<ul>
+<li>Baseline: hide the baselines (starting with R-3.x)</li>
+<li>Nightly: hide the nightly builds (starting with 'N')</li>
+<li>Old: hide all builds before last milestone except earlier milestones</li>
+</ul>
+
+<h3>Scenarios filter</h3>
+<ul>
+<li>Advanced scenarios: hide the scenarios which are not in the fingerprints</li>
+</ul>
+<p>
+As baselines results are not really useful for the survey, the filter is activated
+by default in this view. Currently the survey only concerns the fingerprints
+scenario, hence the corresponding filter is also activated by default.
+</p>
+
+<h2><a name="writestatus">Write status</h2>
+<p>
+From this view, it is also possible to write the status file for the last
+active build (see <a href="preferences.html#lastbuild">Last build</a>) by
+using the <b>Write status</b> item of the View menu:
+</p>
+<p><img src="images/write-status-menu.png" alt="Write status menu item"/></p>
+<p>
+The written status file will contain all scenarios which have failures,
+except those excluded by the status preferences set in the preferences page
+(see <a href="preferences.html#status">Status</a>)
+</p><p>
+Then it's easy to see the new regression occurring in a build when comparing
+to the previous build status file:
+</p>
+<p><img src="images/write-status-comparison.png" alt="Compare write status files"/></p>
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-baseline.png b/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-baseline.png
new file mode 100644
index 0000000..42af1f4
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-baseline.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-fingerprints.png b/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-fingerprints.png
new file mode 100644
index 0000000..5a746ca
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-fingerprints.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-menu.png b/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-menu.png
new file mode 100644
index 0000000..c2d8a62
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/builds-generate-menu.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/builds-missinglocaldata.png b/bundles/org.eclipse.test.performance.ui/html/images/builds-missinglocaldata.png
new file mode 100644
index 0000000..c49239c
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/builds-missinglocaldata.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/builds-nolocaldata.png b/bundles/org.eclipse.test.performance.ui/html/images/builds-nolocaldata.png
new file mode 100644
index 0000000..a745a51
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/builds-nolocaldata.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/builds-update.png b/bundles/org.eclipse.test.performance.ui/html/images/builds-update.png
new file mode 100644
index 0000000..d3935e5
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/builds-update.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/builds-updateall.png b/bundles/org.eclipse.test.performance.ui/html/images/builds-updateall.png
new file mode 100644
index 0000000..7740942
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/builds-updateall.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/components.png b/bundles/org.eclipse.test.performance.ui/html/images/components.png
new file mode 100644
index 0000000..5a41cc7
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/components.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/perspective.png b/bundles/org.eclipse.test.performance.ui/html/images/perspective.png
new file mode 100644
index 0000000..a010ac4
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/perspective.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences-database.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences-database.png
new file mode 100644
index 0000000..3cbceb4
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences-database.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences-default-dim.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences-default-dim.png
new file mode 100644
index 0000000..cbc02d6
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences-default-dim.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences-eclipse-versions.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences-eclipse-versions.png
new file mode 100644
index 0000000..2771215
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences-eclipse-versions.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences-lastbuild.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences-lastbuild.png
new file mode 100644
index 0000000..26d85c0
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences-lastbuild.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences-milestones.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences-milestones.png
new file mode 100644
index 0000000..7f666af
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences-milestones.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences-results-dim.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences-results-dim.png
new file mode 100644
index 0000000..dbb6726
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences-results-dim.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences-status.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences-status.png
new file mode 100644
index 0000000..6aa53dd
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences-status.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/preferences.png b/bundles/org.eclipse.test.performance.ui/html/images/preferences.png
new file mode 100644
index 0000000..321ba22
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/preferences.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/properties-survey-comment.png b/bundles/org.eclipse.test.performance.ui/html/images/properties-survey-comment.png
new file mode 100644
index 0000000..71255cb
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/properties-survey-comment.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/properties-warnings.png b/bundles/org.eclipse.test.performance.ui/html/images/properties-warnings.png
new file mode 100644
index 0000000..9954107
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/properties-warnings.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/verification-comment.png b/bundles/org.eclipse.test.performance.ui/html/images/verification-comment.png
new file mode 100644
index 0000000..7594237
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/verification-comment.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/verification-components.png b/bundles/org.eclipse.test.performance.ui/html/images/verification-components.png
new file mode 100644
index 0000000..2369c2e
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/verification-components.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/verification-failures.png b/bundles/org.eclipse.test.performance.ui/html/images/verification-failures.png
new file mode 100644
index 0000000..7c739c4
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/verification-failures.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/write-comparison-menu.png b/bundles/org.eclipse.test.performance.ui/html/images/write-comparison-menu.png
new file mode 100644
index 0000000..d34b368
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/write-comparison-menu.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/write-status-comparison.png b/bundles/org.eclipse.test.performance.ui/html/images/write-status-comparison.png
new file mode 100644
index 0000000..51cc333
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/write-status-comparison.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/images/write-status-menu.png b/bundles/org.eclipse.test.performance.ui/html/images/write-status-menu.png
new file mode 100644
index 0000000..41bb7fa
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/images/write-status-menu.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/html/local_data.html b/bundles/org.eclipse.test.performance.ui/html/local_data.html
new file mode 100644
index 0000000..1d55a69
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/local_data.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Local data</title>
+</head>
+
+<body>
+<h1>Local data</h1>
+<p>
+As the connection to the server database is too slow, local files containing
+all necessary information are written while generating performance results on
+the server.
+</p><p>
+The performance results tool then can read these files to avoid long distance
+access to data information. Of course, it's first necessary to copy these files
+on a location easily accessible from the machine where the tool is launched.
+</p><p>
+<i>Notes
+<ul>
+<li>these files are accessible for some authorized users onto
+<b>ottcvs1.ottawa.ibm.com</b> at <b>/home/users/ecperf</b> directory</li>
+<li>SFTP is necessary to copy these files from that location</li>
+</ul>
+</i>
+</p><p>
+To read the local files, select the <b>Local data -> Read...</b> menu item and
+chose the directory where the files have been copied.
+</p><p>
+<i>Notes
+<ul>
+<li>this manual read action only has to be done once, as it will be automatically
+done at the next start of the tool</li>
+<li>no verification is currently done that the local files matches
+the connected DB (if any). Mixing them should give unpredictable results!</li>
+</ul>
+</i>
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/overview.html b/bundles/org.eclipse.test.performance.ui/html/overview.html
new file mode 100644
index 0000000..5b22fea
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/overview.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Overview</title>
+</head>
+
+<body>
+<h1>Overview</h1>
+The performance results tool provide an easy way to look at results collected
+while running performance tests on a build. It is accessible using the
+<b>Performance Results</b> perspective:
+<p><img src="images/perspective.png" alt="Performance Results perspective"/></p>
+<p>
+There are 4 views in this perspective:
+<ul>
+<li><a href="components.html">Components view</a>: This is the view to see
+the results in details for each components</li>
+<li><a href="builds.html">Builds view</a>: This is the view to see all builds,
+update local data and generate results when necessary</li>
+<li><a href="results.html">Components Results view</a>: This is the view to
+see whole results for each machine in a single table</li>
+<li><a href="properties.html">Properties view</a>: Display element properties
+(typically results numbers and other database information)</li>
+</ul>
+</p><p>
+The goal of this tool is both to look at performance results database numbers
+and to verify whether a build result have scenarios showing performance regression.
+</p><p>
+The view to use to look at performance results number is the
+<a href="components.html">Components view</a>. In this view, results are
+displayed in a hierarchical tree which have a similar structure than
+the generated HTML pages (e.g.
+<a href="http://download.eclipse.org/eclipse/downloads/drops/R-3.6-201006080911/performance/performance.php">Eclipse 3.6.0</a>).
+The complete performance results information is provided on each element of the tree in the
+<a href="properties.html">Properties view</a>. This is typically useful when a scenario
+results looks weird to have the ability to verify that the numbers looks correct
+or not.
+</p><p>
+The view to verify whether a regression occurs is the <a href="results.html">Components Results view</a>.
+All results for one component are shown in a single table which also provides
+all necessary information to decide whether a noticed regression could be confirmed
+or not.
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/preferences.html b/bundles/org.eclipse.test.performance.ui/html/preferences.html
new file mode 100644
index 0000000..000be5d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/preferences.html
@@ -0,0 +1,134 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Preferences</title>
+</head>
+
+<body>
+<h1>Preferences</h1>
+<p>
+The tool have some preferences which may be configured by advanced users.
+This must be done carefully otherwise, either no data could be read by the tool
+and/or generatead results could be puzzled.
+</p><p>
+
+<h3><a name="eclipse_version">Eclipse version</h3>
+<p><img src="images/preferences-eclipse-versions.png" alt="Preferences Eclipse versions"/></p>
+<p>
+The Eclipse version on which the performance results belongs to. There are
+only two possible choice: the maintenance and the current versions.
+</p>
+
+<h3><a name="database">Database</h3>
+<p><img src="images/preferences-database.png" alt="Preferences Database"/></p>
+<p>
+By default the tool does not connect to any performance results database as common
+users might not have enough rights to access it. However, users having these
+rights may want to look at the database contents and update the local data files
+with it. Hence, it is possible to configure the tool to connect to a database
+which may be either local or on the releng server (<code><b>minsky</b></code>).
+</p><p>
+Note that the folder for the local database must be the parent of the
+<code>perfDb3x</code> folder otherwise you'll get an error message.
+</p>
+
+<h3><a name="status">Status</h3>
+<p><img src="images/preferences-status.png" alt="Preferences Status"/></p>
+<p>
+The status preferences can be set to exclude some tests while written
+the status file (see <a href="components.html#writestatus">Write status</a>).
+</p><p>
+Flying over each check-box or radio buttons gives a short explanation of each
+preference value
+</p><p>
+Here are the detailed explanation of these status preferences:
+<ul>
+<li><b>Values</b>: Check-box to include the values of the failing tests in the
+status file. Note that is not recommended to do so when status want to be
+compared between builds</li>
+<li><b>Error level</b>: Level of the error from which the test result is
+excluded from the file:
+<ul>
+<li>None: the test is always included in the status file whatever the error
+level is</li>
+<li>Invalid: the test is not included when the error level is over 100%</li>
+<li>Weird: the test is not included when the error level is over 50%</li>
+<li>Suspicious: the test is not included when the error level is over 25%</li>
+<li>Noticeable: the test is not included when the error level is over 3%</li>
+</ul>
+</li>
+<li><b>Small value</b>: The test is not included when a small value is detected:
+<ul>
+<li>Build value: the test is not included when the value for this build test value
+is below than 100ms (<i>as the test duration is below the recommended minimum
+value it's not necessary to strongly survey it...</i>)</li>
+<li>Delta value: the test is not included when the value of the difference between
+the build and the baseline is below than 100ms (<i>as the regression is below
+what a normal user can detect, it may not be necessary to report it...</i>)</li>
+</ul>
+</li>
+<li><b>Statistics</b>: Level of deviation of test history since the first build
+from which the test result is not included in the status file:
+<ul>
+<li>None: the test is always included in the status file whatever the deviation
+is</li>
+<li>Unstable: the test is not included when the deviation is over 10%</li>
+<li>Erratic: the test is not included when the error level is over 20%</li>
+</ul>
+</li>
+<li><b>Builds to confirm</b>: The number of builds to confirm a regression.
+As tests may have <i>natural</i> variation, it's often necessary to have several
+builds to confirm that a regression really occurs. This preference allow to
+define how many consecutive builds must show a regression before including
+a test result in the status file...
+</li>
+</ul>
+</p>
+
+<h3><a name="milestones">Milestones</h3>
+<p><img src="images/preferences-milestones.png" alt="Preferences Milestones"/></p>
+<p>
+These are the list of the version milestones. Each milestone is a date string
+using the <b>yyyymmddHHMM</b> format. When a new milestone is shipped, then a new
+date must be added to this preference to let the tool to identify it in the builds
+list and emphasize it...
+</p>
+
+<h3><a name="lastbuild">Last build</h3>
+<p><img src="images/preferences-lastbuild.png" alt="Preferences Last build"/></p>
+<p>
+The last build on which verifications and/or generation want to be done. When
+not set (which is the default value) the last build of the database is taken
+into account.
+</p><p>
+All builds after the selected one are ignored by the tool likewise they would
+have no local data. Changing this value will trigger the initialization of
+the local data which will be read again.
+</p>
+
+<h2><a name="defaultdim">Default dimension</h2>
+<p><img src="images/preferences-default-dim.png" alt="Preferences Default dimension"/></p>
+<p>
+This is the dimension used to compute delta and make the verification. Currently
+this is the <b>Elapsed Process Time</b> dimension.
+</p><p>
+<i>Note that the default dimension must belong to the <b>Results dimensions</b>
+described below, hence a new selected dimensions will always be automatically
+added to the list...</i>
+</p>
+
+<h2><a name="resultsdim">Results dimensions</h2>
+<p><img src="images/preferences-results-dim.png" alt="Preferences Results dimension"/></p>
+<p>
+These are dimensions displayed in the scenario data HTML pages. Currently there
+are the <b>Elapsed Process Time</b> and the <b>CPU Time</b>. Having these dimensions
+configurable may be interesting to display others dimensions and see whether their
+numbers may be relevant or not (e.g. <b>Used Hava Heap</b>).
+</p><p>
+<i>Note that the default dimension described above must belong to the selected
+dimensions, hence it will always be automatically added to the new selected list...</i>
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/properties.html b/bundles/org.eclipse.test.performance.ui/html/properties.html
new file mode 100644
index 0000000..6470322
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/properties.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Properties view</title>
+</head>
+
+<body>
+<h1>Properties view</h1>
+<p>
+Properties view contents is different for each select element of the component hierarchy.
+But they all have three sections:
+<ul>
+<li>Results:	Performances results information</li>
+<li>Status:	Various severity messages</li>
+<li>Survey:	Comment added while surveying</li>
+</ul>
+<p>
+Note that the status may have multiple warning messages. So, this property is
+in fact a combo-box, to display all the messages, click on the drop-down button:
+<p><img src="images/properties-warnings.png" alt="Properties warnings"/></p>
+<p>
+It's also possible to add a text into the survey comment on each properties:
+<p><img src="images/properties-survey-comment.png" alt="Properties survey comment"/></p>
+<p>
+This allow to remember what was the conclusion of the last survey concerning an element of the tree...
+</p>
+
+<h2>Component properties</h2>
+TODO
+
+<h2>Scenario properties</h2>
+TODO
+
+<h2>Config properties</h2>
+TODO
+
+<h2>Build properties</h2>
+TODO
+
+<h2>Dimension properties</h2>
+TODO
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/results.html b/bundles/org.eclipse.test.performance.ui/html/results.html
new file mode 100644
index 0000000..fd9e98e
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/results.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Components Results view</title>
+</head>
+
+<body>
+<h1>Components Results view</h1>
+<p>
+This view show for each of the performance machine the entire results since the beginning.
+</p>
+
+<h2>Config tab</h2>
+<p>
+Each tab of this view represent a performance machine (also named config).
+The name of the machine is the name of the tab. The results are displayed in
+a table where each column represents a scenario and each line a build.
+The lines are ordered in descending order (from the most recent to the oldest build).
+</p><p>
+Here are some tips about this view:
+<ul>
+<li>when old builds are not filtered, the milestone build are highlighted in bold
++ blueref color background</li>
+<li>results color depends on the diff of the delta with the baseline:
+<ul>
+<li>red: <code>diff &lt;= -5%</code></li>
+<li>magenta: <code>-5% &lt; diff &lt; 0%</code></li>
+<li>black: <code>0% &lt;= diff &lt; +10%</code></li>
+<li>blue: <code>+10% &lt;= diff &lt; +25%</code></li>
+<li>green: <code>diff &gt;= +25%</code></li>
+</ul></li>
+<li>icons have the same meaning than in the Components view</li>
+<li>more information are available in the cell tooltip when either there's a
+warning/information icon and/or when the result is in displayed italic</li>
+</ul>
+
+<h2>Filters</h2>
+<p>
+There are 3 possible filters in this view:
+</p>
+<h3>Builds filters</h3>
+<ul>
+<li>Nightly: hide the nightly builds (starting with 'N')</li>
+<li>Old: hide all builds before last milestone except earlier milestones</li>
+</ul>
+
+<h3>Scenarios filter</h3>
+<ul>
+<li>Advanced scenarios: hide the scenarios which are not in the fingerprints</li>
+</ul>
+<p>
+These filters are synchronized with the Components view, hence have the same default.
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/html/verification.html b/bundles/org.eclipse.test.performance.ui/html/verification.html
new file mode 100644
index 0000000..70f2c0b
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/verification.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+	<title>Verification</title>
+</head>
+
+<body>
+<h1>Verification</h1>
+<p>
+While verifying a build results, the first thing to do is to look for failures.
+The tool helps to know rapidly which components have failure showing a red-cross
+icon in the <a href="components.html">Components view</a>:
+<p><img src="images/verification-components.png" alt="Verification: failing components"/></p>
+</p><p>
+When clicking on one of the component having error(s), the tool automatically
+selects the first configuration (i.e. test machine) which has an error in its
+last build results. Then, flying over the failures may give a first indication
+whether the regressions are confirmed or not.
+</p><p>
+In the following example, a tool tip is displayed saying that the deviation has
+a small value and may be not enough important to report a bug:
+<p><img src="images/verification-failures.png" alt="Verification: failing test"/></p>
+<p>
+Then this analyze can be stored to see whether this scenario will still have
+a similar error on the next build performance run:
+<p><img src="images/verification-comment.png" alt="Verification: failure comment"/></p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/icons/builds.gif b/bundles/org.eclipse.test.performance.ui/icons/builds.gif
new file mode 100644
index 0000000..cb55e33
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/icons/builds.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/icons/compare_view.gif b/bundles/org.eclipse.test.performance.ui/icons/compare_view.gif
new file mode 100644
index 0000000..dc549bc
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/icons/compare_view.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/icons/components.gif b/bundles/org.eclipse.test.performance.ui/icons/components.gif
new file mode 100644
index 0000000..2344861
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/icons/components.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/icons/perfs.gif b/bundles/org.eclipse.test.performance.ui/icons/perfs.gif
new file mode 100644
index 0000000..3bd4e36
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/icons/perfs.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/icons/results.gif b/bundles/org.eclipse.test.performance.ui/icons/results.gif
new file mode 100644
index 0000000..0193dbe
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/icons/results.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/icons/sample.gif b/bundles/org.eclipse.test.performance.ui/icons/sample.gif
new file mode 100644
index 0000000..34fb3c9
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/icons/sample.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/FAIL.gif b/bundles/org.eclipse.test.performance.ui/images/FAIL.gif
new file mode 100644
index 0000000..c65be5a
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/FAIL.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/FAIL_caution.gif b/bundles/org.eclipse.test.performance.ui/images/FAIL_caution.gif
new file mode 100644
index 0000000..d2680d7
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/FAIL_caution.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/FAIL_greyed.gif b/bundles/org.eclipse.test.performance.ui/images/FAIL_greyed.gif
new file mode 100644
index 0000000..1d80a8d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/FAIL_greyed.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/OK.gif b/bundles/org.eclipse.test.performance.ui/images/OK.gif
new file mode 100644
index 0000000..be6df8d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/OK.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/OK_caution.gif b/bundles/org.eclipse.test.performance.ui/images/OK_caution.gif
new file mode 100644
index 0000000..b5af61f
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/OK_caution.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/Unknown.gif b/bundles/org.eclipse.test.performance.ui/images/Unknown.gif
new file mode 100644
index 0000000..1f69b55
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/Unknown.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/descriptions.html b/bundles/org.eclipse.test.performance.ui/images/descriptions.html
new file mode 100644
index 0000000..dc9db05
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/descriptions.html
@@ -0,0 +1,18 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+		<title>Descriptions</title>
+	</head>
+
+<body bgcolor="#FFFFFF" text="#000000">
+<p><a name="fp"></a><font size="+2"><b>Fingerprint Example</b></font></p>
+<p><img src="fp.bmp"> </p>
+<p>&nbsp;</p><hr>
+<p><a name="sst"></a><font size="+2"><b>Scenario Status Table Example</b></font></p>
+<p><img src="sst.bmp"></p>
+<p>&nbsp;</p><hr>
+<p><a name="sr"></a><b><font size="+2">Scenario Results Example</font></b></p>
+<p><img src="sr.bmp"></p>
+</body>
+</html>
diff --git a/bundles/org.eclipse.test.performance.ui/images/fp.bmp b/bundles/org.eclipse.test.performance.ui/images/fp.bmp
new file mode 100644
index 0000000..1d7863e
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/fp.bmp
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/light.gif b/bundles/org.eclipse.test.performance.ui/images/light.gif
new file mode 100644
index 0000000..542dddc
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/light.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/sr.bmp b/bundles/org.eclipse.test.performance.ui/images/sr.bmp
new file mode 100644
index 0000000..2c063f0
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/sr.bmp
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/sst.bmp b/bundles/org.eclipse.test.performance.ui/images/sst.bmp
new file mode 100644
index 0000000..8f36cf6
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/sst.bmp
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/warning_obj.gif b/bundles/org.eclipse.test.performance.ui/images/warning_obj.gif
new file mode 100644
index 0000000..2b2e50f
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/warning_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/performanceui.jar b/bundles/org.eclipse.test.performance.ui/performanceui.jar
new file mode 100644
index 0000000..ab92fef
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/performanceui.jar
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/plugin.properties b/bundles/org.eclipse.test.performance.ui/plugin.properties
new file mode 100644
index 0000000..0b19a7b
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2007 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
+###############################################################################
+Bundle-Name= Ui Performance Test Framework Plug-in
+Bundle-Vendor= Eclipse.org
diff --git a/bundles/org.eclipse.test.performance.ui/plugin.xml b/bundles/org.eclipse.test.performance.ui/plugin.xml
new file mode 100644
index 0000000..04d80d3
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/plugin.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+<!-- Applications -->
+   <extension
+         id="resultGenerator"
+         point="org.eclipse.core.runtime.applications">
+      <application>
+         <run
+               class="org.eclipse.test.performance.ui.Main">
+         </run>
+      </application>
+   </extension>
+   <extension
+         point="org.eclipse.ui.preferencePages">
+      <page
+            class="org.eclipse.test.internal.performance.results.ui.PerformanceResultsPreferencePage"
+            id="org.eclipse.test.performance.ui.prpp"
+            name="Performances">
+      </page>
+   </extension>
+
+   <extension
+         point="org.eclipse.ui.views">
+      <category
+            name="Performances"
+            id="Performances">
+      </category>
+      <view
+            category="Performances"
+            class="org.eclipse.test.internal.performance.results.ui.ComponentsView"
+            icon="icons/components.gif"
+            id="org.eclipse.test.internal.performance.results.ui.ComponentsView"
+            name="Components">
+      </view>
+      <view
+            category="Performances"
+            class="org.eclipse.test.internal.performance.results.ui.BuildsView"
+            icon="icons/builds.gif"
+            id="org.eclipse.test.internal.performance.results.ui.BuildsView"
+            name="Builds">
+      </view>
+      <view
+            category="Performances"
+            class="org.eclipse.test.internal.performance.results.ui.ComponentResultsView"
+            icon="icons/results.gif"
+            id="org.eclipse.test.internal.performance.results.ui.ComponentsResultsView"
+            name="Component Results"
+            restorable="true">
+      </view>
+      <view
+            category="Performances"
+            class="org.eclipse.test.internal.performance.results.ui.BuildsComparisonView"
+            icon="icons/compare_view.gif"
+            id="org.eclipse.test.internal.performance.results.ui.BuildsComparisonView"
+            name="Builds comparison"
+            restorable="true">
+      </view>
+   </extension>
+   <extension
+         point="org.eclipse.core.runtime.preferences">
+      <initializer
+            class="org.eclipse.test.internal.performance.results.ui.PerformanceResultsPreferenceInitializer">
+      </initializer>
+   </extension>
+   <extension
+         point="org.eclipse.ui.perspectives">
+      <perspective
+            class="org.eclipse.test.internal.performance.results.ui.PerformanceResultsPerspective"
+            icon="icons/perfs.gif"
+            id="org.eclipse.test.performance.ui.perspectives.PerformanceResultsPerspective"
+            name="Performances Results">
+         <description>
+            Perspective to survey the performances of the Eclipse components
+         </description>
+      </perspective>
+   </extension>
+   <extension
+         point="org.eclipse.help.contexts">
+      <contexts
+            file="contexts.xml">
+      </contexts>
+   </extension>
+   <extension
+         point="org.eclipse.help.toc">
+      <toc
+            file="toc.xml"
+            primary="true">
+      </toc>
+   </extension>
+</plugin>
diff --git a/bundles/org.eclipse.test.performance.ui/readme.html b/bundles/org.eclipse.test.performance.ui/readme.html
new file mode 100644
index 0000000..1a2fc86
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/readme.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html lang="en">
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+		<title>org.eclipse.test.performance.ui readme</title>
+	</head>
+
+<p><b><font size="+2">README</font></b><br> </p><p>Last updated: October 26, 2005<br> 
+</p><p><b>Introduction</b><br> <br> This readme describes how to create performance 
+charts and tables similar to those available on the eclipse platform download 
+pages using the stand-alone java program <b>org.eclipse.test.performance.ui.Main</b> 
+stored in this project. Although the program is currently geared to generating 
+results for the Eclipse Platform builds, the program may work for you given the 
+following conditions:</p><ul> <li>The &quot;eclipse.perf.config&quot; system property 
+used to run tests used three keys config, build and jvm <ul> <li>for example, 
+"eclipse.perf.config=config=relengbuildwin2;build=I20050219-1500;jvm=sun1.4.2_06". 
+<br> <br> </li></ul></li><li>Values for the "build" key begin with either an 'I' 
+or 'N' with the exception of the reference builds (for example, 3.0). <ul> <li>The 
+line graphs plot the reference build, all builds starting with 'I' and the seven 
+'N' builds preceding the current build.</li><li>This can be overriden with the use of the &quot;-current.prefix&quot; parameter where you can specify a comma-separated list of prefixes for build ids to include in the main line graph.<br> <br> </li></ul></li><li>Scenario names 
+all contain a prefix &quot;&lt;component&gt;.test&quot; <ul> <li>For example, 
+it is assumed that the scenario &quot;org.eclipse.help.tests.performance.appserver.TestAppserver#testAppserverCycle()&quot; 
+belongs to the &quot;org.eclipse.help&quot; component. </li><li>Unique scenario 
+prefixes are derived from all the scenarios in the database and are used to group 
+results.</li></ul></li></ul><p><br> For purposes of this document, the term &quot;<a href="images/descriptions.html#fp">fingerprint</a>&quot; 
+will be used to refer to a bar graph which represents the performance summary 
+at a global or component level for each test configuration.<br> <br> The term 
+&quot;<a href="images/descriptions.html#sst">scenario status table</a>&quot; will 
+be used to refer to a table of scenarios with green checks and red x indicators 
+below a component fingerprint. This table gives an overall view of the status 
+for all scenarios for a component for all configurations.<br> </p><p>The term 
+&quot;<a href="images/descriptions.html#sr">scenario results</a>&quot; will be 
+used to refer to a web page comparing current performance measurements to the 
+baseline measurements for one scenario on one configuration. The web page displays 
+the raw data for the current and baseline measurements and also displays line 
+graphs showing the measured values over the course of builds between the current 
+and baseline.</p><p><br> <b>Requirements</b> </p><ul> <li>Windows or Linux (x86) 
+operating system</li><li>Cloudscape or Derby plug-in</li><li>org.eclipse.releng.basebuilder</li><li>1.4 
+level jvm installed and on path</li></ul><p><b>Setup</b><br> </p><ul> <li>Checkout 
+org.eclipse.releng.basebuilder from cvs repository dev.eclipse.org:/home/eclipse 
+(HEAD should be fine).</li><li>Create a Cloudscape or derby plug-in as described 
+<A HREF="http://dev.eclipse.org/viewcvs/index.cgi/*checkout*/org.eclipse.test.performance/doc/Performance%20Tests%20HowTo.html?rev=HEAD&content-type=text/html#cloudscape">here</A>.</li><li>Set 
+the environment variable &quot;dbloc&quot; to the location of the database containing 
+the performance data<br> For example:<br> <ul> <li> export dbloc=net://minsky.ottawa.ibm.com 
+(Linux)</li><li>set dbloc==net://minsky.ottawa.ibm.com (Windows)<br> </li></ul></li></ul><p><b>Running 
+the program</b></p><p>From a terminal or dos prompt,<br> </p><OL><LI>Set the environment 
+variable &quot;dbloc&quot; to the location of the database containing the performance 
+data<BR> For example:<UL><LI>export dbloc=net://minsky.ottawa.ibm.com (Linux)</LI><LI>set 
+dbloc==net://minsky.ottawa.ibm.com (Windows)<BR><BR></LI></UL></LI><LI>cd 
+to org.eclipse.releng.basebuilder\plugins\org.eclipse.test.performance.ui\scripts<BR></LI><LI>If 
+running on Linux, execute &quot;chmod 755 genresults&quot;</LI><LI>Execute the 
+following command:<b><br> <br> genresults -baseline &lt;baseline build id&gt; 
+-current &lt;current build id&gt; -config &lt;comma-separated list of configs&gt; 
+-jvm &lt;jvm name&gt; -output &lt;output dir&gt; [-config.properties &lt;properties&gt;] 
+[highlight &lt;buildid patterns&gt;] [scenario.pattern &lt;scenario prefix 
+patterns&gt;] [-fingerprints][-scenarioresults][-current.prefix &lt;comma-separated list of build id prefixes&gt;][-baseline.prefix &lt;comma-separated list of build id prefixes&gt;]</b></LI></OL><br> Parameter descriptions: 
+<blockquote> <p>-baseline &lt;baseline build id. A previous value used in the 
+&quot;build&quot; key in the eclipse.perf.config system property or the eclipse.perf.assertAgainst 
+property. &gt; <br> -current &lt;current build id. The value used in the &quot;build&quot; 
+key in the eclipse.perf.config system property.&gt;<br> -config &lt;a comma-separated 
+list of names of configurations for which to produce results. The values used 
+should be the same as values specified for the &quot;config&quot; key in the eclipse.perf.config 
+system property.&gt;<br> -jvm &lt;jvm description. The value used in the &quot;jvm&quot; 
+key in the eclipse.perf.config system property.&gt;<br> -output &lt;path to an 
+output directory for fingerprints and scenarios for each configuration specified 
+in -config parameter. Fingerprint gifs and html files are generated in the directory 
+specified, configuration specific subdirectories are created to store scenario 
+results and line graphs.&gt;</p></blockquote><p><br> Optional arguments:</p><blockquote> 
+<p>-config.properties &lt;semi-colon separated list of: config, alternate config 
+description.  The value should be quoted if there are spaces in the value.&gt;<BR>of builds which match any prefix in this list.  When omitted, a magenta line is draw on the graph which represents the baseline value specified in the -baseline parameter.&gt;<br> -highlight &lt;Comma-separated 
+list of build Id prefixes(excluding 'I' and 'N'), which are used to add and highlight 
+the most recent matching buildId on line graphs.&quot;&gt;<br> -scenario.pattern 
+&lt;Scenario prefix pattern used to query performance results database. Can be 
+used to generate results for a single component or subset of scenarios.&gt;<br> 
+-fingerprints or -scenarioresults &lt;use one or the other to generate fingerprints 
+or scenario results only. Not specifying either will execute both.&gt;<BR>-baseline.prefix &lt;semi-colon separated list of: build id prefixes used in eclipse.perf.assertAgainst property or eclipse.perf.config system property.  Values used to generate a second line graph representing changes in repeated baseline test runs&gt;.<BR>-current.prefix &lt;semi-colon separated list of: build id prefixes used in eclipse.perf.config system property.  Values here used to override default of &quot;N,I&quot;.  Used to select build id's to include in main line graph.&gt;</p></blockquote>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/Fingerprints.js b/bundles/org.eclipse.test.performance.ui/scripts/Fingerprints.js
new file mode 100644
index 0000000..d1914e9
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/Fingerprints.js
@@ -0,0 +1,32 @@
+function toggleFingerprints() {
+	var formSelect=document.forms[0].elements[0];
+	var type=formSelect.selectedIndex;
+	var idx=document.URL.indexOf("php?");
+	if (idx==-1) {
+		window.open(document.URL+"?fp_type="+type, "_self");
+	} else {
+		window.open(document.URL.substring(0,idx)+"php?fp_type="+type, "_self");
+	}
+}
+
+function setFingerprintsType() {
+	var idx=document.URL.indexOf("?");
+	var type=0;
+	if (idx != -1) {
+		var typeStr=document.URL.substring(idx+1, document.URL.length);
+		idx=typeStr.indexOf("=");
+		if (idx != -1) {
+			var ch=typeStr.substring(idx+1, idx+2)
+			switch (ch) {
+				case '1':
+					type=1;
+					break;
+				case '2':
+					type=2;
+					break;
+			}
+		}
+	}
+	var formSelect=document.forms[0].elements[0];
+	formSelect.selectedIndex=type;
+}
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/ToolTip.css b/bundles/org.eclipse.test.performance.ui/scripts/ToolTip.css
new file mode 100644
index 0000000..d9114d2
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/ToolTip.css
@@ -0,0 +1,34 @@
+
+.hidden_tooltip {
+	display:none;
+}
+
+span.visible_tooltip {
+	display:block;
+	position:absolute;
+	top:10px;
+	left:21px;
+	width:300px;
+	font-size:9px;;
+	padding-left:5px;
+	padding-right:5px;
+	border:1px solid black;
+	background-color:#FFFFCC;
+	text-decoration:none;
+	color:#000000;
+	z-index:10;
+} 
+
+a{  
+	text-decoration:none;
+}
+span {
+z-index=100;
+}
+img, table{
+z-index:1;
+}
+
+.tooltipSource {
+	position:relative;
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/ToolTip.js b/bundles/org.eclipse.test.performance.ui/scripts/ToolTip.js
new file mode 100644
index 0000000..98008cc
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/ToolTip.js
@@ -0,0 +1,23 @@
+// General purpose functions. 
+
+function hide_element(elementId) {
+	element = document.getElementById(elementId);
+	if(element != null) {
+		currentClass = element.className;
+		if(currentClass =='visible_tooltip') {
+			element.className = 'hidden_tooltip';
+		}
+	}
+}
+
+function show_element(elementId) {
+	element = document.getElementById(elementId);
+	if(element != null) {
+		currentClass = element.className;
+		if(currentClass == 'hidden_tooltip') {
+			element.className = 'visible_tooltip';
+		}
+	}
+}
+
+
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/genresults b/bundles/org.eclipse.test.performance.ui/scripts/genresults
new file mode 100644
index 0000000..8dfb7de
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/genresults
@@ -0,0 +1,7 @@
+# !/bin/sh
+
+#export dbloc=net://minsky.ottawa.ibm.com
+#required when running on Linux Motif
+export LD_LIBRARY_PATH=./../../..
+
+java -Declipse.perf.dbloc=$dbloc -jar ./../../../plugins/org.eclipse.equinox.launcher.jar -application org.eclipse.test.performance.ui.resultGenerator $*
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/genresults.bat b/bundles/org.eclipse.test.performance.ui/scripts/genresults.bat
new file mode 100644
index 0000000..fcc1b37
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/genresults.bat
@@ -0,0 +1,6 @@
+@echo off
+
+REM set dbloc=net://minsky.ottawa.ibm.com
+
+java -Declipse.perf.dbloc=%dbloc% -jar .\..\..\..\plugins\org.eclipse.equinox.launcher.jar -application org.eclipse.test.performance.ui.resultGenerator %*
+
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/plugin.xml.template b/bundles/org.eclipse.test.performance.ui/scripts/plugin.xml.template
new file mode 100644
index 0000000..cf91a73
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/plugin.xml.template
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin
+   id="Cloudscape"
+   name="Cloudscape"
+   version="1.0.0">
+   
+   <runtime>
+      <library name="db2jcc.jar">
+         <export name="*"/>
+      </library>
+      <library name="db2jcc_license_c.jar">
+         <export name="*"/>
+      </library>
+      <library name="cstools.jar">
+         <export name="*"/>
+      </library>
+      <library name="csnet.jar">
+         <export name="*"/>
+      </library>
+      <library name="cs.jar">
+         <export name="*"/>
+      </library>
+   </runtime>
+   <requires>
+      <import plugin="org.eclipse.osgi"/>
+   </requires>
+</plugin>
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/results.properties b/bundles/org.eclipse.test.performance.ui/scripts/results.properties
new file mode 100644
index 0000000..979c6bf
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/results.properties
@@ -0,0 +1,20 @@
+###############################################################################
+# Copyright (c) 2005, 2006 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
+###############################################################################
+args=\
+-baseline 3.0_200406251208_200505301645 \
+-baseline.prefix 3.0_ \
+-highlight 3.0.2_,2.1.3_,3.1M6_,3.1M7_ ,3.1RC1 \
+-current 3.1RC1_200505271300_200505301912 \
+-jvm sun1.4.2_08 \
+-output d:/m7perf \
+-config eclipseperfwin2 \
+-config.properties "eclipseperfwin1,Win XP Sun 1.4.2_08 (2 GHz 512 MB),win,d:/m7perf/win;eclipseperflnx1,RHEL 3.0 Sun 1.4.2_08 (2 GHz 512 MB),linux,d:/m7perf/linux;eclipseperfwin2,Win XP Sun 1.4.2_08 (3 GHz 2 GB),win2,d:/m7perf/win2;eclipseperflnx2,RHEL 3.0 Sun 1.4.2_08 (3 GHz 2 GB),linux2,d:/m7perf/linux2" \
+-scenario.pattern org.eclipse.ant
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/AbstractResults.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/AbstractResults.java
new file mode 100644
index 0000000..679bf42
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/AbstractResults.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * 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.db;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+
+/**
+ * Abstract class to store performance results.
+ *
+ * Each results gives access to specific children depending on model.
+ */
+public abstract class AbstractResults implements Comparable {
+
+	public static final double[] INVALID_RESULTS = new double[] {2};
+	public static final double[] NO_BUILD_RESULTS = new double[0];
+	public static final int BUILD_VALUE_INDEX = 0;
+	public static final int BASELINE_VALUE_INDEX = 1;
+	public static final int DELTA_VALUE_INDEX = 2;
+	public static final int DELTA_ERROR_INDEX = 3;
+	public static final int BUILD_ERROR_INDEX = 4;
+	public static final int BASELINE_ERROR_INDEX = 5;
+	public static final int NUMBERS_LENGTH = 6;
+
+	AbstractResults parent;
+	int id = -1;
+	String name;
+	List children;
+	private static boolean NEW_LINE = true;
+	PrintStream printStream = null;
+
+AbstractResults(AbstractResults parent, String name) {
+	this.parent = parent;
+	this.children = new ArrayList();
+	this.name = name;
+}
+
+AbstractResults(AbstractResults parent, int id) {
+	this.parent = parent;
+	this.children = new ArrayList();
+	this.id = id;
+}
+
+/*
+ * Add a child to current results, using specific sort
+ * order if specified.
+ */
+void addChild(Comparable child, boolean sort) {
+	if (sort) {
+		int size = this.children.size();
+		for (int i=0; i<size; i++) {
+			Object results = this.children.get(i);
+			if (child.compareTo(results) < 0) {
+				this.children.add(i, child);
+				return;
+			}
+		}
+	}
+	this.children.add(child);
+}
+
+/**
+ * Compare the results to the given one using the name.
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+public int compareTo(Object obj) {
+	if (obj instanceof AbstractResults) {
+		AbstractResults res = (AbstractResults) obj;
+		return getName().compareTo(res.getName());
+	}
+	return -1;
+}
+
+/**
+ * Returns whether two results are equals using the name
+ * to compare them.
+ *
+ * @param obj  The results to compare with
+ * @return <code>true</code> if the name are equals,
+ * 	<code>false</code> otherwise
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+public boolean equals(Object obj) {
+	if (obj instanceof AbstractResults) {
+		return this.name.equals(((AbstractResults)obj).getName());
+	}
+	return super.equals(obj);
+}
+
+/**
+ * Return an array built on the current results children list.
+ *
+ * @return An array of the children list
+ */
+public AbstractResults[] getChildren() {
+	AbstractResults[] elements = new AbstractResults[size()];
+	this.children.toArray(elements);
+	return elements;
+}
+
+ComponentResults getComponentResults() {
+	if (this.parent != null) {
+		return this.parent.getComponentResults();
+	}
+	return null;
+}
+
+int getId() {
+	return this.id;
+}
+
+/**
+ * Returns the name of the results object.
+ *
+ * @return The name of the results
+ */
+public String getName() {
+	return this.name;
+}
+
+/**
+ * Returns the parent
+ *
+ * @return The parent
+ */
+public AbstractResults getParent() {
+	return this.parent;
+}
+
+PerformanceResults getPerformance() {
+	if (this.parent != null) {
+		return this.parent.getPerformance();
+	}
+	return null;
+}
+
+String getPath() {
+	String path = this.parent==null || this.parent.parent==null ? "" : this.parent.getPath() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
+	return path+this.name;
+}
+
+/**
+ * Return the children list of the current results.
+ *
+ * @return An iterator on the children list
+ */
+public Iterator getResults() {
+	return this.children.iterator();
+}
+
+AbstractResults getResults(String resultName) {
+	int size = this.children.size();
+	for (int i=0; i<size; i++) {
+		AbstractResults searchedResults = (AbstractResults) this.children.get(i);
+		if (searchedResults.getName().equals(resultName)) {
+			return searchedResults;
+		}
+	}
+	return null;
+}
+
+AbstractResults getResults(int searchedId) {
+	int size = this.children.size();
+	for (int i=0; i<size; i++) {
+		AbstractResults searchedResults = (AbstractResults) this.children.get(i);
+		if (searchedResults.id == searchedId) {
+			return searchedResults;
+		}
+	}
+	return null;
+}
+
+public int hashCode() {
+	return this.name.hashCode();
+}
+
+void printTab() {
+	if (this.parent != null) {
+		if (this.printStream != null) this.printStream.print("\t"); //$NON-NLS-1$
+		this.parent.printTab();
+	}
+}
+void print(String text) {
+	if (this.printStream != null) {
+		if (NEW_LINE) printTab();
+		this.printStream.print(text);
+		NEW_LINE = false;
+	}
+}
+
+void printGlobalTime(long start) {
+	printGlobalTime(start, null);
+}
+
+void printGlobalTime(long start, String end) {
+	long time = System.currentTimeMillis();
+	String resultsName = getName();
+	StringBuffer buffer;
+	if (resultsName == null) {
+		buffer = new StringBuffer(" => time spent was "); //$NON-NLS-1$
+	} else {
+		buffer = new StringBuffer(" => time spent in '"); //$NON-NLS-1$
+		buffer.append(resultsName);
+		buffer.append("' was "); //$NON-NLS-1$
+	}
+	buffer.append(Util.timeString(time-start));
+	if (end != null) {
+		buffer.append(". "); //$NON-NLS-1$
+		buffer.append(end.trim());
+	}
+	println(buffer);
+}
+
+void println() {
+	if (this.printStream != null) {
+		this.printStream.println();
+		NEW_LINE = true;
+	}
+}
+
+void println(String text) {
+	if (this.printStream != null) {
+		if (NEW_LINE) printTab();
+		this.printStream.println(text);
+		NEW_LINE = true;
+	}
+}
+
+void println(StringBuffer buffer) {
+	println(buffer.toString());
+}
+
+public int size() {
+	return this.children == null ? 0 : this.children.size();
+}
+
+public String toString() {
+	return getPath();
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/BuildResults.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/BuildResults.java
new file mode 100644
index 0000000..21e3021
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/BuildResults.java
@@ -0,0 +1,588 @@
+/*******************************************************************************
+ * 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.db;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.StringTokenizer;
+
+import org.eclipse.test.internal.performance.InternalPerformanceMeter;
+import org.eclipse.test.internal.performance.PerformanceTestPlugin;
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+/**
+ * Class providing numbers of a scenario running on a specific configuration
+ * at a specific time (for example 'I20070615-1200').
+ */
+public class BuildResults extends AbstractResults {
+
+	private static final double IMPOSSIBLE_VALUE = -1E6;
+
+	// Build information
+	String date;
+	String comment;
+	int summaryKind = -1;
+
+	// Dimensions information
+	Dim[] dimensions;
+	double[] average, stddev;
+	long[] count;
+	double[][] values;
+	boolean hadValues = false;
+	private int defaultDimIndex = -1;
+
+	// Comparison information
+	boolean baseline;
+	String failure;
+
+BuildResults(AbstractResults parent) {
+	super(parent, -1);
+}
+
+BuildResults(AbstractResults parent, int id) {
+	super(parent, id);
+	this.name = DB_Results.getBuildName(id);
+	this.baseline = this.name.startsWith(DB_Results.getDbBaselinePrefix());
+}
+
+/*
+ * Clean values when several measures has been done for the same build.
+ */
+void cleanValues() {
+	int length = this.values.length;
+	for (int dim_id=0; dim_id<length; dim_id++) {
+		int vLength = this.values[dim_id].length;
+		/* Log clean operation
+		if (dim_id == 0) {
+			IStatus status = new Status(IStatus.WARNING, PerformanceTestPlugin.PLUGIN_ID, "Clean "+vLength+" values for "+this.parent+">"+this.name+" ("+this.count[dim_id]+" measures)...");    //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ //$NON-NLS-5$
+			PerformanceTestPlugin.log(status);
+		}
+		*/
+		this.average[dim_id] = 0;
+		for (int i=0; i<vLength; i++) {
+			this.average[dim_id] += this.values[dim_id][i];
+		}
+		this.average[dim_id] /= vLength;
+		double squaredDeviations= 0;
+		for (int i=0; i<vLength; i++) {
+		    double deviation= this.average[dim_id] - this.values[dim_id][i];
+		    squaredDeviations += deviation * deviation;
+		}
+		this.stddev[dim_id] = Math.sqrt(squaredDeviations / (this.count[dim_id] - 1)); // unbiased sample stdev
+		this.values[dim_id] = null;
+	}
+	for (int i=0; i<length; i++) {
+		if (this.values[i] != null) {
+			return;
+		}
+	}
+	this.values = null;
+	this.hadValues = true;
+}
+
+/**
+ * Compare build results using the date of the build.
+ *
+ * @see Comparable#compareTo(Object)
+ */
+public int compareTo(Object obj) {
+	if (obj instanceof BuildResults) {
+		BuildResults res = (BuildResults) obj;
+		return getDate().compareTo(res.getDate());
+	}
+	return -1;
+}
+
+/**
+ * Returns the most recent baseline build results.
+ *
+ * @return The {@link BuildResults baseline build results}.
+ * @see BuildResults
+ */
+public BuildResults getBaselineBuildResults() {
+	return ((ConfigResults)this.parent).getBaselineBuildResults();
+}
+
+/**
+ * Returns the comment associated with the scenario for the current build.
+ *
+ * @return The comment associated with the scenario for the current build
+ * 	or <code>null</code> if no comment was stored for it.
+ */
+public String getComment() {
+	return this.comment;
+}
+
+/**
+ * Return the number of stored values for the default dimension
+ *
+ * @return the number of stored values for the default dimension
+ */
+public long getCount() {
+	if (this.defaultDimIndex < 0) {
+		this.defaultDimIndex = DB_Results.getDefaultDimensionIndex();
+	}
+	return this.count[this.defaultDimIndex];
+}
+
+/**
+ * Return the number of stored values for the given dimension.
+ *
+ * @param dim_id The id of the dimension (see {@link Dim#getId()})
+ * @return the number of stored values for the given dimension
+ *
+ */
+public long getCount(int dim_id) {
+	return this.count[getDimIndex(dim_id)];
+}
+
+/**
+ * Returns the date of the build which is a part of its name.
+ *
+ * @return The date of the build as yyyyMMddHHmm
+ */
+public String getDate() {
+	if (this.date == null) {
+		if (this.baseline) {
+			int length = this.name.length();
+			this.date = this.name.substring(length-12, length);
+		} else {
+			char first = this.name.charAt(0);
+			if (first == 'N' || first == 'I' || first == 'M') { // TODO (frederic) should be buildIdPrefixes...
+				if (this.name.length() == 14) {
+					this.date = this.name.substring(1, 9)+this.name.substring(10, 14);
+				} else {
+					this.date = this.name.substring(1);
+				}
+			} else {
+				int length = this.name.length() - 12 /* length of date */;
+				for (int i=0; i<=length; i++) {
+					try {
+						String substring = i == 0 ? this.name : this.name.substring(i);
+						Util.DATE_FORMAT.parse(substring);
+						this.date = substring; // if no exception is raised then the substring has a correct date format => store it
+						break;
+					} catch(ParseException ex) {
+						// skip
+					}
+				}
+			}
+		}
+	}
+	return this.date;
+}
+
+/**
+ * Returns the standard deviation of the default dimension computed
+ * while running the scenario for the current build.
+ *
+ * @return The value of the standard deviation
+ */
+public double getDeviation() {
+	if (this.defaultDimIndex < 0) {
+		this.defaultDimIndex = DB_Results.getDefaultDimensionIndex();
+	}
+	return this.stddev[this.defaultDimIndex];
+}
+
+/**
+ * Returns the standard deviation of the given dimension computed
+ * while running the scenario for the current build.
+ *
+ * @param dim_id The id of the dimension (see {@link Dim#getId()})
+ * @return The value of the standard deviation
+ */
+public double getDeviation(int dim_id) {
+	final int dimIndex = getDimIndex(dim_id);
+	return dimIndex < 0 ? 0 : this.stddev[dimIndex];
+}
+
+/**
+ * Returns the dimensions supported for the current build.
+ *
+ * @return An array of dimensions.
+ */
+public Dim[] getDimensions() {
+	return this.dimensions;
+}
+
+/**
+ * Returns the kind of summary for the scenario of the current build.
+ *
+ * @return -1 if the scenario has no summary, 1 if it's a global summary, 0 otherwise.
+ */
+public int getSummaryKind() {
+	return this.summaryKind;
+}
+
+/**
+ * Returns whether the current build had several values stored in database.
+ *
+ * @return <code>true</code> if the measure was committed several times,
+ * <code>false</code> otherwise.
+ */
+public boolean hadValues() {
+	return this.hadValues;
+}
+
+/*
+ * Return the index of the dimension corresponding to the given
+ * dimension id (see {@link Dim#getId()})
+ */
+int getDimIndex(int dim_id) {
+	if (this.dimensions == null) return -1;
+	int length = this.dimensions.length;
+	for (int i=0; i<length; i++) {
+		if (this.dimensions[i] == null) break;
+		if (this.dimensions[i].getId() == dim_id) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+/**
+ * Return the error computed while storing values for the default dimension
+ *
+ * @return the error of the measures stored for the default dimension
+ */
+public double getError() {
+	long n = getCount();
+	if (n == 1) return Double.NaN;
+	return getDeviation() / Math.sqrt(n);
+}
+
+/**
+ * Return the error computed while storing values for the given dimension.
+ *
+ * @param dim_id The id of the dimension (see {@link Dim#getId()})
+ * @return the error of the measures stored for the given dimension
+ */
+public double getError(int dim_id) {
+	long n = getCount(dim_id);
+	if (n == 1) return Double.NaN;
+	return getDeviation(dim_id) / Math.sqrt(n);
+}
+
+/**
+ * Return the failure message which may happened on this scenario
+ * while running the current build
+ *
+ * @return The failure message or <code>null</null> if the scenario passed.
+ */
+public String getFailure() {
+	return this.failure;
+}
+
+/**
+ * Return the value of the performance result stored
+ * for the given dimension of the current build.
+ *
+ * @param dim_id The id of the dimension (see {@link Dim#getId()})
+ * @return The value of the performance result
+ */
+public double getValue(int dim_id) {
+	int idx = getDimIndex(dim_id);
+	if (idx < 0) return Double.NaN;
+	return this.average[idx];
+}
+
+/**
+ * Return the value of the performance result stored
+ * for the default dimension of the current build.
+ *
+ * @return The value of the performance result
+ */
+public double getValue() {
+	if (this.defaultDimIndex < 0) {
+		this.defaultDimIndex = DB_Results.getDefaultDimensionIndex();
+	}
+	return this.average[this.defaultDimIndex];
+}
+
+/**
+ * Returns whether the build is a baseline build or not.
+ *
+ * @return <code>true</code> if the build name starts with the baseline prefix
+ * 	(see {@link PerformanceResults#getBaselinePrefix()} or <code>false</code>
+ * 	otherwise.
+ */
+public boolean isBaseline() {
+	return this.baseline;
+}
+
+/**
+ * Returns whether the build has a summary or not. Note that the summary
+ * may be global or not.
+ *
+ * @return <code>true</code> if the summary kind is equals to 0 or 1
+ * 	<code>false</code> otherwise.
+ */
+public boolean hasSummary() {
+	return this.summaryKind >= 0;
+}
+/**
+ * Returns whether the build has a global summary or not.
+ *
+ * @return <code>true</code> if the summary kind is equals to 1
+ * 	<code>false</code> otherwise.
+ */
+public boolean hasGlobalSummary() {
+	return this.summaryKind == 1;
+}
+
+/*
+ * Returns a given pattern match the build name or not.
+ */
+boolean match(String pattern) {
+	if (pattern.equals("*")) return true; //$NON-NLS-1$
+	if (pattern.indexOf('*') < 0 && pattern.indexOf('?') < 0) {
+		pattern += "*"; //$NON-NLS-1$
+	}
+	StringTokenizer tokenizer = new StringTokenizer(pattern, "*?", true); //$NON-NLS-1$
+	int start = 0;
+	String previous = ""; //$NON-NLS-1$
+	while (tokenizer.hasMoreTokens()) {
+		String token = tokenizer.nextToken();
+		if (!token.equals("*") && !token.equals("?")) { //$NON-NLS-1$ //$NON-NLS-2$
+			if (previous.equals("*")) { //$NON-NLS-1$
+				int idx = this.name.substring(start).indexOf(token);
+				if (idx < 0) return false;
+				start += idx;
+			} else {
+				if (previous.equals("?")) start++; //$NON-NLS-1$
+				if (!this.name.substring(start).startsWith(token)) return false;
+			}
+			start += token.length();
+		}
+		previous = token;
+	}
+	if (previous.equals("*")) { //$NON-NLS-1$
+		return true;
+	} else if (previous.equals("?")) { //$NON-NLS-1$
+		return this.name.length() == start;
+	}
+	return this.name.endsWith(previous);
+}
+
+/*
+ * Read the build results data from the given stream.
+ */
+void readData(DataInputStream stream) throws IOException {
+	long timeBuild = stream.readLong();
+	this.date = new Long(timeBuild).toString();
+	byte kind = stream.readByte();
+	this.baseline = kind == 0;
+	if (this.baseline) {
+		this.name = getPerformance().baselinePrefix + '_' + this.date;
+	} else {
+		String suffix = this.date.substring(0, 8) + '-' + this.date.substring(8);
+		switch (kind) {
+			case 1:
+				this.name = "N" + suffix; //$NON-NLS-1$
+				break;
+			case 2:
+				this.name = "I" + suffix; //$NON-NLS-1$
+				break;
+			case 3:
+				this.name = "M" + suffix; //$NON-NLS-1$
+				break;
+			default:
+				this.name = stream.readUTF();
+				break;
+		}
+	}
+	int length = stream.readInt();
+	this.dimensions = new Dim[length];
+	this.average = new double[length];
+	this.stddev = new double[length];
+	this.count = new long[length];
+	for (int i=0; i<length; i++) {
+		int dimId = stream.readInt();
+		DB_Results.storeDimension(dimId);
+		this.dimensions[i] = (Dim) PerformanceTestPlugin.getDimension(dimId);
+		this.average[i] = stream.readLong();
+		this.count[i] = stream.readLong();
+		this.stddev[i] = stream.readDouble();
+	}
+	this.id = DB_Results.getBuildId(this.name);
+
+	// read summary
+	this.summaryKind = stream.readInt();
+
+	// read comment
+	String str = stream.readUTF();
+	if (str.length() > 0) {
+		this.comment = str;
+	}
+}
+
+/*
+ * Set the build summary and its associated comment.
+ */
+void setComment(String comment) {
+	if (comment != null && this.comment == null) {
+		this.comment = comment;
+	}
+}
+
+/*
+ * Set the build summary and its associated comment.
+ */
+void setSummary(int kind, String comment) {
+	this.comment = comment;
+	this.summaryKind = kind;
+}
+
+/*
+ * Set the build failure.
+ */
+void setFailure(String failure) {
+	this.failure = failure;
+}
+
+/*
+ * Set the build value from database information.
+ */
+void setValue(int dim_id, int step, long value) {
+	int length = DB_Results.getDimensions().length;
+	Dim dimension = (Dim) PerformanceTestPlugin.getDimension(dim_id);
+	int idx = 0;
+	if (this.dimensions == null){
+		this.dimensions = new Dim[length];
+		this.average = new double[length];
+		this.stddev = new double[length];
+		this.count = new long[length];
+		this.dimensions[0] = dimension;
+		for (int i=0; i<length; i++) {
+			// init average numbers with an impossible value
+			// to clearly identify whether it's already set or not
+			// when several measures are made for the same build
+			this.average[i] = IMPOSSIBLE_VALUE;
+		}
+	} else {
+		length = this.dimensions.length;
+		for (int i=0; i<length; i++) {
+			if (this.dimensions[i] == null) {
+				this.dimensions[i] = dimension;
+				idx = i;
+				break;
+			}
+			if (this.dimensions[i].getId() == dim_id) {
+				idx = i;
+				break;
+			}
+		}
+	}
+	switch (step) {
+		case InternalPerformanceMeter.AVERAGE:
+			if (this.average[idx] != IMPOSSIBLE_VALUE) {
+				if (this.values == null) {
+					this.values = new double[length][];
+					this.values[idx] = new double[2];
+					this.values[idx][0] = this.average[idx];
+					this.values[idx][1] = value;
+					this.average[idx] = IMPOSSIBLE_VALUE;
+				} else if (this.values[idx] == null) {
+					this.values[idx] = new double[2];
+					this.values[idx][0] = this.average[idx];
+					this.values[idx][1] = value;
+					this.average[idx] = IMPOSSIBLE_VALUE;
+				}
+			} else if (this.values != null && this.values[idx] != null) {
+				int vLength = this.values[idx].length;
+				System.arraycopy(this.values[idx], 0, this.values[idx] = new double[vLength+1], 0, vLength);
+				this.values[idx][vLength] = value;
+			} else {
+				this.average[idx] = value;
+			}
+			break;
+		case InternalPerformanceMeter.STDEV:
+			this.stddev[idx] += Double.longBitsToDouble(value);
+			break;
+		case InternalPerformanceMeter.SIZE:
+			this.count[idx] += value;
+			break;
+	}
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.AbstractResults#toString()
+ */
+public String toString() {
+	StringBuffer buffer = new StringBuffer(this.name);
+	buffer.append(": "); //$NON-NLS-1$
+	int length = this.dimensions.length;
+	for (int i=0; i<length; i++) {
+		if (i>0)	buffer.append(", "); //$NON-NLS-1$
+		buffer.append('[');
+		buffer.append(this.dimensions[i].getId());
+		buffer.append("]="); //$NON-NLS-1$
+		buffer.append(this.average[i]);
+		buffer.append('/');
+		buffer.append(this.count[i]);
+		buffer.append('/');
+		buffer.append(Math.round(this.stddev[i]*1000)/1000.0);
+	}
+	return buffer.toString();
+}
+
+/*
+ * Write the build results data in the given stream.
+ */
+void write(DataOutputStream stream) throws IOException {
+	long timeBuild = -1;
+    try {
+	    timeBuild = Long.parseLong(getDate());
+    } catch (NumberFormatException nfe) {
+	    // do nothing
+    	nfe.printStackTrace();
+    }
+	stream.writeLong(timeBuild);
+	byte kind = 0; // baseline
+	if (!this.baseline) {
+		switch (this.name.charAt(0)) {
+			case 'N':
+				kind = 1;
+				break;
+			case 'I':
+				kind = 2;
+				break;
+			case 'M':
+				kind = 3;
+				break;
+			default:
+				kind = 4;
+				break;
+		}
+	}
+	stream.writeByte(kind);
+	if (kind == 4) {
+		stream.writeUTF(this.name);
+	}
+	int length = this.dimensions == null ? 0 : this.dimensions.length;
+	stream.writeInt(length);
+	for (int i=0; i<length; i++) {
+		stream.writeInt(this.dimensions[i].getId());
+		stream.writeLong((long)this.average[i]) ;
+		stream.writeLong(this.count[i]);
+		stream.writeDouble(this.stddev[i]);
+	}
+
+	// Write extra infos (summary, failure and comment)
+	stream.writeInt(this.summaryKind);
+	stream.writeUTF(this.comment == null ? "" : this.comment) ; //$NON-NLS-1$
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ComponentResults.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ComponentResults.java
new file mode 100644
index 0000000..ca3e7fb
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ComponentResults.java
@@ -0,0 +1,445 @@
+/*******************************************************************************
+ * 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.db;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+/**
+ * Class to handle performance results of an eclipse component
+ * (for example 'org.eclipse.jdt.core').
+ *
+ * It gives access to results for each scenario run for this component.
+ *
+ * @see ScenarioResults
+ */
+public class ComponentResults extends AbstractResults {
+
+public ComponentResults(AbstractResults parent, String name) {
+	super(parent, name);
+	this.printStream = parent.printStream;
+}
+
+Set getAllBuildNames() {
+	Set buildNames = new HashSet();
+	int size = size();
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
+		Set builds = scenarioResults.getAllBuildNames();
+		buildNames.addAll(builds);
+	}
+	return buildNames;
+}
+
+/**
+ * Return all the build names for this component sorted by ascending order.
+ *
+ * @return An array of names
+ */
+public String[] getAllSortedBuildNames() {
+	return getAllSortedBuildNames(false/*ascending order*/);
+}
+
+String[] getAllSortedBuildNames(final boolean reverse) {
+	Set allBuildNames = getAllBuildNames();
+	String[] sortedNames = new String[allBuildNames.size()];
+	allBuildNames.toArray(sortedNames);
+	Arrays.sort(sortedNames, new Comparator() {
+		public int compare(Object o1, Object o2) {
+			String s1 = (String) (reverse ? o2 : o1);
+			String s2 = (String) (reverse ? o1 : o2);
+			return Util.getBuildDate(s1).compareTo(Util.getBuildDate(s2));
+	    }
+	});
+	return sortedNames;
+}
+
+ComponentResults getComponentResults() {
+	return this;
+}
+
+/**
+ * Get all results numbers for a given machine of the current component.
+ *
+ * @param configName The name of the configuration to get numbers
+ * @param fingerprints Set whether only fingerprints scenario should be taken into account
+ * @return A list of lines. Each line represent a build and is a list of either strings or values.
+  * 	Values are an array of double:
+ * 	<ul>
+ * 		<li>{@link #BUILD_VALUE_INDEX}: the build value in milliseconds</li>
+ * 		<li>{@link #BASELINE_VALUE_INDEX}: the baseline value in milliseconds</li>
+ * 		<li>{@link #DELTA_VALUE_INDEX}: the difference between the build value and its more recent baseline</li>
+ * 		<li>{@link #DELTA_ERROR_INDEX}: the error made while computing the difference</li>
+ * 		<li>{@link #BUILD_ERROR_INDEX}: the error made while measuring the build value</li>
+ * 		<li>{@link #BASELINE_ERROR_INDEX}: the error made while measuring the baseline value</li>
+ * 	</ul>
+*/
+public List getConfigNumbers(String configName, boolean fingerprints, List differences) {
+
+	// Initialize lists
+	AbstractResults[] scenarios = getChildren();
+	int length = scenarios.length;
+
+	// Print scenario names line
+	List firstLine = new ArrayList();
+	for (int i=0; i<length; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios[i];
+		if (!fingerprints || scenarioResults.hasSummary()) {
+			firstLine.add(scenarioResults.getName());
+		}
+	}
+
+	// Print each build line
+	String[] builds = getAllSortedBuildNames(true/*descending order*/);
+//	int milestoneIndex = 0;
+//	String milestoneDate = Util.getMilestoneDate(milestoneIndex);
+	String currentBuildName = null;
+	int buildsLength= builds.length;
+	firstLine.add(0, new Integer(buildsLength));
+	differences.add(firstLine);
+	for (int i=0; i<buildsLength; i++) {
+		List line = new ArrayList();
+		String buildName = builds[i];
+		line.add(buildName);
+		if (!buildName.startsWith(DB_Results.getDbBaselinePrefix())) {
+			for (int j=0; j<length; j++) {
+				ScenarioResults scenarioResults = (ScenarioResults) scenarios[j];
+				if (!fingerprints || scenarioResults.hasSummary()) {
+					ConfigResults configResults = scenarioResults.getConfigResults(configName);
+					BuildResults buildResults = configResults == null ? null : configResults.getBuildResults(buildName);
+					if (buildResults == null) {
+						// no result for this scenario in this build
+						line.add(NO_BUILD_RESULTS);
+					} else {
+						line.add(configResults.getNumbers(buildResults, configResults.getBaselineBuildResults(buildName)));
+					}
+				}
+			}
+			differences.add(line);
+			if (currentBuildName != null && currentBuildName.charAt(0) != 'N') {
+            }
+			currentBuildName = buildName;
+		}
+//		if (milestoneDate != null) { // update previous builds
+//			int dateComparison = milestoneDate.compareTo(Util.getBuildDate(buildName));
+//			if (dateComparison <= 0) {
+//				if (dateComparison == 0) {
+//                }
+//				if (++milestoneIndex == Util.MILESTONES.length) {
+//					milestoneDate = null;
+//				} else {
+//					milestoneDate = Util.getMilestoneDate(milestoneIndex);
+//				}
+//			}
+//		}
+	}
+
+	// Write differences lines
+	int last = buildsLength-1;
+	String lastBuildName = builds[last];
+	while (last > 0 && lastBuildName.startsWith(DB_Results.getDbBaselinePrefix())) {
+		lastBuildName = builds[--last];
+	}
+//	appendDifferences(lastBuildName, configName, previousMilestoneName, differences, fingerprints);
+//	appendDifferences(lastBuildName, configName, previousBuildName, differences, fingerprints);
+
+	// Return the computed differences
+	return differences;
+}
+
+/*
+double[] getConfigNumbers(BuildResults buildResults, BuildResults baselineResults) {
+	if (baselineResults == null) {
+		return INVALID_RESULTS;
+	}
+	double[] values = new double[NUMBERS_LENGTH];
+	for (int i=0 ;i<NUMBERS_LENGTH; i++) {
+		values[i] = Double.NaN;
+	}
+	double buildValue = buildResults.getValue();
+	values[BUILD_VALUE_INDEX] = buildValue;
+	double baselineValue = baselineResults.getValue();
+	values[BASELINE_VALUE_INDEX] = baselineValue;
+	double delta = (baselineValue - buildValue) / baselineValue;
+	values[DELTA_VALUE_INDEX] = delta;
+	if (Double.isNaN(delta)) {
+		return values;
+	}
+	long baselineCount = baselineResults.getCount();
+	long currentCount = buildResults.getCount();
+	if (baselineCount > 1 && currentCount > 1) {
+		double baselineError = baselineResults.getError();
+		double currentError = buildResults.getError();
+		values[BASELINE_ERROR_INDEX] = baselineError;
+		values[BUILD_ERROR_INDEX] = currentError;
+		values[DELTA_ERROR_INDEX] = Double.isNaN(baselineError)
+				? currentError / baselineValue
+				: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
+	}
+	return values;
+}
+*/
+
+private ScenarioResults getScenarioResults(List scenarios, int searchedId) {
+	int size = scenarios.size();
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
+		if (scenarioResults.id == searchedId) {
+			return scenarioResults;
+		}
+	}
+	return null;
+}
+
+/**
+ * Returns a list of scenario results which have a summary
+ *
+ * @param global Indicates whether the summary must be global or not.
+ * @param config Configuration name
+ * @return A list of {@link ScenarioResults scenario results} which have a summary
+ */
+public List getSummaryScenarios(boolean global, String config) {
+	int size= size();
+	List scenarios = new ArrayList(size);
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
+		ConfigResults configResults = scenarioResults.getConfigResults(config);
+		if (configResults != null) {
+			BuildResults buildResults = configResults.getCurrentBuildResults();
+			if ((global && buildResults.summaryKind == 1) || (!global && buildResults.summaryKind >= 0)) {
+				scenarios.add(scenarioResults);
+			}
+		}
+	}
+	return scenarios;
+}
+
+private String lastBuildName(int kind) {
+	String[] builds = getAllSortedBuildNames();
+	int idx = builds.length-1;
+	String lastBuildName = builds[idx--];
+	switch (kind) {
+		case 1: // no ref
+			while (lastBuildName.startsWith(DB_Results.getDbBaselinePrefix())) {
+				lastBuildName = builds[idx--];
+			}
+			break;
+		case 2: // only I-build or M-build
+			char ch = lastBuildName.charAt(0);
+			while (ch != 'I' && ch != 'M') {
+				lastBuildName = builds[idx--];
+				ch = lastBuildName.charAt(0);
+			}
+			break;
+		default:
+			break;
+	}
+	return lastBuildName;
+}
+
+/*
+ * Read local file contents and populate the results model with the collected
+ * information.
+ */
+String readLocalFile(File dir, List scenarios) throws FileNotFoundException {
+//	if (!dir.exists()) return null;
+	File dataFile = new File(dir, getName()+".dat");	//$NON-NLS-1$
+	if (!dataFile.exists()) throw new FileNotFoundException();
+	DataInputStream stream = null;
+	try {
+		// Read local file info
+		stream = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
+		print(" - read local files info"); //$NON-NLS-1$
+		String lastBuildName = stream.readUTF(); // first string is the build name
+
+		// Next field is the number of scenarios for the component
+		int size = stream.readInt();
+
+		// Then follows all the scenario information
+		for (int i=0; i<size; i++) {
+			// ... which starts with the scenario id
+			int scenario_id = stream.readInt();
+			ScenarioResults scenarioResults = scenarios == null ? null : getScenarioResults(scenarios, scenario_id);
+			if (scenarioResults == null) {
+				// this can happen if scenario pattern does not cover all those stored in local data file
+				// hence, creates a fake scenario to read the numbers and skip to the next scenario
+				scenarioResults = new ScenarioResults(-1, null, null);
+//				scenarioResults.parent = this;
+//				scenarioResults.readData(stream);
+				// Should no longer occur as we get all scenarios from database now
+//				throw new RuntimeException("Unexpected unfound scenario!"); //$NON-NLS-1$
+			}
+			scenarioResults.parent = this;
+			scenarioResults.printStream = this.printStream;
+			scenarioResults.readData(stream);
+			addChild(scenarioResults, true);
+			if (this.printStream != null) this.printStream.print('.');
+		}
+		println();
+		println("	=> "+size+" scenarios data were read from file "+dataFile); //$NON-NLS-1$ //$NON-NLS-2$
+
+		// Return last build name stored in the local files
+		return lastBuildName;
+	} catch (IOException ioe) {
+		println("	!!! "+dataFile+" should be deleted as it contained invalid data !!!"); //$NON-NLS-1$ //$NON-NLS-2$
+	} finally {
+		try {
+	        stream.close();
+        } catch (IOException e) {
+	        // nothing else to do!
+        }
+	}
+	return null;
+}
+
+/*
+ * Read the database values for a build name and a list of scenarios.
+ * The database is read only if the components does not already knows the
+ * given build (i.e. if it has not been already read) or if the force arguments is set.
+ */
+void updateBuild(String buildName, List scenarios, boolean force, File dataDir, SubMonitor subMonitor, PerformanceResults.RemainingTimeGuess timeGuess) {
+
+	// Read all variations
+	println("Component '"+this.name+"':"); //$NON-NLS-1$ //$NON-NLS-2$
+
+	// manage monitor
+	int size = scenarios.size();
+	subMonitor.setWorkRemaining(size+1);
+	StringBuffer buffer = new StringBuffer("Component "); //$NON-NLS-1$
+	buffer.append(this.name);
+	buffer.append("..."); //$NON-NLS-1$
+	String title = buffer.toString();
+	subMonitor.subTask(title+timeGuess.display());
+	timeGuess.count++;
+	subMonitor.worked(1);
+	if (subMonitor.isCanceled()) return;
+
+	// Read new values for the local result
+	boolean dirty = false;
+	long readTime = System.currentTimeMillis();
+	String log = " - read scenarios from DB:"; //$NON-NLS-1$
+	if (size > 0) {
+		for (int i=0; i<size; i++) {
+
+			// manage monitor
+			subMonitor.subTask(title+timeGuess.display());
+			timeGuess.count++;
+			if (log != null) {
+				println(log);
+				log = null;
+			}
+
+			// read results
+			ScenarioResults nextScenarioResults= (ScenarioResults) scenarios.get(i);
+			ScenarioResults scenarioResults = (ScenarioResults) getResults(nextScenarioResults.id);
+			if (scenarioResults == null) {
+				// Scenario is not known yet, force an update
+				scenarioResults = nextScenarioResults;
+				scenarioResults.parent = this;
+				scenarioResults.printStream = this.printStream;
+				scenarioResults.updateBuild(buildName, true);
+				dirty = true;
+				addChild(scenarioResults, true);
+			} else {
+				if (scenarioResults.updateBuild(buildName, force)) {
+					dirty = true;
+				}
+			}
+			if (dataDir != null && dirty && (System.currentTimeMillis() - readTime) > 300000) { // save every 5mn
+				writeData(buildName, dataDir, true, true);
+				dirty = false;
+				readTime = System.currentTimeMillis();
+			}
+
+			// manage monitor
+			subMonitor.worked(1);
+			if (subMonitor.isCanceled()) return;
+		}
+	}
+
+	// Write local files
+	if (dataDir != null) {
+		writeData(buildName, dataDir, false, dirty);
+	}
+
+	// Print global time
+	printGlobalTime(readTime);
+
+}
+
+/*
+ * Write the component results data to the file '<component name>.dat' in the given directory.
+ */
+void writeData(String buildName, File dir, boolean temp, boolean dirty) {
+//	if (!dir.exists() && !dir.mkdirs()) {
+//		System.err.println("can't create directory "+dir); //$NON-NLS-1$
+//	}
+	File tmpFile = new File(dir, getName()+".tmp"); //$NON-NLS-1$
+	File dataFile = new File(dir, getName()+".dat"); //$NON-NLS-1$
+	if (!dirty) { // only possible on final write
+		if (tmpFile.exists()) {
+			if (dataFile.exists()) dataFile.delete();
+			tmpFile.renameTo(dataFile);
+			println("	=> rename temporary file to "+dataFile); //$NON-NLS-1$
+		}
+		return;
+	}
+	if (tmpFile.exists()) {
+		tmpFile.delete();
+	}
+	File file;
+	if (temp) {
+		file = tmpFile;
+	} else {
+		if (dataFile.exists()) {
+			dataFile.delete();
+		}
+		file = dataFile;
+	}
+	try {
+		DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+		try {
+			int size = this.children.size();
+			stream.writeUTF(lastBuildName(0));
+			stream.writeInt(size);
+			for (int i=0; i<size; i++) {
+				ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
+				scenarioResults.write(stream);
+			}
+		}
+		finally {
+			stream.close();
+			println("	=> extracted data "+(temp?"temporarily ":"")+"written in file "+file); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+		}
+	} catch (FileNotFoundException e) {
+		System.err.println("can't create output file"+file); //$NON-NLS-1$
+	} catch (IOException e) {
+		e.printStackTrace();
+	}
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ConfigResults.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ConfigResults.java
new file mode 100644
index 0000000..46225a0
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ConfigResults.java
@@ -0,0 +1,663 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.db;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.test.internal.performance.InternalDimensions;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+/**
+ * Class to handle results for an Eclipse performance test box
+ * (called a <i>configuration</i>).
+ *
+ * It gives access to results for each build on which this configuration has been run.
+ *
+ * @see BuildResults
+ */
+public class ConfigResults extends AbstractResults {
+	BuildResults baseline, current;
+	boolean baselined = false, valid = false;
+	double delta, error;
+
+public ConfigResults(AbstractResults parent, int id) {
+	super(parent, id);
+	this.name = parent.getPerformance().sortedConfigNames[id];
+	this.printStream = parent.printStream;
+}
+
+/*
+ * Complete results with additional database information.
+ */
+void completeResults(String[] builds) {
+	/*if (this.baseline == null || this.current == null) */initialize();
+	ScenarioResults scenarioResults = (ScenarioResults) this.parent;
+	DB_Results.queryScenarioSummaries(scenarioResults, this.name, builds);
+}
+
+/**
+ * Returns the baseline build name used to compare results with.
+ *
+ * @return The name of the baseline build
+ * @see #getBaselineBuildResults()
+ */
+public String getBaselineBuildName() {
+	if (this.baseline == null) {
+	    initialize();
+    }
+	return this.baseline.getName();
+}
+
+/**
+ * Returns the most recent baseline build results.
+ *
+ * @return The {@link BuildResults baseline build results}.
+ * @see BuildResults
+ */
+public BuildResults getBaselineBuildResults() {
+	if (this.baseline == null) {
+	    initialize();
+    }
+	return this.baseline;
+}
+
+/**
+ * Return the baseline build results run just before the given build name.
+ *
+ * @param buildName The build name
+ * @return The {@link BuildResults baseline results} preceding the given build name
+ * 	or <code>null</code> if none was found.
+ */
+public BuildResults getBaselineBuildResults(String buildName) {
+	if (this.baseline == null) {
+	    initialize();
+    }
+	int size = this.children.size();
+	String buildDate = Util.getBuildDate(buildName);
+	for (int i=size-1; i>=0; i--) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		if (buildResults.isBaseline() && buildResults.getDate().compareTo(buildDate) < 0) {
+			return buildResults;
+		}
+	}
+	return this.baseline;
+
+}
+
+/**
+ * Returns the most recent baseline build result value.
+ *
+ * @return The value of the most recent baseline build results.
+ */
+public double getBaselineBuildValue() {
+	if (this.baseline == null) {
+	    initialize();
+    }
+	return this.baseline.getValue();
+}
+
+/**
+ * Returns the configuration description (currently the box name).
+ *
+ * @return The configuration description (currently the box name).
+ */
+public String getDescription() {
+	return getPerformance().sortedConfigDescriptions[this.id];
+}
+
+/**
+ * Return the results for the given build name.
+ *
+ * @param buildName The build name
+ * @return The {@link BuildResults results} for the given build name
+ * 	or <code>null</code> if none was found.
+ */
+public BuildResults getBuildResults(String buildName) {
+	return (BuildResults) getResults(buildName);
+}
+
+/**
+ * Returns the build results matching a given pattern.
+ *
+ * @param buildPattern The pattern of searched builds
+ * @return The list of the builds which names match the given pattern.
+ * 	The list is ordered by build results date.
+ */
+public List getBuilds(String buildPattern) {
+	List builds = new ArrayList();
+	int size = size();
+	for (int i=0; i<size; i++) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		if (buildPattern == null || buildResults.match(buildPattern)) {
+			builds.add(buildResults);
+		}
+	}
+	return builds;
+}
+
+/**
+ * Returns the build results before a given build name.
+ *
+ * @param buildName Name of the last build (included)
+ * @return The list of the builds which precedes the given build name.
+ */
+public List getBuildsBefore(String buildName) {
+	String buildDate = Util.getBuildDate(buildName);
+	List builds = new ArrayList();
+	int size = size();
+	for (int i=0; i<size; i++) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		if (buildName == null || buildResults.getDate().compareTo(buildDate) <= 0) {
+			builds.add(buildResults);
+		}
+	}
+	return builds;
+}
+
+/**
+ * Returns a list of build results which names starts with one of the given prefixes.
+ *
+ * @param prefixes List of expected prefixes
+ * @return A list of builds which names start with one of the given patterns.
+ */
+public List getBuildsMatchingPrefixes(List prefixes) {
+	List builds = new ArrayList();
+	int size = size();
+	int length = prefixes.size();
+	for (int i=0; i<size; i++) {
+		AbstractResults buildResults = (AbstractResults) this.children.get(i);
+		String buildName = buildResults.getName();
+		for (int j=0; j<length; j++) {
+			if (buildName.startsWith((String)prefixes.get(j))) {
+				builds.add(buildResults);
+			}
+		}
+	}
+	return builds;
+}
+
+/**
+ * Get all results numbers for the max last builds.
+ *
+ * @param max The number of last builds to get numbers.
+ * @return An 2 dimensions array of doubles. At the first level of the array each slot
+ * 		represents one build. That means that the dimension of the array matches
+ * 		the given numbers as soon as there are enough builds in the database.
+ * <p>
+ * 		The slots of the second level are the numbers values:
+ * 	<ul>
+ * 		<li>{@link #BUILD_VALUE_INDEX}: the build value in milliseconds</li>
+ * 		<li>{@link #BASELINE_VALUE_INDEX}: the baseline value in milliseconds</li>
+ * 		<li>{@link #DELTA_VALUE_INDEX}: the difference between the build value and its more recent baseline</li>
+ * 		<li>{@link #DELTA_ERROR_INDEX}: the error made while computing the difference</li>
+ * 		<li>{@link #BUILD_ERROR_INDEX}: the error made while measuring the build value</li>
+ * 		<li>{@link #BASELINE_ERROR_INDEX}: the error made while measuring the baseline value</li>
+ * 	</ul>
+*/
+public double[][] getLastNumbers(int max) {
+
+	// Return null if no previous builds are expected
+	if (max <= 0) return null;
+
+	// Add numbers for each previous build
+	int size = size();
+	double[][] numbers = new double[Math.min(max, size)][];
+	int n = 0;
+	for (int i=size-1; i>=0 && n<max; i--) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		if (!buildResults.isBaseline()) {
+			numbers[n] = getNumbers(buildResults, getBaselineBuildResults(buildResults.getName()));
+			n++;
+		}
+	}
+
+	// Return the numbers
+	return numbers;
+}
+
+/**
+ * Returns interesting numbers for the current configuration.
+ *
+ * @return Values in an array of double:
+ * 	<ul>
+ * 		<li>{@link AbstractResults#BUILD_VALUE_INDEX}: the build value in milliseconds</li>
+ * 		<li>{@link AbstractResults#BASELINE_VALUE_INDEX}: the baseline value in milliseconds</li>
+ * 		<li>{@link AbstractResults#DELTA_VALUE_INDEX}: the difference between the build value and its more recent baseline</li>
+ * 		<li>{@link AbstractResults#DELTA_ERROR_INDEX}: the error made while computing the difference</li>
+ * 		<li>{@link AbstractResults#BUILD_ERROR_INDEX}: the error made while measuring the build value</li>
+ * 		<li>{@link AbstractResults#BASELINE_ERROR_INDEX}: the error made while measuring the baseline value</li>
+ * 	</ul>
+ */
+public double[] getNumbers(BuildResults buildResults, BuildResults baselineResults) {
+	if (baselineResults == null) {
+		return null;
+	}
+	double[] values = new double[NUMBERS_LENGTH];
+	for (int i=0 ;i<NUMBERS_LENGTH; i++) {
+		values[i] = Double.NaN;
+	}
+	double buildValue = buildResults.getValue();
+	values[BUILD_VALUE_INDEX] = buildValue;
+	double baselineValue = baselineResults.getValue();
+	values[BASELINE_VALUE_INDEX] = baselineValue;
+	double buildDelta = (baselineValue - buildValue) / baselineValue;
+	values[DELTA_VALUE_INDEX] = buildDelta;
+	if (Double.isNaN(buildDelta)) {
+		return values;
+	}
+	long baselineCount = baselineResults.getCount();
+	long currentCount = buildResults.getCount();
+	if (baselineCount > 1 && currentCount > 1) {
+		double baselineError = baselineResults.getError();
+		double currentError = buildResults.getError();
+		values[BASELINE_ERROR_INDEX] = baselineError;
+		values[BUILD_ERROR_INDEX] = currentError;
+		values[DELTA_ERROR_INDEX] = Double.isNaN(baselineError)
+				? currentError / baselineValue
+				: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
+	}
+	return values;
+}
+
+/**
+ * Return the deviation value and its associated standard error for the default dimension
+ * (currently {@link InternalDimensions#ELAPSED_PROCESS}).
+ *
+ * @return an array of double. First number is the deviation itself and
+ * 	the second is the standard error.
+ */
+public double[] getCurrentBuildDeltaInfo() {
+	if (this.baseline == null || this.current == null) {
+		initialize();
+	}
+	return new double[] { this.delta, this.error };
+}
+
+/**
+ * Returns the error of the current build results
+ *
+ * @return the error made during the current build measure
+ */
+public double getCurrentBuildError() {
+	if (this.current == null) {
+	    initialize();
+    }
+	return this.current.getError();
+}
+
+/**
+ * Returns the current build name.
+ *
+ * @return The name of the current build
+ * @see #getCurrentBuildResults()
+ */
+public String getCurrentBuildName() {
+	if (this.current == null) {
+	    initialize();
+    }
+	return this.current.getName();
+}
+
+/**
+ * Returns the current build results.
+ * <p>
+ * This build is currently the last integration or nightly
+ * build which has performance results in the database.
+ * It may differ from the {@link PerformanceResults#getName()}.
+ *
+ * @return The current build results.
+ * @see BuildResults
+ */
+public BuildResults getCurrentBuildResults() {
+	if (this.current == null) {
+	    initialize();
+    }
+	return this.current;
+}
+
+/**
+ * Returns the current build result value.
+ *
+ * @return The value of the current build results.
+ */
+public double getCurrentBuildValue() {
+	if (this.current == null) {
+	    initialize();
+    }
+	return this.current.getValue();
+}
+
+/**
+ * Returns the delta between current and baseline builds results.
+ *
+ * @return the delta
+ */
+public double getDelta() {
+	if (this.baseline == null || this.current == null) {
+		initialize();
+	}
+	return this.delta;
+}
+
+/**
+ * Returns the standard error of the delta between current and baseline builds results.
+ *
+ * @return the delta
+ * @see #getDelta()
+ */
+public double getError() {
+	if (this.baseline == null || this.current == null) {
+		initialize();
+	}
+	return this.error;
+}
+
+/**
+ * Return the name of the machine associated with the current config.
+ *
+ * @return The name of the machine.
+ */
+public String getLabel() {
+	return this.parent.getPerformance().sortedConfigDescriptions[this.id];
+}
+
+/**
+ * Get all dimension builds default dimension statistics for all builds.
+ *
+ * @return An array of double built as follows:
+ * <ul>
+ * <li>0:	numbers of values</li>
+ * <li>1:	mean of values</li>
+ * <li>2:	standard deviation of these values</li>
+ * <li>3:	coefficient of variation of these values</li>
+ * </ul>
+ */
+public double[] getStatistics() {
+	return getStatistics(Util.ALL_BUILD_PREFIXES, DB_Results.getDefaultDimension().getId());
+}
+
+/**
+ * Get all dimension builds default dimension statistics for a given list of build
+ * prefixes.
+ *
+ * @param prefixes List of prefixes to filter builds. If <code>null</code>
+ * 	then all the builds are taken to compute statistics.
+ * @return An array of double built as follows:
+ * <ul>
+ * <li>0:	numbers of values</li>
+ * <li>1:	mean of values</li>
+ * <li>2:	standard deviation of these values</li>
+ * <li>3:	coefficient of variation of these values</li>
+ * </ul>
+ */
+public double[] getStatistics(List prefixes) {
+	return getStatistics(prefixes, DB_Results.getDefaultDimension().getId());
+}
+
+/**
+ * Get all dimension builds statistics for a given list of build prefixes
+ * and a given dimension.
+ *
+ * @param prefixes List of prefixes to filter builds. If <code>null</code>
+ * 	then all the builds are taken to compute statistics.
+ * @param dim_id The id of the dimension on which the statistics must be computed
+ * @return An array of double built as follows:
+ * <ul>
+ * <li>0:	numbers of values</li>
+ * <li>1:	mean of values</li>
+ * <li>2:	standard deviation of these values</li>
+ * <li>3:	coefficient of variation of these values</li>
+ * </ul>
+ */
+public double[] getStatistics(List prefixes, int dim_id) {
+	int size = size();
+	int length = prefixes == null ? 0 : prefixes.size();
+	int count = 0;
+	double mean=0, stddev=0, variation=0;
+	double[] values = new double[size];
+	count = 0;
+	mean = 0.0;
+	for (int i=0; i<size; i++) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		String buildName = buildResults.getName();
+		if (isBuildConcerned(buildResults)) {
+			if (prefixes == null) {
+				double value = buildResults.getValue(dim_id);
+				values[count] = value;
+				mean += value;
+				count++;
+			} else {
+				for (int j=0; j<length; j++) {
+					if (buildName.startsWith((String)prefixes.get(j))) {
+						double value = buildResults.getValue(dim_id);
+						values[count] = value;
+						mean += value;
+						count++;
+					}
+				}
+			}
+		}
+	}
+	mean /= count;
+	for (int i=0; i<count; i++) {
+		stddev += Math.pow(values[i] - mean, 2);
+	}
+	stddev = Math.sqrt((stddev / (count - 1)));
+	variation = stddev / mean;
+	return new double[] { count, mean, stddev, variation };
+}
+
+private void initialize() {
+	reset();
+	// Get performance results builds name
+	PerformanceResults perfResults = getPerformance();
+	String baselineBuildName = perfResults.getBaselineName();
+	String baselineBuildDate = baselineBuildName == null ? null : Util.getBuildDate(baselineBuildName);
+	String currentBuildName = perfResults.name;
+	String currentBuildDate = currentBuildName == null ? null : Util.getBuildDate(currentBuildName);
+
+	// Set baseline and current builds
+	BuildResults lastBaseline = null;
+	int size = size();
+	if (size == 0) return;
+	for (int i=0; i<size; i++) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		if (buildResults.values != null) {
+			buildResults.cleanValues();
+		}
+		if (buildResults.isBaseline()) {
+			if (lastBaseline == null || baselineBuildDate == null || baselineBuildDate.compareTo(buildResults.getDate()) >= 0) {
+				lastBaseline = buildResults;
+			}
+			if (baselineBuildName != null && buildResults.getName().equals(baselineBuildName)) {
+				this.baseline = buildResults;
+				this.baselined = true;
+			}
+		} else if (currentBuildName == null || currentBuildDate == null || (this.current == null && buildResults.getDate().compareTo(currentBuildDate) >= 0)) {
+			this.current = buildResults;
+			this.valid = true;
+		}
+	}
+	if (this.baseline == null) {
+		this.baseline = (lastBaseline == null) ? (BuildResults) this.children.get(0) : lastBaseline;
+	}
+	if (this.current == null) {
+		int idx = size() - 1;
+		BuildResults previous = (BuildResults) this.children.get(idx--);
+		while (idx >= 0 && previous.isBaseline()) {
+			previous = (BuildResults) this.children.get(idx--);
+		}
+		this.current = previous;
+	}
+
+	// Set delta between current vs. baseline and the corresponding error
+	int dim_id = DB_Results.getDefaultDimension().getId();
+	double baselineValue = this.baseline.getValue();
+	double currentValue = this.current.getValue();
+	this.delta = (currentValue - baselineValue) / baselineValue;
+	if (Double.isNaN(this.delta)) {
+		this.error = Double.NaN;
+	} else {
+		long baselineCount = this.baseline.getCount(dim_id);
+		long currentCount = this.current.getCount(dim_id);
+		if (baselineCount == 1 || currentCount == 1) {
+			this.error = Double.NaN;
+		} else {
+			double baselineError = this.baseline.getError(dim_id);
+			double currentError = this.current.getError(dim_id);
+			this.error = Double.isNaN(baselineError)
+					? currentError / baselineValue
+					: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
+		}
+	}
+
+	// Set the failure on the current build if necessary
+	int failure_threshold = getPerformance().failure_threshold;
+	if (this.delta >= (failure_threshold/100.0)) {
+		StringBuffer buffer = new StringBuffer("Performance criteria not met when compared to '"); //$NON-NLS-1$
+		buffer.append(this.baseline.getName());
+		buffer.append("': "); //$NON-NLS-1$
+		buffer.append(DB_Results.getDefaultDimension().getName());
+		buffer.append("= "); //$NON-NLS-1$
+		buffer.append(Util.timeString((long)this.current.getValue()));
+		buffer.append(" is not within [0%, "); //$NON-NLS-1$
+		buffer.append(100+failure_threshold);
+		buffer.append("'%] of "); //$NON-NLS-1$
+		buffer.append(Util.timeString((long)this.baseline.getValue()));
+		this.current.setFailure(buffer.toString());
+	}
+}
+
+/**
+ * Returns whether the configuration has results for the performance
+ * baseline build or not.
+ *
+ * @return <code>true</code> if the configuration has results
+ * 	for the performance baseline build, <code>false</code> otherwise.
+ */
+public boolean isBaselined() {
+	if (this.baseline == null || this.current == null) {
+	    initialize();
+    }
+	return this.baselined;
+}
+
+boolean isBuildConcerned(BuildResults buildResults) {
+	String buildDate = buildResults.getDate();
+	String currentBuildDate = getCurrentBuildResults() == null ? null : getCurrentBuildResults().getDate();
+	String baselineBuildDate = getBaselineBuildResults() == null ? null : getBaselineBuildResults().getDate();
+	return ((currentBuildDate == null || buildDate.compareTo(currentBuildDate) <= 0) &&
+		(baselineBuildDate == null || buildDate.compareTo(baselineBuildDate) <= 0));
+}
+/**
+ * Returns whether the configuration has results for the performance
+ * current build or not.
+ *
+ * @return <code>true</code> if the configuration has results
+ * 	for the performance current build, <code>false</code> otherwise.
+ */
+public boolean isValid() {
+	if (this.baseline == null || this.current == null) {
+	    initialize();
+    }
+	return this.valid;
+}
+
+/**
+ * Returns the 'n' last nightly build names.
+ *
+ * @param n Number of last nightly builds to return
+ * @return Last n nightly build names preceding current.
+ */
+public List lastNightlyBuildNames(int n) {
+	List labels = new ArrayList();
+	for (int i=size()-2; i>=0; i--) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		if (isBuildConcerned(buildResults)) {
+			String buildName = buildResults.getName();
+			if (buildName.startsWith("N")) { //$NON-NLS-1$
+				labels.add(buildName);
+				if (labels.size() >= n) {
+	                break;
+                }
+			}
+		}
+	}
+	return labels;
+}
+
+/*
+ * Read all configuration builds results data from the given stream.
+ */
+void readData(DataInputStream stream) throws IOException {
+	int size = stream.readInt();
+	for (int i=0; i<size; i++) {
+		BuildResults buildResults = new BuildResults(this);
+		buildResults.readData(stream);
+		String lastBuildName = getPerformance().lastBuildName;
+		if (lastBuildName == null || buildResults.getDate().compareTo(Util.getBuildDate(lastBuildName)) <= 0) {
+			addChild(buildResults, true);
+		}
+	}
+}
+
+private void reset() {
+	this.current = null;
+	this.baseline = null;
+	this.baselined = false;
+	this.valid = false;
+	this.delta = 0;
+	this.error = -1;
+}
+
+/*
+ * Set the configuration value from database information
+ */
+void setInfos(int build_id, int summaryKind, String comment) {
+	BuildResults buildResults = (BuildResults) getResults(build_id);
+	if (buildResults == null) {
+		buildResults = new BuildResults(this, build_id);
+		addChild(buildResults, true);
+	}
+	buildResults.summaryKind = summaryKind;
+	buildResults.comment = comment;
+}
+
+/*
+ * Set the configuration value from database information
+ */
+void setValue(int build_id, int dim_id, int step, long value) {
+//	reset();
+	BuildResults buildResults = (BuildResults) getResults(build_id);
+	if (buildResults == null) {
+		buildResults = new BuildResults(this, build_id);
+		addChild(buildResults, true);
+	}
+	buildResults.setValue(dim_id, step, value);
+}
+
+/*
+ * Write all configuration builds results data into the given stream.
+ */
+void write(DataOutputStream stream) throws IOException {
+	int size = size();
+	stream.writeInt(this.id);
+	stream.writeInt(size);
+	for (int i=0; i<size; i++) {
+		BuildResults buildResults = (BuildResults) this.children.get(i);
+		buildResults.write(stream);
+	}
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/DB_Results.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/DB_Results.java
new file mode 100644
index 0000000..b686c11
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/DB_Results.java
@@ -0,0 +1,1300 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.db;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.eclipse.test.internal.performance.PerformanceTestPlugin;
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.db.DB;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.test.performance.Dimension;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Specific and private implementation of {@link org.eclipse.test.internal.performance.db.DB} class
+ * to get massive results from performance results database.
+ * TODO (frederic) Should be at least a subclass of {@link DB}...
+ */
+public class DB_Results {
+
+
+	private static final String DEFAULT_DB_BASELINE_PREFIX = "R-";
+	private static final Dim[] NO_DIMENSION = new Dim[0];
+	private static final String[] EMPTY_LIST = new String[0];
+	static final boolean DEBUG = false;
+    static final boolean LOG = false;
+
+    // the two supported DB types
+    private static final String DERBY= "derby"; //$NON-NLS-1$
+    private static final String CLOUDSCAPE= "cloudscape"; //$NON-NLS-1$
+
+    private static DB_Results fgDefault;
+
+    private Connection fConnection;
+    private SQL_Results fSQL;
+//    private boolean fIsEmbedded;
+    private String fDBType;	// either "derby" or "cloudscape"
+
+	    // Preferences info
+    public static boolean DB_CONNECTION = false;
+    private static String DB_NAME;
+    private static String DB_LOCATION;
+	private static String DB_BASELINE_PREFIX = DEFAULT_DB_BASELINE_PREFIX;
+	private static String DB_VERSION;
+	private static String DB_VERSION_REF;
+
+	/**
+	 * Get the name of the database.
+	 *
+	 * @return The name as a string.
+	 */
+    public static String getDbName() {
+    	if (DB_NAME == null) initDbContants();
+    	return DB_NAME;
+    }
+
+	/**
+	 * Set the name of the database.
+	 *
+	 * @param dbName The name as a string.
+	 */
+    public static void setDbName(String dbName) {
+    	Assert.isNotNull(dbName);
+    	DB_NAME = dbName;
+    }
+
+	/**
+	 * Get the location of the database.
+	 *
+	 * @return The location as a string.
+	 */
+    public static String getDbLocation() {
+    	if (!DB_CONNECTION) return null;
+    	if (DB_LOCATION == null) initDbContants();
+    	return DB_LOCATION;
+    }
+
+	/**
+	 * Set the location of the database.
+	 *
+	 * @param dbLocation The location as a string.
+	 */
+    public static void setDbLocation(String dbLocation) {
+    	Assert.isNotNull(dbLocation);
+    	DB_LOCATION = dbLocation;
+    }
+
+	/**
+	 * Get the default baseline prefix.
+	 *
+	 * @return The prefix as a string.
+	 */
+    public static String getDbBaselinePrefix() {
+    	return DB_BASELINE_PREFIX;
+    }
+
+	/**
+	 * Set the baseline prefix of the database.
+	 *
+	 * @param baselinePrefix The prefix as a string.
+	 */
+    public static void setDbDefaultBaselinePrefix(String baselinePrefix) {
+    	Assert.isNotNull(baselinePrefix);
+    	Assert.isTrue(baselinePrefix.startsWith(DEFAULT_DB_BASELINE_PREFIX));
+    	DB_BASELINE_PREFIX = baselinePrefix;
+    }
+
+	/**
+	 * Get the baseline reference version of the database.
+	 *
+	 * @return The version as a string.
+	 */
+    public static String getDbBaselineRefVersion() {
+    	if (DB_VERSION_REF == null) initDbContants();
+    	return DB_VERSION_REF;
+    }
+
+	/**
+	 * Get the version of the database.
+	 *
+	 * @return The version as a string.
+	 */
+    public static String getDbVersion() {
+    	if (DB_VERSION == null) initDbContants();
+    	return DB_VERSION;
+    }
+
+	/**
+	 * Set the version of the database.
+	 *
+	 * @param version The version as a string.
+	 */
+    public static void setDbVersion(String version) {
+    	Assert.isNotNull(version);
+    	Assert.isTrue(version.startsWith("v3"));
+    	DB_VERSION = version;
+    }
+
+	/**
+	 * Update the database constants from a new database location.
+	 * @param connected Tells whether the database should be connected or not.
+	 * @param databaseLocation The database location.
+	 * 	May be a path to a local folder or a net address
+	 * 	(see {@link IPerformancesConstants#NETWORK_DATABASE_LOCATION}).
+	 */
+	public static boolean updateDbConstants(boolean connected, int eclipseVersion, String databaseLocation) {
+		if (DB_CONNECTION != connected || DB_LOCATION == null || DB_NAME == null ||
+			((databaseLocation == null && !DB_LOCATION.equals(IPerformancesConstants.NETWORK_DATABASE_LOCATION)) ||
+					!DB_LOCATION.equals(databaseLocation)) ||
+			!DB_NAME.equals(IPerformancesConstants.DATABASE_NAME_PREFIX + eclipseVersion)) {
+			shutdown();
+			DB_CONNECTION = connected;
+			DB_LOCATION = databaseLocation == null ? IPerformancesConstants.NETWORK_DATABASE_LOCATION : databaseLocation;
+			DB_NAME = IPerformancesConstants.DATABASE_NAME_PREFIX + eclipseVersion;
+			DB_VERSION = "v" + eclipseVersion;
+			DB_VERSION_REF = "R-3." + (eclipseVersion % 10 - 1);
+			if (connected) {
+				return getDefault().fSQL != null;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Returns a title including DB version and name.
+	 *
+	 * @return A title as a string.
+	 */
+	public static String getDbTitle() {
+    	if (!DB_CONNECTION) return null;
+		String title = "Eclipse " + DB_VERSION + " - ";
+		if (DB_LOCATION.startsWith("net:")) {
+			title += " Network DB";
+		} else {
+			title += " Local DB";
+		}
+		return title;
+	}
+
+	/**
+	 * The list of all the configurations (i.e. machine) stored in the database.
+	 */
+	private static String[] CONFIGS;
+
+	/**
+	 * The list of all the components stored in the database.
+	 */
+	private static String[] COMPONENTS;
+
+	/**
+	 * The list of all the builds stored in the database.
+	 */
+	private static String[] BUILDS;
+
+	/**
+	 * The list of all the dimensions stored in the database.
+	 */
+	private static int[] DIMENSIONS;
+
+	/**
+	 * The default dimension used to display results (typically in fingerprints).
+	 */
+	private static Dim DEFAULT_DIM;
+	private static int DEFAULT_DIM_INDEX;
+
+	/**
+	 * The list of all the dimensions displayed while generating results.
+	 */
+	private static Dim[] RESULTS_DIMENSIONS;
+
+	/**
+	 * The list of all the VMs stored in the database.
+	 */
+	private static String[] VMS;
+
+	/**
+	 * The list of possible test boxes.
+	 * <p>
+	 * Only used if no specific configurations are specified
+	 * (see {@link PerformanceResults#readAll(String, String[][], String, File, int, org.eclipse.core.runtime.IProgressMonitor)}.
+	 * </p>
+	 * Note that this is a copy of the the property "eclipse.perf.config.descriptors"
+	 * defined in org.eclipse.releng.eclipsebuilder/eclipse/helper.xml file
+	 */
+	private static String[] CONFIG_DESCRIPTIONS;
+
+	/**
+	 * The list of known Eclipse components.
+	 */
+	private final static String[] ECLIPSE_COMPONENTS = {
+		"org.eclipse.ant", //$NON-NLS-1$
+		"org.eclipse.compare", //$NON-NLS-1$
+		"org.eclipse.core", //$NON-NLS-1$
+		"org.eclipse.help", //$NON-NLS-1$
+		"org.eclipse.jdt.core", //$NON-NLS-1$
+		"org.eclipse.jdt.debug", //$NON-NLS-1$
+		"org.eclipse.jdt.text", //$NON-NLS-1$
+		"org.eclipse.jdt.ui", //$NON-NLS-1$
+		"org.eclipse.jface", //$NON-NLS-1$
+		"org.eclipse.osgi", //$NON-NLS-1$
+		"org.eclipse.pde.api.tools", //$NON-NLS-1$
+		"org.eclipse.pde.ui", //$NON-NLS-1$
+		"org.eclipse.swt", //$NON-NLS-1$
+		"org.eclipse.team", //$NON-NLS-1$
+		"org.eclipse.ua", //$NON-NLS-1$
+		"org.eclipse.ui" //$NON-NLS-1$
+	};
+	private static String[] KNOWN_COMPONENTS = ECLIPSE_COMPONENTS;
+
+
+	    // Store debug info
+	final static StringWriter DEBUG_STR_WRITER;
+	final static PrintWriter DEBUG_WRITER;
+	static {
+		if (DEBUG) {
+			DEBUG_STR_WRITER= new StringWriter();
+			DEBUG_WRITER= new PrintWriter(DEBUG_STR_WRITER);
+		} else {
+			DEBUG_STR_WRITER= null;
+			DEBUG_WRITER= null;
+		}
+	}
+
+    // Store log info
+    final static StringWriter LOG_STR_WRITER = new StringWriter();
+    final static LogWriter LOG_WRITER = new LogWriter();
+    static class LogWriter extends PrintWriter {
+		long[] starts = new long[10];
+		long[] times = new long[10];
+    	StringBuffer[] buffers = new StringBuffer[10];
+    	int depth = -1, max = -1;
+    	public LogWriter() {
+	        super(LOG_STR_WRITER);
+        }
+		void starts(String log) {
+    		if (++this.depth >= this.buffers.length) {
+    			System.arraycopy(this.times, 0, this.times = new long[this.depth+10], 0, this.depth);
+    			System.arraycopy(this.buffers, 0, this.buffers= new StringBuffer[this.depth+10], 0, this.depth);
+    		}
+    		StringBuffer buffer = this.buffers[this.depth];
+    		if (this.buffers[this.depth] == null) buffer = this.buffers[this.depth] = new StringBuffer();
+    		buffer.append(log);
+    		this.starts[this.depth] = System.currentTimeMillis();
+    		if (this.depth > this.max) this.max = this.depth;
+    	}
+		void ends(String log) {
+			if (this.depth < 0)
+				throw new RuntimeException("Invalid call to ends (missing corresponding starts call)!"); //$NON-NLS-1$
+    		this.buffers[this.depth].append(log);
+    		if (this.depth > 0) {
+    			this.times[this.depth] += System.currentTimeMillis() - this.starts[this.depth];
+    			this.depth--;
+    			return;
+    		}
+    		for (int i=0; i<this.max; i++) {
+	    		print(this.buffers[i].toString());
+	    		print(" ( in "); //$NON-NLS-1$
+	    		print(this.times[this.depth]);
+    			println("ms)"); //$NON-NLS-1$
+    		}
+    		this.depth = this.max = -1;
+			this.starts = new long[10];
+			this.times = new long[10];
+    		this.buffers = new StringBuffer[10];
+    	}
+		public String toString() {
+	        return LOG_STR_WRITER.toString();
+        }
+    }
+
+	// Data storage from queries
+	static String LAST_CURRENT_BUILD, LAST_BASELINE_BUILD;
+	private static int BUILDS_LENGTH;
+	private static String[] SCENARII;
+	private static String[] COMMENTS;
+
+    //---- private implementation
+
+	/**
+     * Private constructor to block instance creation.
+     */
+    private DB_Results() {
+    	// empty implementation
+    }
+
+    synchronized static DB_Results getDefault() {
+        if (fgDefault == null) {
+            fgDefault= new DB_Results();
+            fgDefault.connect();
+            if (PerformanceTestPlugin.getDefault() == null) {
+            	// not started as plugin
+	            Runtime.getRuntime().addShutdownHook(
+	                new Thread() {
+	                    public void run() {
+	                    	shutdown();
+	                    }
+	                }
+	            );
+            }
+        } else if (fgDefault.fSQL == null) {
+        	fgDefault.connect();
+        }
+        return fgDefault;
+    }
+
+    public static void shutdown() {
+        if (fgDefault != null) {
+            fgDefault.disconnect();
+            fgDefault= null;
+            BUILDS = null;
+            LAST_BASELINE_BUILD = null;
+            LAST_CURRENT_BUILD = null;
+            DIMENSIONS = null;
+            CONFIGS = null;
+            COMPONENTS = null;
+            SCENARII = null;
+            COMMENTS = null;
+            DB_VERSION = null;
+            DB_VERSION_REF = null;
+            DEFAULT_DIM =null;
+            DEFAULT_DIM_INDEX = -1;
+            RESULTS_DIMENSIONS = null;
+            VMS = null;
+            CONFIG_DESCRIPTIONS = null;
+            KNOWN_COMPONENTS = ECLIPSE_COMPONENTS;
+        }
+        if (DEBUG) {
+        	DEBUG_WRITER.println("DB.shutdown"); //$NON-NLS-1$
+        	System.out.println(DEBUG_STR_WRITER.toString());
+        }
+        if (LOG) {
+        	System.out.println(LOG_STR_WRITER.toString());
+        }
+    }
+
+/**
+ * Return the build id from a given name.
+ *
+ * @param name The build name (eg. I20070615-1200)
+ * @return The id of the build (ie. the index in the {@link #BUILDS} list)
+ */
+static int getBuildId(String name) {
+	if (BUILDS == null) return -1;
+	return Arrays.binarySearch(BUILDS, name, Util.BUILD_DATE_COMPARATOR);
+}
+
+/**
+ * Return the build name from a given id.
+ *
+ * @param id The build id
+ * @return The name of the build (eg. I20070615-1200)
+ */
+static String getBuildName(int id) {
+	if (BUILDS == null) return null;
+	return BUILDS[id];
+}
+
+/**
+ * Returns all the builds names read from the database.
+ *
+ * @return The list of all builds names matching the scenario pattern used while reading data
+ */
+public static String[] getBuilds() {
+	if (BUILDS == null) {
+		queryAllVariations("%"); //$NON-NLS-1$
+	}
+	if (BUILDS_LENGTH == 0) return EMPTY_LIST;
+	String[] builds = new String[BUILDS_LENGTH];
+	System.arraycopy(BUILDS, 0, builds, 0, BUILDS_LENGTH);
+	return builds;
+}
+
+/**
+ * Returns the number of builds stored int the database.
+ *
+ * @return The number of builds stored in the database.
+ */
+public static int getBuildsNumber() {
+	if (BUILDS == null) {
+		queryAllVariations("%"); //$NON-NLS-1$
+	}
+	return BUILDS_LENGTH;
+}
+
+/**
+ * Get component name from a scenario.
+ *
+ * @param scenarioName The name of the scenario
+ * @return The component name
+ */
+static String getComponentNameFromScenario(String scenarioName) {
+	int length = KNOWN_COMPONENTS.length;
+	for (int i=0; i<length; i++) {
+		if (scenarioName.startsWith(KNOWN_COMPONENTS[i])) {
+			return KNOWN_COMPONENTS[i];
+		}
+	}
+	StringTokenizer tokenizer = new StringTokenizer(scenarioName, ".");
+	StringBuffer buffer = new StringBuffer(tokenizer.nextToken());
+	if (tokenizer.hasMoreTokens()) {
+		buffer.append('.');
+		buffer.append(tokenizer.nextToken());
+		if (tokenizer.hasMoreTokens()) {
+			buffer.append('.');
+			buffer.append(tokenizer.nextToken());
+		}
+	}
+	String componentName = buffer.toString();
+	System.err.println(scenarioName+" does not belongs to a known Eclipse component. So use scenario prefix "+componentName+" as component name by default and add it to the know components"); //$NON-NLS-1$
+	System.arraycopy(KNOWN_COMPONENTS, 0, KNOWN_COMPONENTS = new String[length+1], 0, length);
+	KNOWN_COMPONENTS[length] = componentName;
+	return componentName;
+}
+
+/**
+ * Get all components read from database.
+ *
+ * @return A list of component names matching the given pattern
+ */
+public static String[] getComponents() {
+	if (COMPONENTS == null) return EMPTY_LIST;
+	int length = COMPONENTS.length;
+	String[] components = new String[length];
+	System.arraycopy(COMPONENTS, 0, components, 0, length);
+	return components;
+}
+
+/**
+ * Return the name of the configuration from the given id.
+ *
+ * @param id The index of the configuration in the stored list.
+ * @return The name of the configuration (eg. eclipseperflnx1_R3.3)
+ */
+static String getConfig(int id) {
+	return CONFIGS[id];
+}
+
+/**
+ * Get all configurations read from the database.
+ *
+ * @return A list of configuration names
+ */
+public static String[] getConfigs() {
+	if (CONFIGS == null) return EMPTY_LIST;
+	int length = CONFIGS.length;
+	String[] configs = new String[length];
+	System.arraycopy(CONFIGS, 0, configs, 0, length);
+	return configs;
+}
+
+/**
+ * Set the default dimension used for performance results.
+ */
+public static void setConfigs(String[] configs) {
+	CONFIGS = configs;
+}
+
+/**
+ * Get all configurations read from the database.
+ *
+ * @return A list of configuration names
+ */
+public static String[] getConfigDescriptions() {
+	if (CONFIG_DESCRIPTIONS == null) {
+		if (CONFIGS == null) return null;
+		int length = CONFIGS.length;
+		CONFIG_DESCRIPTIONS = new String[length];
+		String[][] configDescriptors = PerformanceTestPlugin.getConfigDescriptors();
+		int cdLength = configDescriptors.length;
+		for (int i = 0; i < length; i++) {
+			boolean found = false;
+			for (int j = 0; j < cdLength; j++) {
+				if (configDescriptors[j][0].equals(CONFIGS[i])) {
+			        CONFIG_DESCRIPTIONS[i] = configDescriptors[j][1];
+			        found = true;
+			        break;
+				}
+			}
+			if (!found) {
+				String kind = CONFIGS[i].indexOf("epwin") < 0 ? "Linux" : "Win XP";
+				CONFIG_DESCRIPTIONS[i] = kind+" perf test box "+CONFIGS[i].substring(5);
+			}
+        }
+	}
+	int length = CONFIG_DESCRIPTIONS.length;
+	String[] descriptions = new String[length];
+	System.arraycopy(CONFIG_DESCRIPTIONS, 0, descriptions, 0, length);
+	return descriptions;
+}
+
+/**
+ * Set the default dimension used for performance results.
+ */
+public static void setConfigDescriptions(String[] descriptions) {
+	CONFIG_DESCRIPTIONS = descriptions;
+}
+
+/**
+ * Get all dimensions read from the database.
+ *
+ * @return A list of dimensions.
+ */
+public static Dim[] getDimensions() {
+	if (DIMENSIONS == null) return NO_DIMENSION;
+	int length = DIMENSIONS.length;
+	Dim[] dimensions = new Dim[length];
+	for (int i = 0; i < length; i++) {
+		Dimension dimension = PerformanceTestPlugin.getDimension(DIMENSIONS[i]);
+		if (dimension == null) {
+			throw new RuntimeException("There is an unsupported dimension stored in the database: " +DIMENSIONS[i]);
+		}
+		dimensions[i] = (Dim) dimension;
+    }
+	return dimensions;
+}
+
+/**
+ * Return the default dimension used for performance results.
+ *
+ * @return The {@link Dim default dimension}.
+ */
+public static Dim getDefaultDimension() {
+	if (DEFAULT_DIM == null) {
+		DEFAULT_DIM = (Dim) PerformanceTestPlugin.getDefaultDimension();
+	}
+	return DEFAULT_DIM;
+}
+
+/**
+ * Set the default dimension used for performance results.
+ */
+public static void setDefaultDimension(String dim) {
+	DEFAULT_DIM = (Dim) PerformanceTestPlugin.getDimension(dim);
+	if (DIMENSIONS != null) {
+		DEFAULT_DIM_INDEX = Arrays.binarySearch(DIMENSIONS, DEFAULT_DIM.getId());
+	}
+}
+
+public static Dim[] getResultsDimensions() {
+	if (RESULTS_DIMENSIONS == null) {
+		Dimension[] resultsDimensions = PerformanceTestPlugin.getResultsDimensions();
+		int length = resultsDimensions.length;
+		RESULTS_DIMENSIONS = new Dim[length];
+		for (int i = 0; i < length; i++) {
+			RESULTS_DIMENSIONS[i] = (Dim) resultsDimensions[i];
+		}
+	}
+	return RESULTS_DIMENSIONS;
+}
+
+/**
+ * Set the default dimension used for performance results.
+ */
+public static void setResultsDimensions(String[] dimensions) {
+	int length = dimensions.length;
+	RESULTS_DIMENSIONS = new Dim[length];
+	for (int i = 0; i < length; i++) {
+		RESULTS_DIMENSIONS[i] = (Dim) PerformanceTestPlugin.getDimension(dimensions[i]);
+	}
+}
+
+/**
+ * Return the default dimension used for performance results.
+ *
+ * @return The {@link Dim default dimension}.
+ */
+public static int getDefaultDimensionIndex() {
+	if (DEFAULT_DIM == null || DEFAULT_DIM_INDEX == -1) {
+		getDefaultDimension(); // init default dimension
+		getDimensions(); // init dimensions
+		DEFAULT_DIM_INDEX = Arrays.binarySearch(DIMENSIONS, DEFAULT_DIM.getId());
+	}
+	return DEFAULT_DIM_INDEX;
+}
+
+/**
+ * Return the ID of the last baseline build before the given date.
+ *
+ * @param date The date the baseline must be run before. If <code>null</code>
+ * 	return the last baseline build stored in the DB.
+ *
+ * @return the ID of the last baseline build before the given date or
+ * 	<code>null</code> if none was run before it...
+ */
+public static String getLastBaselineBuild(String date) {
+	if (BUILDS == null) {
+		queryAllVariations("%"); //$NON-NLS-1$
+	}
+	if (date == null) {
+		if (LAST_BASELINE_BUILD == null) {
+			return BUILDS[0];
+		}
+		return LAST_BASELINE_BUILD;
+	}
+	String lastBaselineBuild = null;
+	for (int i=0; i<BUILDS_LENGTH; i++) {
+		String build = BUILDS[i];
+		if (build.startsWith(DB_VERSION_REF)) {
+			String buildDate = build.substring(build.indexOf('_')+1);
+			if (buildDate.compareTo(date) < 0) {
+				if (lastBaselineBuild == null || build.compareTo(lastBaselineBuild) > 0) {
+					lastBaselineBuild = build;
+				}
+			}
+		}
+	}
+	if (lastBaselineBuild == null && BUILDS.length > 0) {
+		return BUILDS[0];
+	}
+	return lastBaselineBuild;
+}
+
+/**
+ * Return the ID of the last baseline build.
+ *
+ * @return the ID of the last baseline build.
+ */
+public static String getLastCurrentBuild() {
+	if (BUILDS == null) {
+		queryAllVariations("%"); //$NON-NLS-1$
+	}
+	return LAST_CURRENT_BUILD;
+}
+
+/**
+ * Returns all the scenarios names read from the database.
+ *
+ * @return The list of all scenarios matching the pattern for a given build.
+ * @see #internalQueryBuildScenarios(String, String)
+ */
+public static List getScenarios() {
+	return Arrays.asList(SCENARII);
+}
+
+/**
+ * Init the constants if necessary.
+ */
+public static void initDbContants() {
+	if (DB_LOCATION == null) {
+		DB_LOCATION = PerformanceTestPlugin.getDBLocation();
+		if (DB_LOCATION == null) {
+			new RuntimeException("Cannot connect to the DB without a location!");
+		}
+	}
+	if (DB_NAME == null) {
+		DB_NAME = PerformanceTestPlugin.getDBName();
+		if (DB_NAME == null) {
+			new RuntimeException("Cannot connect to the DB without a name!");
+		}
+	}
+	if (DB_VERSION == null) {
+		DB_VERSION = "v" + DB_NAME.substring(DB_NAME.length()-2);
+		DB_VERSION_REF = "R-3."+(Character.digit(DB_NAME.charAt(DB_NAME.length()-1), 10)-1);
+	}
+}
+
+/**
+ * Get all scenarios read from database.
+ *
+ * @return A list of all scenario names matching the default pattern
+ */
+public static Map queryAllScenarios() {
+	return getDefault().internalQueryBuildScenarios("%", null); //$NON-NLS-1$
+}
+
+/**
+ * Get all scenarios read from database matching a given pattern.
+ * Note that all scenarios are returned if the pattern is <code>null</code>.
+ *
+ * @param scenarioPattern The pattern of the requested scenarios
+ * @return A map of all scenarios matching the given pattern.
+ * 	The map keys are component names and values are the scenarios list for
+ * 	each component.
+ */
+static Map queryAllScenarios(String scenarioPattern) {
+	String pattern = scenarioPattern==null ? "%" : scenarioPattern; //$NON-NLS-1$
+	return getDefault().internalQueryBuildScenarios(pattern, null);
+}
+
+/**
+ * Get all scenarios read from database matching a given pattern.
+ * Note that all scenarios are returned if the pattern is <code>null</code>.
+ *
+ * @param scenarioPattern The pattern of the requested scenarios
+ * @param buildName The build name
+ * @return A list of scenario names matching the given pattern
+ */
+static Map queryAllScenarios(String scenarioPattern, String buildName) {
+	return getDefault().internalQueryBuildScenarios(scenarioPattern, buildName);
+}
+
+/**
+ * Get all variations read from database matching a given configuration pattern.
+ *
+ * @param configPattern The pattern of the requested configurations
+ */
+static void queryAllVariations(String configPattern) {
+	getDefault().internalQueryAllVariations(configPattern);
+}
+
+/**
+ * Get all summaries from DB for a given scenario and configuration pattern
+ *
+ * @param scenarioResults The scenario results where to store data
+ * @param configPattern The configuration pattern concerned by the query
+ * @param builds All builds to get summaries, if <code>null</code>, then all DB
+ * 	builds will be concerned.
+ */
+static void queryScenarioSummaries(ScenarioResults scenarioResults, String configPattern, String[] builds) {
+	getDefault().internalQueryScenarioSummaries(scenarioResults, configPattern, builds);
+}
+
+/**
+ * Query and store all values for given scenario results
+ *
+ * @param scenarioResults The scenario results where the values has to be put
+ * @param configPattern The pattern of the configuration concerned by the query
+ * @param buildName Name of the last build on which data were stored locally
+ *
+*/
+static void queryScenarioValues(ScenarioResults scenarioResults, String configPattern, String buildName) {
+	getDefault().internalQueryScenarioValues(scenarioResults, configPattern, buildName);
+}
+
+/**
+ * dbloc=						embed in home directory
+ * dbloc=/tmp/performance			embed given location
+ * dbloc=net://localhost			connect to local server
+ * dbloc=net://www.eclipse.org	connect to remove server
+ */
+private void connect() {
+
+	if (this.fConnection != null || !DB_CONNECTION)
+		return;
+
+	if (DEBUG) DriverManager.setLogWriter(new PrintWriter(System.out));
+
+	// Init DB location and name if not already done
+	if (DB_LOCATION == null) {
+		initDbContants();
+	}
+
+	String url = null;
+	java.util.Properties info = new java.util.Properties();
+
+	if (DEBUG) {
+		DEBUG_WRITER.println();
+		DEBUG_WRITER.println("==========================================================="); //$NON-NLS-1$
+		DEBUG_WRITER.println("Database debug information stored while processing"); //$NON-NLS-1$
+	}
+	if (LOG) {
+		LOG_WRITER.println();
+		LOG_WRITER.println("==========================================================="); //$NON-NLS-1$
+		LOG_WRITER.println("Database log information stored while processing"); //$NON-NLS-1$
+	}
+
+	this.fDBType = DERBY; // assume we are using Derby
+	try {
+		if (DB_LOCATION.startsWith("net://")) { //$NON-NLS-1$
+			// remote
+//			fIsEmbedded = false;
+			// connect over network
+			if (DEBUG)
+				DEBUG_WRITER.println("Trying to connect over network..."); //$NON-NLS-1$
+			Class.forName("com.ibm.db2.jcc.DB2Driver"); //$NON-NLS-1$
+			info.put("user", PerformanceTestPlugin.getDBUser()); //$NON-NLS-1$
+			info.put("password", PerformanceTestPlugin.getDBPassword()); //$NON-NLS-1$
+			info.put("retrieveMessagesFromServerOnGetMessage", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+			info.put("create", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+			url = DB_LOCATION + '/' + DB_NAME;
+		} else if (DB_LOCATION.startsWith("//")) { //$NON-NLS-1$
+			// remote
+//			fIsEmbedded = false;
+			// connect over network
+			if (DEBUG)
+				DEBUG_WRITER.println("Trying to connect over network..."); //$NON-NLS-1$
+			Class.forName("org.apache.derby.jdbc.ClientDriver"); //$NON-NLS-1$
+			info.put("user", PerformanceTestPlugin.getDBUser()); //$NON-NLS-1$
+			info.put("password", PerformanceTestPlugin.getDBPassword()); //$NON-NLS-1$
+			info.put("create", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+			url = DB_LOCATION + '/' + DB_NAME;
+		} else {
+			// workaround for Derby issue:
+			// http://nagoya.apache.org/jira/browse/DERBY-1
+			if ("Mac OS X".equals(System.getProperty("os.name"))) //$NON-NLS-1$//$NON-NLS-2$
+				System.setProperty("derby.storage.fileSyncTransactionLog", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+
+			// embedded
+			try {
+				Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); //$NON-NLS-1$
+//				fIsEmbedded = true;
+			} catch (ClassNotFoundException e) {
+				Class.forName("com.ihost.cs.jdbc.CloudscapeDriver"); //$NON-NLS-1$
+				this.fDBType = CLOUDSCAPE;
+			}
+			if (DEBUG)
+				DEBUG_WRITER.println("Loaded embedded " + this.fDBType); //$NON-NLS-1$
+			File f;
+			if (DB_LOCATION.length() == 0) {
+				String user_home = System.getProperty("user.home"); //$NON-NLS-1$
+				if (user_home == null)
+					return;
+				f = new File(user_home, this.fDBType);
+			} else
+				f = new File(DB_LOCATION);
+			url = new File(f, DB_NAME).getAbsolutePath();
+			info.put("user", PerformanceTestPlugin.getDBUser()); //$NON-NLS-1$
+			info.put("password", PerformanceTestPlugin.getDBPassword()); //$NON-NLS-1$
+			info.put("create", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		try {
+			this.fConnection = DriverManager.getConnection("jdbc:" + this.fDBType + ":" + url, info); //$NON-NLS-1$ //$NON-NLS-2$
+		} catch (SQLException e) {
+			if ("08001".equals(e.getSQLState()) && DERBY.equals(this.fDBType)) { //$NON-NLS-1$
+				if (DEBUG)
+					DEBUG_WRITER.println("DriverManager.getConnection failed; retrying for cloudscape"); //$NON-NLS-1$
+				// try Cloudscape
+				this.fDBType = CLOUDSCAPE;
+				this.fConnection = DriverManager.getConnection("jdbc:" + this.fDBType + ":" + url, info); //$NON-NLS-1$ //$NON-NLS-2$
+			} else
+				throw e;
+		}
+		if (DEBUG)
+			DEBUG_WRITER.println("connect succeeded!"); //$NON-NLS-1$
+
+		this.fConnection.setAutoCommit(false);
+		this.fSQL = new SQL_Results(this.fConnection);
+		this.fConnection.commit();
+
+	} catch (SQLException ex) {
+		PerformanceTestPlugin.logError(ex.getMessage());
+
+	} catch (ClassNotFoundException e) {
+		PerformanceTestPlugin.log(e);
+	}
+}
+
+private void disconnect() {
+	if (DEBUG)
+		DEBUG_WRITER.println("disconnecting from DB"); //$NON-NLS-1$
+	if (this.fSQL != null) {
+		try {
+			this.fSQL.dispose();
+		} catch (SQLException e1) {
+			PerformanceTestPlugin.log(e1);
+		}
+		this.fSQL = null;
+	}
+	if (this.fConnection != null) {
+		try {
+			this.fConnection.commit();
+		} catch (SQLException e) {
+			PerformanceTestPlugin.log(e);
+		}
+		try {
+			this.fConnection.close();
+		} catch (SQLException e) {
+			PerformanceTestPlugin.log(e);
+		}
+		this.fConnection = null;
+	}
+
+	/*
+	if (fIsEmbedded) {
+		try {
+			DriverManager.getConnection("jdbc:" + fDBType + ":;shutdown=true"); //$NON-NLS-1$ //$NON-NLS-2$
+		} catch (SQLException e) {
+			String message = e.getMessage();
+			if (message.indexOf("system shutdown.") < 0) //$NON-NLS-1$
+				e.printStackTrace();
+		}
+	}
+	*/
+}
+
+/*
+ * Return the index of the given configuration in the stored list.
+ */
+private int getConfigId(String config) {
+	if (CONFIGS == null) return -1;
+	return Arrays.binarySearch(CONFIGS, config);
+}
+
+SQL_Results getSQL() {
+    return this.fSQL;
+}
+
+/*
+ * Query all comments from database
+ */
+private void internalQueryAllComments() {
+	if (this.fSQL == null) return;
+	if (COMMENTS != null) return;
+	long start = System.currentTimeMillis();
+	if (DEBUG) DEBUG_WRITER.print("		[DB query all comments..."); //$NON-NLS-1$
+	ResultSet result = null;
+	try {
+		String[] comments = null;
+		result = this.fSQL.queryAllComments();
+		while (result.next()) {
+			int commentID = result.getInt(1);
+			// Ignore kind as there's only one
+			// int commentKind = result.getInt(2);
+			String comment = result.getString(3);
+			if (comments == null) {
+				comments = new String[commentID+10];
+			} else if (commentID >= comments.length) {
+				int length = comments.length;
+				System.arraycopy(comments, 0, comments = new String[commentID+10], 0, length);
+			}
+			comments[commentID] = comment;
+		}
+		COMMENTS = comments;
+	} catch (SQLException e) {
+		PerformanceTestPlugin.log(e);
+	} finally {
+		if (result != null) {
+			try {
+				result.close();
+			} catch (SQLException e1) {
+				// ignored
+			}
+		}
+		if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+}
+
+/*
+ * Query all variations. This method stores all config and build names.
+ */
+private void internalQueryAllVariations(String configPattern) {
+	if (this.fSQL == null) return;
+	if (BUILDS != null) return;
+	long start = System.currentTimeMillis();
+	if (DEBUG) {
+		DEBUG_WRITER.print("	- DB query all variations for configuration pattern: "+configPattern); //$NON-NLS-1$
+		DEBUG_WRITER.print("..."); //$NON-NLS-1$
+	}
+	ResultSet result = null;
+	try {
+		CONFIGS = null;
+		BUILDS = null;
+		BUILDS_LENGTH = 0;
+		result = this.fSQL.queryAllVariations(configPattern);
+		while (result.next()) {
+			String variation = result.getString(1); //  something like "||build=I20070615-1200||config=eclipseperfwin2_R3.3||jvm=sun|"
+			StringTokenizer tokenizer = new StringTokenizer(variation, "=|"); //$NON-NLS-1$
+			tokenizer.nextToken(); 												// 'build'
+			storeBuildName(tokenizer.nextToken());				// 'I20070615-1200'
+			tokenizer.nextToken();												// 'config'
+			storeConfig(tokenizer.nextToken()); 	// 'eclipseperfwin2_R3.3'
+			tokenizer.nextToken();												// 'jvm'
+			storeVm(tokenizer.nextToken());					// 'sun'
+		}
+		if (BUILDS_LENGTH == 0) {
+			BUILDS = EMPTY_LIST;
+		}
+	} catch (SQLException e) {
+		PerformanceTestPlugin.log(e);
+	} finally {
+		if (result != null) {
+			try {
+				result.close();
+			} catch (SQLException e1) {
+				// ignored
+			}
+		}
+		if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+}
+
+private Map internalQueryBuildScenarios(String scenarioPattern, String buildName) {
+	if (this.fSQL == null) return null;
+	long start = System.currentTimeMillis();
+	if (DEBUG) {
+		DEBUG_WRITER.print("	- DB query all scenarios"); //$NON-NLS-1$
+		if (scenarioPattern != null) DEBUG_WRITER.print(" with pattern "+scenarioPattern); //$NON-NLS-1$
+		if (buildName != null) DEBUG_WRITER.print(" for build: "+buildName); //$NON-NLS-1$
+	}
+	ResultSet result = null;
+	Map allScenarios = new HashMap();
+	try {
+		if (buildName == null) {
+			result = this.fSQL.queryBuildAllScenarios(scenarioPattern);
+		} else {
+			result = this.fSQL.queryBuildScenarios(scenarioPattern, buildName);
+		}
+		int previousId = -1;
+		List scenarios = null;
+		List scenariosNames = new ArrayList();
+		while (result.next()) {
+			int id = result.getInt(1);
+			String name = result.getString(2);
+			scenariosNames.add(name);
+			String shortName = result.getString(3);
+			int component_id = storeComponent(getComponentNameFromScenario(name));
+			if (component_id != previousId) {
+				allScenarios.put(COMPONENTS[component_id], scenarios = new ArrayList());
+				previousId = component_id;
+			}
+			scenarios.add(new ScenarioResults(id, name, shortName));
+		}
+		SCENARII = new String[scenariosNames.size()];
+		scenariosNames.toArray(SCENARII);
+	} catch (SQLException e) {
+		PerformanceTestPlugin.log(e);
+	} finally {
+		if (result != null) {
+			try {
+				result.close();
+			} catch (SQLException e1) { // ignored
+			}
+		}
+		if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+	return allScenarios;
+}
+
+private void internalQueryScenarioValues(ScenarioResults scenarioResults, String configPattern, String buildName) {
+	if (this.fSQL == null) return;
+	if (DEBUG) {
+		DEBUG_WRITER.print("	- DB query all data points for config pattern: "+configPattern+" for scenario: " + scenarioResults.getShortName()); //$NON-NLS-1$ //$NON-NLS-2$
+		if (buildName != null) DEBUG_WRITER.print(" for build: "+buildName); //$NON-NLS-1$
+	}
+	internalQueryAllVariations(configPattern); // need to read all variations to have all build names
+	ResultSet result = null;
+	try {
+		int count = 0;
+
+		result = buildName == null
+			?	this.fSQL.queryScenarioDataPoints(configPattern, scenarioResults.getId())
+			:	this.fSQL.queryScenarioBuildDataPoints(configPattern, scenarioResults.getId(), buildName);
+		while (result.next()) {
+			int dp_id = result.getInt(1);
+			int step = result.getInt(2);
+			String variation = result.getString(3); //  something like "|build=I20070615-1200||config=eclipseperfwin2_R3.3||jvm=sun|"
+			StringTokenizer tokenizer = new StringTokenizer(variation, "=|"); //$NON-NLS-1$
+			tokenizer.nextToken(); 													// 'build'
+			int build_id = getBuildId(tokenizer.nextToken());		// 'I20070615-1200'
+			tokenizer.nextToken();													// 'config'
+			int config_id = getConfigId(tokenizer.nextToken()); 		// 'eclipseperflnx3'
+			ResultSet rs2 = this.fSQL.queryDimScalars(dp_id);
+			while (rs2.next()) {
+				int dim_id = rs2.getInt(1);
+				storeDimension(dim_id);
+				BigDecimal decimalValue = rs2.getBigDecimal(2);
+				long value = decimalValue.longValue();
+				if (build_id >= 0) { // build id may be negative (i.e. not stored in the array) if new run starts while we're getting results
+					scenarioResults.setValue(build_id, dim_id, config_id, step, value);
+				}
+				count= count + 1;
+			}
+		}
+		if (LOG) LOG_WRITER.ends("		-> " + count + " values read");  //$NON-NLS-1$ //$NON-NLS-2$
+	} catch (SQLException e) {
+		PerformanceTestPlugin.log(e);
+	} finally {
+		if (result != null) {
+			try {
+				result.close();
+			} catch (SQLException e1) {
+				// ignored
+			}
+		}
+	}
+}
+
+private void internalQueryScenarioSummaries(ScenarioResults scenarioResults, String config, String[] builds) {
+	if (this.fSQL == null) return;
+	long start = System.currentTimeMillis();
+	if (DEBUG) {
+		DEBUG_WRITER.print("	- DB query all summaries for scenario '"+scenarioResults.getShortName()+"' of '"+config+"' config"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+	internalQueryAllComments();
+	ResultSet result = null;
+	try {
+		int scenarioID = scenarioResults.getId();
+		// First try to get summaries of elapsed process dimension
+		result = this.fSQL.queryScenarioSummaries(scenarioID, config, builds);
+		while (result.next()) {
+			String variation = result.getString(1); //  something like "|build=I20070615-1200||config=eclipseperfwin2_R3.3||jvm=sun|"
+			int summaryKind = result.getShort(2);
+			int comment_id = result.getInt(3);
+			int dim_id = result.getInt(4);
+			if (dim_id != 0) storeDimension(dim_id);
+			StringTokenizer tokenizer = new StringTokenizer(variation, "=|"); //$NON-NLS-1$
+			tokenizer.nextToken(); 													// 'build'
+			String buildName = tokenizer.nextToken();					// 'I20070615-1200'
+			tokenizer.nextToken();													// 'config'
+			int config_id = getConfigId(tokenizer.nextToken()); 		// 'eclipseperflnx3'
+			int build_id = getBuildId(buildName);
+			if (build_id >= 0) {
+				scenarioResults.setInfos(config_id, build_id, dim_id==0?-1:summaryKind, COMMENTS[comment_id]);
+			}
+		}
+	} catch (SQLException e) {
+		PerformanceTestPlugin.log(e);
+	} finally {
+		if (result != null) {
+			try {
+				result.close();
+			} catch (SQLException e1) {
+				// ignored
+			}
+		}
+		if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+}
+
+/*
+ * Store a build name in the dynamic list.
+ * The list is sorted alphabetically.
+ */
+private int storeBuildName(String build) {
+	boolean isVersion = build.startsWith(DB_BASELINE_PREFIX);
+	if (BUILDS == null) {
+		BUILDS = new String[1];
+		BUILDS[BUILDS_LENGTH++] = build;
+		if (isVersion) {
+			LAST_BASELINE_BUILD = build;
+		} else {
+			LAST_CURRENT_BUILD = build;
+		}
+		return 0;
+	}
+	int idx = Arrays.binarySearch(BUILDS, build, Util.BUILD_DATE_COMPARATOR);
+	if (idx >= 0) return idx;
+	int index = -idx-1;
+	int length = BUILDS.length;
+	if (BUILDS_LENGTH == length) {
+		String[] array = new String[length+1];
+		if (index > 0) System.arraycopy(BUILDS, 0, array, 0, index);
+		array[index] = build;
+		if (index < length) {
+			System.arraycopy(BUILDS, index, array, index+1, length-index);
+		}
+		BUILDS = array;
+	}
+	BUILDS_LENGTH++;
+	if (isVersion) {
+		if (LAST_BASELINE_BUILD == null || LAST_CURRENT_BUILD == null) {
+			LAST_BASELINE_BUILD = build;
+		} else {
+			String buildDate = LAST_CURRENT_BUILD.substring(1, 9)+LAST_CURRENT_BUILD.substring(10, LAST_CURRENT_BUILD.length());
+			String baselineDate = LAST_BASELINE_BUILD.substring(LAST_BASELINE_BUILD.indexOf('_')+1);
+			if (build.compareTo(LAST_BASELINE_BUILD) > 0 && baselineDate.compareTo(buildDate) < 0) {
+				LAST_BASELINE_BUILD = build;
+			}
+		}
+	} else {
+		if (LAST_CURRENT_BUILD == null || build.substring(1).compareTo(LAST_CURRENT_BUILD.substring(1)) >= 0) {
+			LAST_CURRENT_BUILD = build;
+		}
+	}
+	return index;
+}
+
+/*
+ * Store a configuration in the dynamic list.
+ * The list is sorted alphabetically.
+ */
+private int storeConfig(String config) {
+	if (CONFIGS== null) {
+		CONFIGS= new String[1];
+		CONFIGS[0] = config;
+		return 0;
+	}
+	int idx = Arrays.binarySearch(CONFIGS, config);
+	if (idx >= 0) return idx;
+	int length = CONFIGS.length;
+	System.arraycopy(CONFIGS, 0, CONFIGS = new String[length+1], 0, length);
+	CONFIGS[length] = config;
+	Arrays.sort(CONFIGS);
+	return length;
+}
+
+/*
+ * Store a component in the dynamic list. The list is sorted alphabetically.
+ * Note that the array is rebuilt each time a new component is discovered
+ * as this does not happen so often (e.g. eclipse only has 10 components).
+ */
+private int storeComponent(String component) {
+	if (COMPONENTS== null) {
+		COMPONENTS= new String[1];
+		COMPONENTS[0] = component;
+		return 0;
+	}
+	int idx = Arrays.binarySearch(COMPONENTS, component);
+	if (idx >= 0) return idx;
+	int length = COMPONENTS.length;
+	System.arraycopy(COMPONENTS, 0, COMPONENTS = new String[length+1], 0, length);
+	COMPONENTS[length] = component;
+	Arrays.sort(COMPONENTS);
+	return length;
+}
+
+/*
+ * Store a dimension in the dynamic list. The list is sorted in ascending order.
+ * Note that the array is rebuilt each time a new dimension is discovered
+ * as this does not happen so often (e.g. eclipse only stores two dimensions).
+ */
+public static int storeDimension(int id) {
+	if (DIMENSIONS == null) {
+		DIMENSIONS = new int[1];
+		DIMENSIONS[0] = id;
+		return 0;
+	}
+	int idx = Arrays.binarySearch(DIMENSIONS, id);
+	if (idx >= 0) return idx;
+	int length = DIMENSIONS.length;
+	System.arraycopy(DIMENSIONS, 0, DIMENSIONS = new int[length+1], 0, length);
+	DIMENSIONS[length] = id;
+	Arrays.sort(DIMENSIONS);
+	return length;
+}
+
+/*
+ * Store a dimension in the dynamic list. The list is sorted alphabetically.
+ * Note that the array is rebuilt each time a new dimension is discovered
+ * as this does not happen so often (e.g. eclipse only stores two dimensions).
+ */
+private int storeVm(String vm) {
+	if (VMS == null) {
+		VMS = new String[1];
+		VMS[0] = vm;
+		return 0;
+	}
+	int idx = Arrays.binarySearch(VMS, vm);
+	if (idx >= 0) return idx;
+	int length = VMS.length;
+	System.arraycopy(VMS, 0, VMS = new String[length+1], 0, length);
+	VMS[length] = vm;
+	Arrays.sort(VMS);
+	return length;
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/PerformanceResults.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/PerformanceResults.java
new file mode 100644
index 0000000..74bcb21
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/PerformanceResults.java
@@ -0,0 +1,969 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.db;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.*;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+
+/**
+ * Root class to handle performance results.
+ *
+ * Usually performance results are built for a current build vs. a baseline build.
+ *
+ * This class allow to read all data from releng performance database for given
+ * configurations and scenario pattern.
+ *
+ * Then it provides easy and speedy access to all stored results.
+ */
+public class PerformanceResults extends AbstractResults {
+
+	String[] allBuildNames = null;
+	Map allScenarios;
+	String lastBuildName; // Name of the last used build
+	String baselineName; // Name of the baseline build used for comparison
+	String baselinePrefix;
+	private String scenarioPattern = "%"; //$NON-NLS-1$
+	private String[] components;
+	String[] configNames, sortedConfigNames;
+	String[] configDescriptions, sortedConfigDescriptions;
+	private String configPattern;
+
+	boolean dbRequired;
+	boolean needToUpdateLocalFile;
+
+	/*
+	 * Local class helping to guess remaining time while reading results from DB
+	 */
+	class RemainingTimeGuess {
+		int all, count;
+		long start;
+		double remaining;
+		RemainingTimeGuess(int all, long start) {
+			this.all = all;
+			this.start = start;
+		}
+		String display() {
+			StringBuffer buffer = new StringBuffer(" [elapsed: "); //$NON-NLS-1$
+			long elapsed = getElapsed();
+			buffer.append(Util.timeChrono(elapsed));
+			if (this.count > 0) {
+				buffer.append(" | left: "); //$NON-NLS-1$
+				long remainingTime = getRemainingTime(elapsed);
+				buffer.append(Util.timeChrono(remainingTime));
+				buffer.append(" | end: "); //$NON-NLS-1$
+				buffer.append(Util.timeEnd(remainingTime));
+			}
+			buffer.append(']');
+			return buffer.toString();
+		}
+		private long getRemainingTime(long elapsed) {
+			return (long) ((((double)elapsed) / this.count) * (this.all - this.count));
+	    }
+		private long getElapsed() {
+			return System.currentTimeMillis() - this.start;
+	    }
+	}
+
+
+	// Failure threshold
+	public static final int DEFAULT_FAILURE_THRESHOLD = 10;
+	int failure_threshold = DEFAULT_FAILURE_THRESHOLD;
+
+public PerformanceResults(PrintStream stream) {
+	super(null, null);
+	this.printStream = stream;
+	this.dbRequired = false;
+	setDefaults();
+}
+
+public PerformanceResults(String name, String baseline, String baselinePrefix, PrintStream stream) {
+	super(null, name);
+	this.baselineName = baseline;
+	this.baselinePrefix = baselinePrefix;
+	this.printStream = stream;
+	this.dbRequired = true;
+	setDefaults();
+}
+
+/**
+ * Returns the list of all builds currently read.
+ *
+ * @return The names list of all currently known builds
+ */
+public String[] getAllBuildNames() {
+	if (this.allBuildNames == null) {
+		setAllBuildNames();
+	}
+	return this.allBuildNames;
+}
+
+/**
+ * Returns the name of the baseline used for extracted results
+ *
+ * @return The build name of the baseline of <code>null</code>
+ * 	if no specific baseline is used for the extracted results.
+ */
+public String getBaselineName() {
+	return this.baselineName;
+}
+
+/*
+ * Get the baseline prefix (computed from #baselineName).
+ */
+String getBaselinePrefix() {
+	return this.baselinePrefix;
+}
+
+/*
+ * Get the build date (see #getBuildDate(String, String)).
+ */
+public String getBuildDate() {
+	String buildName = getName();
+	if (buildName == null) return ""; //$NON-NLS-1$
+	return Util.getBuildDate(getName(), getBaselinePrefix());
+}
+
+/**
+ * Return the list of components concerned by performance results.
+ *
+ * @return The list of the components
+ */
+public String[] getComponents() {
+	return this.components;
+}
+
+/**
+ * Get the scenarios of a given component.
+ *
+ * @param componentName The component name. Should not be <code>null</code>
+ * @return A list of {@link ScenarioResults scenario results}
+ */
+public List getComponentScenarios(String componentName) {
+	ComponentResults componentResults = (ComponentResults) getResults(componentName);
+	if (componentResults == null) return null;
+	return Collections.unmodifiableList(componentResults.children);
+}
+
+/**
+ * Get the scenarios which have a summary for a given component.
+ *
+ * @param componentName The component name
+ * @param config Configuration name
+ * @return A list of {@link ScenarioResults scenario results} which have a summary
+ */
+public List getComponentSummaryScenarios(String componentName, String config) {
+	if (componentName == null) {
+		int size = size();
+		List scenarios = new ArrayList();
+		for (int i=0; i< size; i++) {
+			ComponentResults componentResults = (ComponentResults) this.children.get(i);
+			scenarios.addAll(componentResults.getSummaryScenarios(true, config));
+		}
+		return scenarios;
+	}
+	ComponentResults componentResults = (ComponentResults) getResults(componentName);
+	return componentResults.getSummaryScenarios(false, config);
+}
+
+/**
+ * Return the configuration boxes considered for this performance results
+ * sorted or not depending on the given flag.
+ *
+ * @param sort Indicates whether the list must be sorted or not.
+ * 	The order is defined by the configuration names, not by the box names
+ * @return The list of configuration boxes sorted by configuration names
+ */
+public String[] getConfigBoxes(boolean sort) {
+	return sort ? this.sortedConfigDescriptions : this.configDescriptions;
+}
+
+/**
+ * Return the configuration names considered for this performance results
+ * sorted or not depending on the given flag.
+ *
+ * @param sort Indicates whether the list must be sorted or not
+ * @return The list of configuration names
+ */
+public String[] getConfigNames(boolean sort) {
+	return sort ?this.sortedConfigNames : this.configNames;
+}
+
+/*
+ * Compute a SQL pattern from all stored configuration names.
+ * For example 'eclipseperflnx1', 'eclipseperflnx2' and 'eclipseperflnx3'
+ * will return 'eclipseperflnx_'.
+ */
+String getConfigurationsPattern() {
+	if (this.configPattern == null) {
+		int length = this.sortedConfigNames == null ? 0 : this.sortedConfigNames.length;
+		if (length == 0) return null;
+		this.configPattern = this.sortedConfigNames[0];
+		int refLength = this.configPattern.length();
+		for (int i=1; i<length; i++) {
+			String config = this.sortedConfigNames[i];
+			StringBuffer newConfig = null;
+			if (refLength != config.length()) return null; // strings have not the same length => cannot find a pattern
+			for (int j=0; j<refLength; j++) {
+				char c = this.configPattern.charAt(j);
+				if (config.charAt(j) != c) {
+					if (newConfig == null) {
+						newConfig = new StringBuffer(refLength);
+						if (j == 0) return null; // first char is already different => cannot find a pattern
+						newConfig.append(this.configPattern.substring(0, j));
+					}
+					newConfig.append('_');
+				} else if (newConfig != null) {
+					newConfig.append(c);
+				}
+			}
+			if (newConfig != null) {
+				this.configPattern = newConfig.toString();
+			}
+		}
+	}
+	return this.configPattern;
+}
+
+/**
+ * Return the name of the last build name except baselines.
+ *
+ * @return the name of the last build
+ */
+public String getLastBuildName() {
+	return getLastBuildName(1/*all except baselines*/);
+}
+/**
+ * Return the name of the last build name
+ *
+ * @param kind Decide what kind of build is taken into account
+ * 	0: all kind of build
+ * 	1: all except baseline builds
+ * 	2: all except baseline and nightly builds
+ * 	3: only integration builds
+ * @return the name of the last build of the selected kind
+ */
+public String getLastBuildName(int kind) {
+	if (this.name == null) {
+		getAllBuildNames(); // init build names if necessary
+		int idx = this.allBuildNames.length-1;
+		this.name = this.allBuildNames[idx];
+		if (kind > 0) {
+			loop: while (idx-- >= 0) {
+				switch (this.name.charAt(0)) {
+					case 'N':
+						if (kind < 2) break loop;
+						break;
+					case 'M':
+						if (kind < 3) break loop;
+						break;
+					case 'I':
+						if (kind < 4) break loop;
+						break;
+				}
+				this.name = this.allBuildNames[idx];
+			}
+		}
+	}
+	return this.name;
+}
+
+public String getName() {
+	if (this.name == null) {
+		setAllBuildNames();
+	}
+	return this.name;
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.AbstractResults#getPerformance()
+ */
+PerformanceResults getPerformance() {
+	return this;
+}
+
+/**
+ * Get the results of a given scenario.
+ *
+ * @param scenarioName The scenario name
+ * @return The {@link ScenarioResults scenario results}
+ */
+public ScenarioResults getScenarioResults(String scenarioName) {
+	ComponentResults componentResults = (ComponentResults) getResults(DB_Results.getComponentNameFromScenario(scenarioName));
+	return componentResults == null ? null : (ScenarioResults) componentResults.getResults(scenarioName);
+}
+
+/*
+ * Init configurations from performance results database.
+ */
+private void initConfigs() {
+	// create config names
+	this.configNames = DB_Results.getConfigs();
+	this.configDescriptions = DB_Results.getConfigDescriptions();
+	int length = this.configNames.length;
+	this.sortedConfigNames = new String[length];
+	for (int i = 0; i < length; i++) {
+	    this.sortedConfigNames[i] = this.configNames[i];
+	}
+
+	// Sort the config names
+	Arrays.sort(this.sortedConfigNames);
+	this.sortedConfigDescriptions = new String[length];
+	for (int i=0; i<length; i++) {
+		for (int j=0; j<length; j++) {
+			if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
+				this.sortedConfigDescriptions[i] = this.configDescriptions[j];
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * Read or update data for a build from a directory supposed to have local files.
+ */
+private String[] read(boolean local, String buildName, String[][] configs, boolean force, File dataDir, String taskName, SubMonitor subMonitor) {
+	if (local && dataDir == null) {
+		throw new IllegalArgumentException("Must specify a directory to read local files!"); //$NON-NLS-1$
+	}
+	subMonitor.setWorkRemaining(100);
+
+	// Update info
+	long start = System.currentTimeMillis();
+	int allScenariosSize;
+	if (DB_Results.DB_CONNECTION) {
+		try {
+			// Read all scenarios
+			allScenariosSize = readScenarios(buildName, subMonitor.newChild(10)) ;
+			if (allScenariosSize < 0) {
+				return null;
+			}
+
+			// Read all builds
+			DB_Results.queryAllVariations(getConfigurationsPattern());
+
+			// Refresh configs
+			if (configs == null) {
+				initConfigs();
+			} else {
+				setConfigInfo(configs);
+			}
+		} catch (OperationCanceledException e) {
+			return null;
+		}
+	} else {
+		if (this.allScenarios == null) return null;
+		allScenariosSize = this.allScenarios.size();
+		if (configs != null) {
+			setConfigInfo(configs);
+		}
+	}
+
+	// Create corresponding children
+	int componentsLength = this.components.length;
+	subMonitor.setWorkRemaining(componentsLength);
+	RemainingTimeGuess timeGuess = null;
+	for (int i=0; i<componentsLength; i++) {
+		String componentName = this.components[i];
+		List scenarios = this.allScenarios == null ? null : (List) this.allScenarios.get(componentName);
+
+		// Manage monitor
+		int percentage = (int) ((((double)(i+1)) / (componentsLength+1)) * 100);
+		StringBuffer tnBuffer= taskName==null ? new StringBuffer() : new StringBuffer(taskName);
+		tnBuffer.append(" ("); //$NON-NLS-1$
+		if (buildName != null) {
+			tnBuffer.append(buildName).append(": "); //$NON-NLS-1$
+		}
+		tnBuffer.append(percentage).append("%)"); //$NON-NLS-1$
+		subMonitor.setTaskName(tnBuffer.toString());
+		StringBuffer subTaskBuffer = new StringBuffer("Component "); //$NON-NLS-1$
+		subTaskBuffer.append(componentName);
+		subTaskBuffer.append("..."); //$NON-NLS-1$
+		subMonitor.subTask(subTaskBuffer.toString());
+
+		// Get component results
+		if (scenarios == null && !local) continue;
+		ComponentResults componentResults;
+		if (local || (buildName == null && force)) {
+			componentResults = new ComponentResults(this, componentName);
+			addChild(componentResults, true);
+		} else {
+			componentResults = (ComponentResults) getResults(componentName);
+			if (componentResults == null) {
+				componentResults = new ComponentResults(this, componentName);
+				addChild(componentResults, true);
+			}
+		}
+
+		// Read the component results
+		if (local) {
+			try {
+				componentResults.readLocalFile(dataDir, scenarios);
+			}
+			catch (FileNotFoundException ex) {
+				return null;
+			}
+			subMonitor.worked(1);
+		} else {
+			if (timeGuess == null) {
+				timeGuess = new RemainingTimeGuess(1+componentsLength+allScenariosSize, start);
+			}
+			componentResults.updateBuild(buildName, scenarios, force, dataDir, subMonitor.newChild(1), timeGuess);
+		}
+		if (subMonitor.isCanceled()) return null;
+	}
+
+	// Update names
+	setAllBuildNames();
+	writeData(dataDir);
+
+	// Print time
+	printGlobalTime(start);
+
+	return this.allBuildNames;
+}
+
+/**
+ * Read all data from performance database for the given configurations
+ * and scenario pattern.
+ *
+ * This method is typically called when generated performance results
+ * from a non-UI application.
+ *
+ * @param buildName The name of the build
+ * @param configs All configurations to extract results. If <code>null</code>,
+ * 	then all known configurations ({@link DB_Results#getConfigs()})  are read.
+ * @param pattern The pattern of the concerned scenarios
+ * @param dataDir The directory where data will be read/stored locally.
+ * 	If <code>null</code>, then database will be read instead and no storage
+ * 	will be performed
+ * @param threshold The failure percentage threshold over which a build result
+ * 	value compared to the baseline is considered as failing.
+ * @param monitor The progress monitor
+ *
+ * @return All known build names
+ */
+public String[] readAll(String buildName, String[][] configs, String pattern, File dataDir, int threshold, IProgressMonitor monitor) {
+
+	// Init
+	this.scenarioPattern = pattern == null ? "%" : pattern; //$NON-NLS-1$
+	this.failure_threshold = threshold;
+	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+
+	// Set default names
+	setDefaults();
+
+	// Read local data files first
+	reset(dataDir);
+	String[] names = read(true, null, configs, true, dataDir, null, subMonitor.newChild(100));
+	if (names==null) {
+		// if one local files is missing then force a full DB read!
+		// TODO moderate this to force the DB read only for the missing file...
+		return read(false, null, configs, true, dataDir, null, subMonitor.newChild(900));
+	}
+
+	// Search build name in read data
+	boolean buildMissing = true;
+	if (buildName != null) {
+		this.name = buildName;
+		buildMissing = Arrays.binarySearch(names, buildName, Util.BUILD_DATE_COMPARATOR) < 0;
+	}
+
+	// Look for missing builds
+	if (buildMissing) {
+		String[] builds = DB_Results.getBuilds();
+		Arrays.sort(builds, Util.BUILD_DATE_COMPARATOR);
+		for (int i=builds.length-1; i>=0; i--) {
+			if (Arrays.binarySearch(names, builds[i], Util.BUILD_DATE_COMPARATOR) >= 0) {
+				break;
+			}
+			read(false, builds[i], configs, true, dataDir, null, subMonitor.newChild(900));
+		}
+	}
+	return this.allBuildNames;
+}
+
+/**
+ * Read all data from performance database for the given configurations
+ * and scenario pattern.
+ *
+ * Note that calling this method flush all previous read data.
+ *
+ * @param dataDir The directory where local files are located
+ * @param monitor  The progress monitor
+ */
+public void readLocal(File dataDir, IProgressMonitor monitor) {
+
+	// Print title
+	String taskName = "Read local performance results"; //$NON-NLS-1$
+	println(taskName);
+
+	// Create monitor
+	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+	subMonitor.setTaskName(taskName);
+
+	// Read
+	reset(dataDir);
+	read(true, null, null, true, dataDir, taskName, subMonitor);
+}
+
+void readLocalFile(File dir) {
+	if (!dir.exists()) return;
+	File dataFile = new File(dir, "performances.dat");	//$NON-NLS-1$
+	if (!dataFile.exists()) return;
+	DataInputStream stream = null;
+	try {
+		// Read local file info
+		stream = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
+
+		// Read build info
+		String str = stream.readUTF();
+		this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) > 0;
+		if (this.name != null && Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) >= 0) {
+			return;
+		}
+		println(" - read performance results local files info: "); //$NON-NLS-1$
+		println("		+ name : "+str);
+		this.name = str == ""  ? null : str;
+		str = stream.readUTF();
+		println("		+ baseline : "+str);
+		if (this.baselineName == null) {
+			this.baselineName = str == "" ? null : str;
+		}
+		str = stream.readUTF();
+		println("		+ baseline prefix: "+str);
+		this.baselinePrefix = str == "" ? null : str;
+
+		// Write configs info
+		int length = stream.readInt();
+		println("		+ "+length+" configs");
+		this.configNames = new String[length];
+		this.sortedConfigNames = new String[length];
+		this.configDescriptions = new String[length];
+		this.sortedConfigDescriptions = new String[length];
+		for (int i = 0; i < length; i++) {
+			this.configNames[i] = stream.readUTF();
+			this.sortedConfigNames[i] = this.configNames[i];
+			this.configDescriptions[i] = stream.readUTF();
+			this.sortedConfigDescriptions[i] = this.configDescriptions[i];
+		}
+		DB_Results.setConfigs(this.configNames);
+		DB_Results.setConfigDescriptions(this.configDescriptions);
+
+		// Write builds info
+		length = stream.readInt();
+		println("		+ "+length+" builds");
+		this.allBuildNames = new String[length];
+		for (int i = 0; i < length; i++) {
+			this.allBuildNames[i] = stream.readUTF();
+		}
+
+		// Write scenarios info
+		length = stream.readInt();
+		println("		+ "+length+" components");
+		this.components = new String[length];
+		this.allScenarios = new HashMap();
+		for (int i = 0; i < length; i++) {
+			this.components[i] = stream.readUTF();
+			int size = stream.readInt();
+			List scenarios = new ArrayList(size);
+			for (int j=0; j<size; j++) {
+				scenarios.add(new ScenarioResults(stream.readInt(), stream.readUTF(), stream.readUTF()));
+			}
+			this.allScenarios.put(this.components[i], scenarios);
+		}
+		println("	=> read from file "+dataFile); //$NON-NLS-1$
+	} catch (IOException ioe) {
+		println("	!!! "+dataFile+" should be deleted as it contained invalid data !!!"); //$NON-NLS-1$ //$NON-NLS-2$
+	} finally {
+		try {
+	        stream.close();
+        } catch (IOException e) {
+	        // nothing else to do!
+        }
+	}
+}
+
+private int readScenarios(String buildName, SubMonitor subMonitor) throws OperationCanceledException {
+	subMonitor.setWorkRemaining(10);
+	long start = System.currentTimeMillis();
+	String titleSuffix;
+	if (buildName == null) {
+		titleSuffix = "all database scenarios..."; //$NON-NLS-1$
+	} else {
+		titleSuffix = "all database scenarios for "+buildName+" build..."; //$NON-NLS-1$ //$NON-NLS-2$
+	}
+	print("	+ get "+titleSuffix); //$NON-NLS-1$
+	subMonitor.subTask("Get "+titleSuffix); //$NON-NLS-1$
+	this.allScenarios = DB_Results.queryAllScenarios(this.scenarioPattern, buildName);
+	if (this.allScenarios == null) return -1;
+	int allScenariosSize = 0;
+	List componentsSet = new ArrayList(this.allScenarios.keySet());
+	Collections.sort(componentsSet);
+	int componentsSize = componentsSet.size();
+	componentsSet.toArray(this.components = new String[componentsSize]);
+	for (int i=0; i<componentsSize; i++) {
+		String componentName = this.components[i];
+		List scenarios = (List) this.allScenarios.get(componentName);
+		allScenariosSize += scenarios.size();
+	}
+	println(" -> "+allScenariosSize+" found in "+(System.currentTimeMillis()-start)+"ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	subMonitor.worked(10);
+	if (subMonitor.isCanceled()) throw new OperationCanceledException();
+	return allScenariosSize;
+}
+
+void reset(File dataDir) {
+	this.allBuildNames = null;
+	this.children = new ArrayList();
+//	this.name = null;
+	this.components = null;
+	this.allScenarios = null;
+	readLocalFile(dataDir);
+}
+
+private void setAllBuildNames() {
+	SortedSet builds = new TreeSet(Util.BUILD_DATE_COMPARATOR);
+	int size = size();
+	if (size == 0) return;
+	for (int i=0; i<size; i++) {
+		ComponentResults componentResults = (ComponentResults) this.children.get(i);
+		Set names = componentResults.getAllBuildNames();
+		builds.addAll(names);
+	}
+	int buildsSize = builds.size();
+	this.allBuildNames = new String[buildsSize];
+	if (buildsSize > 0) {
+		int n = 0;
+		Iterator buildNames = builds.iterator();
+		while (buildNames.hasNext()) {
+			String buildName = (String) buildNames.next();
+			if (this.lastBuildName == null || Util.getBuildDate(buildName).compareTo(Util.getBuildDate(this.lastBuildName)) <= 0) {
+				this.allBuildNames[n++] = buildName;
+			}
+		}
+		if (n < buildsSize) {
+			System.arraycopy(this.allBuildNames, 0, this.allBuildNames = new String[n], 0, n);
+		}
+		int idx = n-1;
+		String lastBuild = this.allBuildNames[idx--];
+		while (idx > 0 && lastBuild.startsWith(DB_Results.getDbBaselinePrefix())) {
+			lastBuild = this.allBuildNames[idx--];
+		}
+		this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(lastBuild).compareTo(Util.getBuildDate(this.name)) > 0;
+		this.name = lastBuild;
+		if (this.baselineName != null) {
+			String lastBuildDate = Util.getBuildDate(lastBuild);
+			if (Util.getBuildDate(this.baselineName).compareTo(lastBuildDate) > 0) {
+				this.baselineName = DB_Results.getLastBaselineBuild(lastBuildDate);
+			}
+		}
+	}
+}
+
+private void setConfigInfo(String[][] configs) {
+	if (configs == null) return;
+
+	// Store config information
+	int length = configs.length;
+	this.configNames = new String[length];
+	this.sortedConfigNames = new String[length];
+	this.configDescriptions = new String[length];
+	for (int i=0; i<length; i++) {
+		this.configNames[i] = this.sortedConfigNames[i] = configs[i][0];
+		this.configDescriptions[i] = configs[i][1];
+	}
+
+	// Sort the config names
+	Arrays.sort(this.sortedConfigNames);
+	length = this.sortedConfigNames.length;
+	this.sortedConfigDescriptions = new String[length];
+	for (int i=0; i<length; i++) {
+		for (int j=0; j<length; j++) {
+			if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
+				this.sortedConfigDescriptions[i] = this.configDescriptions[j];
+				break;
+			}
+		}
+	}
+}
+
+
+/**
+ * Set the name of the baseline used for extracted results
+ *
+ * @param buildName The name of the baseline build
+ */
+public void setBaselineName(String buildName) {
+	this.baselineName = buildName;
+	if (this.baselinePrefix == null || !this.baselineName.startsWith(this.baselinePrefix)) {
+		// Usually hat baseline name format is *always* x.y_yyyyMMddhhmm_yyyyMMddhhmm
+		int index = this.baselineName.lastIndexOf('_');
+		if (index > 0) {
+			this.baselinePrefix = this.baselineName.substring(0, index);
+		} else {
+//				this.baselinePrefix = DB_Results.getDbBaselinePrefix();
+			this.baselinePrefix = this.baselineName;
+		}
+	}
+}
+
+private void setDefaults() {
+
+	// Set builds if none
+	if (size() == 0 && DB_Results.DB_CONNECTION) {
+		this.allBuildNames = DB_Results.getBuilds();
+		this.components = DB_Results.getComponents();
+		initConfigs();
+	}
+
+	// Set name if null
+	if (this.name == null) {
+		setAllBuildNames();
+		if (this.name == null) { // does not know any build
+			this.name = DB_Results.getLastCurrentBuild();
+			if (this.dbRequired) {
+				if (this.name == null) {
+//					throw new RuntimeException("Cannot find any current build!"); //$NON-NLS-1$
+					this.name = "No current build!";
+				}
+				this.allBuildNames = DB_Results.getBuilds();
+				this.components = DB_Results.getComponents();
+				initConfigs();
+			}
+			if (this.printStream != null) {
+				this.printStream.println("	+ no build specified => use last one: "+this.name); //$NON-NLS-1$
+			}
+		}
+	}
+
+	// Init baseline name if not set
+	if (this.baselineName == null && getName() != null) {
+		String buildDate = Util.getBuildDate(getName());
+		this.baselineName = DB_Results.getLastBaselineBuild(buildDate);
+		if (this.baselineName == null && this.dbRequired) {
+			throw new RuntimeException("Cannot find any baseline to refer!"); //$NON-NLS-1$
+		}
+		if (this.printStream != null) {
+			this.printStream.println("	+ no baseline specified => use last one: "+this.baselineName); //$NON-NLS-1$
+		}
+	}
+
+	// Init baseline prefix if not set
+	if (this.baselineName != null) {
+		if (this.baselinePrefix == null || !this.baselineName.startsWith(this.baselinePrefix)) {
+			// Usually hat baseline name format is *always* x.y_yyyyMMddhhmm_yyyyMMddhhmm
+			int index = this.baselineName.lastIndexOf('_');
+			if (index > 0) {
+				this.baselinePrefix = this.baselineName.substring(0, index);
+			} else {
+//				this.baselinePrefix = DB_Results.getDbBaselinePrefix();
+				this.baselinePrefix = this.baselineName;
+			}
+		}
+	}
+
+	// Set scenario pattern default
+	if (this.scenarioPattern == null) {
+		this.scenarioPattern = "%"; //$NON-NLS-1$
+	}
+
+	// Flush print stream
+	if (this.printStream != null) {
+		this.printStream.println();
+		this.printStream.flush();
+	}
+}
+
+public void setLastBuildName(String lastBuildName) {
+	this.lastBuildName = lastBuildName;
+//	if (lastBuildName == null) {
+//		int idx = this.allBuildNames.length-1;
+//		String lastBuild = this.allBuildNames[idx--];
+//		while (this.name.startsWith(DB_Results.getDbBaselinePrefix())) {
+//			lastBuild = this.allBuildNames[idx--];
+//		}
+//		this.name = lastBuild;
+//	} else {
+//		this.name = lastBuildName;
+//	}
+}
+
+/**
+ * Update a given build information with database contents.
+ *
+ * @param builds The builds to read new data
+ * @param force Force the update from the database, even if the build is
+ * 	already known.
+ * @param dataDir The directory where data should be stored locally if necessary.
+ * 	If <code>null</code>, then information changes won't be persisted.
+ * @param monitor The progress monitor
+ * @return All known builds
+ */
+public String[] updateBuilds(String[] builds, boolean force, File dataDir, IProgressMonitor monitor) {
+
+	// Print title
+	StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
+	int length = builds == null ? 0 : builds.length;
+	switch (length) {
+		case 0:
+			buffer.append("all builds"); //$NON-NLS-1$
+			reset(dataDir);
+			break;
+		case 1:
+			buffer.append("one build"); //$NON-NLS-1$
+			break;
+		default:
+			buffer.append("several builds"); //$NON-NLS-1$
+			break;
+	}
+	String taskName = buffer.toString();
+	println(buffer);
+
+	// Create sub-monitor
+	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000*length);
+	subMonitor.setTaskName(taskName);
+
+	// Read
+	for (int i=0; i<length;  i++) {
+		read(false, builds[i], null, force, dataDir, taskName, subMonitor.newChild(1000));
+	}
+
+	// Return new builds list
+	return this.allBuildNames;
+}
+
+/**
+ * Update a given build information with database contents.
+ *
+ * @param buildName The build name to read new data
+ * @param force Force the update from the database, even if the build is
+ * 	already known.
+ * @param dataDir The directory where data should be stored locally if necessary.
+ * 	If <code>null</code>, then information changes won't be persisted.
+ * @param monitor The progress monitor
+ * @return All known builds
+ */
+public String[] updateBuild(String buildName, boolean force, File dataDir, IProgressMonitor monitor) {
+
+	// Print title
+	StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
+	if (buildName == null) {
+		buffer.append("all builds"); //$NON-NLS-1$
+		reset(dataDir);
+	} else {
+		buffer.append("one build"); //$NON-NLS-1$
+	}
+	String taskName = buffer.toString();
+	println(buffer);
+
+	// Create sub-monitor
+	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+	subMonitor.setTaskName(taskName);
+
+	// Read
+	read(false, buildName, null, force, dataDir, taskName, subMonitor);
+
+	// Refresh name
+	if (buildName != null && !buildName.startsWith(DB_Results.getDbBaselinePrefix())) {
+		this.name = buildName;
+	}
+
+	// Return new list all build names
+	return this.allBuildNames;
+}
+
+/*
+ * Write general information.
+ */
+void writeData(File dir) {
+	if (!DB_Results.DB_CONNECTION) {
+		// Only write new local file if there's a database connection
+		// otherwise contents may not be complete...
+		return;
+	}
+	if (dir ==null || (!dir.exists() && !dir.mkdirs())) {
+		System.err.println("can't create directory " + dir); //$NON-NLS-1$
+		return;
+	}
+	File dataFile = new File(dir, "performances.dat"); //$NON-NLS-1$
+	if (dataFile.exists()) {
+		if (!this.needToUpdateLocalFile) {
+			return;
+		}
+		dataFile.delete();
+	}
+	try {
+		DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));
+
+		// Write build info
+		stream.writeUTF(this.name == null ? DB_Results.getLastCurrentBuild() : this.name);
+		stream.writeUTF(this.baselineName == null ? DB_Results.getLastBaselineBuild(null) : this.baselineName);
+		stream.writeUTF(this.baselinePrefix == null ? "" : this.baselinePrefix);
+
+		// Write configs info
+		int length = this.sortedConfigNames.length;
+		stream.writeInt(length);
+		for (int i = 0; i < length; i++) {
+			stream.writeUTF(this.sortedConfigNames[i]);
+			stream.writeUTF(this.sortedConfigDescriptions[i]);
+		}
+
+		// Write builds info
+		String[] builds = this.allBuildNames == null ? DB_Results.getBuilds() : this.allBuildNames;
+		length = builds.length;
+		stream.writeInt(length);
+		for (int i = 0; i < length; i++) {
+			stream.writeUTF(builds[i]);
+		}
+
+		// Write scenarios info
+		length = this.components.length;
+		stream.writeInt(length);
+		for (int i = 0; i < length; i++) {
+			stream.writeUTF(this.components[i]);
+			List scenarios = (List) this.allScenarios.get(this.components[i]);
+			int size = scenarios.size();
+			stream.writeInt(size);
+			for (int j=0; j<size; j++) {
+				final ScenarioResults scenarioResults = (ScenarioResults)scenarios.get(j);
+				stream.writeInt(scenarioResults.getId());
+				stream.writeUTF(scenarioResults.getName());
+				stream.writeUTF(scenarioResults.getLabel());
+			}
+		}
+
+		// Close
+		stream.close();
+		println("	=> performance results general data  written in file " + dataFile); //$NON-NLS-1$
+	} catch (FileNotFoundException e) {
+		System.err.println("can't create output file" + dataFile); //$NON-NLS-1$
+	} catch (IOException e) {
+		e.printStackTrace();
+	}
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/SQL_Results.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/SQL_Results.java
new file mode 100644
index 0000000..a4e1aff
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/SQL_Results.java
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * 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.db;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+
+import org.eclipse.test.internal.performance.InternalDimensions;
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.db.SQL;
+
+/**
+ * Specific implementation for massive database requests.
+ */
+public class SQL_Results extends SQL {
+
+private PreparedStatement queryBuildAllScenarios,
+	queryBuildScenarios,
+	queryScenarioSummaries,
+	queryAllComments,
+	queryScenariosBuilds,
+	queryScenarioDataPoints,
+	queryScenarioTimestampDataPoints,
+	queryScenarioBuildDataPoints,
+	queryDimScalars,
+	queryAllVariations;
+
+SQL_Results(Connection con) throws SQLException {
+	    super(con);
+	    // TODO Auto-generated constructor stub
+    }
+
+protected void dispose() throws SQLException {
+	super.dispose();
+	if (this.queryBuildScenarios != null)
+		this.queryBuildScenarios.close();
+	if (this.queryBuildAllScenarios != null)
+		this.queryBuildAllScenarios.close();
+	if (this.queryScenarioSummaries != null)
+		this.queryScenarioSummaries.close();
+	if (this.queryAllComments != null)
+		this.queryAllComments.close();
+	if (this.queryScenariosBuilds != null)
+		this.queryScenariosBuilds.close();
+	if (this.queryScenarioDataPoints != null)
+		this.queryScenarioDataPoints.close();
+	if (this.queryDimScalars != null)
+		this.queryDimScalars.close();
+	if (this.queryAllVariations != null)
+		this.queryAllVariations.close();
+}
+
+/**
+ * Get all comments from database
+ *
+ * @return A set of the query result
+ * @throws SQLException
+ */
+ResultSet queryAllComments() throws SQLException {
+	if (this.queryAllComments == null)
+		this.queryAllComments = this.fConnection.prepareStatement("select ID, KIND, TEXT from COMMENT"); //$NON-NLS-1$
+	return this.queryAllComments.executeQuery();
+}
+
+/**
+ * Get all variations from database.
+ *
+ * @param configPattern The pattern for all the concerned configurations
+ * @return A set of the query result
+ * @throws SQLException
+ */
+ResultSet queryAllVariations(String configPattern) throws SQLException {
+	long start = System.currentTimeMillis();
+	if (DB_Results.DEBUG) DB_Results.DEBUG_WRITER.print("[SQL query (config pattern="+configPattern); //$NON-NLS-1$
+	if (this.queryAllVariations == null) {
+		this.queryAllVariations = this.fConnection.prepareStatement("select KEYVALPAIRS from VARIATION where KEYVALPAIRS like ? order by KEYVALPAIRS"); //$NON-NLS-1$
+	}
+	this.queryAllVariations.setString(1, "%"+configPattern+"%"); //$NON-NLS-1$ //$NON-NLS-2$
+	ResultSet resultSet =  this.queryAllVariations.executeQuery();
+	if (DB_Results.DEBUG) DB_Results.DEBUG_WRITER.print(")=" + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
+	return resultSet;
+}
+
+/**
+ * Query all scenarios corresponding to the default scenario pattern
+ *
+ * @param scenarioPattern The pattern for all the concerned scenarios
+ * @return Set of the query result
+ * @throws SQLException
+ */
+ResultSet queryBuildAllScenarios(String scenarioPattern) throws SQLException {
+	if (this.queryBuildAllScenarios == null) {
+		String statement = "select distinct SCENARIO.ID, SCENARIO.NAME , SCENARIO.SHORT_NAME from SCENARIO where " + //$NON-NLS-1$
+			"SCENARIO.NAME LIKE ? " + //$NON-NLS-1$
+			"order by SCENARIO.NAME"; //$NON-NLS-1$
+		this.queryBuildAllScenarios = this.fConnection.prepareStatement(statement);
+	}
+	this.queryBuildAllScenarios.setString(1, scenarioPattern);
+	return this.queryBuildAllScenarios.executeQuery();
+}
+
+/**
+ * Query all scenarios corresponding to a given scenario pattern
+ * and for a specific build name.
+ *
+ * @param scenarioPattern The pattern for all the concerned scenarios
+ * @param buildName The name of the concerned build
+ * @return Set of the query result
+ * @throws SQLException
+ */
+ResultSet queryBuildScenarios(String scenarioPattern, String buildName) throws SQLException {
+	if (this.queryBuildScenarios == null) {
+		String statement = "select distinct SCENARIO.ID, SCENARIO.NAME , SCENARIO.SHORT_NAME from SCENARIO, SAMPLE, VARIATION where " + //$NON-NLS-1$
+			"SAMPLE.VARIATION_ID = VARIATION.ID and VARIATION.KEYVALPAIRS LIKE ? and " + //$NON-NLS-1$
+			"SAMPLE.SCENARIO_ID = SCENARIO.ID and SCENARIO.NAME LIKE ? " + //$NON-NLS-1$
+			"order by SCENARIO.NAME"; //$NON-NLS-1$
+		this.queryBuildScenarios = this.fConnection.prepareStatement(statement);
+	}
+	this.queryBuildScenarios.setString(1, "|build=" + buildName + '%'); //$NON-NLS-1$
+	this.queryBuildScenarios.setString(2, scenarioPattern);
+	return this.queryBuildScenarios.executeQuery();
+}
+
+/**
+ * Query all scalars for a given data point.
+ *
+ * @param datapointId The id of the data point
+ * @return Set of the query result
+ * @throws SQLException
+ */
+ResultSet queryDimScalars(int datapointId) throws SQLException {
+	if (this.queryDimScalars == null) {
+		StringBuffer buffer = new StringBuffer("select DIM_ID, VALUE from SCALAR where "); //$NON-NLS-1$
+		buffer.append("DATAPOINT_ID = ? and "); //$NON-NLS-1$
+		Dim[] dimensions = DB_Results.getResultsDimensions();
+		int length = dimensions.length;
+		for (int i=0; i<length; i++) {
+			if (i==0) {
+				buffer.append("(");
+			} else {
+				buffer.append(" or ");
+			}
+			buffer.append("DIM_ID = ");
+			buffer.append(dimensions[i].getId());
+		}
+		buffer.append(") order by DIM_ID");
+		this.queryDimScalars = this.fConnection.prepareStatement(buffer.toString());
+	}
+	this.queryDimScalars.setInt(1, datapointId);
+	return this.queryDimScalars.executeQuery();
+}
+
+/**
+ * Get all data points for a given scenario and configuration.
+ *
+ * @param config The name of the concerned configuration
+ * @param scenarioID The id of the scenario
+ * @param lastBuildName Name of the last build on which data were stored locally
+ * @param lastBuildTime Date in ms of the last build on which data were stored locally
+ * @return A set of the query result
+ * @throws SQLException
+ */
+ResultSet queryScenarioTimestampDataPoints(String config, int scenarioID, String lastBuildName, long lastBuildTime) throws SQLException {
+	if (DB_Results.LOG) DB_Results.LOG_WRITER.starts("		+ SQL query (config="+config+", scenario ID="+scenarioID+", build name="+lastBuildName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	if (this.queryScenarioTimestampDataPoints== null) {
+		String statement = "select DATAPOINT.ID, DATAPOINT.STEP, VARIATION.KEYVALPAIRS from SAMPLE, DATAPOINT, VARIATION where " + //$NON-NLS-1$
+			"SAMPLE.SCENARIO_ID = ? and " + //$NON-NLS-1$
+			"DATAPOINT.SAMPLE_ID = SAMPLE.ID and " + //$NON-NLS-1$
+			"SAMPLE.STARTTIME > ? and " + //$NON-NLS-1$
+			"SAMPLE.VARIATION_ID = VARIATION.ID " + //$NON-NLS-1$
+			"ORDER BY DATAPOINT.ID, DATAPOINT.STEP"; //$NON-NLS-1$
+		this.queryScenarioTimestampDataPoints = this.fConnection.prepareStatement(statement);
+	}
+	this.queryScenarioTimestampDataPoints.setInt(1, scenarioID);
+	Timestamp timestamp = new Timestamp(lastBuildTime+(5*3600L*1000)); // create a time-stamp 5h after the given build time
+	this.queryScenarioTimestampDataPoints.setTimestamp(2, timestamp);
+	ResultSet resultSet =  this.queryScenarioTimestampDataPoints.executeQuery();
+	if (DB_Results.LOG) DB_Results.LOG_WRITER.ends(")"); //$NON-NLS-1$
+	return resultSet;
+}
+ResultSet queryScenarioBuildDataPoints(String config, int scenarioID, String buildName) throws SQLException {
+	if (DB_Results.LOG) DB_Results.LOG_WRITER.starts("		+ SQL query (config="+config+", scenario ID="+scenarioID+", build name="+buildName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	if (this.queryScenarioBuildDataPoints== null) {
+		String statement = "select DATAPOINT.ID, DATAPOINT.STEP, VARIATION.KEYVALPAIRS from SAMPLE, DATAPOINT, VARIATION where " + //$NON-NLS-1$
+			"SAMPLE.VARIATION_ID = VARIATION.ID and VARIATION.KEYVALPAIRS LIKE ? and " + //$NON-NLS-1$
+			"SAMPLE.SCENARIO_ID = ? and " + //$NON-NLS-1$
+			"DATAPOINT.SAMPLE_ID = SAMPLE.ID " + //$NON-NLS-1$
+			"ORDER BY DATAPOINT.ID, DATAPOINT.STEP"; //$NON-NLS-1$
+		this.queryScenarioBuildDataPoints = this.fConnection.prepareStatement(statement);
+	}
+	this.queryScenarioBuildDataPoints.setString(1, "|build=" + buildName + '%'); //$NON-NLS-1$
+	this.queryScenarioBuildDataPoints.setInt(2, scenarioID);
+	ResultSet resultSet =  this.queryScenarioBuildDataPoints.executeQuery();
+	if (DB_Results.LOG) DB_Results.LOG_WRITER.ends(")"); //$NON-NLS-1$
+	return resultSet;
+}
+
+/**
+ * Get all data points for a given scenario and configuration.
+ *
+ * @param config The name of the concerned configuration
+ * @param scenarioID The id of the scenario
+ * @return A set of the query result
+ * @throws SQLException
+ */
+ResultSet queryScenarioDataPoints(String config, int scenarioID) throws SQLException {
+	long start = System.currentTimeMillis();
+	if (DB_Results.DEBUG) DB_Results.DEBUG_WRITER.print("[SQL query (config="+config+", scenario ID="+scenarioID); //$NON-NLS-1$ //$NON-NLS-2$
+	if (this.queryScenarioDataPoints== null) {
+		String statement = "select DATAPOINT.ID, DATAPOINT.STEP, VARIATION.KEYVALPAIRS from VARIATION, SAMPLE, DATAPOINT where " + //$NON-NLS-1$
+			"VARIATION.KEYVALPAIRS like ? and SAMPLE.VARIATION_ID = VARIATION.ID and " + //$NON-NLS-1$
+			"SAMPLE.SCENARIO_ID = ? and " + //$NON-NLS-1$
+			"DATAPOINT.SAMPLE_ID = SAMPLE.ID " + //$NON-NLS-1$
+			"ORDER BY DATAPOINT.ID, DATAPOINT.STEP"; //$NON-NLS-1$
+		this.queryScenarioDataPoints = this.fConnection.prepareStatement(statement);
+	}
+	this.queryScenarioDataPoints.setString(1, "%"+config+"%"); //$NON-NLS-1$ //$NON-NLS-2$
+	this.queryScenarioDataPoints.setInt(2, scenarioID);
+	ResultSet resultSet =  this.queryScenarioDataPoints.executeQuery();
+	if (DB_Results.DEBUG) DB_Results.DEBUG_WRITER.print(")=" + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
+	return resultSet;
+}
+
+/**
+ * Query all summaries from database for a given scenario,
+ * configuration and builds.
+ *
+ * @param config The name of the concerned configuration
+ * @param scenarioID The id of the scenario
+ * @param builds The list of builds to get summaries. When <code>null</code>
+ * 	summaries for all DB builds will be read.
+ *
+ * @return Set of the query result
+ * @throws SQLException
+ */
+ResultSet queryScenarioSummaries(int scenarioID, String config, String[] builds) throws SQLException {
+	int length = builds==null ? 0 : builds.length;
+	String buildPattern;
+	switch (length) {
+		case 0:
+			buildPattern = "%"; //$NON-NLS-1$
+			break;
+		case 1:
+			buildPattern = builds[0];
+			break;
+		default:
+			StringBuffer buffer = new StringBuffer();
+			loop: for (int idx=0; idx < builds[0].length(); idx++) {
+				char ch = builds[0].charAt(idx);
+				for (int i=1; i<length; i++) {
+					if (idx == builds[i].length()) {
+						break loop;
+					}
+					if (builds[i].charAt(idx) != ch) {
+						buffer.append('_');
+						continue loop;
+					}
+				}
+				buffer.append(ch);
+			}
+			buffer.append("%"); //$NON-NLS-1$
+			buildPattern = buffer.toString();
+			break;
+	}
+	if (this.queryScenarioSummaries == null) {
+		this.queryScenarioSummaries= this.fConnection.prepareStatement("select KEYVALPAIRS , IS_GLOBAL, COMMENT_ID, DIM_ID from VARIATION, SUMMARYENTRY where " + //$NON-NLS-1$
+			"KEYVALPAIRS like ? and " + //$NON-NLS-1$
+			"VARIATION_ID = VARIATION.ID and " + //$NON-NLS-1$
+			"SCENARIO_ID = ? and " + //$NON-NLS-1$
+			"(DIM_ID = "+InternalDimensions.ELAPSED_PROCESS.getId()+" or DIM_ID = 0)" + //$NON-NLS-1$ //$NON-NLS-2$
+			" order by VARIATION_ID, DIM_ID"); //$NON-NLS-1$
+	}
+	this.queryScenarioSummaries.setString(1, "|build="+buildPattern+"||config="+ config + "||jvm=sun|"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	this.queryScenarioSummaries.setInt(2, scenarioID);
+	return this.queryScenarioSummaries.executeQuery();
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ScenarioResults.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ScenarioResults.java
new file mode 100644
index 0000000..fb6dfe0
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/ScenarioResults.java
@@ -0,0 +1,407 @@
+/*******************************************************************************
+ * 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.db;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+
+/**
+ * Class to handle performance results of a component's scenario
+ * (for example 'org.eclipse.jdt.core.FullSourceWorkspaceSearchTest#searchAllTypeNames()').
+ *
+ * It gives access to results for each configuration run on this scenario.
+ *
+ * @see ConfigResults
+ */
+public class ScenarioResults extends AbstractResults {
+	String fileName;
+	String label;
+	String shortName;
+
+public ScenarioResults(int id, String name, String shortName) {
+	super(null, id);
+	this.name = name;
+	this.label = shortName;
+}
+
+/*
+ * Complete results with additional database information.
+ */
+void completeResults(String lastBuildName) {
+	String[] builds = DB_Results.getBuilds();
+	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));
+	    }
+	}
+	BuildDateComparator comparator = new BuildDateComparator();
+	Arrays.sort(builds, comparator);
+	int idx = Arrays.binarySearch(builds, lastBuildName, comparator);
+	if (idx < 0) {
+		builds = null;
+	} else {
+		int size = builds.length - ++idx;
+		System.arraycopy(builds, idx, builds = new String[size], 0, size);
+	}
+//	String[] builds = null;
+	int size = size();
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		configResults.completeResults(builds);
+	}
+}
+
+/**
+ * Returns the first configuration baseline build name.
+ *
+ * @return The name of the baseline build
+ * @see ConfigResults#getBaselineBuildName()
+ */
+public String getBaselineBuildName() {
+	int size = size();
+	StringBuffer buffer = new StringBuffer();
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		if (configResults.isValid()) {
+			return configResults.getBaselineBuildName();
+			/* TODO (frederic) decide what return when baseline is not the same on all configs...
+			 * Currently returns the first found, but may be a comma-separated list?
+			String baselineName = configResults.getBaselineBuildName();
+			if (buffer.indexOf(baselineName) < 0) {
+				if (buffer.length() > 0) buffer.append('|');
+				buffer.append(baselineName);
+			}
+			*/
+		}
+	}
+	return buffer.toString();
+}
+
+Set getAllBuildNames() {
+	Set buildNames = new HashSet();
+	int size = size();
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		List builds = configResults.getBuilds(null);
+		int length = builds.size();
+		for (int j=0; j<length; j++) {
+			buildNames.add(((BuildResults)builds.get(j)).getName());
+		}
+	}
+	return buildNames;
+}
+
+/**
+ * Return the results of the given configuration.
+ *
+ * @param config The configuration name
+ * @return The {@link ConfigResults results} for the given configuration
+ * 	or <code>null</code> if none was found.
+ */
+public ConfigResults getConfigResults(String config) {
+	return (ConfigResults) getResults(config);
+}
+
+/**
+ * Return a name which can be used as a file name to store information
+ * related to this scenario. This name does not contain the extension.
+ *
+ * @return The file name
+ */
+public String getFileName() {
+	if (this.fileName == null) {
+		this.fileName = "Scenario" + this.id; //$NON-NLS-1$
+	}
+	return this.fileName;
+}
+
+/**
+ * Returns the scenario label. If no label exist as there's no associated summary,
+ * then the short name is returned
+ *
+ * @return The label of the scenario or it's short name if no summary exists
+ */
+public String getLabel() {
+	return this.label == null ? getShortName() : this.label;
+}
+
+/**
+ * Returns the short name of the scenario. Short name is the name scenario
+ * from which package declaration has been removed.
+ *
+ * @return The scenario short name
+ */
+public String getShortName() {
+	if (this.shortName == null) {
+		// Remove class name qualification
+		int testSeparator = this.name.indexOf('#');
+		boolean hasClassName = testSeparator >= 0;
+		if (!hasClassName) {
+			testSeparator = this.name.lastIndexOf('.');
+			if (testSeparator <= 0) {
+				return this.shortName = this.name;
+			}
+		}
+		int classSeparator = this.name.substring(0, testSeparator).lastIndexOf('.');
+		if (classSeparator < 0) {
+			return this.shortName = this.name;
+		}
+		int length = this.name.length();
+		String testName = this.name.substring(classSeparator+1, length);
+		if (!hasClassName && testName.startsWith("test.")) { // specific case for swt... //$NON-NLS-1$
+			testName = testName.substring(5);
+		}
+
+		// Remove qualification from test name
+		StringTokenizer tokenizer = new StringTokenizer(testName, " :,", true); //$NON-NLS-1$
+		StringBuffer buffer = new StringBuffer(tokenizer.nextToken());
+		while (tokenizer.hasMoreTokens()) {
+			String token = tokenizer.nextToken();
+			char fc = token.charAt(0);
+			while (fc == ' ' || fc == ',' || fc == ':') {
+				buffer.append(token); // add the separator
+				token = tokenizer.nextToken();
+				fc = token.charAt(0);
+			}
+			int last = token.lastIndexOf('.');
+			if (last >= 3) {
+				int first = token .indexOf('.');
+				if (first == last) {
+					buffer.append(token);
+				} else {
+					buffer.append(token.substring(last+1));
+				}
+			} else {
+				buffer.append(token);
+			}
+		}
+		this.shortName = buffer.toString();
+	}
+	return this.shortName;
+}
+
+/**
+ * Returns whether one of the scenario's config has a summary or not.
+ *
+ * @return <code>true</code> if one of the scenario's config has a summary
+ * 	<code>false</code> otherwise.
+ */
+public boolean hasSummary() {
+	int size = size();
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
+		if (currentBuildResults != null && currentBuildResults.hasSummary()) return true;
+	}
+	return false;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.AbstractResults#hashCode()
+ */
+public int hashCode() {
+	return this.id;
+}
+
+/**
+ * Returns whether the current scenario is valid or not.
+ *
+ * @return <code>true</code> if all the builds contained in the database are
+ * 	known by the scenario (ie. at least one its configuration knows each of the
+ * 	db builds), <code>false</code> otherwise.
+ */
+public boolean isValid() {
+	int size = this.children.size();
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		if (configResults.isValid()) {
+			return true;
+		}
+	}
+	return false;
+}
+
+/**
+ * Returns whether the current build of the given config has valid results or not.
+ *
+ * @param config The name of the configuration
+ * @return <code>true</code> if the build has valid results
+ * 	<code>false</code> otherwise.
+ */
+public boolean isValid(String config) {
+	return getResults(config) != null;
+}
+
+/**
+ * Returns whether the current scenario knows a build or not.
+ *
+ * @param buildName The name of the build
+ * @return <code>true</code> if the at least one of scenario configuration
+ * 	knows the given build, <code>false</code> otherwise.
+ */
+public boolean knowsBuild(String buildName) {
+	String[] buildNames = buildName == null
+		? DB_Results.getBuilds()
+		: new String[] { buildName };
+	Set scenarioBuilds = getAllBuildNames();
+	int length = buildNames.length;
+	for (int i=0; i<length; i++) {
+		if (!scenarioBuilds.contains(buildNames[i])) {
+			return false;
+		}
+	}
+	return true;
+}
+
+/*
+ * Read scenario results information from database.
+ *
+void read(String buildName, long lastBuildTime) {
+
+	// Get values
+	print("	+ scenario '"+getShortName()+"': values..."); //$NON-NLS-1$ //$NON-NLS-2$
+	long start = System.currentTimeMillis();
+	String configPattern = getPerformance().getConfigurationsPattern();
+	DB_Results.queryScenarioValues(this, configPattern, buildName, lastBuildTime);
+	print(timeString(System.currentTimeMillis()-start));
+
+	// Set baseline and current builds
+	print(", infos..."); //$NON-NLS-1$
+	start = System.currentTimeMillis();
+	int size = size();
+	String[] builds = buildName == null ? null : new String[] { buildName };
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		configResults.completeResults(builds);
+	}
+	println(timeString(System.currentTimeMillis()-start));
+}
+*/
+
+/*
+ * Read data from a local file
+ */
+void readData(DataInputStream stream) throws IOException {
+
+	// Read data stored locally
+	int size = stream.readInt();
+	for (int i=0; i<size; i++) {
+		int config_id = stream.readInt();
+		ConfigResults configResults = (ConfigResults) getResults(config_id);
+		if (configResults == null) {
+			configResults = new ConfigResults(this, config_id);
+			addChild(configResults, true);
+		}
+		configResults.readData(stream);
+	}
+}
+
+/*
+ * Read new data from the database.
+ * This is typically needed when the build results are not in the local file...
+ *
+boolean readNewData(String lastBuildName, boolean force) {
+	if (lastBuildName == null) {
+		read(null, -1);
+		return true;
+	}
+	PerformanceResults performanceResults = getPerformance();
+	String lastBuildDate = getBuildDate(lastBuildName, getBaselinePrefix());
+	if (force || performanceResults.getBuildDate().compareTo(lastBuildDate) > 0) {
+		long lastBuildTime = 0;
+	    try {
+		    lastBuildTime = DATE_FORMAT.parse(lastBuildDate).getTime();
+	    } catch (ParseException e) {
+		    // should not happen
+	    }
+	    read(lastBuildName, lastBuildTime);
+		return true;
+	}
+	return false;
+}
+*/
+
+/*
+ * Set value from database information.
+ */
+void setInfos(int config_id, int build_id, int summaryKind, String comment) {
+	ConfigResults configResults = (ConfigResults) getResults(config_id);
+	if (configResults == null) {
+		configResults = new ConfigResults(this, config_id);
+		addChild(configResults, true);
+	}
+	configResults.setInfos(build_id, summaryKind, comment);
+}
+
+/*
+ * Set value from database information.
+ */
+void setValue(int build_id, int dim_id, int config_id, int step, long value) {
+	ConfigResults configResults = (ConfigResults) getResults(config_id);
+	if (configResults == null) {
+		configResults = new ConfigResults(this, config_id);
+		addChild(configResults, true);
+	}
+	configResults.setValue(build_id, dim_id, step, value);
+}
+
+/*
+ * Read scenario results information from database.
+ */
+boolean updateBuild(String buildName, boolean force) {
+
+	if (!force && knowsBuild(buildName)) {
+		return false;
+	}
+
+	// Get values
+	print("	+ scenario '"+getShortName()+"': values..."); //$NON-NLS-1$ //$NON-NLS-2$
+	long start = System.currentTimeMillis();
+	String configPattern = getPerformance().getConfigurationsPattern();
+	DB_Results.queryScenarioValues(this, configPattern, buildName);
+	print(Util.timeString(System.currentTimeMillis()-start));
+
+	// Set baseline and current builds
+	print(", infos..."); //$NON-NLS-1$
+	start = System.currentTimeMillis();
+	int size = size();
+	String[] builds = buildName == null ? null : new String[] { buildName };
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		configResults.completeResults(builds);
+	}
+	println(Util.timeString(System.currentTimeMillis()-start));
+	return true;
+}
+
+void write(DataOutputStream stream) throws IOException {
+	int size = size();
+	stream.writeInt(this.id);
+	stream.writeInt(size);
+	for (int i=0; i<size; i++) {
+		ConfigResults configResults = (ConfigResults) this.children.get(i);
+		configResults.write(stream);
+	}
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/BuildResultsElement.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/BuildResultsElement.java
new file mode 100644
index 0000000..4fb226d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/BuildResultsElement.java
@@ -0,0 +1,372 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.eval.StatisticsUtil;
+import org.eclipse.test.internal.performance.results.db.*;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.views.properties.ComboBoxPropertyDescriptor;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.PropertyDescriptor;
+import org.eclipse.ui.views.properties.TextPropertyDescriptor;
+
+public class BuildResultsElement extends ResultsElement {
+
+	// Property descriptors
+    static final String P_ID_BUILD_DATE = "BuildResultsElement.date"; //$NON-NLS-1$
+    static final String P_ID_BUILD_BASELINE = "BuildResultsElement.baseline"; //$NON-NLS-1$
+    static final String P_ID_BUILD_COMMENT = "BuildResultsElement.comment"; //$NON-NLS-1$
+    static final String P_ID_BUILD_SUMMARY_KIND = "BuildResultsElement.summarykind"; //$NON-NLS-1$
+    static final String P_ID_BUILD_IS_BASELINE = "BuildResultsElement.isbaseline"; //$NON-NLS-1$
+    static final String P_ID_BUILD_FAILURE = "BuildResultsElement.failure"; //$NON-NLS-1$
+    static final String P_ID_BUILD_DELTA = "BuildResultsElement.delta"; //$NON-NLS-1$
+    static final String P_ID_BUILD_ERROR = "BuildResultsElement.error"; //$NON-NLS-1$
+    static final String P_ID_BUILD_TTEST = "BuildResultsElement.ttest"; //$NON-NLS-1$
+
+    static final String P_STR_BUILD_DATE = "date"; //$NON-NLS-1$
+    static final String P_STR_BUILD_COMMENT = "comment"; //$NON-NLS-1$
+    static final String P_STR_BUILD_SUMMARY_KIND = "summary kind"; //$NON-NLS-1$
+    static final String P_STR_BUILD_IS_BASELINE = "is baseline"; //$NON-NLS-1$
+    static final String P_STR_BUILD_BASELINE = "baseline"; //$NON-NLS-1$
+    static final String P_STR_BUILD_FAILURE = "failure"; //$NON-NLS-1$
+    static final String P_STR_BUILD_DELTA = "delta with baseline"; //$NON-NLS-1$
+    static final String P_STR_BUILD_ERROR = "delta error"; //$NON-NLS-1$
+    static final String P_STR_BUILD_TTEST = "student's ttest"; //$NON-NLS-1$
+
+	private static final TextPropertyDescriptor BUILD_DATE_DESCRIPTOR = new TextPropertyDescriptor(P_ID_BUILD_DATE, P_STR_BUILD_DATE);
+	private static final TextPropertyDescriptor BUILD_COMMENT_DESCRIPTOR = new TextPropertyDescriptor(P_ID_BUILD_COMMENT, P_STR_BUILD_COMMENT);
+	private static final TextPropertyDescriptor BUILD_SUMMARY_DESCRIPTOR = new TextPropertyDescriptor(P_ID_BUILD_SUMMARY_KIND, P_STR_BUILD_SUMMARY_KIND);
+	private static final PropertyDescriptor BUILD_IS_BASELINE_DESCRIPTOR = new PropertyDescriptor(P_ID_BUILD_IS_BASELINE, P_STR_BUILD_IS_BASELINE);
+	private static final PropertyDescriptor BUILD_BASELINE_DESCRIPTOR = new PropertyDescriptor(P_ID_BUILD_BASELINE, P_STR_BUILD_BASELINE);
+	private static final TextPropertyDescriptor BUILD_TEST_FAILURE_DESCRIPTOR = new TextPropertyDescriptor(P_ID_BUILD_FAILURE, P_STR_BUILD_FAILURE);
+	private static final PropertyDescriptor BUILD_TEST_DELTA_DESCRIPTOR = new PropertyDescriptor(P_ID_BUILD_DELTA, P_STR_BUILD_DELTA);
+	private static final PropertyDescriptor BUILD_TEST_ERROR_DESCRIPTOR = new PropertyDescriptor(P_ID_BUILD_ERROR, P_STR_BUILD_ERROR);
+	private static final PropertyDescriptor BUILD_STUDENTS_TTEST_DESCRIPTOR = new PropertyDescriptor(P_ID_BUILD_TTEST, P_STR_BUILD_TTEST);
+
+    private static Vector DESCRIPTORS;
+    static Vector initDescriptors(int status) {
+		DESCRIPTORS = new Vector();
+		// Status category
+		DESCRIPTORS.add(getInfosDescriptor(status));
+		DESCRIPTORS.add(getWarningsDescriptor(status));
+		DESCRIPTORS.add(ERROR_DESCRIPTOR);
+		ERROR_DESCRIPTOR.setCategory("Status");
+		// Results category
+        DESCRIPTORS.add(BUILD_DATE_DESCRIPTOR);
+		BUILD_DATE_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_BASELINE_DESCRIPTOR);
+		BUILD_BASELINE_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_COMMENT_DESCRIPTOR);
+		BUILD_COMMENT_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_SUMMARY_DESCRIPTOR);
+		BUILD_SUMMARY_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_IS_BASELINE_DESCRIPTOR);
+		BUILD_IS_BASELINE_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_TEST_FAILURE_DESCRIPTOR);
+		BUILD_TEST_FAILURE_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_TEST_DELTA_DESCRIPTOR);
+		BUILD_TEST_DELTA_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_TEST_ERROR_DESCRIPTOR);
+		BUILD_TEST_ERROR_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.add(BUILD_STUDENTS_TTEST_DESCRIPTOR);
+		BUILD_STUDENTS_TTEST_DESCRIPTOR.setCategory("Results");
+		// Survey category
+		DESCRIPTORS.add(COMMENT_DESCRIPTOR);
+		COMMENT_DESCRIPTOR.setCategory("Survey");
+        return DESCRIPTORS;
+	}
+    static ComboBoxPropertyDescriptor getInfosDescriptor(int status) {
+		List list = new ArrayList();
+		if ((status & SMALL_VALUE) != 0) {
+			list.add("This test and/or its variation has a small value, hence it may not be necessary to spend time on fixing it if a regression occurs");
+		}
+		if ((status & STUDENT_TTEST) != 0) {
+			list.add("The student-t test error on this test is over the threshold");
+		}
+		String[] infos = new String[list.size()];
+		if (list.size() > 0) {
+			list.toArray(infos);
+		}
+		ComboBoxPropertyDescriptor infoDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_INFO, P_STR_STATUS_INFO, infos);
+		infoDescriptor.setCategory("Status");
+		return infoDescriptor;
+	}
+    static PropertyDescriptor getWarningsDescriptor(int status) {
+		List list = new ArrayList();
+		if ((status & BIG_ERROR) != 0) {
+			list.add("The error on this test is over the 3% threshold, hence its result may not be really reliable");
+		}
+		if ((status & NOT_RELIABLE) != 0) {
+			list.add("The results history shows that the variation of its delta is over 20%, hence its result is surely not reliable");
+		}
+		if ((status & NOT_STABLE) != 0) {
+			list.add("The results history shows that the variation of its delta is between 10% and 20%, hence its result may not be really reliable");
+		}
+		if ((status & NO_BASELINE) != 0) {
+			list.add("There's no baseline to compare with");
+		}
+		if ((status & SINGLE_RUN) != 0) {
+			list.add("This test has only one run, hence no error can be computed to verify if it's stable enough to be reliable");
+		}
+		String[] warnings = new String[list.size()];
+		if (list.size() > 0) {
+			list.toArray(warnings);
+		}
+		ComboBoxPropertyDescriptor warningDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_WARNING, P_STR_STATUS_WARNING, warnings);
+		warningDescriptor.setCategory("Status");
+		return warningDescriptor;
+    }
+    static Vector getDescriptors() {
+    	return DESCRIPTORS;
+	}
+
+	    // Model info
+	boolean important;
+	boolean milestone;
+
+public BuildResultsElement(AbstractResults results, ResultsElement parent) {
+	super(results, parent);
+	initInfo();
+}
+
+public BuildResultsElement(String name, ResultsElement parent) {
+	super(name, parent);
+	initInfo();
+}
+
+public int compareTo(Object o) {
+	if (o instanceof BuildResultsElement && getName() != null) {
+		BuildResultsElement element = (BuildResultsElement)o;
+		if (element.getName() != null) {
+			String buildDate = Util.getBuildDate(element.name);
+			return Util.getBuildDate(this.name).compareTo(buildDate);
+		}
+	}
+	return super.compareTo(o);
+}
+
+ResultsElement createChild(AbstractResults testResults) {
+	return null;
+}
+
+BuildResults getBuildResults() {
+	return (BuildResults) this.results;
+}
+
+public Object[] getChildren(Object o) {
+	if (this.results == null) {
+		return new Object[0];
+	}
+	if (this.children == null) {
+		initChildren();
+	}
+	return this.children;
+}
+
+public Object getEditableValue() {
+	if (this.results == null)  {
+		return "Build "+this.name;
+	}
+	return this.results.toString();
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
+ */
+public IPropertyDescriptor[] getPropertyDescriptors() {
+	Vector descriptors = getDescriptors();
+	if (descriptors == null) {
+		descriptors = initDescriptors(getStatus());
+	}
+	int size = descriptors.size();
+	IPropertyDescriptor[] descriptorsArray = new IPropertyDescriptor[size];
+	descriptorsArray[0] = getInfosDescriptor(getStatus());
+	descriptorsArray[1] = getWarningsDescriptor(getStatus());
+	for (int i=2; i<size; i++) {
+		descriptorsArray[i] = (IPropertyDescriptor) descriptors.get(i);
+	}
+	return descriptorsArray;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
+ */
+public Object getPropertyValue(Object propKey) {
+	BuildResults buildResults = getBuildResults();
+	if (buildResults != null) {
+		ConfigResults configResults = (ConfigResults) buildResults.getParent();
+		BuildResults baselineResults = configResults.getBaselineBuildResults(buildResults.getName());
+	    if (propKey.equals(P_ID_BUILD_DATE))
+	        return buildResults.getDate();
+	    if (propKey.equals(P_ID_BUILD_COMMENT))
+	        return buildResults.getComment();
+	    if (propKey.equals(P_ID_BUILD_SUMMARY_KIND)) {
+	    	int summaryKind = buildResults.getSummaryKind();
+			if (summaryKind == 1) {
+	    		return "global";
+	    	}
+	    	if (summaryKind >= 0) {
+	    		return "component";
+	    	}
+	    	return "none";
+	    }
+	    if (propKey.equals(P_ID_BUILD_IS_BASELINE))
+	        return new Boolean(buildResults.isBaseline());
+	    if (propKey.equals(P_ID_BUILD_FAILURE))
+	        return buildResults.getFailure();
+	    if (baselineResults != null) {
+		    if (propKey.equals(P_ID_BUILD_BASELINE)) {
+		        return baselineResults.getName();
+		    }
+			double buildValue = buildResults.getValue();
+			double baselineValue = baselineResults.getValue();
+			double delta = (baselineValue - buildValue) / baselineValue;
+			if (Double.isNaN(delta)) {
+			    if (propKey.equals(P_ID_BUILD_DELTA) || propKey.equals(P_ID_BUILD_ERROR)) {
+			        return new Double(Double.NaN);
+			    }
+			} else  if (propKey.equals(P_ID_BUILD_DELTA)) {
+				return new Double(delta);
+		    } else {
+				long baselineCount = baselineResults.getCount();
+				long currentCount = buildResults.getCount();
+				if (baselineCount > 1 && currentCount > 1) {
+					if (propKey.equals(P_ID_BUILD_TTEST)) {
+						double ttestValue = Util.computeTTest(baselineResults, buildResults);
+						int degreeOfFreedom = (int) (baselineResults.getCount()+buildResults.getCount()-2);
+						if (ttestValue >= 0 && StatisticsUtil.getStudentsT(degreeOfFreedom, StatisticsUtil.T90) >= ttestValue) {
+							return new Double(ttestValue);
+						}
+					}
+				    if (propKey.equals(P_ID_BUILD_ERROR)) {
+						double baselineError = baselineResults.getError();
+						double currentError = buildResults.getError();
+						double error = Double.isNaN(baselineError)
+								? currentError / baselineValue
+								: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
+				        return new Double(error);
+				    }
+				} else {
+				    if (propKey.equals(P_ID_BUILD_ERROR))
+				        return new Double(-1);
+				}
+			}
+	    }
+	}
+	if (propKey.equals(P_ID_STATUS_ERROR)) {
+		if ((getStatus() & BIG_DELTA) != 0) {
+			return "The delta on this test is over the 10% threshold, hence may indicate a possible regression.";
+		}
+	}
+	return super.getPropertyValue(propKey);
+}
+
+/**
+ * Return the statistics of the build along its history.
+ *
+ * @return An array of double built as follows:
+ * <ul>
+ * <li>0:	numbers of values</li>
+ * <li>1:	mean of values</li>
+ * <li>2:	standard deviation of these values</li>
+ * <li>3:	coefficient of variation of these values</li>
+ * </ul>
+ */
+double[] getStatistics() {
+	if (this.statistics  == null) {
+		this.statistics = ((ConfigResults)getBuildResults().getParent()).getStatistics(Util.BASELINE_BUILD_PREFIXES);
+	}
+	return this.statistics;
+}
+
+void initChildren() {
+	BuildResults buildResults = (BuildResults) this.results;
+	Dim[] dimensions = buildResults.getDimensions();
+	int length = dimensions.length;
+	this.children = new DimResultsElement[length];
+	for (int i=0; i<length; i++) {
+		this.children[i] = new DimResultsElement(this.results, this, dimensions[i]);
+	}
+}
+
+/*
+ * Init information
+ */
+void initInfo() {
+	this.milestone = Util.isMilestone(getName());
+	this.important = this.milestone || Util.getNextMilestone(this.name) == null;
+}
+
+void initStatus() {
+	if (this.results == null) {
+		if (this.parent.isInitialized()) {
+			if (((PerformanceResultsElement) this.parent).hasRead(this)) {
+				this.status = READ;
+			} else {
+				this.status = UNREAD;
+			}
+		} else {
+			this.status = UNKNOWN;
+		}
+	} else if (getBuildResults().isBaseline()) {
+		// TODO (frederic) report high variation in baseline results along history
+		this.status = READ;
+	} else {
+		initStatus(getBuildResults());
+	}
+}
+
+/**
+ * Returns whether the build is important to be shown.
+ * This is the case for milestone builds or for the last builds.
+ *
+ * @return <code>true</code>  or <code>false</code> .
+ */
+public boolean isImportant() {
+	return this.important;
+}
+
+/**
+ * Returns whether the build is a milestone one or not.
+ *
+ * @return <code>true</code>  or <code>false</code> .
+ */
+public boolean isMilestone() {
+	return this.milestone;
+}
+
+public boolean isRead() {
+	return (getStatus()  & STATE_MASK) == READ;
+}
+
+public boolean isUnknown() {
+	return (getStatus()  & STATE_MASK) == UNKNOWN;
+}
+
+/* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+	return getName();
+}
+public boolean isBefore(String build) {
+	if (this.results != null) {
+		return Util.getBuildDate(this.name).compareTo(Util.getBuildDate(build)) <= 0;
+	}
+	return true;
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/BuildResultsProperties.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/BuildResultsProperties.java
new file mode 100644
index 0000000..443c00f
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/BuildResultsProperties.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.Vector;
+
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.TextPropertyDescriptor;
+
+public class BuildResultsProperties {
+
+	    // Property descriptors
+    static final String P_ID_SMALL_VALUE = "BuildResultsProperties.small_value"; //$NON-NLS-1$
+    static final String P_ID_NO_BASELINE = "BuildResultsProperties.no_baseline"; //$NON-NLS-1$
+    static final String P_ID_SINGLE_RUN = "BuildResultsProperties.single_run"; //$NON-NLS-1$
+    static final String P_ID_BIG_ERROR = "BuildResultsProperties.big_error"; //$NON-NLS-1$
+    static final String P_ID_STUDENT_TTEST = "BuildResultsProperties.ttest"; //$NON-NLS-1$
+    static final String P_ID_NOT_STABLE = "BuildResultsProperties.not_stable"; //$NON-NLS-1$
+    static final String P_ID_NOT_RELIABLE = "BuildResultsProperties.not_reliable"; //$NON-NLS-1$
+    static final String P_ID_BIG_DELTA = "BuildResultsProperties.big_delta"; //$NON-NLS-1$
+    static final String P_STR_SMALL_VALUE = "small value"; //$NON-NLS-1$
+    static final String P_STR_NO_BASELINE = "no baseline"; //$NON-NLS-1$
+    static final String P_STR_SINGLE_RUN = "single run"; //$NON-NLS-1$
+    static final String P_STR_BIG_ERROR = "big error"; //$NON-NLS-1$
+    static final String P_STR_STUDENT_TTEST = "student ttest"; //$NON-NLS-1$
+    static final String P_STR_NOT_STABLE = "not stable"; //$NON-NLS-1$
+    static final String P_STR_NOT_RELIABLE = "not reliable"; //$NON-NLS-1$
+    static final String P_STR_BIG_DELTA = "delta error"; //$NON-NLS-1$
+    private static Vector descriptors;
+    static {
+        descriptors = new Vector();
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_SMALL_VALUE, P_STR_SMALL_VALUE));
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_NO_BASELINE, P_STR_NO_BASELINE));
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_SINGLE_RUN, P_STR_SINGLE_RUN));
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_BIG_ERROR, P_STR_BIG_ERROR));
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_STUDENT_TTEST, P_STR_STUDENT_TTEST));
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_NOT_STABLE, P_STR_NOT_STABLE));
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_NOT_RELIABLE, P_STR_NOT_RELIABLE));
+        descriptors.addElement(new TextPropertyDescriptor(P_ID_BIG_DELTA, P_STR_BIG_DELTA));
+    }
+    static Vector getDescriptors() {
+        return descriptors;
+	}
+
+    private int status;
+
+public BuildResultsProperties() {
+}
+
+void setStatus(int status) {
+	this.status = status;
+}
+
+public Object getEditableValue() {
+	return null;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
+ */
+public IPropertyDescriptor[] getPropertyDescriptors() {
+    return (IPropertyDescriptor[]) getDescriptors().toArray(
+            new IPropertyDescriptor[getDescriptors().size()]);
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
+ */
+public Object getPropertyValue(Object propKey) {
+	if (propKey.equals(P_ID_SMALL_VALUE)) {
+		if ((this.status & ResultsElement.SMALL_VALUE) != 0) {
+			return "This test and/or its variation has a small value, hence it may not be necessary to spend time on fixing it if a regression occurs.";
+		}
+	}
+	if (propKey.equals(P_ID_NO_BASELINE)) {
+		if ((this.status & ResultsElement.NO_BASELINE) != 0) {
+			return "There's no baseline to compare with.";
+		}
+	}
+	if (propKey.equals(P_ID_SINGLE_RUN)) {
+		if ((this.status & ResultsElement.SINGLE_RUN) != 0) {
+			return "This test has only one run, hence no error can be computed to verify if it's stable enough to be reliable.";
+		}
+	}
+	if (propKey.equals(P_ID_BIG_ERROR)) {
+		if ((this.status & ResultsElement.BIG_ERROR) != 0) {
+			return "The error on this test is over the 3% threshold, hence its result may not be really reliable.";
+		}
+	}
+	if (propKey.equals(P_ID_STUDENT_TTEST)) {
+		if ((this.status & ResultsElement.STUDENT_TTEST) != 0) {
+			return "The student-t on this test is over the threshold";
+		}
+	}
+	if (propKey.equals(P_ID_NOT_STABLE)) {
+		if ((this.status & ResultsElement.NOT_STABLE) != 0) {
+			return "The results history shows that the variation of its delta is between 10% and 20%, hence its result may not be really reliable.";
+		}
+	}
+	if (propKey.equals(P_ID_NOT_RELIABLE)) {
+		if ((this.status & ResultsElement.NOT_RELIABLE) != 0) {
+			return "The results history shows that the variation of its delta is over 20%, hence its result is surely not reliable.";
+		}
+	}
+	if (propKey.equals(P_ID_BIG_DELTA)) {
+		if ((this.status & ResultsElement.BIG_DELTA) != 0) {
+			return "The delta on this test is over the 10% threshold, hence may indicate a possible regression.";
+		}
+	}
+	return null;
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ComponentResultsElement.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ComponentResultsElement.java
new file mode 100644
index 0000000..d72c0f4
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ComponentResultsElement.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.test.internal.performance.results.db.AbstractResults;
+import org.eclipse.test.internal.performance.results.db.ComponentResults;
+import org.eclipse.test.internal.performance.results.db.PerformanceResults;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.PropertyDescriptor;
+import org.eclipse.ui.views.properties.TextPropertyDescriptor;
+
+public class ComponentResultsElement extends ResultsElement {
+
+	// Property descriptors
+	static final String P_ID_NAME = "ComponentResultsElement.name"; //$NON-NLS-1$
+	static final String P_ID_CURRENT_BUILD = "ComponentResultsElement.currentbuild"; //$NON-NLS-1$
+	static final String P_ID_BASELINE_BUILD = "ComponentResultsElement.baselinebuild"; //$NON-NLS-1$
+
+	static final String P_STR_NAME = "name"; //$NON-NLS-1$
+	static final String P_STR_CURRENT_BUILD = "current build"; //$NON-NLS-1$
+	static final String P_STR_BASELINE_BUILD = "baseline build"; //$NON-NLS-1$
+
+	private static final TextPropertyDescriptor NAME_DESCRIPTOR = new TextPropertyDescriptor(P_ID_NAME, P_STR_NAME);
+	private static final PropertyDescriptor CURRENT_BUILD_DESCRIPTOR = new PropertyDescriptor(P_ID_CURRENT_BUILD, P_STR_CURRENT_BUILD);
+	private static final PropertyDescriptor BASELINE_BUILD_DESCRIPTOR = new PropertyDescriptor(P_ID_BASELINE_BUILD, P_STR_BASELINE_BUILD);
+
+    private static Vector DESCRIPTORS;
+    static Vector initDescriptors(int status) {
+        DESCRIPTORS = new Vector();
+		// Status category
+		DESCRIPTORS.add(getInfosDescriptor(status));
+		DESCRIPTORS.add(getWarningsDescriptor(status));
+		DESCRIPTORS.add(ERROR_DESCRIPTOR);
+		ERROR_DESCRIPTOR.setCategory("Status");
+		// Results category
+		DESCRIPTORS.addElement(NAME_DESCRIPTOR);
+		NAME_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CURRENT_BUILD_DESCRIPTOR);
+		CURRENT_BUILD_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(BASELINE_BUILD_DESCRIPTOR);
+		BASELINE_BUILD_DESCRIPTOR.setCategory("Results");
+		// Survey category
+		DESCRIPTORS.add(COMMENT_DESCRIPTOR);
+		COMMENT_DESCRIPTOR.setCategory("Survey");
+        return DESCRIPTORS;
+	}
+    static Vector getDescriptors() {
+    	return DESCRIPTORS;
+	}
+
+public ComponentResultsElement(String name, ResultsElement parent) {
+	super(name, parent);
+}
+
+public ComponentResultsElement(AbstractResults results, ResultsElement parent) {
+	super(results, parent);
+}
+
+/*
+ * Do not create non-fingerprint child when only fingerprint is specified.
+ *
+ * @see org.eclipse.test.internal.performance.results.model.ResultsElement#createChild(org.eclipse.test.internal.performance.results.db.AbstractResults)
+ */
+ResultsElement createChild(AbstractResults testResults) {
+//	if (onlyFingerprints()) {
+//		ScenarioResults scenarioResults = (ScenarioResults) testResults;
+//		if (!scenarioResults.hasSummary()) {
+//			return null;
+//		}
+//	}
+	return new ScenarioResultsElement(testResults, this);
+}
+
+/**
+ * Get all results numbers for a given machine of the current component.
+ *
+ * @param configName The name of the configuration to get numbers
+ * @param fingerprints Set whether only fingerprints scenario should be taken into account
+ * @return A list of lines. Each line represent a build and is a list of either strings or values.
+ */
+public List getConfigNumbers(String configName, boolean fingerprints) {
+	if (this.results == null) return null;
+	return ((ComponentResults)this.results).getConfigNumbers(configName, fingerprints, new ArrayList());
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
+ */
+public IPropertyDescriptor[] getPropertyDescriptors() {
+	Vector descriptors = getDescriptors();
+	if (descriptors == null) {
+		descriptors = initDescriptors(getStatus());
+	}
+	int size = descriptors.size();
+	IPropertyDescriptor[] descriptorsArray = new IPropertyDescriptor[size];
+	descriptorsArray[0] = getInfosDescriptor(getStatus());
+	descriptorsArray[1] = getWarningsDescriptor(getStatus());
+	for (int i=2; i<size; i++) {
+		descriptorsArray[i] = (IPropertyDescriptor) descriptors.get(i);
+	}
+	return descriptorsArray;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
+ */
+public Object getPropertyValue(Object propKey) {
+	if (propKey.equals(P_ID_NAME)) {
+		return getName();
+	}
+	if (propKey.equals(P_ID_CURRENT_BUILD)) {
+		if (this.results == null) {
+			PerformanceResultsElement performanceResultsElement = (PerformanceResultsElement) getParent(null);
+			return performanceResultsElement.getName();
+		}
+		PerformanceResults performanceResults = (PerformanceResults) this.results.getParent();
+		return performanceResults.getName();
+	}
+	if (propKey.equals(P_ID_BASELINE_BUILD)) {
+		if (this.results == null) {
+			return "?";
+		}
+		PerformanceResults performanceResults = (PerformanceResults) this.results.getParent();
+		return performanceResults.getBaselineName();
+	}
+    return super.getPropertyValue(propKey);
+}
+
+/**
+ * Get the list of the scenarios results from the model. Put only fingerprint ones if specified.
+ *
+ * @param fingerprint Tell whether only fingerprint scenarios are expected or not.
+ * @return A list of {@link ScenarioResults}.
+ */
+public List getScenarios(boolean fingerprint) {
+	if (!fingerprint) {
+		return Arrays.asList(this.results.getChildren());
+	}
+	List scenarios = new ArrayList();
+	if (this.results != null) {
+		Iterator iterator = this.results.getResults();
+		while (iterator.hasNext()) {
+			ScenarioResults scenarioResults = (ScenarioResults) iterator.next();
+			if (scenarioResults.hasSummary()) {
+				scenarios.add(scenarioResults);
+			}
+		}
+	}
+	return scenarios;
+}
+
+/**
+ * Get the list of the scenarios names. Put only fingerprint ones if specified.
+ *
+ * @param fingerprint Tell whether only fingerprint scenarios are expected or not.
+ * @return A list of {@link String}.
+ */
+public List getScenariosLabels(boolean fingerprint) {
+	List labels = new ArrayList();
+	if (this.results != null) {
+		AbstractResults[] scenarios = this.results.getChildren();
+		int length = scenarios.length;
+		for (int i=0; i<length; i++) {
+			ScenarioResults scenarioResults = (ScenarioResults) scenarios[i];
+			if (!fingerprint || scenarioResults.hasSummary()) {
+				labels.add(scenarioResults.getLabel());
+			}
+		}
+	}
+	return labels;
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.model.ResultsElement#initStatus()
+ */
+void initStatus() {
+	if (this.results == null) {
+		this.status = UNREAD;
+	} else {
+		super.initStatus();
+	}
+}
+
+StringBuffer getFailures(StringBuffer buffer, int kind, StringBuffer excluded) {
+	// Write status for scenarios having error
+	if ((getStatus() & ERROR_MASK) != 0) {
+
+		// Get children status
+		StringBuffer childrenBuffer = super.getFailures(new StringBuffer(), kind, excluded);
+
+		// Write status on file if not excluded
+		if (childrenBuffer.length() > 0) {
+			buffer.append(getName());
+			IEclipsePreferences preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+			String comment = preferences.get(getId(), null);
+			if (comment != null) {
+				if ((kind & IPerformancesConstants.STATUS_VALUES) != 0) {
+					buffer.append("												");
+				} else {
+					buffer.append("			");
+				}
+				buffer.append(comment);
+			}
+			buffer.append(Util.LINE_SEPARATOR);
+			buffer.append(childrenBuffer);
+			buffer.append(Util.LINE_SEPARATOR);
+		}
+	}
+	return buffer;
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ConfigResultsElement.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ConfigResultsElement.java
new file mode 100644
index 0000000..e6d199b
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ConfigResultsElement.java
@@ -0,0 +1,498 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.test.internal.performance.results.db.*;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.views.properties.ComboBoxPropertyDescriptor;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.PropertyDescriptor;
+import org.eclipse.ui.views.properties.TextPropertyDescriptor;
+
+public class ConfigResultsElement extends ResultsElement {
+
+	// Elements
+	BuildResultsElement currentBuild, baselineBuild;
+
+	// Property descriptors
+	static final String P_ID_CONFIG_NAME = "ConfigResultsElement.name"; //$NON-NLS-1$
+	static final String P_ID_CONFIG_DESCRIPTION = "ConfigResultsElement.description"; //$NON-NLS-1$
+	static final String P_ID_CONFIG_CURRENT_BUILD = "ConfigResultsElement.currentbuild"; //$NON-NLS-1$
+	static final String P_ID_CONFIG_BASELINE_BUILD = "ConfigResultsElement.baselinebuild"; //$NON-NLS-1$
+	static final String P_ID_CONFIG_BASELINED = "ConfigResultsElement.baselined"; //$NON-NLS-1$
+	static final String P_ID_CONFIG_VALID = "ConfigResultsElement.valid"; //$NON-NLS-1$
+	static final String P_ID_CONFIG_DELTA = "ConfigResultsElement.delta"; //$NON-NLS-1$
+	static final String P_ID_CONFIG_ERROR = "ConfigResultsElement.error"; //$NON-NLS-1$
+
+	static final String P_STR_CONFIG_NAME = "internal name"; //$NON-NLS-1$
+	static final String P_STR_CONFIG_DESCRIPTION = "description"; //$NON-NLS-1$
+	static final String P_STR_CONFIG_CURRENT_BUILD = "current build"; //$NON-NLS-1$
+	static final String P_STR_CONFIG_BASELINE_BUILD = "baseline build"; //$NON-NLS-1$
+	static final String P_STR_CONFIG_BASELINED = "has baseline"; //$NON-NLS-1$
+	static final String P_STR_CONFIG_VALID = "is valid"; //$NON-NLS-1$
+	static final String P_STR_CONFIG_DELTA = "delta with baseline"; //$NON-NLS-1$
+	static final String P_STR_CONFIG_ERROR = "delta error"; //$NON-NLS-1$
+
+	private static final TextPropertyDescriptor CONFIG_NAME_DESCRIPTOR = new TextPropertyDescriptor(P_ID_CONFIG_NAME, P_STR_CONFIG_NAME);
+	private static final TextPropertyDescriptor CONFIG_DESCRIPTION_DESCRIPTOR = new TextPropertyDescriptor(P_ID_CONFIG_DESCRIPTION, P_STR_CONFIG_DESCRIPTION);
+	private static final PropertyDescriptor CONFIG_CURRENT_BUILD_DESCRIPTOR = new PropertyDescriptor(P_ID_CONFIG_CURRENT_BUILD, P_STR_CONFIG_CURRENT_BUILD);
+	private static final PropertyDescriptor CONFIG_BASELINE_BUILD_DESCRIPTOR = new PropertyDescriptor(P_ID_CONFIG_BASELINE_BUILD, P_STR_CONFIG_BASELINE_BUILD);
+	private static final PropertyDescriptor CONFIG_BASELINED_DESCRIPTOR = new PropertyDescriptor(P_ID_CONFIG_BASELINED, P_STR_CONFIG_BASELINED);
+	private static final PropertyDescriptor CONFIG_VALID_DESCRIPTOR = new PropertyDescriptor(P_ID_CONFIG_VALID, P_STR_CONFIG_VALID);
+	private static final PropertyDescriptor CONFIG_DELTA_DESCRIPTOR = new PropertyDescriptor(P_ID_CONFIG_DELTA, P_STR_CONFIG_DELTA);
+	private static final PropertyDescriptor CONFIG_ERROR_DESCRIPTOR = new PropertyDescriptor(P_ID_CONFIG_ERROR, P_STR_CONFIG_ERROR);
+
+    private static Vector DESCRIPTORS;
+    static Vector initDescriptors(int status) {
+		DESCRIPTORS = new Vector();
+		// Status category
+		DESCRIPTORS.add(getInfosDescriptor(status));
+		DESCRIPTORS.add(getWarningsDescriptor(status));
+		DESCRIPTORS.add(ERROR_DESCRIPTOR);
+		ERROR_DESCRIPTOR.setCategory("Status");
+		// Results category
+		DESCRIPTORS.addElement(CONFIG_NAME_DESCRIPTOR);
+		CONFIG_NAME_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CONFIG_DESCRIPTION_DESCRIPTOR);
+		CONFIG_DESCRIPTION_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CONFIG_CURRENT_BUILD_DESCRIPTOR);
+		CONFIG_CURRENT_BUILD_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CONFIG_BASELINE_BUILD_DESCRIPTOR);
+		CONFIG_BASELINE_BUILD_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CONFIG_BASELINED_DESCRIPTOR);
+		CONFIG_BASELINED_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CONFIG_VALID_DESCRIPTOR);
+		CONFIG_VALID_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CONFIG_DELTA_DESCRIPTOR);
+		CONFIG_DELTA_DESCRIPTOR.setCategory("Results");
+		DESCRIPTORS.addElement(CONFIG_ERROR_DESCRIPTOR);
+		CONFIG_ERROR_DESCRIPTOR.setCategory("Results");
+		// Survey category
+		DESCRIPTORS.add(COMMENT_DESCRIPTOR);
+		COMMENT_DESCRIPTOR.setCategory("Survey");
+		return DESCRIPTORS;
+	}
+    static ComboBoxPropertyDescriptor getInfosDescriptor(int status) {
+		List list = new ArrayList();
+		if ((status & SMALL_VALUE) != 0) {
+			list.add("This test and/or its variation has a small value on this machine, hence it may not be necessary to spend time on fixing it if a regression occurs");
+		}
+		if ((status & STUDENT_TTEST) != 0) {
+			list.add("The student-t test error on this machine is over the threshold");
+		}
+		String[] infos = new String[list.size()];
+		if (list.size() > 0) {
+			list.toArray(infos);
+		}
+		ComboBoxPropertyDescriptor infoDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_INFO, P_STR_STATUS_INFO, infos);
+		infoDescriptor.setCategory("Status");
+		return infoDescriptor;
+	}
+    static PropertyDescriptor getWarningsDescriptor(int status) {
+		List list = new ArrayList();
+		if ((status & BIG_ERROR) != 0) {
+			list.add("The error on this machine is over the 3% threshold, hence its result may not be really reliable");
+		}
+		if ((status & NOT_RELIABLE) != 0) {
+			list.add("The results history for this machine shows that the variation of its delta is over 20%, hence its result is surely not reliable");
+		}
+		if ((status & NOT_STABLE) != 0) {
+			list.add("The results history for this machine shows that the variation of its delta is between 10% and 20%, hence its result may not be really reliable");
+		}
+		if ((status & NO_BASELINE) != 0) {
+			list.add("There's no baseline for this machine to compare with");
+		}
+		if ((status & SINGLE_RUN) != 0) {
+			list.add("This test has only one run on this machine, hence no error can be computed to verify if it's stable enough to be reliable");
+		}
+		if ((status & STUDENT_TTEST) != 0) {
+			list.add("The student-t test error on this machine is over the threshold");
+		}
+		String[] warnings = new String[list.size()];
+		if (list.size() > 0) {
+			list.toArray(warnings);
+		}
+		ComboBoxPropertyDescriptor warningDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_WARNING, P_STR_STATUS_WARNING, warnings);
+		warningDescriptor.setCategory("Status");
+		return warningDescriptor;
+	}
+    static Vector getDescriptors() {
+    	return DESCRIPTORS;
+	}
+
+public ConfigResultsElement(AbstractResults results, ResultsElement parent) {
+	super(results, parent);
+}
+
+public int compareTo(Object o) {
+	// TODO Auto-generated method stub
+	return super.compareTo(o);
+}
+ResultsElement createChild(AbstractResults testResults) {
+	return new BuildResultsElement(testResults, this);
+}
+
+BuildResultsElement getBaselineBuild() {
+	if (this.baselineBuild == null) {
+		this.baselineBuild = new BuildResultsElement(getConfigResults().getBaselineBuildResults(), this);
+	}
+	return this.baselineBuild;
+}
+
+/**
+ * Get the baseline build used for this configuration.
+ *
+ * @param buildName The name of the build to have the baseline
+ * @return The baseline build as {@link BuildResultsElement}.
+ */
+public String getBaselineBuildName(String buildName) {
+	return getConfigResults().getBaselineBuildResults(buildName).getName();
+}
+
+private ConfigResults getConfigResults() {
+	return (ConfigResults) this.results;
+}
+
+BuildResultsElement getCurrentBuild() {
+	if (this.currentBuild == null) {
+		this.currentBuild = new BuildResultsElement(getConfigResults().getCurrentBuildResults(), this);
+	}
+	return this.currentBuild;
+}
+
+public String getLabel(Object o) {
+	String description = getConfigResults().getDescription();
+	int index = description.indexOf(" (");
+	if (index <= 0) {
+		return description;
+	}
+	return description.substring(0, index);
+}
+
+/*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
+ */
+public IPropertyDescriptor[] getPropertyDescriptors() {
+	Vector descriptors = getDescriptors();
+	if (descriptors == null) {
+		descriptors = initDescriptors(getStatus());
+	}
+	int size = descriptors.size();
+	IPropertyDescriptor[] descriptorsArray = new IPropertyDescriptor[size];
+	descriptorsArray[0] = getInfosDescriptor(getStatus());
+	descriptorsArray[1] = getWarningsDescriptor(getStatus());
+	for (int i=2; i<size; i++) {
+		descriptorsArray[i] = (IPropertyDescriptor) descriptors.get(i);
+	}
+	return descriptorsArray;
+}
+
+/*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang
+ * .Object)
+ */
+public Object getPropertyValue(Object propKey) {
+	ConfigResults configResults = getConfigResults();
+	if (propKey.equals(P_ID_CONFIG_NAME)) {
+		return configResults.getName();
+	}
+	if (propKey.equals(P_ID_CONFIG_DESCRIPTION)) {
+		return configResults.getDescription();
+	}
+	if (propKey.equals(P_ID_CONFIG_CURRENT_BUILD)) {
+		return getCurrentBuild();
+	}
+	if (propKey.equals(P_ID_CONFIG_BASELINE_BUILD)) {
+		return getBaselineBuild();
+	}
+	if (propKey.equals(P_ID_CONFIG_BASELINED)) {
+		return new Boolean(configResults.isBaselined());
+	}
+	if (propKey.equals(P_ID_CONFIG_VALID)) {
+		return new Boolean(configResults.isValid());
+	}
+	if (propKey.equals(P_ID_CONFIG_DELTA)) {
+		return new Double(configResults.getDelta());
+	}
+	if (propKey.equals(P_ID_CONFIG_ERROR)) {
+		return new Double(configResults.getError());
+	}
+	if (propKey.equals(P_ID_STATUS_ERROR)) {
+		if (getStatus() == MISSING) {
+			PerformanceResultsElement performanceResultsElement = (PerformanceResultsElement) ((ResultsElement)((ResultsElement)getParent(null)).getParent(null)).getParent(null);
+			return "No result for build "+performanceResultsElement.getName()+" on this machine!";
+		}
+		if ((getStatus() & BIG_DELTA) != 0) {
+			return "The delta on this machine is over the 10% threshold, hence may indicate a possible regression";
+		}
+	}
+	return super.getPropertyValue(propKey);
+}
+
+/**
+ * Return the statistics of the build along its history.
+ *
+ * @return An array of double built as follows:
+ * <ul>
+ * <li>0:	numbers of values</li>
+ * <li>1:	mean of values</li>
+ * <li>2:	standard deviation of these values</li>
+ * <li>3:	coefficient of variation of these values</li>
+ * </ul>
+ */
+public double[] getStatistics() {
+	if (this.statistics  == null) {
+		this.statistics = getConfigResults().getStatistics(Util.BASELINE_BUILD_PREFIXES);
+	}
+	return this.statistics;
+}
+
+void initStatus() {
+	ConfigResults configResults = getConfigResults();
+	if (configResults.isValid()) {
+		initStatus(configResults.getCurrentBuildResults());
+	} else {
+		this.status = MISSING;
+	}
+}
+
+/*
+ * Write the element status in the given stream
+ */
+StringBuffer getFailures(StringBuffer buffer, int kind, StringBuffer excluded) {
+	if ((this.status & BIG_DELTA) != 0) { // there's a failure on this config
+
+		// Get numbers
+		int buildsNumber = kind & IPerformancesConstants.STATUS_BUILDS_NUMBER_MASK;
+		ConfigResults configResults = getConfigResults();
+		double[][] numbers = configResults.getLastNumbers(buildsNumber);
+		int numbersLength = numbers.length;
+
+		// if there are several builds to confirm the regression, then verify all deltas
+		if (numbersLength > 1) {
+			if (numbersLength < buildsNumber) {
+				// there's not enough builds to wee whether there's a real regression, hence skip result
+				if (excluded != null) {
+					excluded.append(configResults+" excluded from status because there's only "+numbersLength+" builds available although "+buildsNumber+" is required to decide a regression is confirmed or not!");
+					excluded.append(Util.LINE_SEPARATOR);
+				}
+				return buffer;
+			}
+			int confirmed = 1;
+			for (int i=1; i<numbersLength; i++) {
+				if (numbers[i] != null && numbers[i][AbstractResults.DELTA_VALUE_INDEX] < -0.1) {
+					confirmed++;
+				}
+			}
+			float ratio = ((float) confirmed) / numbersLength;
+			if (ratio < 0.8) {
+				// more than 20% of previous build didn't fail, hence skip result
+				if (excluded != null) {
+					excluded.append(configResults+" excluded from status because only "+confirmed+" builds failed on last "+buildsNumber+" ones!");
+					excluded.append(Util.LINE_SEPARATOR);
+				}
+				return buffer;
+			}
+		}
+
+		// Add values
+		double[] values = numbers[0];
+		double buildValue = values[AbstractResults.BUILD_VALUE_INDEX];
+		double baselineValue = values[AbstractResults.BASELINE_VALUE_INDEX];
+		double delta = values[AbstractResults.DELTA_VALUE_INDEX];
+		double error = values[AbstractResults.DELTA_ERROR_INDEX];
+		StringBuffer localBuffer = new StringBuffer("		");
+		localBuffer.append(configResults.getName());
+		double[] stats = null;
+		boolean printValues = (kind & IPerformancesConstants.STATUS_VALUES) != 0;
+		if (printValues) {
+			localBuffer.append("	");
+			localBuffer.append(buildValue);
+			localBuffer.append("	");
+			localBuffer.append(baselineValue);
+			localBuffer.append("	");
+			localBuffer.append(buildValue-baselineValue);
+			localBuffer.append("	");
+			localBuffer.append(Util.PERCENTAGE_FORMAT.format(delta));
+			localBuffer.append("	");
+			localBuffer.append(Util.PERCENTAGE_FORMAT.format(error));
+			stats = getStatistics();
+			if (stats != null) {
+				localBuffer.append("	");
+				localBuffer.append((int) stats[0]);
+				localBuffer.append("	");
+				localBuffer.append(Util.DOUBLE_FORMAT.format(stats[1]));
+				localBuffer.append("	");
+				localBuffer.append(Util.DOUBLE_FORMAT.format(stats[2]));
+				localBuffer.append("	");
+				localBuffer.append(Util.PERCENTAGE_FORMAT.format(stats[3]));
+			}
+		}
+
+		/* Add comment
+		IEclipsePreferences preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+		String comment = preferences.get(getId(), null);
+		if (comment != null) {
+			if (stats == null && printValues) {
+				buffer.append("				");
+			}
+			buffer.append("	");
+			buffer.append(comment);
+		}
+		*/
+
+		// Add status info
+		if (this.status != BIG_DELTA) { // there's some other info in the status
+//			if (comment == null) {
+				if (stats == null && printValues) {
+					localBuffer.append("				");
+				}
+//			}
+			localBuffer.append("	");
+			String separator = "";
+
+			// Error
+			if ((this.status & BIG_ERROR) != 0) {
+				int statusErrorLevel = kind & IPerformancesConstants.STATUS_ERROR_LEVEL_MASK;
+				if (statusErrorLevel == IPerformancesConstants.STATUS_ERROR_NOTICEABLE) {
+					// Skip result
+					if (excluded != null) {
+						excluded.append(configResults+" excluded from status due to a noticeable error!");
+						excluded.append(Util.LINE_SEPARATOR);
+					}
+					return buffer;
+				}
+				localBuffer.append(separator);
+				localBuffer.append("error (");
+				localBuffer.append(Util.PERCENTAGE_FORMAT.format(error));
+				localBuffer.append(")");
+				separator = "+";
+				double ratio = -(error/delta);
+				if (ratio > 1) {
+					switch (statusErrorLevel) {
+						case IPerformancesConstants.STATUS_ERROR_INVALID:
+						case IPerformancesConstants.STATUS_ERROR_WEIRD:
+						case IPerformancesConstants.STATUS_ERROR_SUSPICIOUS:
+							// Skip result
+							if (excluded != null) {
+								excluded.append(configResults+" excluded from status due to an invalid error!");
+								excluded.append(Util.LINE_SEPARATOR);
+							}
+							return buffer;
+					}
+					localBuffer.append(": invalid measure!");
+				} else if (ratio > 0.5) {
+					switch (statusErrorLevel) {
+						case IPerformancesConstants.STATUS_ERROR_WEIRD:
+						case IPerformancesConstants.STATUS_ERROR_SUSPICIOUS:
+							// Skip result
+							if (excluded != null) {
+								excluded.append(configResults+" excluded from status due to a weird error!");
+								excluded.append(Util.LINE_SEPARATOR);
+							}
+							return buffer;
+					}
+					localBuffer.append(": weird measure!");
+				} else if (ratio > 0.25) {
+					if (statusErrorLevel == IPerformancesConstants.STATUS_ERROR_SUSPICIOUS) {
+						// Skip result
+						if (excluded != null) {
+							excluded.append(configResults+" excluded from status due to a suspicious error!");
+							excluded.append(Util.LINE_SEPARATOR);
+						}
+						return buffer;
+					}
+					localBuffer.append(": suspicious measure!");
+				}
+			}
+
+			// Small value
+			if ((this.status & SMALL_VALUE) != 0) {
+				int statusSmallValue = kind & IPerformancesConstants.STATUS_SMALL_VALUE_MASK;
+				localBuffer.append(separator);
+				if (buildValue < 100) {
+					if (statusSmallValue == IPerformancesConstants.STATUS_SMALL_VALUE_BUILD) {
+						// Skip result
+						if (excluded != null) {
+							excluded.append(configResults+" excluded from status due to a small build value!");
+							excluded.append(Util.LINE_SEPARATOR);
+						}
+						return buffer;
+					}
+					localBuffer.append("small build value (");
+					localBuffer.append((int)buildValue);
+					localBuffer.append("ms)");
+				}
+				int diff = (int) Math.abs(baselineValue - buildValue);
+				if (diff < 100) {
+					if (statusSmallValue == IPerformancesConstants.STATUS_SMALL_VALUE_DELTA) {
+						// Skip result
+						if (excluded != null) {
+							excluded.append(configResults+" excluded from status due to a small delta value!");
+							excluded.append(Util.LINE_SEPARATOR);
+						}
+						return buffer;
+					}
+					localBuffer.append("small delta value (");
+					localBuffer.append(diff);
+					localBuffer.append("ms)");
+				}
+				separator = "+";
+			}
+
+			// Statistics
+			if ((this.status & NOT_RELIABLE) != 0) {
+				switch (kind & IPerformancesConstants.STATUS_STATISTICS_MASK) {
+					case IPerformancesConstants.STATUS_STATISTICS_UNSTABLE:
+					case IPerformancesConstants.STATUS_STATISTICS_ERRATIC:
+						// Skip result
+						if (excluded != null) {
+							excluded.append(configResults+" excluded from status due to erratic statistics!");
+							excluded.append(Util.LINE_SEPARATOR);
+						}
+						return buffer;
+				}
+				localBuffer.append(separator);
+				localBuffer.append("erratic");
+				separator = "+";
+			} else if ((this.status & NOT_STABLE) != 0) {
+				if ((kind & IPerformancesConstants.STATUS_STATISTICS_UNSTABLE) != 0) {
+					// Skip result
+					if (excluded != null) {
+						excluded.append(configResults+" excluded from status due to unstable statistics!");
+						excluded.append(Util.LINE_SEPARATOR);
+					}
+					return buffer;
+				}
+				localBuffer.append(separator);
+				localBuffer.append("unstable");
+				separator = "+";
+			}
+		}
+
+		// Write status
+		buffer.append(localBuffer);
+		buffer.append(Util.LINE_SEPARATOR);
+	}
+	return buffer;
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/DimResultsElement.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/DimResultsElement.java
new file mode 100644
index 0000000..3a3b88d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/DimResultsElement.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.Vector;
+
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.results.db.*;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.PropertyDescriptor;
+import org.eclipse.ui.views.properties.TextPropertyDescriptor;
+
+public class DimResultsElement extends ResultsElement {
+
+	Dim dim;
+
+	// Property descriptors
+    static final String P_ID_DIMENSION = "DimResultsElement.dim"; //$NON-NLS-1$
+    static final String P_ID_COUNT = "DimResultsElement.count"; //$NON-NLS-1$
+    static final String P_ID_AVERAGE = "DimResultsElement.average"; //$NON-NLS-1$
+    static final String P_ID_STDDEV = "DimResultsElement.stddev"; //$NON-NLS-1$
+    static final String P_ID_ERROR= "DimResultsElement.error"; //$NON-NLS-1$
+    static final String P_ID_HAD_VALUES = "DimResultsElement.hadvalues"; //$NON-NLS-1$
+
+    static final String P_STR_DIMENSION = "dimension"; //$NON-NLS-1$
+    static final String P_STR_COUNT= "count"; //$NON-NLS-1$
+    static final String P_STR_AVERAGE = "average"; //$NON-NLS-1$
+    static final String P_STR_STDDEV = "std dev"; //$NON-NLS-1$
+    static final String P_STR_ERROR = "error"; //$NON-NLS-1$
+    static final String P_STR_HAD_VALUES = "had values"; //$NON-NLS-1$
+
+	private static final TextPropertyDescriptor DIMENSION_DESCRIPTOR = new TextPropertyDescriptor(P_ID_DIMENSION, P_STR_DIMENSION);
+	private static final PropertyDescriptor DIM_COUNT_DESCRIPTOR = new PropertyDescriptor(P_ID_COUNT, P_STR_COUNT);
+	private static final PropertyDescriptor DIM_AVERAGE_DESCRIPTOR = new PropertyDescriptor(P_ID_AVERAGE, P_STR_AVERAGE);
+	private static final PropertyDescriptor DIM_STDDEV_DESCRIPTOR = new PropertyDescriptor(P_ID_STDDEV, P_STR_STDDEV);
+	private static final PropertyDescriptor DIM_ERROR_DESCRIPTOR = new PropertyDescriptor(P_ID_ERROR, P_STR_ERROR);
+	private static final PropertyDescriptor DIM_HAD_VALUES_DESCRIPTOR = new PropertyDescriptor(P_ID_HAD_VALUES, P_STR_HAD_VALUES);
+
+    private static Vector DESCRIPTORS;
+    static Vector initDescriptors(int status) {
+        DESCRIPTORS = new Vector();
+		// Status category
+		DESCRIPTORS.add(getInfosDescriptor(status));
+		DESCRIPTORS.add(getWarningsDescriptor(status));
+		DESCRIPTORS.add(ERROR_DESCRIPTOR);
+		ERROR_DESCRIPTOR.setCategory("Status");
+		// Results category
+        DESCRIPTORS.addElement(DIMENSION_DESCRIPTOR);
+		DIMENSION_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.addElement(DIM_COUNT_DESCRIPTOR);
+		DIM_COUNT_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.addElement(DIM_AVERAGE_DESCRIPTOR);
+		DIM_AVERAGE_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.addElement(DIM_STDDEV_DESCRIPTOR);
+		DIM_STDDEV_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.addElement(DIM_ERROR_DESCRIPTOR);
+		DIM_ERROR_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.addElement(DIM_HAD_VALUES_DESCRIPTOR);
+		DIM_HAD_VALUES_DESCRIPTOR.setCategory("Results");
+		// Survey category
+		DESCRIPTORS.add(COMMENT_DESCRIPTOR);
+		COMMENT_DESCRIPTOR.setCategory("Survey");
+        return DESCRIPTORS;
+   	}
+    static Vector getDescriptors() {
+    	return DESCRIPTORS;
+	}
+
+public DimResultsElement(AbstractResults results, ResultsElement parent, Dim d) {
+	super(results, parent);
+	this.dim = d;
+}
+
+ResultsElement createChild(AbstractResults testResults) {
+	return null;
+}
+
+private BuildResults getBuildResults() {
+	return (BuildResults) this.results;
+}
+
+public String getLabel(Object o) {
+	return this.dim.getName();
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
+ */
+public IPropertyDescriptor[] getPropertyDescriptors() {
+	Vector descriptors = getDescriptors();
+	if (descriptors == null) {
+		descriptors = initDescriptors(getStatus());
+	}
+	int size = descriptors.size();
+	IPropertyDescriptor[] descriptorsArray = new IPropertyDescriptor[size];
+	descriptorsArray[0] = getInfosDescriptor(getStatus());
+	descriptorsArray[1] = getWarningsDescriptor(getStatus());
+	for (int i=2; i<size; i++) {
+		descriptorsArray[i] = (IPropertyDescriptor) descriptors.get(i);
+	}
+	return descriptorsArray;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
+ */
+public Object getPropertyValue(Object propKey) {
+	BuildResults buildResults = getBuildResults();
+    if (propKey.equals(P_ID_DIMENSION)) {
+    	return  this.dim.getDescription();
+    }
+    if (propKey.equals(P_ID_COUNT)) {
+	    long count = buildResults.getCount(this.dim.getId());
+	    return new Double(count);
+    }
+    if (propKey.equals(P_ID_AVERAGE))
+        return new Double(buildResults.getValue(this.dim.getId()));
+    if (propKey.equals(P_ID_STDDEV))
+        return new Double(buildResults.getDeviation(this.dim.getId()));
+    if (propKey.equals(P_ID_ERROR))
+        return new Double(buildResults.getError(this.dim.getId()));
+    if (propKey.equals(P_ID_HAD_VALUES))
+        return new Boolean(buildResults.hadValues());
+    return super.getPropertyValue(propKey);
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/PerformanceResultsElement.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/PerformanceResultsElement.java
new file mode 100644
index 0000000..467bb86
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/PerformanceResultsElement.java
@@ -0,0 +1,379 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.model;
+
+import java.io.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Arrays;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.test.internal.performance.results.db.*;
+import org.eclipse.test.internal.performance.results.ui.BuildsComparisonTable;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.test.performance.ui.Utils;
+
+public class PerformanceResultsElement extends ResultsElement {
+
+// Singleton pattern
+public static PerformanceResultsElement PERF_RESULTS_MODEL = new PerformanceResultsElement();
+
+	String[] buildNames;
+	String lastBuildName;
+	boolean fingerprints = true;
+
+public PerformanceResultsElement() {
+	super();
+}
+
+ResultsElement createChild(AbstractResults testResults) {
+	return new ComponentResultsElement(testResults, this);
+}
+
+public String[] getBaselines() {
+	getBuildNames();
+	if (this.buildNames == null) {
+		return new String[0];
+	}
+	int length = this.buildNames.length;
+	String[] baselines = new String[length];
+	int count = 0;
+	for (int i=0; i<length; i++) {
+		if (this.buildNames[i].startsWith("R-")) {
+			baselines[count++] = this.buildNames[i];
+		}
+	}
+	if (count < length) {
+		System.arraycopy(baselines, 0, baselines = new String [count], 0, count);
+	}
+	return baselines;
+}
+
+public String[] getBuildNames() {
+	if (this.buildNames == null) {
+		this.buildNames = DB_Results.DB_CONNECTION
+			? DB_Results.getBuilds()
+			: this.results == null
+				? new String[0]
+				: getPerformanceResults().getAllBuildNames();
+	}
+	return this.buildNames;
+}
+
+public Object[] getBuilds() {
+	getBuildNames();
+	int length = this.buildNames == null ? 0 : this.buildNames.length;
+	BuildResultsElement[] elements = new BuildResultsElement[length];
+	for (int i=0; i<length; i++) {
+		elements[i] = new BuildResultsElement(this.buildNames[i], this);
+	}
+	return elements;
+}
+
+public String[] getComponents() {
+	if (!isInitialized()) {
+		String[] components = DB_Results.getComponents();
+		int length = components.length;
+		if (length == 0) {
+			DB_Results.queryAllScenarios();
+			components = DB_Results.getComponents();
+		}
+		return components;
+	}
+	return getPerformanceResults().getComponents();
+}
+
+/**
+ * Returns the names of the configurations.
+ *
+ * @return An array of String
+ */
+public String[] getConfigs() {
+	if (!isInitialized()) {
+		String[] configs = DB_Results.getConfigs();
+		int length = configs.length;
+		if (length == 0) {
+			DB_Results.queryAllScenarios();
+			configs = DB_Results.getConfigs();
+		}
+		return configs;
+	}
+	return getPerformanceResults().getConfigNames(false);
+}
+
+/**
+ * Returns the descriptions of the configurations.
+ *
+ * @return An array of String
+ */
+public String[] getConfigDescriptions() {
+	if (!isInitialized()) {
+		String[] descriptions = DB_Results.getConfigDescriptions();
+		int length = descriptions.length;
+		if (length == 0) {
+			DB_Results.queryAllScenarios();
+			descriptions = DB_Results.getConfigDescriptions();
+		}
+		return descriptions;
+	}
+	return getPerformanceResults().getConfigBoxes(false);
+}
+
+public Object[] getElements() {
+	if (!isInitialized()) {
+		String[] components = getComponents();
+		int length = components.length;
+		ComponentResultsElement[] elements = new ComponentResultsElement[length];
+		for (int i=0; i<length; i++) {
+			elements[i] = new ComponentResultsElement(components[i], this);
+		}
+		return elements;
+	}
+	return getChildren(null);
+}
+
+public PerformanceResults getPerformanceResults() {
+	return (PerformanceResults) this.results;
+}
+
+boolean hasRead(BuildResultsElement buildResultsElement) {
+	String[] builds = this.results == null ? getBuildNames() : getPerformanceResults().getAllBuildNames();
+	if (Arrays.binarySearch(builds, buildResultsElement.getName(), Util.BUILD_DATE_COMPARATOR) < 0) {
+		return false;
+	}
+	return true;
+}
+
+public boolean isInitialized() {
+	return super.isInitialized() && this.results.size() > 0;
+}
+
+public void readLocal(File dataDir, IProgressMonitor monitor, String lastBuild) {
+	reset(lastBuild);
+	PerformanceResults performanceResults = getPerformanceResults();
+	performanceResults.setLastBuildName(lastBuild);
+	performanceResults.readLocal(dataDir, monitor);
+}
+
+public void reset(String buildName) {
+	if (buildName == null) {
+		this.results = new PerformanceResults(this.lastBuildName, null, null, System.out);
+	} else {
+		this.results = new PerformanceResults(buildName, null, null, System.out);
+	}
+	this.children = null;
+	this.buildNames = null;
+}
+
+public void resetBuildNames() {
+	this.buildNames = null;
+}
+
+public void updateBuild(String buildName, boolean force, File dataDir, IProgressMonitor monitor) {
+	if (this.results == null) {
+		reset(buildName);
+	}
+	getPerformanceResults().updateBuild(buildName, force, dataDir, monitor);
+}
+
+public void updateBuilds(String[] builds, boolean force, File dataDir, IProgressMonitor monitor) {
+	if (this.results == null) {
+		reset(null);
+	}
+	getPerformanceResults().updateBuilds(builds, force, dataDir, monitor);
+}
+
+/**
+ * Set whether only fingerprints should be taken into account or not.
+ *
+ * @param fingerprints
+ */
+public void setFingerprints(boolean fingerprints) {
+	this.fingerprints = fingerprints;
+	resetStatus();
+}
+
+public void setLastBuildName(String lastBuildName) {
+	this.lastBuildName = lastBuildName;
+	this.name = null;
+}
+
+/*
+ * Write the component status in the given file
+ */
+public StringBuffer writeFailures(File resultsFile, int kind) {
+	if (this.results == null) {
+		return null;
+	}
+	boolean values = (kind & IPerformancesConstants.STATUS_VALUES) != 0;
+	// Write status only for component with error
+	StringBuffer excluded = new StringBuffer();
+	try {
+		DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(resultsFile)));
+		try {
+			StringBuffer buffer = new StringBuffer();
+			// Print build name
+			buffer.append("Status for ");
+			buffer.append(getPerformanceResults().getName());
+			buffer.append(Util.LINE_SEPARATOR);
+			// Print status options
+			if ((kind & ~IPerformancesConstants.STATUS_VALUES) > 0) {
+				buffer.append("Options: ");
+				buffer.append(Util.LINE_SEPARATOR);
+				final int errorLevel = kind & IPerformancesConstants.STATUS_ERROR_LEVEL_MASK;
+				if (errorLevel != 0) {
+					buffer.append("	error level: ");
+					switch (errorLevel) {
+						case IPerformancesConstants.STATUS_ERROR_NONE:
+							buffer.append("include all failures whatever the error level is");
+							break;
+						case IPerformancesConstants.STATUS_ERROR_NOTICEABLE:
+							buffer.append("all failures with at least a noticeable error (> 3%) are excluded!");
+							break;
+						case IPerformancesConstants.STATUS_ERROR_SUSPICIOUS:
+							buffer.append("all failures with at least a suspicious error (> 25%) are excluded!");
+							break;
+						case IPerformancesConstants.STATUS_ERROR_WEIRD:
+							buffer.append("all failures with at least a weird error (> 50%) are excluded!");
+							break;
+						case IPerformancesConstants.STATUS_ERROR_INVALID:
+							buffer.append("all failures with an invalid error (> 100%) are excluded!");
+							break;
+					}
+					buffer.append(Util.LINE_SEPARATOR);
+				}
+				final int smallValue = kind & IPerformancesConstants.STATUS_SMALL_VALUE_MASK;
+				if (smallValue > 0) {
+					buffer.append("	small value: ");
+					switch (smallValue) {
+						case IPerformancesConstants.STATUS_SMALL_VALUE_BUILD:
+							buffer.append("all failures with a small build value (<100ms) are excluded!");
+							break;
+						case IPerformancesConstants.STATUS_SMALL_VALUE_DELTA:
+							buffer.append("all failures with a small delta value (<100ms) are excluded!");
+							break;
+						case IPerformancesConstants.STATUS_SMALL_VALUE_MASK:
+							buffer.append("all failures with a small build or delta value (<100ms) are excluded!");
+							break;
+					}
+					buffer.append(Util.LINE_SEPARATOR);
+				}
+				final int stats = kind & IPerformancesConstants.STATUS_STATISTICS_MASK;
+				if (stats > 0) {
+					buffer.append("	statistics: ");
+					switch (stats) {
+						case IPerformancesConstants.STATUS_STATISTICS_ERRATIC:
+							buffer.append("all failures with erratic baseline results (variation > 20%) are excluded!");
+							break;
+						case IPerformancesConstants.STATUS_STATISTICS_UNSTABLE:
+							buffer.append("all failures with unstable baseline results (10% < variation < 20%) are excluded!");
+							break;
+					}
+					buffer.append(Util.LINE_SEPARATOR);
+				}
+				int buildsNumber = kind & IPerformancesConstants.STATUS_BUILDS_NUMBER_MASK;
+				buffer.append("	builds to confirm a regression: ");
+				buffer.append(buildsNumber);
+				buffer.append(Util.LINE_SEPARATOR);
+			}
+			// Print columns title
+			buffer.append("Component");
+			buffer.append("	Scenario");
+			buffer.append("	Machine");
+			if (values) {
+				buffer.append("			Build		");
+				buffer.append("		History		");
+			}
+			buffer.append("	Comment");
+			buffer.append(Util.LINE_SEPARATOR);
+			if (values) {
+				buffer.append("			value");
+				buffer.append("	baseline");
+				buffer.append("	variation");
+				buffer.append("	delta");
+				buffer.append("	error");
+				buffer.append("	n");
+				buffer.append("	mean");
+				buffer.append("	deviation");
+				buffer.append("	coeff");
+				buffer.append(Util.LINE_SEPARATOR);
+			}
+			stream.write(buffer.toString().getBytes());
+			StringBuffer componentBuffer = getFailures(new StringBuffer(), kind, excluded);
+			if (componentBuffer.length() > 0) {
+				stream.write(componentBuffer.toString().getBytes());
+			}
+		}
+		finally {
+			stream.close();
+		}
+	} catch (FileNotFoundException e) {
+		System.err.println("Can't create output file"+resultsFile); //$NON-NLS-1$
+	} catch (IOException e) {
+		e.printStackTrace();
+	}
+	return excluded;
+}
+
+/*
+ * Write the comparison between two builds in the given file
+ */
+public void writeComparison(File resultsFile, String build, String reference) {
+	if (this.results == null) {
+		return;
+	}
+	try {
+		// Create the stream
+		PrintStream stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(resultsFile)));
+
+		// Print main title
+		stream.print("<link href=\""+Utils.TOOLTIP_STYLE+"\" rel=\"stylesheet\" type=\"text/css\">\n");
+		stream.print("<script src=\""+Utils.TOOLTIP_SCRIPT+"\"></script>\n");
+		stream.print("<script src=\""+Utils.FINGERPRINT_SCRIPT+"\"></script>\n");
+		stream.print(Utils.HTML_DEFAULT_CSS);
+		stream.print("<body>");
+		stream.print("<h2>Performance comparison of ");
+		stream.print(build);
+		stream.print(" relative to ");
+		int index = reference.indexOf('_');
+		if (index > 0) {
+			stream.print(reference.substring(0, index));
+			stream.print(" (");
+			index = reference.lastIndexOf('_');
+			stream.print(reference.substring(index+1, reference.length()));
+			stream.print(')');
+		} else {
+			stream.print(reference);
+		}
+		stream.print("</h2>\n");
+
+		// Print a comparison table for each component
+		try {
+			int length = this.children.length;
+			for (int i=0; i<length; i++) {
+				BuildsComparisonTable table = new BuildsComparisonTable(this.children[i].getName(), stream, build, reference);
+				table.print(getPerformanceResults());
+			}
+		}
+		finally {
+			stream.print("</body>");
+			stream.close();
+		}
+	} catch (FileNotFoundException e) {
+		System.err.println("Can't create output file"+resultsFile); //$NON-NLS-1$
+	}
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ResultsElement.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ResultsElement.java
new file mode 100644
index 0000000..ecd5aac
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ResultsElement.java
@@ -0,0 +1,599 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.test.internal.performance.eval.StatisticsUtil;
+import org.eclipse.test.internal.performance.results.db.AbstractResults;
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+import org.eclipse.ui.views.properties.ComboBoxPropertyDescriptor;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.IPropertySource;
+import org.eclipse.ui.views.properties.PropertyDescriptor;
+import org.eclipse.ui.views.properties.TextPropertyDescriptor;
+import org.osgi.service.prefs.BackingStoreException;
+
+/**
+ * An Organization Element
+ */
+public abstract class ResultsElement implements IAdaptable, IPropertySource, IWorkbenchAdapter, Comparable {
+
+	// Image descriptors
+	private static final ISharedImages WORKBENCH_SHARED_IMAGES = PlatformUI.getWorkbench().getSharedImages();
+	public static final Image ERROR_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
+	public static final ImageDescriptor ERROR_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJS_ERROR_TSK);
+	public static final Image WARN_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
+	public static final ImageDescriptor WARN_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJS_WARN_TSK);
+	public static final Image INFO_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_OBJS_INFO_TSK);
+	public static final ImageDescriptor INFO_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK);
+	public static final Image HELP_IMAGE = WORKBENCH_SHARED_IMAGES.getImage(ISharedImages.IMG_LCL_LINKTO_HELP);
+	public static final ImageDescriptor HELP_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_LCL_LINKTO_HELP);
+	public static final ImageDescriptor FOLDER_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER);
+	public static final ImageDescriptor CONNECT_IMAGE_DESCRIPTOR = WORKBENCH_SHARED_IMAGES.getImageDescriptor(ISharedImages.IMG_ELCL_SYNCED);
+
+	// Model
+    ResultsElement parent;
+	AbstractResults results;
+	ResultsElement[] children;
+	String name;
+	int status = -1;
+
+	// Stats
+    double[] statistics;
+
+	// Status constants
+	// state
+	static final int UNKNOWN = 0x01;
+	static final int UNREAD = 0x02;
+	static final int READ = 0x04;
+	static final int MISSING = 0x08;
+	public static final int STATE_MASK = 0x0F;
+	// info
+	static final int SMALL_VALUE = 0x0010;
+	static final int STUDENT_TTEST = 0x0020;
+	public static final int INFO_MASK = 0x0030;
+	// warning
+	static final int NO_BASELINE = 0x0040;
+	static final int SINGLE_RUN = 0x0080;
+	static final int BIG_ERROR = 0x0100;
+	static final int NOT_STABLE = 0x0200;
+	static final int NOT_RELIABLE = 0x0400;
+	public static final int WARNING_MASK = 0x0FC0;
+	// error
+	static final int BIG_DELTA = 0x1000;
+	public static final int ERROR_MASK = 0xF000;
+
+	// Property descriptors
+	static final String P_ID_STATUS_INFO = "ResultsElement.status_info"; //$NON-NLS-1$
+	static final String P_ID_STATUS_WARNING = "ResultsElement.status_warning"; //$NON-NLS-1$
+	static final String P_ID_STATUS_ERROR = "ResultsElement.status_error"; //$NON-NLS-1$
+	static final String P_ID_STATUS_COMMENT = "ResultsElement.status_comment"; //$NON-NLS-1$
+
+	static final String P_STR_STATUS_INFO = " info"; //$NON-NLS-1$
+	static final String P_STR_STATUS_WARNING = "warning"; //$NON-NLS-1$
+	static final String P_STR_STATUS_ERROR = "error"; //$NON-NLS-1$
+	static final String P_STR_STATUS_COMMENT = "comment"; //$NON-NLS-1$
+	static final String[] NO_VALUES = new String[0];
+
+	private static Vector DESCRIPTORS;
+	static final TextPropertyDescriptor COMMENT_DESCRIPTOR = new TextPropertyDescriptor(P_ID_STATUS_COMMENT, P_STR_STATUS_COMMENT);
+	static final TextPropertyDescriptor ERROR_DESCRIPTOR = new TextPropertyDescriptor(P_ID_STATUS_ERROR, P_STR_STATUS_ERROR);
+    static Vector initDescriptors(int status) {
+		DESCRIPTORS = new Vector();
+		// Status category
+		DESCRIPTORS.add(getInfosDescriptor(status));
+		DESCRIPTORS.add(getWarningsDescriptor(status));
+		DESCRIPTORS.add(ERROR_DESCRIPTOR);
+		ERROR_DESCRIPTOR.setCategory("Status");
+		// Survey category
+		DESCRIPTORS.add(COMMENT_DESCRIPTOR);
+		COMMENT_DESCRIPTOR.setCategory("Survey");
+		return DESCRIPTORS;
+	}
+    static Vector getDescriptors() {
+    	return DESCRIPTORS;
+	}
+    static ComboBoxPropertyDescriptor getInfosDescriptor(int status) {
+		List list = new ArrayList();
+		if ((status & SMALL_VALUE) != 0) {
+			list.add("Some builds have tests with small values");
+		}
+		if ((status & STUDENT_TTEST) != 0) {
+			list.add("Some builds have student-t test error over the threshold");
+		}
+		String[] infos = new String[list.size()];
+		if (list.size() > 0) {
+			list.toArray(infos);
+		}
+		ComboBoxPropertyDescriptor infoDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_INFO, P_STR_STATUS_INFO, infos);
+		infoDescriptor.setCategory("Status");
+		return infoDescriptor;
+	}
+    static PropertyDescriptor getWarningsDescriptor(int status) {
+		List list = new ArrayList();
+		if ((status & BIG_ERROR) != 0) {
+			list.add("Some builds have tests with error over 3%");
+		}
+		if ((status & NOT_RELIABLE) != 0) {
+			list.add("Some builds have no reliable tests");
+		}
+		if ((status & NOT_STABLE) != 0) {
+			list.add("Some builds have no stable tests");
+		}
+		if ((status & NO_BASELINE) != 0) {
+			list.add("Some builds have no baseline to compare with");
+		}
+		if ((status & SINGLE_RUN) != 0) {
+			list.add("Some builds have single run tests");
+		}
+		String[] warnings = new String[list.size()];
+		if (list.size() > 0) {
+			list.toArray(warnings);
+		}
+		ComboBoxPropertyDescriptor warningDescriptor = new ComboBoxPropertyDescriptor(P_ID_STATUS_WARNING, P_STR_STATUS_WARNING, warnings);
+		warningDescriptor.setCategory("Status");
+		return warningDescriptor;
+	}
+
+ResultsElement() {
+}
+
+ResultsElement(AbstractResults results, ResultsElement parent) {
+    this.parent = parent;
+    this.results = results;
+}
+
+ResultsElement(String name, ResultsElement parent) {
+	this.parent = parent;
+	this.name = name;
+}
+
+public int compareTo(Object o) {
+	if (this.results == null) {
+		if (o instanceof ResultsElement && this.name != null) {
+			ResultsElement element = (ResultsElement) o;
+			return this.name.compareTo(element.getName());
+		}
+		return -1;
+	}
+	if (o instanceof ResultsElement) {
+		return this.results.compareTo(((ResultsElement)o).results);
+	}
+	return -1;
+}
+
+abstract ResultsElement createChild(AbstractResults testResults);
+
+/* (non-Javadoc)
+ * Method declared on IAdaptable
+ */
+public Object getAdapter(Class adapter) {
+    if (adapter == IPropertySource.class) {
+        return this;
+    }
+    if (adapter == IWorkbenchAdapter.class) {
+        return this;
+    }
+    return null;
+}
+
+/**
+ * Iterate the element children.
+ */
+public ResultsElement[] getChildren() {
+	if (this.results == null) {
+		return new ResultsElement[0];
+	}
+	if (this.children == null) {
+		initChildren();
+	}
+    return this.children;
+}
+
+/* (non-Javadoc)
+ * Method declared on IWorkbenchAdapter
+ */
+public Object[] getChildren(Object o) {
+	if (this.results == null) {
+		return new Object[0];
+	}
+	if (this.children == null) {
+		initChildren();
+	}
+    return this.children;
+}
+
+/* (non-Javadoc)
+ * Method declared on IPropertySource
+ */
+public Object getEditableValue() {
+    return this;
+}
+
+final String getId() {
+	return getId(new StringBuffer()).toString();
+}
+
+private StringBuffer getId(StringBuffer buffer) {
+	if (this.parent != null) {
+		return this.parent.getId(buffer).append('/').append(getName());
+	}
+	return buffer.append(DB_Results.getDbName());
+}
+
+/* (non-Javadoc)
+ * Method declared on IWorkbenchAdapter
+ */
+public ImageDescriptor getImageDescriptor(Object object) {
+	if (object instanceof ResultsElement) {
+		ResultsElement resultsElement = (ResultsElement) object;
+// DEBUG
+//		if (resultsElement.getName().equals("I20090806-0100")) {
+//			if (resultsElement.results != null) {
+//				String toString = resultsElement.results.getParent().toString();
+//				String toString = resultsElement.results.toString();
+//				if (toString.indexOf("testStoreExists")>0 && toString.indexOf("eplnx2")>0) {
+//					System.out.println("stop");
+//				}
+//			}
+//		}
+		int elementStatus = resultsElement.getStatus();
+		if (elementStatus == MISSING) {
+			return HELP_IMAGE_DESCRIPTOR;
+		}
+		if ((elementStatus & ResultsElement.ERROR_MASK) != 0) {
+			return ERROR_IMAGE_DESCRIPTOR;
+		}
+		if ((elementStatus & ResultsElement.WARNING_MASK) != 0) {
+			return WARN_IMAGE_DESCRIPTOR;
+		}
+		if ((elementStatus & ResultsElement.INFO_MASK) != 0) {
+			return INFO_IMAGE_DESCRIPTOR;
+		}
+	}
+	return null;
+}
+
+/* (non-Javadoc)
+ * Method declared on IWorkbenchAdapter
+ */
+public String getLabel(Object o) {
+    return getName();
+}
+
+/**
+ * Returns the name
+ */
+public String getName() {
+	if (this.name == null && this.results != null) {
+		this.name = this.results.getName();
+	}
+	return this.name;
+}
+
+/**
+ * Returns the parent
+ */
+public Object getParent(Object o) {
+    return this.parent;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
+ */
+public IPropertyDescriptor[] getPropertyDescriptors() {
+	Vector descriptors = getDescriptors();
+	if (descriptors == null) {
+		descriptors = initDescriptors(getStatus());
+	}
+	int size = descriptors.size();
+	IPropertyDescriptor[] descriptorsArray = new IPropertyDescriptor[size];
+	descriptorsArray[0] = getInfosDescriptor(getStatus());
+	descriptorsArray[1] = getWarningsDescriptor(getStatus());
+	for (int i=2; i<size; i++) {
+		descriptorsArray[i] = (IPropertyDescriptor) descriptors.get(i);
+	}
+	return descriptorsArray;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
+ */
+public Object getPropertyValue(Object propKey) {
+	if (propKey.equals(P_ID_STATUS_INFO)) {
+		if ((getStatus() & INFO_MASK) != 0) {
+			return new Integer(0);
+		}
+	}
+	if (propKey.equals(P_ID_STATUS_WARNING)) {
+		if ((getStatus() & WARNING_MASK) != 0) {
+			return new Integer(0);
+		}
+	}
+	if (propKey.equals(P_ID_STATUS_ERROR)) {
+		if ((getStatus() & BIG_DELTA) != 0) {
+			return "Some builds have tests with regression";
+		}
+	}
+	if (propKey.equals(P_ID_STATUS_COMMENT)) {
+		IEclipsePreferences preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+		return preferences.get(getId(), "");
+	}
+	return null;
+}
+
+public ResultsElement getResultsElement(String resultName) {
+	int length = getChildren(null).length;
+	for (int i=0; i<length; i++) {
+		ResultsElement searchedResults = this.children[i];
+		if (searchedResults.getName().equals(resultName)) {
+			return searchedResults;
+		}
+	}
+	return null;
+}
+
+/**
+ * Return the status of the element.
+ *
+ * The status is a bit mask pattern where digits are
+ * allowed as follow:
+ *	<ul>
+ * 		<li>0-3: bits for state showing whether the element is
+ * 			<ul>
+ * 				<li>{@link #UNKNOWN} : not connected to a db</li>
+ * 				<li>{@link #UNREAD} : is not valid (e.g. NaN results)</li>
+ * 				<li>{@link #MISSING} : no results (e.g. the perf machine crashed and didn't store any results)</li>
+ * 				<li>{@link #READ} : has valid results</li>
+ * 			</ul>
+ * 		</li>
+ * 		<li>4-5: bits for information. Current possible information are
+ * 			<ul>
+ * 				<li>{@link #SMALL_VALUE} : build results or delta with baseline value is under 100ms</li>
+ * 				<li>{@link #STUDENT_TTEST} : the Student T-test is over the threshold (old yellow color for test results)</li>
+ * 			</ul>
+ * 		</li>
+ * 		<li>6-11: bits for warnings. Current possible warnings are
+ * 			<ul>
+ * 				<li>{@link #NO_BASELINE} : no baseline for the current build</li>
+ * 				<li>{@link #SINGLE_RUN} : the test has only one run (i.e. no error could be computed), hence its reliability cannot be evaluated</li>
+ * 				<li>{@link #BIG_ERROR} : the test result is over the 3% threshold</li>
+ * 				<li>{@link #NOT_STABLE} : the test history shows a deviation between 10% and 20% (may mean that this test is not so reliable)</li>
+ * 				<li>{@link #NOT_RELIABLE} : the test history shows a deviation over 20% (surely means that this test is too erratic to be reliable)</li>
+ * 			</ul>
+ * 		</li>
+ * 		<li>12-15: bits for errors. Current possible errors are
+ * 			<ul>
+ * 				<li>{@link #BIG_DELTA} : the delta for the test is over the 10% threshold</li>
+ * 			</ul>
+ * 		</li>
+ *	</ul>
+ *
+ * Note that these explanation applied to {@link BuildResultsElement}, and {@link DimResultsElement}.
+ * For {@link ComponentResultsElement}, and {@link ScenarioResultsElement}, it's the merge of all the children status
+ * and means "Some tests have..." instead of "The test has...". For {@link ConfigResultsElement}, it means the status
+ * of the most recent build compared to its most recent baseline.
+ *
+ * @return An int with each bit set when the corresponding symptom applies.
+ */
+public final int getStatus() {
+	if (this.status < 0) {
+		initStatus();
+	}
+	return this.status;
+}
+
+/**
+ * Return the statistics of the build along its history.
+ *
+ * @return An array of double built as follows:
+ * <ul>
+ * <li>0:	numbers of values</li>
+ * <li>1:	mean of values</li>
+ * <li>2:	standard deviation of these values</li>
+ * <li>3:	coefficient of variation of these values</li>
+ * </ul>
+ */
+double[] getStatistics() {
+	return this.statistics;
+}
+
+/**
+ * Returns whether the element (or one in its hierarchy) has an error.
+ *
+ * @return <code> true</code> if the element or one in its hierarchy has an error,
+ * 	<code> false</code>  otherwise
+ */
+public final boolean hasError() {
+	return (getStatus() & ERROR_MASK) != 0;
+}
+
+void initChildren() {
+	AbstractResults[] resultsChildren = this.results.getChildren();
+	int length = resultsChildren.length;
+	this.children = new ResultsElement[length];
+	int count = 0;
+	for (int i=0; i<length; i++) {
+		ResultsElement childElement = createChild(resultsChildren[i]);
+		if (childElement != null) {
+			this.children[count++] = childElement;
+		}
+	}
+	if (count < length) {
+		System.arraycopy(this.children, 0, this.children = new ResultsElement[count], 0, count);
+	}
+}
+void initStatus() {
+	this.status = READ;
+	if (this.results != null) {
+		if (this.children == null) initChildren();
+		int length = this.children.length;
+		for (int i=0; i<length; i++) {
+			this.status |= this.children[i].getStatus();
+		}
+	}
+}
+
+int initStatus(BuildResults buildResults) {
+	this.status = READ;
+
+	// Get values
+	double buildValue = buildResults.getValue();
+	ConfigResults configResults = (ConfigResults) buildResults.getParent();
+	BuildResults baselineResults = configResults.getBaselineBuildResults(buildResults.getName());
+	double baselineValue = baselineResults.getValue();
+	double delta = (baselineValue - buildValue) / baselineValue;
+
+	// Store if there's no baseline
+	if (Double.isNaN(delta)) {
+		this.status |= NO_BASELINE;
+	}
+
+	// Store if there's only one run
+	long baselineCount = baselineResults.getCount();
+	long currentCount = buildResults.getCount();
+	double error = Double.NaN;
+	if (baselineCount == 1 || currentCount == 1) {
+		this.status |= SINGLE_RUN;
+	}
+
+	// Store if the T-test is not good
+	double ttestValue = Util.computeTTest(baselineResults, buildResults);
+	int degreeOfFreedom = (int) (baselineResults.getCount()+buildResults.getCount()-2);
+	if (ttestValue >= 0 && StatisticsUtil.getStudentsT(degreeOfFreedom, StatisticsUtil.T90) >= ttestValue) {
+		this.status |= STUDENT_TTEST;
+	}
+
+	// Store if there's a big error (over 3%)
+	double baselineError = baselineResults.getError();
+	double currentError = buildResults.getError();
+	error = Double.isNaN(baselineError)
+			? currentError / baselineValue
+			: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
+	if (error > 0.03) {
+		this.status |= BIG_ERROR;
+	}
+
+	// Store if there's a big delta (over 10%)
+	if (delta <= -0.1) {
+		this.status |= BIG_DELTA;
+		double currentBuildValue = buildResults.getValue();
+		double diff = Math.abs(baselineValue - currentBuildValue);
+		if (currentBuildValue < 100 || diff < 100) { // moderate the status when
+			// diff is less than 100ms
+			this.status |= SMALL_VALUE;
+		} else {
+			double[] stats = getStatistics();
+			if (stats != null) {
+				if (stats[3] > 0.2) { // invalidate the status when the test
+					// historical deviation is over 20%
+					this.status |= NOT_RELIABLE;
+				} else if (stats[3] > 0.1) { // moderate the status when the test
+					// historical deviation is between 10%
+					// and 20%
+					this.status |= NOT_STABLE;
+				}
+			}
+		}
+	}
+
+	return this.status;
+}
+
+public boolean isInitialized() {
+	return this.results != null;
+}
+
+/* (non-Javadoc)
+ * Method declared on IPropertySource
+ */
+public boolean isPropertySet(Object property) {
+    return false;
+}
+
+boolean onlyFingerprints() {
+	if (this.parent != null) {
+		return this.parent.onlyFingerprints();
+	}
+	return ((PerformanceResultsElement)this).fingerprints;
+}
+
+/* (non-Javadoc)
+ * Method declared on IPropertySource
+ */
+public void resetPropertyValue(Object property) {
+}
+
+void resetStatus() {
+	this.status = -1;
+	if (this.results != null) {
+		if (this.children == null) initChildren();
+		int length = this.children.length;
+		for (int i=0; i<length; i++) {
+			this.children[i].resetStatus();
+		}
+	}
+}
+
+public void setPropertyValue(Object name, Object value) {
+	if (name.equals(P_ID_STATUS_COMMENT)) {
+		IEclipsePreferences preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+		preferences.put(getId(), (String) value);
+		try {
+			preferences.flush();
+		} catch (BackingStoreException e) {
+			// skip
+		}
+	}
+}
+
+/**
+ * Sets the image descriptor
+ */
+void setImageDescriptor(ImageDescriptor desc) {
+//    this.imageDescriptor = desc;
+}
+
+public String toString() {
+	if (this.results == null) {
+		return getName();
+	}
+	return this.results.toString();
+}
+
+/*
+ * Write the failures of the element in the given buffer
+ */
+StringBuffer getFailures(StringBuffer buffer, int kind, StringBuffer excluded) {
+	int length = getChildren().length;
+	for (int i=0; i<length; i++) {
+		this.children[i].getFailures(buffer, kind, excluded);
+	}
+	return buffer;
+}
+
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ScenarioResultsElement.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ScenarioResultsElement.java
new file mode 100644
index 0000000..40daddd
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/ScenarioResultsElement.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.Vector;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.test.internal.performance.results.db.*;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.TextPropertyDescriptor;
+
+public class ScenarioResultsElement extends ResultsElement {
+
+	// Property descriptors
+    static final String P_ID_SCENARIO_LABEL = "ScenarioResultsElement.label"; //$NON-NLS-1$
+    static final String P_ID_SCENARIO_FILE_NAME = "ScenarioResultsElement.filename"; //$NON-NLS-1$
+    static final String P_ID_SCENARIO_SHORT_NAME = "ScenarioResultsElement.shortname"; //$NON-NLS-1$
+
+    static final String P_STR_SCENARIO_LABEL = "label"; //$NON-NLS-1$
+    static final String P_STR_SCENARIO_FILE_NAME = "file name"; //$NON-NLS-1$
+    static final String P_STR_SCENARIO_SHORT_NAME = "short name"; //$NON-NLS-1$
+
+    private static final TextPropertyDescriptor SCENARIO_LABEL_DESCRIPTOR = new TextPropertyDescriptor(P_ID_SCENARIO_LABEL, P_STR_SCENARIO_LABEL);
+	private static final TextPropertyDescriptor SCENARIO_FILE_NAME_DESCRIPTOR = new TextPropertyDescriptor(P_ID_SCENARIO_FILE_NAME, P_STR_SCENARIO_FILE_NAME);
+	private static final TextPropertyDescriptor SCENARIO_SHORT_NAME_DESCRIPTOR = new TextPropertyDescriptor(P_ID_SCENARIO_SHORT_NAME, P_STR_SCENARIO_SHORT_NAME);
+
+    private static Vector DESCRIPTORS;
+    static Vector initDescriptors(int status) {
+        DESCRIPTORS = new Vector();
+		// Status category
+		DESCRIPTORS.add(getInfosDescriptor(status));
+		DESCRIPTORS.add(getWarningsDescriptor(status));
+		DESCRIPTORS.add(ERROR_DESCRIPTOR);
+		ERROR_DESCRIPTOR.setCategory("Status");
+		// Results category
+        DESCRIPTORS.addElement(SCENARIO_LABEL_DESCRIPTOR);
+		SCENARIO_LABEL_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.addElement(SCENARIO_FILE_NAME_DESCRIPTOR);
+		SCENARIO_FILE_NAME_DESCRIPTOR.setCategory("Results");
+        DESCRIPTORS.addElement(SCENARIO_SHORT_NAME_DESCRIPTOR);
+		SCENARIO_SHORT_NAME_DESCRIPTOR.setCategory("Results");
+		// Survey category
+		DESCRIPTORS.add(COMMENT_DESCRIPTOR);
+		COMMENT_DESCRIPTOR.setCategory("Survey");
+        return DESCRIPTORS;
+	}
+    static Vector getDescriptors() {
+    	return DESCRIPTORS;
+	}
+
+ScenarioResultsElement(AbstractResults results, ResultsElement parent) {
+    super(results, parent);
+}
+
+ResultsElement createChild(AbstractResults testResults) {
+	return new ConfigResultsElement(testResults, this);
+}
+
+public String getLabel(Object o) {
+	return ((ScenarioResults) this.results).getShortName();
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
+ */
+public IPropertyDescriptor[] getPropertyDescriptors() {
+	Vector descriptors = getDescriptors();
+	if (descriptors == null) {
+		descriptors = initDescriptors(getStatus());
+	}
+	int size = descriptors.size();
+	IPropertyDescriptor[] descriptorsArray = new IPropertyDescriptor[size];
+	descriptorsArray[0] = getInfosDescriptor(getStatus());
+	descriptorsArray[1] = getWarningsDescriptor(getStatus());
+	for (int i=2; i<size; i++) {
+		descriptorsArray[i] = (IPropertyDescriptor) descriptors.get(i);
+	}
+	return descriptorsArray;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
+ */
+public Object getPropertyValue(Object propKey) {
+	ScenarioResults scenarioResults = (ScenarioResults) this.results;
+    if (propKey.equals(P_ID_SCENARIO_LABEL))
+        return scenarioResults.getLabel();
+    if (propKey.equals(P_ID_SCENARIO_FILE_NAME))
+        return scenarioResults.getFileName();
+    if (propKey.equals(P_ID_SCENARIO_SHORT_NAME))
+        return scenarioResults.getShortName();
+    return super.getPropertyValue(propKey);
+}
+
+/**
+ * Returns whether one of the scenario's config has a summary or not.
+ *
+ * @return <code>true</code> if one of the scenario's config has a summary
+ * 	<code>false</code> otherwise.
+ */
+public boolean hasSummary() {
+	if (this.results == null) return false;
+	return ((ScenarioResults)this.results).hasSummary();
+}
+
+void initStatus() {
+	if (onlyFingerprints()) {
+		if (hasSummary()) {
+			super.initStatus();
+		} else {
+			this.status = READ;
+		}
+	} else {
+		super.initStatus();
+	}
+}
+
+StringBuffer getFailures(StringBuffer buffer, int kind, StringBuffer excluded) {
+	// Write status for scenarios having error
+	if ((getStatus() & ERROR_MASK) != 0) {
+
+		// Get children status
+		StringBuffer childrenBuffer = super.getFailures(new StringBuffer(), kind, excluded);
+
+		// Write status on file if not excluded
+		if (childrenBuffer.length() > 0) {
+			buffer.append("	");
+			buffer.append(getLabel(null));
+			IEclipsePreferences preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+			String comment = preferences.get(getId(), null);
+			if (comment != null) {
+				if ((kind & IPerformancesConstants.STATUS_VALUES) != 0) {
+					buffer.append("											");
+				} else {
+					buffer.append("		");
+				}
+				buffer.append(comment);
+			}
+			buffer.append(Util.LINE_SEPARATOR);
+			buffer.append(childrenBuffer);
+			buffer.append(Util.LINE_SEPARATOR);
+		}
+	}
+	return buffer;
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonTab.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonTab.java
new file mode 100644
index 0000000..5615b23
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonTab.java
@@ -0,0 +1,539 @@
+/*******************************************************************************
+ * 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.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.ToolTip;
+import org.eclipse.test.internal.performance.results.db.AbstractResults;
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.PerformanceResults;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
+import org.eclipse.test.internal.performance.results.model.PerformanceResultsElement;
+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;
+
+
+/**
+ * Tab to display all performances results numbers for a configuration (i.e a performance machine).
+ */
+public class BuildsComparisonTab {
+
+	// Colors
+	static final Display DEFAULT_DISPLAY = Display.getDefault();
+	static final Color BLUE= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLUE);
+	static final Color DARK_GREEN= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_DARK_GREEN);
+	static final Color GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GRAY);
+	static final Color MAGENTA = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_MAGENTA);
+	static final Color RED = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_RED);
+
+	// SWT resources
+	Shell shell;
+	Display display;
+	Table table;
+	private GC gc;
+	private Color lightred;
+	private Color lightyellow;
+	private Color darkyellow;
+	private Color blueref;
+	private Font boldFont;
+	private Font italicFont;
+	private Font boldItalicFont;
+	Map toolTips;
+
+	// Information
+	String componentName;
+	double[][] allValues;
+	double[][] allErrors;
+
+	// Cells management
+	Point tableOrigin, tableSize;
+	int columnsCount, rowsCount;
+	List firstLine;
+
+	// Eclipse preferences
+	private IEclipsePreferences preferences;
+
+/*
+ * Default constructor.
+ */
+public BuildsComparisonTab(String name) {
+    this.componentName = name;
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+}
+
+/**
+ * Creates the tab folder page.
+ *
+ * @param view The view on which to create the tab folder page
+ * @return the new page for the tab folder
+ */
+Composite createTabFolderPage (BuildsComparisonView view) {
+	// Cache the shell and display.
+	this.shell = view.tabFolder.getShell();
+	this.display = this.shell.getDisplay();
+
+	// Remove old table if present
+	boolean initResources = this.table == null;
+	if (this.table != null) {
+		disposeTable();
+	}
+
+	// Create the "children" table
+	int style = SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION;
+	this.table = new Table(view.tabFolder, style);
+	this.table.setLinesVisible (true);
+	this.table.setHeaderVisible (true);
+	GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+	gridData.heightHint = 150;
+	this.table.setLayoutData (gridData);
+	this.gc = new GC(this.table);
+
+	// Init resources
+	if (initResources) initResources();
+
+	// Add columns to the table
+	TableColumn firstColumn = new TableColumn(this.table, SWT.CENTER);
+	firstColumn.setText("");
+	PerformanceResultsElement results = view.getResults();
+	String [] configDescriptions = results.getConfigDescriptions();
+	int length = configDescriptions.length;
+	for (int i = 0; i < length; i++) {
+		TableColumn column = new TableColumn(this.table, SWT.CENTER);
+		column.setText(configDescriptions[i]);
+	}
+
+	// Add lines to the table
+	this.toolTips = new HashMap();
+	boolean fingerprints = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+	fillTable(results, view.currentBuild, view.referenceBuild, fingerprints);
+
+	// Updated columns
+	for (int i=0; i<=length; i++) {
+		TableColumn column = this.table.getColumn(i);
+		column.setWidth(i==0?120:100);
+	}
+
+	// Store table info
+	this.columnsCount = length;
+
+	// Listen to mouse track events to display the table cell corresponding tooltip.
+	MouseTrackListener mouseTrackListener = new MouseTrackListener() {
+		ToolTip currentTooltip;
+		public void mouseHover(MouseEvent e) {
+			if (this.currentTooltip != null) {
+				this.currentTooltip.setVisible(false);
+				this.currentTooltip = null;
+			}
+			Point cellPosition = currentCellPosition(e.x, e.y);
+			if (cellPosition != null) {
+				ToolTip tooltip = (ToolTip) BuildsComparisonTab.this.toolTips.get(cellPosition);
+				if (tooltip != null) {
+					Point location = BuildsComparisonTab.this.table.toDisplay(new Point(e.x, e.y));
+					tooltip.setLocation(location);
+					tooltip.setVisible(true);
+					this.currentTooltip = tooltip;
+				}
+			}
+		}
+		public void mouseEnter(MouseEvent e) {
+		}
+		public void mouseExit(MouseEvent e) {
+		}
+	};
+	this.table.addMouseTrackListener(mouseTrackListener);
+
+	// Select the first line by default (as this is the current build)
+	this.table.select(0);
+
+	// Return the built composite
+	return this.table;
+}
+
+/*
+ * Create and store a tooltip with the given information and at the given position.
+ */
+void createToolTip(String toolTipText, String toolTipMessage, int toolTipStyle, Point position) {
+	ToolTip toolTip = new ToolTip(this.table.getShell(), toolTipStyle);
+	toolTip.setAutoHide(true);
+	toolTip.setText(toolTipText);
+	toolTip.setMessage(/*"("+col+","+row+") "+*/toolTipMessage);
+	this.toolTips.put(position, toolTip);
+}
+
+/*
+ * Get the current cell position (column, row) from a point position.
+ */
+Point currentCellPosition(int x, int y) {
+
+	// Compute the origin of the visible area
+	if (this.tableOrigin == null) {
+		this.tableOrigin = new Point(0, this.table.getHeaderHeight());
+	}
+
+	// Increment width until over current y position
+	int height= this.tableOrigin.y;
+	int row = this.table.getTopIndex();
+	while (row<this.rowsCount && height<y) {
+		height += this.table.getItemHeight();
+		row++;
+	}
+	if (height < y) {
+		// return when position is over the last line
+		return null;
+	}
+	row--;
+
+	// Increment width until being over current x position
+	int col = 0;
+	TableItem tableItem = this.table.getItem(row);
+	Rectangle bounds = tableItem.getBounds(col);
+	while (col<this.columnsCount) {
+		int max = bounds.x + bounds.width + this.table.getGridLineWidth();
+		if (x <= max) break;
+		if (col == this.columnsCount) {
+			// return when position is over the last column
+			return null;
+		}
+		col++;
+		bounds = tableItem.getBounds(col);
+	}
+
+	// Return the found table cell position
+	return new Point(col, row);
+}
+
+/*
+ * Dispose all SWT resources.
+ */
+public void dispose() {
+	if (this.boldFont != null) {
+		this.boldFont.dispose();
+	}
+	if (this.italicFont != null) {
+		this.italicFont.dispose();
+	}
+	if (this.boldItalicFont != null) {
+		this.boldItalicFont.dispose();
+	}
+	if (this.darkyellow != null) {
+		this.darkyellow.dispose();
+	}
+	if (this.lightyellow != null) {
+		this.lightyellow.dispose();
+	}
+	if (this.lightred != null) {
+		this.lightred.dispose();
+	}
+	if (this.blueref != null) {
+		this.blueref.dispose();
+	}
+	disposeTable();
+}
+
+/*
+ * Dispose all SWT controls associated with the table.
+ */
+private void disposeTable() {
+	if (this.toolTips != null) {
+		Iterator cells = this.toolTips.keySet().iterator();
+		while (cells.hasNext()) {
+			ToolTip toolTip = (ToolTip) this.toolTips.get(cells.next());
+			toolTip.dispose();
+		}
+	}
+	this.table.dispose();
+	this.tableOrigin = null;
+	this.firstLine = null;
+}
+
+/*
+ * Fill the lines of the tables.
+ * Get all the information from the model which are returned in a list (lines) of lists (columns).
+ */
+private void fillTable(PerformanceResultsElement results, String currentBuild, String referenceBuild, boolean fingerprints) {
+
+	// Get all the scenario for the component
+	final PerformanceResults performanceResults = results.getPerformanceResults();
+	List scenarios = performanceResults.getComponentScenarios(this.componentName);
+	int size = scenarios.size();
+
+	// Get thresholds
+	int failurePref = this.preferences.getInt(IPerformancesConstants.PRE_COMPARISON_THRESHOLD_FAILURE, IPerformancesConstants.DEFAULT_COMPARISON_THRESHOLD_FAILURE);
+	int errorPref = this.preferences.getInt(IPerformancesConstants.PRE_COMPARISON_THRESHOLD_ERROR, IPerformancesConstants.DEFAULT_COMPARISON_THRESHOLD_ERROR);
+	int improvementPref = this.preferences.getInt(IPerformancesConstants.PRE_COMPARISON_THRESHOLD_IMPROVEMENT, IPerformancesConstants.DEFAULT_COMPARISON_THRESHOLD_IMPROVEMENT);
+	final double failureThreshold = -failurePref / 100.0;
+	final double errorThreshold = errorPref / 100.0;
+	final double improvementThreshold = improvementPref / 100.0;
+
+	// Build the map made of one line per scenario results
+	final String[] configs = performanceResults.getConfigNames(true/*sort*/);
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
+		if (!scenarioResults.isValid()) continue;
+		final boolean fingerprint = scenarioResults.hasSummary();
+		if (!fingerprints || fingerprint) {
+
+			// The first column is the scenario name
+			String scenarioName = scenarioResults.getShortName();
+			TableItem item = new TableItem (this.table, SWT.NONE);
+			item.setText(scenarioName);
+
+			// Bold font and blue ref background color if this is a fingerprint test
+			Font italic;
+			if (fingerprint) {
+				item.setFont(0, this.boldFont);
+				item.setBackground(this.blueref);
+				italic = this.boldItalicFont;
+			} else {
+				italic = this.italicFont;
+			}
+
+			// Fill config columns
+			int length = configs.length;
+			for (int j=0; j<length; j++) {
+
+				// See whether there's available numbers
+				int col = j+1;
+				final ConfigResults configResults = scenarioResults.getConfigResults(configs[j]);
+				if (configResults == null || !configResults.isValid()) {
+					item.setText(col, "Invalid");
+					item.setForeground(col, GRAY);
+					item.setFont(col, italic);
+					continue;
+				}
+				final BuildResults buildResults = configResults.getBuildResults(currentBuild);
+				if (buildResults == null) {
+					item.setText(col, "Missing results");
+					item.setForeground(col, GRAY);
+					item.setFont(col, italic);
+					continue;
+				}
+				final BuildResults referenceResults = configResults.getBuildResults(referenceBuild);
+				if (referenceResults == null) {
+					item.setText(col, "Missing ref");
+					item.setForeground(col, GRAY);
+					item.setFont(col, italic);
+					continue;
+				}
+
+				// Get numbers and infos
+				double[] values = configResults.getNumbers(buildResults, referenceResults);
+
+				// Reset tooltip info
+				String toolTipText = null;
+				String toolTipMessage = null;
+				int toolTipStyle = SWT.BALLOON;
+
+				// Get values array
+				final double buildValue = values[AbstractResults.BUILD_VALUE_INDEX];
+				final double referenceValue = values[AbstractResults.BASELINE_VALUE_INDEX];
+				final double delta = values[AbstractResults.DELTA_VALUE_INDEX];
+				final double error = values[AbstractResults.DELTA_ERROR_INDEX];
+
+				// Set text with delta value
+				final StringBuffer address = new StringBuffer("http://fullmoon.ottawa.ibm.com/downloads/drops/");
+				address.append(currentBuild);
+				address.append("/performance/");
+				address.append(configResults.getName());
+				address.append('/');
+				address.append(scenarioResults.getFileName());
+				address.append(".html");
+				StringBuffer buffer = new StringBuffer("<a href=\"");
+				buffer.append(address);
+				buffer.append("\">");
+				final String itemText = Util.PERCENTAGE_FORMAT.format(delta);
+				buffer.append(itemText);
+				buffer.append("</a>");
+
+				// Simple text
+				item.setText(col, itemText);
+
+				/* Link + Editor
+				Link link = new Link(this.table, SWT.CENTER);
+				link.setText(buffer.toString());
+				link.addSelectionListener(new SelectionAdapter() {
+					public void widgetSelected(SelectionEvent e) {
+						Program.launch(address.toString());
+					}
+				});
+				final TableEditor editor = new TableEditor(this.table);
+				editor.grabHorizontal = editor.grabVertical = true;
+				editor.verticalAlignment = SWT.CENTER;
+				editor.horizontalAlignment = SWT.CENTER;
+				editor.setEditor(link, item, col);
+				*/
+
+				// Compute the tooltip to display on the cell
+				if (error > errorThreshold) {
+					// error is over the threshold
+					item.setForeground(col, this.darkyellow);
+					toolTipText = "May be not reliable";
+					toolTipMessage = "The error on this result is "+Util.PERCENTAGE_FORMAT.format(error)+", hence it may be not reliable";
+					toolTipStyle |= SWT.ICON_WARNING;
+				}
+				if (delta < -0.1) {
+					// delta < -10%: failure shown by an red-cross icon + text in red
+					item.setImage(col, ResultsElement.ERROR_IMAGE);
+				}
+				if (delta < failureThreshold) {
+					// negative delta over the threshold shown in red
+					item.setForeground(col, RED);
+				} else if (delta > improvementThreshold) {
+					// positive delta over the threshold are shown in green
+					item.setForeground(col, DARK_GREEN);
+				}
+
+				// Moderate the status if the build value or the difference is small
+				if (buildValue < 100 || referenceValue < 100) {
+					if (toolTipText == null) {
+						toolTipText = "";
+					} else {
+						toolTipText += ", ";
+					}
+					toolTipText += "Small value";
+					if (toolTipMessage == null) {
+						toolTipMessage = "";
+					} else {
+						toolTipMessage += ".\n";
+					}
+					toolTipMessage += "This test has a small value ("+buildValue+"ms)";
+					toolTipStyle |= SWT.ICON_WARNING;
+					item.setImage(col, ResultsElement.WARN_IMAGE);
+				}
+
+				// Add information in tooltip when history shows big variation
+				double deviation = configResults.getStatistics(Util.BASELINE_BUILD_PREFIXES)[3];
+				if (deviation > 0.2) {
+					// deviation is over 20% over the entire history
+					if (toolTipText == null) {
+						toolTipText = "";
+					} else {
+						toolTipText += ", ";
+					}
+					toolTipText += "History shows erratic values";
+					if (toolTipMessage == null) {
+						toolTipMessage = "";
+					} else {
+						toolTipMessage += ".\n";
+					}
+					toolTipMessage += "The results history shows that the variation of its delta is over 20% ("+Util.PERCENTAGE_FORMAT.format(deviation)+"), hence its result is surely not reliable";
+					// set the text in italic
+					item.setFont(col, italic);
+					toolTipStyle |= SWT.ICON_INFORMATION;
+				} else if (deviation > 0.1) { // moderate the status when the test
+					// deviation is between 10% and 20% over the entire history
+					if (toolTipText == null) {
+						toolTipText = "";
+					} else {
+						toolTipText += ", ";
+					}
+					toolTipText += "History shows unstable values";
+					if (toolTipMessage == null) {
+						toolTipMessage = "";
+					} else {
+						toolTipMessage += ".\n";
+					}
+					toolTipMessage += "The results history shows that the variation of its delta is between 10% and 20% ("+Util.PERCENTAGE_FORMAT.format(deviation)+"), hence its result may not be really reliable";
+					// set the text in italic
+					item.setFont(col, italic);
+					if (toolTipStyle == SWT.BALLOON && delta >= -0.1) {
+						toolTipStyle |= SWT.ICON_INFORMATION;
+					} else {
+						// reduce icon severity from error to warning
+						toolTipStyle |= SWT.ICON_WARNING;
+					}
+				}
+
+				// Set tooltip
+				if (toolTipText != null) {
+					createToolTip(toolTipText, toolTipMessage, toolTipStyle, new Point(col, i));
+				}
+			}
+		}
+	}
+	this.rowsCount = size;
+}
+
+protected Shell getShell() {
+	return this.shell;
+}
+
+/*
+ * The tab text is the full machine name.
+ */
+public String getTabText() {
+	return Util.componentDisplayName(this.componentName);
+}
+
+/*
+ * Init the SWT resources
+ */
+private void initResources() {
+	// Fonts
+	String fontDataName = this.gc.getFont().getFontData()[0].toString();
+	FontData fdItalic = new FontData(fontDataName);
+	fdItalic.setStyle(SWT.ITALIC);
+	this.italicFont = new Font(this.display, fdItalic);
+	FontData fdBold = new FontData(fontDataName);
+	fdBold.setStyle(SWT.BOLD);
+	this.boldFont = new Font(this.display, fdBold);
+	FontData fdBoldItalic = new FontData(fontDataName);
+	fdBoldItalic.setStyle(SWT.BOLD | SWT.ITALIC);
+	this.boldItalicFont = new Font(this.display, fdBoldItalic);
+
+	// Colors
+	this.lightred = new Color(DEFAULT_DISPLAY, 220, 50, 50);
+	this.lightyellow = new Color(DEFAULT_DISPLAY, 255, 255, 160);
+	this.darkyellow = new Color(DEFAULT_DISPLAY, 160, 160, 0);
+	this.blueref = new Color(DEFAULT_DISPLAY, 200, 200, 255);
+}
+
+/*
+ * Select the line corresponding to the given build.
+ */
+void select(BuildResultsElement buildResultsElement) {
+	int count = this.table.getItemCount();
+	String buildName = buildResultsElement.getName();
+	TableItem[] items = this.table.getItems();
+	for (int i=0; i<count; i++) {
+		if (items[i].getText().endsWith(buildName)) {
+			this.table.deselect(this.table.getSelectionIndex());
+			this.table.select(i);
+			return;
+		}
+	}
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonTable.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonTable.java
new file mode 100644
index 0000000..440c052
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonTable.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.PrintStream;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.test.internal.performance.results.db.AbstractResults;
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.PerformanceResults;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+import org.eclipse.test.performance.ui.Utils;
+
+/**
+ * This class is responsible to print html table with the difference between two
+ * builds for all the scenarios and the configuration of a specific component.
+ */
+public class BuildsComparisonTable {
+
+	private static final double DEFAULT_FAILURE_THRESHOLD = PerformanceResults.DEFAULT_FAILURE_THRESHOLD / 100.0 / 2;
+	private String component;
+	private PrintStream stream;
+	String buildName, referenceName;
+
+public BuildsComparisonTable(String name, PrintStream stream, String build, String reference) {
+    this.component = name;
+    this.stream = stream;
+    this.buildName = build;
+    this.referenceName = reference;
+}
+
+/**
+ * Prints the HTML representation of scenario status table into the given stream.
+ */
+public void print(PerformanceResults performanceResults) {
+
+	List scenarios = performanceResults.getComponentScenarios(this.component);
+	int size = scenarios.size();
+
+	// Print titles
+	printTitle();
+	this.stream.print("<table border=\"1\">\n");
+	this.stream.print("<tr>\n");
+	this.stream.print("<td><h4>All ");
+	this.stream.print(computeSize(scenarios));
+	this.stream.print(" scenarios</h4></td>\n");
+	printColumnsTitle(size, performanceResults);
+
+	// Print one line per scenario results
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
+		if (!scenarioResults.isValid()) continue;
+		this.stream.print("<tr>\n");
+		this.stream.print("<td>");
+		boolean hasSummary = scenarioResults.hasSummary();
+		if (hasSummary) this.stream.print("<b>");
+		this.stream.print(scenarioResults.getShortName());
+		if (hasSummary) this.stream.print("</b>");
+		this.stream.print("\n");
+		String[] configs = performanceResults.getConfigNames(true/*sort*/);
+		int length = configs.length;
+		for (int j=0; j<length; j++) {
+			printConfigStats(scenarioResults, configs[j]);
+		}
+	}
+	this.stream.print("</table>\n");
+}
+
+private int computeSize(List scenarios) {
+	int size = scenarios.size();
+	int n = 0;
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
+		if (scenarioResults.isValid()) n++;
+	}
+	return n;
+}
+
+/*
+ * Print the table columns title.
+ */
+private void printColumnsTitle(int size, PerformanceResults performanceResults) {
+	String[] configNames = performanceResults.getConfigNames(true/*sort*/);
+	String[] configBoxes = performanceResults.getConfigBoxes(true/*sort*/);
+	int length = configNames.length;
+	for (int i=0; i<length; i++) {
+		String columnTitle = configNames[i];
+		String boxName = configBoxes[i];
+		int idx = boxName.indexOf('(');
+		if (idx < 0) {
+			columnTitle = boxName;
+		} else {
+			// first line
+			StringTokenizer tokenizer = new StringTokenizer(boxName.substring(0, idx).trim(), " ");
+			StringBuffer buffer = new StringBuffer(tokenizer.nextToken());
+			while (tokenizer.hasMoreTokens()) {
+				buffer.append("&nbsp;");
+				buffer.append(tokenizer.nextToken());
+			}
+			buffer.append(' ');
+			// second line
+			tokenizer = new StringTokenizer(boxName.substring(idx).trim(), " ");
+			buffer.append(tokenizer.nextToken());
+			while (tokenizer.hasMoreTokens()) {
+				buffer.append("&nbsp;");
+				buffer.append(tokenizer.nextToken());
+			}
+			columnTitle = buffer.toString();
+		}
+		this.stream.print("<td><h5>");
+		this.stream.print(columnTitle);
+		this.stream.print("</h5>\n");
+	}
+}
+
+/*
+ * Print the scenario statistics value for the given configuration.
+ */
+private void printConfigStats(ScenarioResults scenarioResults, String config) {
+	ConfigResults configResults = scenarioResults.getConfigResults(config);
+	if (configResults == null || !configResults.isValid()) {
+		this.stream.print("<td>n/a</td>");
+		return;
+	}
+	final BuildResults buildResults = configResults.getBuildResults(this.buildName);
+	if (buildResults == null) {
+		this.stream.print("<td>no results</td>");
+		return;
+	}
+	final BuildResults referenceResults = configResults.getBuildResults(this.referenceName);
+	if (referenceResults == null) {
+		this.stream.print("<td>no reference</td>");
+		return;
+	}
+	double[] numbers = configResults.getNumbers(buildResults, referenceResults);
+	final double delta = numbers[AbstractResults.DELTA_VALUE_INDEX];
+	final double[] deviation = new double[] { -delta, numbers[AbstractResults.DELTA_ERROR_INDEX] };
+	boolean hasFailure = delta < -DEFAULT_FAILURE_THRESHOLD;
+	String comment = buildResults.getComment();
+	String image = Utils.getImage(0, hasFailure, comment != null);
+	this.stream.print("<td><a ");
+
+	// write deviation with error in table
+	this.stream.print("href=\"http://fullmoon.ottawa.ibm.com/downloads/drops/I20100817-0800/performance/");
+	this.stream.print(configResults.getName());
+	this.stream.print('/');
+	this.stream.print(scenarioResults.getFileName());
+	this.stream.print(".html\">\n");
+	this.stream.print("<img hspace=\"10\" border=\"0\" src=\"");
+	this.stream.print(image);
+	this.stream.print("\"/></a>\n");
+	String result = Utils.failureMessage(deviation, false);
+	this.stream.print(result);
+	this.stream.print("\n");
+}
+
+/*
+ * Print the comparison table title.
+ */
+private void printTitle() {
+	this.stream.print("<br><h3>Component ");
+	this.stream.print(this.component);
+	this.stream.print("</h3>\n");
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonView.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonView.java
new file mode 100644
index 0000000..5616059
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsComparisonView.java
@@ -0,0 +1,404 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
+import org.eclipse.test.internal.performance.results.model.PerformanceResultsElement;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+
+/**
+ * This view shows the difference between two selected builds.
+ * <p>
+ * Each component are shown in a separate tab.
+ * </p><p>
+ * The displayed results can be written in a file, either all component or only
+ * the current one.
+ * </p><p>
+ * It's also possible to filter the scenario to display only the fingerprint ones.
+ * <br>
+ * Note that this filter is synchronized with the one applied in the
+ * {@link ComponentsView Components view}.
+ * </p>
+ *
+ * @see ConfigTab Folder tab containing all the results for a configuration.
+ */
+public class BuildsComparisonView extends ViewPart implements ISelectionChangedListener, IPreferenceChangeListener {
+
+	// SWT resources
+	Shell shell;
+	CTabFolder tabFolder;
+
+	// Model information
+	BuildsComparisonTab[] tabs;
+	PerformanceResultsElement results;
+
+	// Action
+	Action filterAdvancedScenarios;
+	Action writeComparison;
+
+	// Write Status
+	static int WRITE_STATUS;
+
+	// Views
+	BuildsView buildsView;
+	IMemento viewState;
+
+	// Eclipse preferences
+	IEclipsePreferences preferences;
+
+	// Comparison
+	String currentBuild;
+	String referenceBuild;
+
+/*
+ * Default constructor:
+ * 	- create the image descriptor
+ * 	- register the view as a properties listener
+ */
+public BuildsComparisonView() {
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+	this.preferences.addPreferenceChangeListener(this);
+	// TODO should be done only once!
+	Util.initMilestones(this.preferences);
+}
+
+/*
+ * Contribute the local tools bar and the pull-down menu to the action bars.
+ */
+void contributeToActionBars() {
+	IActionBars bars = getViewSite().getActionBars();
+	fillLocalPullDown(bars.getMenuManager());
+	fillLocalToolBar(bars.getToolBarManager());
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+public void createPartControl(Composite parent) {
+
+	// Create the tab folder
+	this.shell = parent.getShell ();
+	this.tabFolder = new CTabFolder(parent, SWT.BORDER);
+
+	// Add results view as listener to viewer selection changes
+	Display.getDefault().asyncExec(new Runnable() {
+		public void run() {
+			PerformancesView performancesView = (PerformancesView) PerformancesView.getWorkbenchView("org.eclipse.test.internal.performance.results.ui.ComponentsView");
+			if (performancesView != null) {
+				performancesView.viewer.addSelectionChangedListener(BuildsComparisonView.this);
+			}
+		}
+	});
+
+	// Set actions
+	PlatformUI.getWorkbench().getHelpSystem().setHelp(this.tabFolder, "org.eclipse.test.performance.ui.results");
+	makeActions();
+	contributeToActionBars();
+
+	// Restore state
+	restoreState();
+
+	// Create tabs
+	createTabs();
+
+	// Set selections (tab and line)
+	this.tabFolder.setSimple(false);
+}
+
+/*
+ * Create the tab folder pages. There's one tab per performance machine.
+ * The list of these machines is got from the DB_Results contants.
+ */
+void createTabs() {
+	if (this.currentBuild == null || this.referenceBuild == null) return;
+	PerformanceResultsElement performanceResultsElement = getBuildsView().results;
+	String[] components = performanceResultsElement.getComponents();
+	int length = components.length;
+	this.tabs = new BuildsComparisonTab[length];
+	for (int i=0; i<length; i++) {
+		this.tabs[i] = new BuildsComparisonTab(components[i]);
+	}
+	for (int i=0; i<this.tabs.length; i++) {
+		CTabItem item = new CTabItem (this.tabFolder, SWT.NONE);
+		item.setText (this.tabs[i].getTabText ());
+		item.setControl (this.tabs[i].createTabFolderPage(this));
+		item.setData (this.tabs[i]);
+	}
+	this.tabFolder.setSelection(0);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#dispose()
+ */
+public void dispose() {
+	this.tabFolder.dispose();
+	int length = this.tabs==null ? 0 : this.tabs.length;
+	for (int i=0; i<length; i++) {
+		this.tabs[i].dispose();
+	}
+	super.dispose();
+}
+
+/*
+ * Fill the filters drop-down menu with:
+ * 	- filter nightly builds
+ * 	- filter non-milestone builds
+ *	- filter non-fingerprint scenarios
+ */
+void fillFiltersDropDown(IMenuManager manager) {
+	manager.add(this.filterAdvancedScenarios);
+}
+
+/*
+ * Fill the local pull down menu.
+ */
+void fillLocalPullDown(IMenuManager manager) {
+	MenuManager filtersManager= new MenuManager("Filters");
+	fillFiltersDropDown(filtersManager);
+	manager.add(filtersManager);
+}
+
+/*
+ * Fill the local tool bar with:
+ * 	- change line selection display
+ */
+void fillLocalToolBar(IToolBarManager manager) {
+	manager.add(this.writeComparison);
+}
+
+/*
+ * Return the components results view.
+ */
+PerformanceResultsElement getResults() {
+	if (this.results == null) {
+		this.results = getBuildsView().results;
+	}
+	return this.results;
+}
+
+/*
+ * Return the components results view.
+ */
+BuildsView getBuildsView() {
+	if (this.buildsView == null) {
+		this.buildsView = (BuildsView) PerformancesView.getWorkbenchView("org.eclipse.test.internal.performance.results.ui.BuildsView");
+	}
+	return this.buildsView;
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
+ */
+public void init(IViewSite site, IMemento memento) throws PartInitException {
+	super.init(site, memento);
+	this.viewState = memento;
+}
+
+/*
+ * Make the actions of the view:
+ * 	- change table line selection display
+ * 	- filter nightly builds
+ * 	- filter non-milestone builds
+ *	- filter non-fingerprint scenarios
+ */
+void makeActions() {
+
+	// Filter non-fingerprints action
+	this.filterAdvancedScenarios = new Action("Advanced &Scenarios", IAction.AS_CHECK_BOX) {
+		public void run() {
+			BuildsComparisonView.this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, isChecked());
+			resetTabFolders(false/*refresh*/);
+        }
+	};
+	this.filterAdvancedScenarios.setChecked(true);
+	this.filterAdvancedScenarios.setToolTipText("Filter advanced scenarios (i.e. not fingerprint ones)");
+
+	// Write comparison
+	this.writeComparison = new Action("Write comparison") {
+		public void run() {
+
+			// Get write directory
+			BuildsView bView = getBuildsView();
+			String filter = (bView.resultsDir == null) ? null : bView.resultsDir.getPath();
+			final File writeDir = bView.changeDir(filter, "Select a directory to write the comparison between two builds");
+			if (writeDir != null) {
+				writeComparison(writeDir);
+			}
+        }
+	};
+	this.writeComparison.setEnabled(false);
+	this.writeComparison.setToolTipText("Write comparison between two builds");
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
+ */
+public void preferenceChange(PreferenceChangeEvent event) {
+	String propertyName = event.getKey();
+	Object newValue = event.getNewValue();
+
+	// Filter non-fingerprints change
+	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS)) {
+		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS : "true".equals(newValue);
+		this.filterAdvancedScenarios.setChecked(checked);
+		resetTabFolders(false/*refresh*/);
+	}
+}
+
+/*
+ * Reset the table tab folders by re-create all the pages.
+ * Selections are set onto the first found error if this is the first tab creation (typically on a component change event from the ComponentsView)
+ * or to the previous one if this is just a refresh.
+ */
+void resetTabFolders(boolean init) {
+
+	// Store current indexes
+	int tabIndex = this.tabFolder.getSelectionIndex();
+	int lineIndex = tabIndex<0 ? -1 : this.tabs[tabIndex].table.getSelectionIndex();
+
+	// Create tab folders
+	CTabItem[] tabItems = this.tabFolder.getItems();
+	int length = tabItems.length;
+	if (length == 0) {
+		createTabs();
+	} else {
+		for (int i=0; i<length; i++) {
+			tabItems[i].setControl(this.tabs [i].createTabFolderPage(this));
+		}
+	}
+
+	// Set part name
+	setPartName(this.currentBuild+" vs. "+this.referenceBuild);
+
+	// Set the selection
+	if (tabIndex >= 0 && lineIndex >= 0) {
+		this.tabFolder.setSelection(tabIndex);
+		Table table = this.tabs[tabIndex].table;
+		table.setSelection(lineIndex);
+	}
+}
+
+/*
+ * Restore the view state from the memento information.
+ */
+void restoreState() {
+
+	// Filter non fingerprints action state
+	boolean checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+	this.filterAdvancedScenarios.setChecked(checked);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
+ */
+public void saveState(IMemento memento) {
+	super.saveState(memento);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
+public void selectionChanged(SelectionChangedEvent event) {
+	final Object[] selection = ((TreeSelection) event.getSelection()).toArray();
+	String firstBuildResults = null;
+	String secondBuildResults = null;
+	if (selection.length == 2) {
+		this.tabFolder.setVisible(true);
+		if (selection[0] instanceof BuildResultsElement) {
+			firstBuildResults = ((BuildResultsElement) selection[0]).getName();
+		}
+		if (selection[1] instanceof BuildResultsElement) {
+			secondBuildResults = ((BuildResultsElement) selection[1]).getName();
+		}
+		if (!firstBuildResults.equals(this.currentBuild) || !secondBuildResults.equals(this.referenceBuild)) {
+			this.currentBuild = firstBuildResults;
+			this.referenceBuild = secondBuildResults;
+			resetTabFolders(true);
+		}
+		this.writeComparison.setEnabled(true);
+	} else {
+		this.writeComparison.setEnabled(false);
+		this.tabFolder.setVisible(false);
+	}
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
+ */
+public void setFocus() {
+	// do nothing
+}
+
+protected void writeComparison(File writeDir) {
+	getBuildsView().resultsDir = writeDir;
+	writeDir = new File(writeDir, "values");
+	if (this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, false)) {
+		writeDir = new File(writeDir, "fingerprints");
+	} else {
+		writeDir = new File(writeDir, "all");
+	}
+	writeDir.mkdirs();
+	String buildDate = this.currentBuild.substring(1);
+	String buildPrefix = "Comparison" + buildDate + "_" + this.currentBuild.charAt(0);
+	File resultsFile = new File(writeDir, buildPrefix+".html");
+	if (resultsFile.exists()) {
+		int i=0;
+		File saveDir = new File(writeDir, "save");
+		saveDir.mkdir();
+		while (true) {
+			String newFileName = buildPrefix+"_";
+			if (i<10) newFileName += "0";
+			newFileName += i;
+			File renamedFile = new File(saveDir, newFileName+".html");
+			if (resultsFile.renameTo(renamedFile)) {
+				break;
+			}
+			i++;
+		}
+	}
+	this.results.writeComparison(resultsFile, this.currentBuild, this.referenceBuild);
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsView.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsView.java
new file mode 100644
index 0000000..dd011ca
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsView.java
@@ -0,0 +1,829 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+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.ISelectionChangedListener;
+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.swt.widgets.Display;
+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
+			IRunnableWithProgress runnable = new IRunnableWithProgress() {
+				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+					try {
+						monitor.beginTask("Generate performance results", BuildsView.this.buildsResults.length*2);
+						generate(baselineName, fingerprints, monitor);
+						monitor.done();
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			};
+			ProgressMonitorDialog readProgress = new ProgressMonitorDialog(getSite().getShell());
+			try {
+				readProgress.run(true, true, runnable);
+			} catch (InvocationTargetException e) {
+				// skip
+			} catch (InterruptedException e) {
+				// skip
+			}
+
+		}
+
+		void generate(final String baselineName, final boolean fingerprints, IProgressMonitor monitor) {
+
+			// Generate result for the build
+			GenerateResults generation = new GenerateResults(fingerprints, BuildsView.this.dataDir);
+
+			// Loop on selected builds
+			final String lastBuildName = BuildsView.this.lastBuild == null ? DB_Results.getLastCurrentBuild() : BuildsView.this.lastBuild;
+			String previousBuild = lastBuildName;
+			int length = BuildsView.this.buildsResults.length;
+			for (int i=0; i<length; i++) {
+
+				BuildResultsElement currentBuild = BuildsView.this.buildsResults[i];
+				String buildName = currentBuild.getName();
+				monitor.setTaskName("Generate perf results for build "+buildName);
+
+				// Create output directory
+				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;
+				}
+
+				// See if it's necessary to refresh data
+				if (!buildName.equals(previousBuild)) {
+					monitor.subTask("Get local data...");
+					BuildsView.this.results.readLocal(BuildsView.this.dataDir, null, buildName);
+					monitor.worked(1);
+				}
+
+				// Generate result for the build
+				GenerateAction.this.status = generation.run(BuildsView.this.results.getPerformanceResults(), buildName, baselineName, genDir, monitor);
+
+				// Store previous build
+				previousBuild = buildName;
+				if (monitor.isCanceled()) {
+					break;
+				}
+			}
+
+			// Refresh data if necessary
+			if (!lastBuildName.equals(previousBuild)) {
+				monitor.subTask("Put back local data for "+lastBuildName+"...");
+				BuildsView.this.results.readLocal(BuildsView.this.dataDir, null, BuildsView.this.lastBuild);
+				monitor.worked(1);
+			}
+
+			// 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) == 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
+	ComponentsView componentsView;
+	BuildsComparisonView buildsComparisonView = null;
+
+	// Results model
+	BuildResultsElement[] buildsResults;
+	String lastBuild;
+
+	// Directories
+	File outputDir;
+
+	// Actions
+	Action generate;
+	UpdateBuildAction updateBuild, updateAllBuilds;
+//	UpdateBuildAction forceUpdateBuild, forceUpdateAllBuilds;
+	Action writeBuildsFailures;
+
+	// 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);
+
+	// Add results view as listener to viewer selection changes
+	Display.getDefault().asyncExec(new Runnable() {
+		public void run() {
+			ISelectionChangedListener listener = getComparisonView();
+			if (listener != null) {
+				BuildsView.this.viewer.addSelectionChangedListener(listener);
+			}
+		}
+	});
+
+	// 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);
+	manager.add(new Separator());
+	manager.add(this.writeBuildsFailures);
+}
+
+/*
+ * 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(this.lastBuild);
+		initResults();
+	}
+	return this.results.getBuilds();
+}
+
+/*
+ * Return the components results view.
+ */
+BuildsComparisonView getComparisonView() {
+	if (this.buildsComparisonView == null) {
+		this.buildsComparisonView = (BuildsComparisonView) getWorkbenchView("org.eclipse.test.internal.performance.results.ui.BuildsComparisonView");
+	}
+	return this.buildsComparisonView;
+}
+
+/*
+ * Return the components view.
+ */
+PerformancesView getSiblingView() {
+	if (this.componentsView == null) {
+		this.componentsView = (ComponentsView) 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");
+
+	// Write status
+	this.writeBuildsFailures = new Action("Write failures") {
+		public void run() {
+			String filter = (BuildsView.this.resultsDir == null) ? null : BuildsView.this.resultsDir.getPath();
+			final File writeDir = changeDir(filter, "Select a directory to write the files");
+			if (writeDir != null) {
+
+				// Create runnable
+				IRunnableWithProgress runnable = new IRunnableWithProgress() {
+
+					public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+						try {
+							monitor.beginTask("Write failures", BuildsView.this.buildsResults.length*2);
+							writeBuildsFailures(writeDir, 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
+				}
+
+				// Refresh views
+				refreshInput();
+				getSiblingView().refreshInput();
+			}
+        }
+	};
+	this.writeBuildsFailures.setEnabled(true);
+	this.writeBuildsFailures.setToolTipText("Write component status to a file");
+
+	// 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);
+	if (debug) {
+		System.out.println("Reset View:");
+		System.out.println("	- eclispe version = "+eclipseVersion);
+		System.out.println("	- connected       = "+connected);
+		System.out.println("	- db location     = "+databaseLocation);
+	}
+	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 (debug) {
+		System.out.println("	- same version:    "+sameVersion);
+		System.out.println("	- same connection: "+sameConnection);
+		System.out.println("	- same DB:         "+sameDB);
+	}
+	final PerformancesView siblingView = getSiblingView();
+	if (sameConnection && sameDB) {
+		// 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) {
+		// 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(this.lastBuild);
+		} 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;
+	this.writeBuildsFailures.setEnabled(false);
+	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];
+		}
+		this.writeBuildsFailures.setEnabled(true);
+	} 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(null);
+	}
+	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(null);
+	}
+	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);
+}
+
+protected void writeBuildsFailures(File writeDir, IProgressMonitor monitor) {
+
+	// Loop on selected builds
+	final String lastBuildName = this.lastBuild == null ? DB_Results.getLastCurrentBuild() : this.lastBuild;
+	String previousBuild = lastBuildName;
+	int length = this.buildsResults.length;
+	for (int i=0; i<length; i++) {
+
+		// See if it's necessary to refresh data
+		BuildResultsElement currentBuild = this.buildsResults[i];
+		String currentBuildName = currentBuild.getName();
+		monitor.setTaskName("Write failures for build "+currentBuildName);
+		if (!currentBuildName.equals(previousBuild)) {
+			monitor.subTask("Get local data...");
+			this.results.readLocal(this.dataDir, null, currentBuildName);
+			monitor.worked(1);
+		}
+
+		// Write the failures for the selected build
+		monitor.subTask("write file...");
+		writeFailures(writeDir, currentBuildName);
+		monitor.worked(1);
+
+		// Store previous build
+		previousBuild = currentBuildName;
+		if (monitor.isCanceled()) {
+			return;
+		}
+	}
+
+	// Refresh data if necessary
+	if (!lastBuildName.equals(previousBuild)) {
+		monitor.subTask("Put back local data for "+lastBuildName+"...");
+		this.results.readLocal(this.dataDir, null, this.lastBuild);
+		monitor.worked(1);
+	}
+}
+
+protected void writeFailures(File writeDir, String buildName) {
+
+	// Set the write directory
+	this.resultsDir = writeDir;
+	boolean filterAdvancedScenarios = 	this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+	if (filterAdvancedScenarios) {
+		writeDir = new File(writeDir, "fingerprints");
+	} else {
+		writeDir = new File(writeDir, "all");
+	}
+	writeDir.mkdir();
+	int writeStatusValue = this.preferences.getInt(IPerformancesConstants.PRE_WRITE_STATUS, IPerformancesConstants.DEFAULT_WRITE_STATUS);
+	if ((writeStatusValue & IPerformancesConstants.STATUS_VALUES) != 0) {
+		writeDir = new File(writeDir, "values");
+	}
+	int buildsNumber = writeStatusValue & IPerformancesConstants.STATUS_BUILDS_NUMBER_MASK;
+	if (buildsNumber > 1) {
+		writeDir = new File(writeDir, Integer.toString(buildsNumber));
+	}
+	writeDir.mkdirs();
+
+	// Create the written file
+	String buildDate = buildName.substring(1);
+	String buildPrefix = buildDate + "_" + buildName.charAt(0);
+	File resultsFile = new File(writeDir, buildPrefix+".log");
+	File exclusionDir = new File(writeDir, "excluded");
+	exclusionDir.mkdir();
+	File exclusionFile = new File(exclusionDir, buildPrefix+".log");
+	if (resultsFile.exists()) {
+		int i=0;
+		File saveDir = new File(writeDir, "save");
+		saveDir.mkdir();
+		while (true) {
+			String newFileName = buildPrefix+"_";
+			if (i<10) newFileName += "0";
+			newFileName += i;
+			File renamedFile = new File(saveDir, newFileName+".log");
+			if (resultsFile.renameTo(renamedFile)) {
+				File renamedExclusionFile = new File(exclusionDir, newFileName+".log");
+				exclusionFile.renameTo(renamedExclusionFile);
+				break;
+			}
+			i++;
+		}
+	}
+
+	// Write status
+	StringBuffer excluded = this.results.writeFailures(resultsFile, writeStatusValue);
+	if (excluded == null) {
+		MessageDialog.openWarning(this.shell, getTitleToolTip(), "The component is not read, hence no results can be written!");
+	}
+
+	// Write exclusion file
+	try {
+		DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(exclusionFile)));
+		try {
+			stream.write(excluded.toString().getBytes());
+		}
+		finally {
+			stream.close();
+		}
+	} catch (FileNotFoundException e) {
+		System.err.println("Can't create exclusion file"+exclusionFile); //$NON-NLS-1$
+	} catch (IOException e) {
+		e.printStackTrace();
+	}
+}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentResultsView.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentResultsView.java
new file mode 100644
index 0000000..d166c3d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentResultsView.java
@@ -0,0 +1,527 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
+import org.eclipse.test.internal.performance.results.model.ComponentResultsElement;
+import org.eclipse.test.internal.performance.results.model.ConfigResultsElement;
+import org.eclipse.test.internal.performance.results.model.DimResultsElement;
+import org.eclipse.test.internal.performance.results.model.PerformanceResultsElement;
+import org.eclipse.test.internal.performance.results.model.ResultsElement;
+import org.eclipse.test.internal.performance.results.model.ScenarioResultsElement;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+
+/**
+ * This view show for each performance machine the results of all builds
+ * run since the beginning of the current version development.
+ * <p>
+ * Each machine results are show in a separate tab.
+ * </p><p>
+ * There's no real action available action from this view, only the possibility
+ * to change how is displayed the line selection (full or only first column)
+ * and also filter the results:
+ * 	<ul>
+ *	<li>Filter for builds:
+ *		<ul>
+ *		<li>Filter nightly:	hide the nightly builds (starting with 'N')</li>
+ *		<li>Filter non-important builds:	hide all non-important builds, which means non-milestone builds and those after the last milestone</li>
+ *		</ul>
+ *	</li>
+ *	</li>Filter for scenarios:
+ *		<ul>
+ *		<li>Filter non-fingerprints: hide the scenarios which are not in the fingerprints</li>
+ *		</ul>
+ *	</li>
+ *	</ul>
+ * </p><p>
+ * Note that these filters are synchronized with the ones applied in the
+ * {@link ComponentsView Components view}.
+ * </p>
+ *
+ * @see ConfigTab Folder tab containing all the results for a configuration.
+ */
+public class ComponentResultsView extends ViewPart implements ISelectionChangedListener, IPreferenceChangeListener {
+
+	// SWT resources
+	CTabFolder tabFolder;
+
+	// Model information
+	ConfigTab[] tabs;
+	ComponentResultsElement componentResultsElement;
+
+	// Action
+	Action fullLineSelection;
+	Action filterAdvancedScenarios;
+	Action filterOldBuilds;
+	Action filterNightlyBuilds;
+	ImageDescriptor fullSelectionImageDescriptor;
+
+	// Views
+	IMemento viewState;
+
+	// Eclipse preferences
+	IEclipsePreferences preferences;
+
+/*
+ * Default constructor:
+ * 	- create the image descriptor
+ * 	- register the view as a properties listener
+ */
+public ComponentResultsView() {
+	this.fullSelectionImageDescriptor = ImageDescriptor.createFromFile(getClass(), "icallout_obj.gif");
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+	this.preferences.addPreferenceChangeListener(this);
+	Util.initMilestones(this.preferences);
+}
+
+/*
+ * Contribute the local tools bar and the pull-down menu to the action bars.
+ */
+void contributeToActionBars() {
+	IActionBars bars = getViewSite().getActionBars();
+	fillLocalPullDown(bars.getMenuManager());
+	fillLocalToolBar(bars.getToolBarManager());
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+public void createPartControl(Composite parent) {
+
+	// Create the tab folder
+	this.tabFolder = new CTabFolder(parent, SWT.BORDER);
+
+	// Add results view as listener to viewer selection changes
+	Display.getDefault().asyncExec(new Runnable() {
+		public void run() {
+			PerformancesView performancesView = (PerformancesView) PerformancesView.getWorkbenchView("org.eclipse.test.internal.performance.results.ui.ComponentsView");
+			if (performancesView != null) {
+				performancesView.viewer.addSelectionChangedListener(ComponentResultsView.this);
+			}
+		}
+	});
+
+	// Set actions
+	PlatformUI.getWorkbench().getHelpSystem().setHelp(this.tabFolder, "org.eclipse.test.performance.ui.results");
+	makeActions();
+	contributeToActionBars();
+
+	// Restore state
+	restoreState();
+
+	// Create tabs
+	createTabs();
+
+	// Set selections (tab and line)
+	this.tabFolder.setSimple(false);
+}
+
+/*
+ * Create the tab folder pages. There's one tab per performance machine.
+ * The list of these machines is got from the DB_Results contants.
+ */
+void createTabs() {
+	if (this.componentResultsElement == null) return;
+	PerformanceResultsElement performanceResultsElement = (PerformanceResultsElement) this.componentResultsElement.getParent(null);
+	String[] configNames = performanceResultsElement.getConfigs();
+	String[] configDescriptions = performanceResultsElement.getConfigDescriptions();
+	int length = configNames.length;
+	this.tabs = new ConfigTab[length];
+	for (int i=0; i<length; i++) {
+		this.tabs[i] = new ConfigTab(configNames[i], configDescriptions[i]);
+	}
+	for (int i=0; i<this.tabs.length; i++) {
+		CTabItem item = new CTabItem (this.tabFolder, SWT.NONE);
+		item.setText (this.tabs[i].getTabText ());
+		item.setControl (this.tabs[i].createTabFolderPage(this.componentResultsElement, this.tabFolder, this.fullLineSelection.isChecked()));
+		item.setData (this.tabs[i]);
+	}
+	this.tabFolder.setSelection(0);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#dispose()
+ */
+public void dispose() {
+	this.tabFolder.dispose();
+	int length = this.tabs==null ? 0 : this.tabs.length;
+	for (int i=0; i<length; i++) {
+		this.tabs[i].dispose();
+	}
+	JFaceResources.getResources().destroyImage(this.fullSelectionImageDescriptor);
+	super.dispose();
+}
+
+/*
+ * Fill the filters drop-down menu with:
+ * 	- filter nightly builds
+ * 	- filter non-milestone builds
+ *	- filter non-fingerprint scenarios
+ */
+void fillFiltersDropDown(IMenuManager manager) {
+	manager.add(this.filterNightlyBuilds);
+	manager.add(this.filterOldBuilds);
+	manager.add(new Separator());
+	manager.add(this.filterAdvancedScenarios);
+}
+
+/*
+ * Fill the local pull down menu.
+ */
+void fillLocalPullDown(IMenuManager manager) {
+	MenuManager filtersManager= new MenuManager("Filters");
+	fillFiltersDropDown(filtersManager);
+	manager.add(filtersManager);
+}
+
+/*
+ * Fill the local tool bar with:
+ * 	- change line selection display
+ */
+void fillLocalToolBar(IToolBarManager manager) {
+	manager.add(this.fullLineSelection);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
+ */
+public void init(IViewSite site, IMemento memento) throws PartInitException {
+	super.init(site, memento);
+	this.viewState = memento;
+}
+
+/*
+ * Make the actions of the view:
+ * 	- change table line selection display
+ * 	- filter nightly builds
+ * 	- filter non-milestone builds
+ *	- filter non-fingerprint scenarios
+ */
+void makeActions() {
+
+	// Full line selection action
+	this.fullLineSelection = new Action("", IAction.AS_CHECK_BOX) {
+		public void run() {
+			resetTabFolders(false/*refresh*/);
+		}
+	};
+	this.fullLineSelection.setImageDescriptor(this.fullSelectionImageDescriptor);
+	this.fullLineSelection.setToolTipText("Full line selection");
+//	this.fullLineSelection.setChecked(true);
+
+	// Filter non-fingerprints action
+	this.filterAdvancedScenarios = new Action("Advanced &Scenarios", IAction.AS_CHECK_BOX) {
+		public void run() {
+			ComponentResultsView.this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, isChecked());
+			resetTabFolders(false/*refresh*/);
+        }
+	};
+	this.filterAdvancedScenarios.setChecked(true);
+	this.filterAdvancedScenarios.setToolTipText("Filter advanced scenarios (i.e. not fingerprint ones)");
+
+	// Filter non-important builds action
+	this.filterOldBuilds = new Action("&Old Builds", IAction.AS_CHECK_BOX) {
+		public void run() {
+			ComponentResultsView.this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_OLD_BUILDS, isChecked());
+			resetTabFolders(false/*refresh*/);
+		}
+	};
+	this.filterOldBuilds.setChecked(false);
+	this.filterOldBuilds.setToolTipText("Filter old builds (i.e. before last milestone) but keep all previous milestones)");
+
+	// Filter nightly action
+	this.filterNightlyBuilds = new Action("&Nightly", IAction.AS_CHECK_BOX) {
+		public void run() {
+			ComponentResultsView.this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS, isChecked());
+			resetTabFolders(false/*refresh*/);
+		}
+	};
+	this.filterNightlyBuilds.setToolTipText("Filter nightly builds");
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
+ */
+public void preferenceChange(PreferenceChangeEvent event) {
+	String propertyName = event.getKey();
+	Object newValue = event.getNewValue();
+
+	// Filter non-fingerprints change
+	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS)) {
+		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS : "true".equals(newValue);
+		this.filterAdvancedScenarios.setChecked(checked);
+		resetTabFolders(false/*refresh*/);
+	}
+
+	// Filter non-milestone change
+	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_OLD_BUILDS)) {
+		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS : "true".equals(newValue);
+		this.filterOldBuilds.setChecked(checked);
+		resetTabFolders(false/*refresh*/);
+	}
+
+	// Filter nightly builds change
+	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS)) {
+		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_NIGHTLY_BUILDS : "true".equals(newValue);
+		this.filterNightlyBuilds.setChecked(checked);
+		resetTabFolders(false/*refresh*/);
+	}
+}
+
+/*
+ * Reset the table tab folders by re-create all the pages.
+ * Selections are set onto the first found error if this is the first tab creation (typically on a component change event from the ComponentsView)
+ * or to the previous one if this is just a refresh.
+ */
+void resetTabFolders(boolean init) {
+
+	// Store current indexes
+	int tabIndex = this.tabFolder.getSelectionIndex();
+	int lineIndex = tabIndex<0 ? -1 : this.tabs[tabIndex].table.getSelectionIndex();
+
+	// Create tab folders
+	CTabItem[] tabItems = this.tabFolder.getItems();
+	int length = tabItems.length;
+	if (length == 0) {
+		createTabs();
+	} else {
+		for (int i=0; i<length; i++) {
+			tabItems[i].setControl(this.tabs [i].createTabFolderPage(this.componentResultsElement, this.tabFolder, this.fullLineSelection.isChecked()));
+		}
+	}
+
+	// Set the part name when possible
+	if (this.componentResultsElement != null) {
+		setPartName(Util.componentDisplayName(this.componentResultsElement.getName()));
+	}
+
+	// If this is the first display then look for the first error to set the selection on it
+	if (init)  {
+		if (this.componentResultsElement != null) {
+			// If the component has
+			if (this.componentResultsElement.hasError()) {
+				ResultsElement[] children = this.componentResultsElement.getChildren(); // get scenarios
+				int childrenLength = children.length;
+				for (int s=0; s<childrenLength; s++) {
+					if (children[s].hasError()) {
+						children = children[s].getChildren(); // get configs
+						for (int c=0; c<childrenLength; c++) {
+							if (children[c].hasError()) {
+								tabIndex = c;
+								break;
+							}
+						}
+						break;
+					}
+				}
+			}
+		}
+		lineIndex = 0;
+	}
+
+	// Set the selection
+	if (tabIndex >= 0 && lineIndex >= 0) {
+		this.tabFolder.setSelection(tabIndex);
+		Table table = this.tabs[tabIndex].table;
+		table.setSelection(lineIndex);
+	}
+}
+
+/*
+ * Restore the view state from the memento information.
+ */
+void restoreState() {
+
+	// Filter baselines action state
+	if (this.viewState != null) {
+		Boolean state = this.viewState.getBoolean(IPerformancesConstants.PRE_FULL_LINE_SELECTION);
+		boolean fullLine = state == null ? true : state.booleanValue();
+		this.fullLineSelection.setChecked(fullLine);
+	}
+
+	// Filter non fingerprints action state
+	boolean checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+	this.filterAdvancedScenarios.setChecked(checked);
+
+	// Filter nightly builds action
+	checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS, IPerformancesConstants.DEFAULT_FILTER_NIGHTLY_BUILDS);
+	this.filterNightlyBuilds.setChecked(checked);
+
+	// Filter non important builds action state
+	checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_OLD_BUILDS, IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS);
+	this.filterOldBuilds.setChecked(checked);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
+ */
+public void saveState(IMemento memento) {
+	super.saveState(memento);
+	memento.putBoolean(IPerformancesConstants.PRE_FULL_LINE_SELECTION, this.fullLineSelection.isChecked());
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
+public void selectionChanged(SelectionChangedEvent event) {
+	ResultsElement selectedElement = (ResultsElement) ((TreeSelection) event.getSelection()).getFirstElement();
+	ComponentResultsElement componentElement = null;
+	ScenarioResultsElement scenarioResultsElement = null;
+	ConfigResultsElement configResultsElement = null;
+	BuildResultsElement buildResultsElement = null;
+	if (selectedElement instanceof ComponentResultsElement) {
+		componentElement = (ComponentResultsElement) selectedElement;
+	} else if (selectedElement instanceof ScenarioResultsElement) {
+		scenarioResultsElement = (ScenarioResultsElement) selectedElement;
+		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
+	} else if (selectedElement instanceof ConfigResultsElement) {
+		configResultsElement = (ConfigResultsElement) selectedElement;
+		scenarioResultsElement = (ScenarioResultsElement) configResultsElement.getParent(null);
+		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
+	} else if (selectedElement instanceof BuildResultsElement) {
+		buildResultsElement = (BuildResultsElement) selectedElement;
+		configResultsElement = (ConfigResultsElement) buildResultsElement.getParent(null);
+		scenarioResultsElement = (ScenarioResultsElement) configResultsElement.getParent(null);
+		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
+	} else if (selectedElement instanceof DimResultsElement) {
+		buildResultsElement = (BuildResultsElement) selectedElement.getParent(null);
+		configResultsElement = (ConfigResultsElement) buildResultsElement.getParent(null);
+		scenarioResultsElement = (ScenarioResultsElement) configResultsElement.getParent(null);
+		componentElement = (ComponentResultsElement) scenarioResultsElement.getParent(null);
+	}
+	if (componentElement != this.componentResultsElement) {
+		this.componentResultsElement = componentElement;
+		if (componentElement == null || this.componentResultsElement.getChildren(null).length > 0) {
+			resetTabFolders(true);
+		}
+	}
+	if (configResultsElement != null) {
+		ConfigTab configTab = this.tabs[this.tabFolder.getSelectionIndex()];
+		if (!configResultsElement.getName().equals(configTab.configName)) {
+			int length = this.tabs.length;
+			for (int i=0; i<length; i++) {
+				if (this.tabs[i].configName.equals(configResultsElement.getName())) {
+					this.tabFolder.setSelection(i);
+				}
+			}
+		}
+		if (buildResultsElement != null) {
+			configTab = this.tabs[this.tabFolder.getSelectionIndex()];
+			configTab.select(buildResultsElement);
+		}
+	}
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
+ */
+public void setFocus() {
+	// do nothing
+}
+
+/*
+ * Set the name of the part.
+ * This name is built from the name of the component selected in the Components view.
+ * The rules to build the name are:
+ * 	1. If the component name does not start then the part name is just "'<component name>' results"
+ * 	2. Otherwise, remove "org.eclipse." form the component name and count the tokens separated by a dot ('.')
+ * 		a. if there's only one remaining token, then the part name is "Platform/"
+ * 			+ "<token uppercased>" if token is less than 3 characters,"<token with first char uppercased>" otherwise
+ * 		b. otherwise then the part name is "<first token uppercased>"
+ * 			+ for each followed additional token:
+ * 				"<token uppercased>" if token is less than 3 characters,"<token with first char uppercased>" otherwise
+ * 			+ " results"
+ * E.g.
+ * 	- org.eclipse.ui -> "Platform/UI"
+ * 	- org.eclipse.swt -> "Platform/SWT"
+ * 	- org.eclipse.team -> "Platform/Team"
+ * 	- org.eclipse.jdt.ui -> "JDT/UI"
+ * 	- org.eclipse.jdt.core -> "JDT/Core"
+ * 	- org.eclipse.pde.api.tools -> "PDE/API Tools"
+ *
+protected void setPartName() {
+	String componentName = this.componentResultsElement.getName();
+	String partName;
+	StringBuffer buffer = null;
+	if (componentName.startsWith(ORG_ECLIPSE)) {
+		partName = componentName.substring(ORG_ECLIPSE.length());
+		StringTokenizer tokenizer = new StringTokenizer(partName, ".");
+		while (tokenizer.hasMoreTokens()) {
+			String token = tokenizer.nextToken();
+			if (buffer == null) {
+				if (tokenizer.hasMoreTokens()) {
+					buffer = new StringBuffer("'"+token.toUpperCase());
+					buffer.append('/');
+				} else {
+					buffer = new StringBuffer("'Platform/");
+					if (token.length() > 3) {
+						buffer.append(Character.toUpperCase(token.charAt(0)));
+						buffer.append(token.substring(1));
+					} else {
+						buffer.append(token.toUpperCase());
+					}
+				}
+			} else {
+				if (token.length() > 3) {
+					buffer.append(Character.toUpperCase(token.charAt(0)));
+					buffer.append(token.substring(1));
+				} else {
+					buffer.append(token.toUpperCase());
+				}
+				if (tokenizer.hasMoreTokens()) buffer.append(' ');
+			}
+		}
+	} else {
+		buffer = new StringBuffer("'");
+		buffer.append(componentName);
+		buffer.append("'");
+	}
+	buffer.append("' results");
+	setPartName(buffer.toString());
+}
+*/
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentsView.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentsView.java
new file mode 100644
index 0000000..9797fb0
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentsView.java
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
+import org.eclipse.test.internal.performance.results.model.ComponentResultsElement;
+import org.eclipse.test.internal.performance.results.model.ConfigResultsElement;
+import org.eclipse.test.internal.performance.results.model.ResultsElement;
+import org.eclipse.test.internal.performance.results.model.ScenarioResultsElement;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+/**
+ * View to see the performance results of all the components in a hierarchical tree.
+ * <p>
+ * A component defines several performance scenarios which are run on several
+ * machines (aka config). All builds results are stored onto each configuration
+ * and 2 dimensions have been stored for each result: the "Elapsed Process Time"
+ * and the "CPU Time".
+ * </p><p>
+ * There's only one available action from this view: read the local data files. This
+ * populates the hierarchy with the numbers stored in these files.
+ * </p><p>
+ * There's also the possibility to filter the results:
+ * 	<ul>
+ *	<li>Filter for builds:
+ *		<ul>
+ *		<li>Filter baselines:	hide the baselines (starting with R-3.x)</li>
+ *		<li>Filter nightly:	hide the nightly builds (starting with 'N')</li>
+ *		<li>Filter non-important builds:	hide all non-important builds, which means non-milestone builds and those after the last milestone</li>
+ *		</ul>
+ *	</li>
+ *	</li>Filter for scenarios:
+ *		<ul>
+ *		<li>Filter non-fingerprints: hide the scenarios which are not in the fingerprints</li>
+ *		</ul>
+ *	</li>
+ *	</ul>
+ * </p>
+ * @see ComponentResultsView
+ */
+public class ComponentsView extends PerformancesView {
+
+	// Viewer filters
+	final static ViewerFilter FILTER_ADVANCED_SCENARIOS = new ViewerFilter() {
+		public boolean select(Viewer v, Object parentElement, Object element) {
+			if (element instanceof ScenarioResultsElement) {
+				ScenarioResultsElement scenarioElement = (ScenarioResultsElement) element;
+				return scenarioElement.hasSummary();
+			}
+	        return true;
+        }
+	};
+
+	// Views
+	BuildsView buildsView;
+	ComponentResultsView componentResultsView = null;
+
+	// Internal
+	Set expandedComponents = new HashSet();
+
+	// Actions
+	Action filterAdvancedScenarios;
+
+	// SWT resources
+	Font boldFont;
+
+/**
+ * Default constructor.
+ */
+public ComponentsView() {
+	super();
+
+	// Get preferences
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+}
+
+/*
+ * (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 components list
+	WorkbenchContentProvider contentProvider = new WorkbenchContentProvider() {
+		public Object[] getElements(Object o) {
+			return ComponentsView.this.getElements();
+		}
+	};
+	this.viewer.setContentProvider(contentProvider);
+
+	// Set the label provider
+	WorkbenchLabelProvider labelProvider = new WorkbenchLabelProvider() {
+
+		protected String decorateText(String input, Object element) {
+			String text = super.decorateText(input, element);
+			if (element instanceof BuildResultsElement) {
+				BuildResultsElement buildElement = (BuildResultsElement) element;
+				if (buildElement.isMilestone()) {
+					text = Util.getMilestoneName(buildElement.getName()) + " - "+text;
+				}
+			}
+			return text;
+		}
+
+		// When all scenarios are displayed, then set fingerprints one in bold.
+		public Font getFont(Object element) {
+			Font font = super.getFont(element);
+			if (element instanceof ScenarioResultsElement) {
+				boolean fingerprints = ComponentsView.this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+				if (!fingerprints) {
+					ScenarioResultsElement scenarioElement = (ScenarioResultsElement) element;
+					if (scenarioElement.hasSummary()) {
+						return getBoldFont(font);
+					}
+				}
+			}
+			if (element instanceof BuildResultsElement) {
+				BuildResultsElement buildElement = (BuildResultsElement) element;
+				if (Util.isMilestone(buildElement.getName())) {
+					return getBoldFont(font);
+				}
+			}
+			return font;
+		}
+	};
+	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) {
+			// Config and Build results are sorted in reverse order
+			if (e1 instanceof BuildResultsElement) {
+				ResultsElement element = (ResultsElement) e2;
+				return element.compareTo(e1);
+			}
+			if (e1 instanceof ResultsElement) {
+				ResultsElement element = (ResultsElement) e1;
+				return element.compareTo(e2);
+			}
+			return super.compare(view, e1, e2);
+		}
+	};
+	this.viewer.setSorter(nameSorter);
+
+	// Add results view as listener to viewer selection changes
+	Display.getDefault().asyncExec(new Runnable() {
+		public void run() {
+			ISelectionChangedListener listener = getResultsView();
+			if (listener != null) {
+				ComponentsView.this.viewer.addSelectionChangedListener(listener);
+			}
+		}
+	});
+
+	// Finalize viewer initialization
+	PlatformUI.getWorkbench().getHelpSystem().setHelp(this.viewer.getControl(), "org.eclipse.test.performance.ui.components");
+	finalizeViewerCreation();
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#dispose()
+ */
+public void dispose() {
+	if (this.boldFont != null) {
+		this.boldFont.dispose();
+	}
+	super.dispose();
+}
+
+/*
+ * (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.filterOldBuilds);
+	manager.add(new Separator());
+	manager.add(this.filterAdvancedScenarios);
+}
+
+/*
+ * Filter non fingerprints scenarios action run.
+ */
+void filterAdvancedScenarios(boolean fingerprints, boolean updatePreference) {
+	this.results.setFingerprints(fingerprints);
+	if (fingerprints) {
+		this.viewFilters.add(FILTER_ADVANCED_SCENARIOS);
+	} else {
+		this.viewFilters.remove(FILTER_ADVANCED_SCENARIOS);
+	}
+	this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, fingerprints);
+	updateFilters();
+}
+
+/*
+ * Returns the bold font.
+ */
+Font getBoldFont(Font font) {
+	if (this.boldFont == null) {
+		FontData[] fontData = (font==null ? JFaceResources.getDefaultFont() : font).getFontData();
+		FontData boldFontData = new FontData(fontData[0].getName(), fontData[0].getHeight(), SWT.BOLD);
+		this.boldFont = new Font(this.display, boldFontData);
+	}
+	return this.boldFont;
+}
+
+/*
+ * Get all the components from the model.
+ */
+Object[] getElements() {
+	if (this.results == null) {
+		initResults();
+		if (this.filterAdvancedScenarios != null) {
+			this.results.setFingerprints(this.filterAdvancedScenarios.isChecked());
+		}
+	}
+	return this.results.getElements();
+}
+
+/*
+ * Return the components results view.
+ */
+ComponentResultsView getResultsView() {
+	if (this.componentResultsView == null) {
+		this.componentResultsView = (ComponentResultsView) getWorkbenchView("org.eclipse.test.internal.performance.results.ui.ComponentsResultsView");
+	}
+	return this.componentResultsView;
+}
+
+/*
+ * Return the builds view.
+ */
+PerformancesView getSiblingView() {
+	if (this.buildsView == null) {
+		this.buildsView = (BuildsView) getWorkbenchView("org.eclipse.test.internal.performance.results.ui.BuildsView");
+	}
+	return this.buildsView;
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.ui.PerformancesView#makeActions()
+ */
+void makeActions() {
+
+	super.makeActions();
+
+	// Filter non-fingerprints action
+	this.filterAdvancedScenarios = new Action("Advanced &Scenarios", IAction.AS_CHECK_BOX) {
+		public void run() {
+			filterAdvancedScenarios(isChecked(), true/*update preference*/);
+        }
+	};
+	this.filterAdvancedScenarios.setChecked(true);
+	this.filterAdvancedScenarios.setToolTipText("Filter advanced scenarios (i.e. not fingerprint ones)");
+
+	// Set filters default
+	this.filterBaselineBuilds.setChecked(true);
+	this.filterNightlyBuilds.setChecked(false);
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
+ */
+public void preferenceChange(PreferenceChangeEvent event) {
+	String propertyName = event.getKey();
+	Object newValue = event.getNewValue();
+
+	// Filter non-fingerprints change
+	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS)) {
+		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS : "true".equals(newValue);
+		filterAdvancedScenarios(checked, false/*do not update preference*/);
+		this.filterAdvancedScenarios.setChecked(checked);
+	}
+
+	// Filter non-milestone change
+	if (propertyName.equals(IPerformancesConstants.PRE_FILTER_OLD_BUILDS)) {
+		boolean checked = newValue == null ? IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS : "true".equals(newValue);
+		filterOldBuilds(checked, false/*do not update preference*/);
+		this.filterOldBuilds.setChecked(checked);
+	}
+
+	super.preferenceChange(event);
+}
+
+void restoreState() {
+	super.restoreState();
+
+	// Filter baselines action default
+	if (this.viewState == null) {
+		this.filterBaselineBuilds.setChecked(true);
+		this.viewFilters.add(FILTER_BASELINE_BUILDS);
+	}
+
+	// Filter non fingerprints action state
+	boolean checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+	this.filterAdvancedScenarios.setChecked(checked);
+	if (checked) {
+		this.viewFilters.add(FILTER_ADVANCED_SCENARIOS);
+	}
+}
+
+public void saveState(IMemento memento) {
+	super.saveState(memento);
+}
+
+/**
+ * Select a results element in the tree.
+ */
+public void select(ComponentResultsElement componentResults, String configName, String scenarioName, String buildName) {
+
+	// Collapse previous expanded components except the requested one
+	// TODO (frederic) also collapse expanded components children elements
+	this.expandedComponents.remove(componentResults);
+	Iterator iterator = this.expandedComponents.iterator();
+	while (iterator.hasNext()) {
+		this.viewer.collapseToLevel(iterator.next(), AbstractTreeViewer.ALL_LEVELS);
+	}
+	this.expandedComponents.clear();
+
+	// Set the tree selection
+	ScenarioResultsElement scenarioResultsElement = (ScenarioResultsElement) componentResults.getResultsElement(scenarioName);
+	if (scenarioResultsElement != null) {
+		ConfigResultsElement configResultsElement = (ConfigResultsElement) scenarioResultsElement.getResultsElement(configName);
+		if (configResultsElement != null) {
+			BuildResultsElement buildResultsElement = (BuildResultsElement) configResultsElement.getResultsElement(buildName);
+			if (buildResultsElement != null) {
+				this.viewer.setSelection(new StructuredSelection(buildResultsElement), true);
+				this.setFocus();
+			}
+		}
+	}
+}
+
+/*
+ * (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);
+	ResultsElement eventResultsElement = (ResultsElement) ((StructuredSelection)event.getSelection()).getFirstElement();
+	if (eventResultsElement != null) {
+		ResultsElement eventComponentElement = eventResultsElement;
+		if (!(eventComponentElement instanceof ComponentResultsElement)) {
+			while (!(eventComponentElement instanceof ComponentResultsElement)) {
+				eventComponentElement = (ResultsElement) eventComponentElement.getParent(null);
+			}
+			this.expandedComponents.add(eventComponentElement);
+		}
+	}
+}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ConfigTab.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ConfigTab.java
new file mode 100644
index 0000000..6b50a04
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ConfigTab.java
@@ -0,0 +1,584 @@
+/*******************************************************************************
+ * 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.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.ToolTip;
+import org.eclipse.test.internal.performance.results.db.AbstractResults;
+import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
+import org.eclipse.test.internal.performance.results.model.ComponentResultsElement;
+import org.eclipse.test.internal.performance.results.model.ConfigResultsElement;
+import org.eclipse.test.internal.performance.results.model.ResultsElement;
+import org.eclipse.test.internal.performance.results.model.ScenarioResultsElement;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+
+/**
+ * Tab to display all performances results numbers for a configuration (i.e a performance machine).
+ */
+public class ConfigTab {
+
+	// Colors
+	static final Display DEFAULT_DISPLAY = Display.getDefault();
+	static final Color BLUE= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLUE);
+	static final Color DARK_GREEN= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_DARK_GREEN);
+	static final Color GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GRAY);
+	static final Color MAGENTA = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_MAGENTA);
+	static final Color RED = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_RED);
+
+	// SWT resources
+	Shell shell;
+	Display display;
+	Table table;
+	private GC gc;
+	private Color lightred;
+	private Color lightyellow;
+	private Color darkyellow;
+	private Color blueref;
+	private Font boldFont;
+	private Font italicFont;
+	private Font boldItalicFont;
+	Map toolTips;
+
+	// Information
+	String configBox, configName;
+	ComponentResultsElement results;
+	double[][] allValues;
+	double[][] allErrors;
+
+	// Cells management
+	Point tableOrigin, tableSize;
+	int columnsCount, rowsCount;
+	List firstLine;
+
+	// Eclipse preferences
+	private IEclipsePreferences preferences;
+
+/*
+ * Default constructor.
+ */
+public ConfigTab(String name, String box) {
+    this.configName = name;
+    int idx = box.indexOf(" (");
+	this.configBox = idx > 0 ? box.substring(0, idx) : box;
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+}
+
+/**
+ * Creates the tab folder page.
+ *
+ * @param tabFolder org.eclipse.swt.widgets.TabFolder
+ * @param fullSelection Tells whether the table should have a full line selection or not
+ * @return the new page for the tab folder
+ */
+Composite createTabFolderPage (ComponentResultsElement componentResultsElement, CTabFolder tabFolder, boolean fullSelection) {
+	// Cache the shell and display.
+	this.shell = tabFolder.getShell();
+	this.display = this.shell.getDisplay();
+
+	// Remove old table is present
+	boolean initResources = this.table == null;
+	if (this.table != null) {
+		disposeTable();
+	}
+
+	// Store results
+	this.results = componentResultsElement;
+
+	// Create the "children" table
+	int style = SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL;
+	if (fullSelection) style |= SWT.FULL_SELECTION;
+	this.table = new Table(tabFolder, style);
+	this.table.setLinesVisible (true);
+	this.table.setHeaderVisible (true);
+	GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+	gridData.heightHint = 150;
+	this.table.setLayoutData (gridData);
+	this.gc = new GC(this.table);
+
+	// Init resources
+	if (initResources) initResources();
+
+	// Add columns to the table
+	boolean fingerprints = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+	String [] columnHeaders = getLayoutDataFieldNames(fingerprints);
+	int length = columnHeaders.length;
+	for (int i = 0; i < length; i++) {
+		TableColumn column = new TableColumn(this.table, SWT.CENTER);
+		column.setText (columnHeaders [i]);
+	}
+
+	// Add lines to the table
+	this.toolTips = new HashMap();
+	fillTableLines(fingerprints);
+
+	// Updated columns
+	for (int i=0; i<length; i++) {
+		TableColumn column = this.table.getColumn(i);
+		column.setWidth(i==0?120:100);
+		if (i > 0) {
+			String text = (String) this.firstLine.get(i);
+			column.setToolTipText(text);
+		}
+	}
+
+	// Store table info
+	this.columnsCount = length;
+
+	// Listen to mouse events to select the corresponding build in the components view
+	// when a click is done in the table cell.
+	final ComponentsView componentsView = (ComponentsView) PerformancesView.getWorkbenchView("org.eclipse.test.internal.performance.results.ui.ComponentsView");
+	MouseListener mouseListener = new MouseListener() {
+		public void mouseUp(MouseEvent e) {
+		}
+		public void mouseDown(MouseEvent e) {
+			Point cellPosition = currentCellPosition(e.x, e.y);
+			Table tabTable = ConfigTab.this.table;
+			componentsView.select(ConfigTab.this.results, ConfigTab.this.configName, (String) ConfigTab.this.firstLine.get(cellPosition.x), tabTable.getItem(cellPosition.y).getText());
+		}
+		public void mouseDoubleClick(MouseEvent e) {
+		}
+	};
+	this.table.addMouseListener(mouseListener);
+
+	// Listen to mouse track events to display the table cell corresponding tooltip.
+	MouseTrackListener mouseTrackListener = new MouseTrackListener() {
+		ToolTip currentTooltip;
+		public void mouseHover(MouseEvent e) {
+			if (this.currentTooltip != null) {
+				this.currentTooltip.setVisible(false);
+				this.currentTooltip = null;
+			}
+			Point cellPosition = currentCellPosition(e.x, e.y);
+			if (cellPosition != null) {
+				ToolTip tooltip = (ToolTip) ConfigTab.this.toolTips.get(cellPosition);
+				if (tooltip != null) {
+					Point location = ConfigTab.this.table.toDisplay(new Point(e.x, e.y));
+					tooltip.setLocation(location);
+					tooltip.setVisible(true);
+					this.currentTooltip = tooltip;
+				}
+			}
+		}
+		public void mouseEnter(MouseEvent e) {
+		}
+		public void mouseExit(MouseEvent e) {
+		}
+	};
+	this.table.addMouseTrackListener(mouseTrackListener);
+
+	// Select the first line by default (as this is the current build)
+	this.table.select(0);
+
+	// Return the built composite
+	return this.table;
+}
+
+/*
+ * Create and store a tooltip with the given information and at the given position.
+ */
+void createToolTip(String toolTipText, String toolTipMessage, int toolTipStyle, Point position) {
+	ToolTip toolTip = new ToolTip(this.table.getShell(), toolTipStyle);
+	toolTip.setAutoHide(true);
+	toolTip.setText(toolTipText);
+	toolTip.setMessage(/*"("+col+","+row+") "+*/toolTipMessage);
+	this.toolTips.put(position, toolTip);
+}
+
+/*
+ * Get the current cell position (column, row) from a point position.
+ */
+Point currentCellPosition(int x, int y) {
+
+	// Compute the origin of the visible area
+	if (this.tableOrigin == null) {
+		this.tableOrigin = new Point(0, this.table.getHeaderHeight());
+	}
+
+	// Increment width until over current y position
+	int height= this.tableOrigin.y;
+	int row = this.table.getTopIndex();
+	while (row<this.rowsCount && height<y) {
+		height += this.table.getItemHeight();
+		row++;
+	}
+	if (height < y) {
+		// return when position is over the last line
+		return null;
+	}
+	row--;
+
+	// Increment width until being over current x position
+	int col = 0;
+	TableItem tableItem = this.table.getItem(row);
+	Rectangle bounds = tableItem.getBounds(col);
+	while (col<this.columnsCount) {
+		int max = bounds.x + bounds.width + this.table.getGridLineWidth();
+		if (x <= max) break;
+		if (col == this.columnsCount) {
+			// return when position is over the last column
+			return null;
+		}
+		col++;
+		bounds = tableItem.getBounds(col);
+	}
+
+	// Return the found table cell position
+	return new Point(col, row);
+}
+
+/*
+ * Dispose all SWT resources.
+ */
+public void dispose() {
+	if (this.boldFont != null) {
+		this.boldFont.dispose();
+	}
+	if (this.italicFont != null) {
+		this.italicFont.dispose();
+	}
+	if (this.boldItalicFont != null) {
+		this.boldItalicFont.dispose();
+	}
+	if (this.darkyellow != null) {
+		this.darkyellow.dispose();
+	}
+	if (this.lightyellow != null) {
+		this.lightyellow.dispose();
+	}
+	if (this.lightred != null) {
+		this.lightred.dispose();
+	}
+	if (this.blueref != null) {
+		this.blueref.dispose();
+	}
+	disposeTable();
+}
+
+/*
+ * Dispose all SWT controls associated with the table.
+ */
+private void disposeTable() {
+	if (this.toolTips != null) {
+		Iterator cells = this.toolTips.keySet().iterator();
+		while (cells.hasNext()) {
+			ToolTip toolTip = (ToolTip) this.toolTips.get(cells.next());
+			toolTip.dispose();
+		}
+	}
+	this.table.dispose();
+	this.tableOrigin = null;
+	this.firstLine = null;
+}
+
+/*
+ * Fill the lines of the tables.
+ * Get all the information from the model which are returned in a list (lines) of lists (columns).
+ */
+private void fillTableLines(boolean fingerprints) {
+
+	// Get preferences information
+	boolean onlyMilestones = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_OLD_BUILDS, IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS);
+	boolean skipNightlyBuilds = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS, IPerformancesConstants.DEFAULT_FILTER_NIGHTLY_BUILDS);
+
+	// Get model information
+	if (this.results == null) return;
+	List differences = this.results.getConfigNumbers(this.configName, fingerprints);
+	if (differences == null) return;
+
+	// Store first information line which are the scenarios full names
+	Iterator lines = differences.iterator();
+	this.firstLine = (List) lines.next();
+
+	// Read each information line (one line per build results)
+	Object[] scenarios = this.results.getChildren(null);
+	ConfigResultsElement[] configs = new ConfigResultsElement[scenarios.length];
+	int row = 0;
+	while (lines.hasNext()) {
+		List line = (List) lines.next();
+		int size = line.size();
+
+		// The first column is the build name
+		String buildName = (String) line.get(0);
+		String milestoneName = Util.getMilestoneName(buildName);
+		TableItem item = null;
+
+		// Set item if the line is not filtered
+		Font italic;
+		if (milestoneName != null) {
+			item = new TableItem (this.table, SWT.NONE);
+			item.setText(milestoneName + " - " + buildName);
+			item.setFont(0, this.boldFont);
+			if (!onlyMilestones) item.setBackground(this.blueref);
+			italic = this.boldItalicFont;
+		} else {
+			if ((onlyMilestones && Util.getNextMilestone(buildName) != null) ||
+				(skipNightlyBuilds && buildName.charAt(0) == 'N')) {
+				// skip line
+				continue;
+			}
+			item = new TableItem (this.table, SWT.NONE);
+			item.setText(0, buildName);
+			italic = this.italicFont;
+		}
+
+		// Read each column value
+		String baselineName = null;
+		for (int col=1; col<size; col++) {
+
+			// Reset tooltip info
+			String toolTipText = null;
+			String toolTipMessage = null;
+			int toolTipStyle = SWT.BALLOON;
+
+			// Otherwise get values for a scenario
+			Font italic2 = italic;
+			ScenarioResultsElement scenarioResultsElement = (ScenarioResultsElement) scenarios[col-1];
+			if (milestoneName != null || (!fingerprints && scenarioResultsElement.hasSummary())) {
+				italic2 = this.boldItalicFont;
+				item.setFont(col, this.boldFont);
+			}
+			// Otherwise get values for a scenario
+			double[] values = (double[]) line.get(col);
+			if (values == AbstractResults.NO_BUILD_RESULTS) {
+				item.setText(col, "Missing");
+				item.setForeground(col, GRAY);
+				item.setFont(col, italic2);
+			} else if (values == AbstractResults.INVALID_RESULTS) {
+				item.setText(col, "Invalid");
+				item.setForeground(col, RED);
+				item.setFont(col, italic2);
+			} else {
+				// Get values array
+				double buildValue = values[AbstractResults.BUILD_VALUE_INDEX];
+				double baselineValue = values[AbstractResults.BASELINE_VALUE_INDEX];
+				double delta = values[AbstractResults.DELTA_VALUE_INDEX];
+				double error = values[AbstractResults.DELTA_ERROR_INDEX];
+
+				// Set text with delta value
+				item.setText(col, Util.PERCENTAGE_FORMAT.format(delta));
+
+				// Compute the tooltip to display on the cell
+				if (error > 0.03) {
+					// error is over the 3% threshold
+					item.setImage(col, ResultsElement.WARN_IMAGE);
+					item.setForeground(col, this.darkyellow);
+					toolTipText = "May be not reliable";
+					toolTipMessage = "The error on this result is "+Util.PERCENTAGE_FORMAT.format(error)+", hence it may be not reliable";
+					toolTipStyle |= SWT.ICON_WARNING;
+				}
+				if (delta < -0.1) {
+					// delta < -10%: failure shown by an red-cross icon + text in red
+					item.setImage(col, ResultsElement.ERROR_IMAGE);
+					item.setForeground(col, RED);
+				} else if (delta < -0.05) {
+					// negative delta over 5% shown in red
+					item.setForeground(col, RED);
+				} else if (delta < 0) {
+					// negative delta shown in magenta
+					item.setForeground(col, MAGENTA);
+				} else if (delta > 0.2) {
+					// positive delta over 20% shown in green
+					item.setForeground(col, DARK_GREEN);
+				} else if (delta > 0.1) {
+					// positive delta between 10% and 20% shown in blue
+					item.setForeground(col, BLUE);
+				}
+
+				// Moderate the status if the build value or the difference is small
+				if (delta < 0) {
+					double diff = Math.abs(baselineValue - buildValue);
+					if (buildValue < 100 || diff < 100) {
+						if (toolTipText == null) {
+							toolTipText = "";
+						} else {
+							toolTipText += ", ";
+						}
+						toolTipText += "Small value";
+						if (toolTipMessage == null) {
+							toolTipMessage = "";
+						} else {
+							toolTipMessage += ".\n";
+						}
+						if (buildValue < 100) {
+							toolTipMessage += "This test has a small value ("+buildValue+"ms)";
+						} else {
+							toolTipMessage += "This test variation has a small value ("+diff+"ms)";
+						}
+						toolTipMessage +=  ", hence it may not be necessary to spend time on fixing this possible regression";
+						// set the text in italic
+						item.setFont(col, italic2);
+						toolTipStyle |= SWT.ICON_INFORMATION;
+					}
+				}
+
+				// Add information in tooltip when history shows big variation
+				ConfigResultsElement configResultsElement = configs[col-1];
+				if (configResultsElement == null) {
+					configResultsElement = (ConfigResultsElement) scenarioResultsElement.getResultsElement(this.configName);
+					configs[col-1] = configResultsElement;
+				}
+				double deviation = configResultsElement == null ? 0 : configResultsElement.getStatistics()[3];
+				if (deviation > 0.2) {
+					// deviation is over 20% over the entire history
+					if (toolTipText == null) {
+						toolTipText = "";
+					} else {
+						toolTipText += ", ";
+					}
+					toolTipText += "History shows erratic values";
+					if (toolTipMessage == null) {
+						toolTipMessage = "";
+					} else {
+						toolTipMessage += ".\n";
+					}
+					toolTipMessage += "The results history shows that the variation of its delta is over 20% ("+Util.PERCENTAGE_FORMAT.format(deviation)+"), hence its result is surely not reliable";
+					// set the text in italic
+					item.setFont(col, italic2);
+					toolTipStyle |= SWT.ICON_INFORMATION;
+				} else if (deviation > 0.1) { // moderate the status when the test
+					// deviation is between 10% and 20% over the entire history
+					if (toolTipText == null) {
+						toolTipText = "";
+					} else {
+						toolTipText += ", ";
+					}
+					toolTipText += "History shows unstable values";
+					if (toolTipMessage == null) {
+						toolTipMessage = "";
+					} else {
+						toolTipMessage += ".\n";
+					}
+					toolTipMessage += "The results history shows that the variation of its delta is between 10% and 20% ("+Util.PERCENTAGE_FORMAT.format(deviation)+"), hence its result may not be really reliable";
+					// set the text in italic
+					item.setFont(col, italic2);
+					if (toolTipStyle == SWT.BALLOON && delta >= -0.1) {
+						toolTipStyle |= SWT.ICON_INFORMATION;
+					} else {
+						// reduce icon severity from error to warning
+						toolTipStyle |= SWT.ICON_WARNING;
+					}
+				}
+			}
+
+			// Set tooltip
+			if (toolTipText != null) {
+				createToolTip(toolTipText, toolTipMessage, toolTipStyle, new Point(col, row));
+			}
+
+			// Baseline name
+			ConfigResultsElement configResultsElement = (ConfigResultsElement) scenarioResultsElement.getResultsElement(this.configName);
+			if (configResultsElement != null) {
+				String configBaselineName = configResultsElement.getBaselineBuildName(buildName);
+				if (baselineName == null) {
+					baselineName = configBaselineName;
+				} else if (baselineName.indexOf(configBaselineName) < 0) {
+					baselineName += ", " +configBaselineName;
+				}
+			}
+		}
+
+		// Set the tooltip over the build name
+		if (baselineName != null) {
+			createToolTip(buildName, "Baseline: "+baselineName, SWT.BALLOON | SWT.ICON_INFORMATION, new Point(0, row));
+		}
+
+		// Increment row counter
+		row++;
+	}
+	this.rowsCount = row;
+}
+
+/*
+ * Get the columns name.
+ */
+private String[] getLayoutDataFieldNames(boolean fingerprints) {
+	if (this.results == null) {
+		return new String[0];
+	}
+	List labels = this.results.getScenariosLabels(fingerprints);
+	labels.add(0, "Build");
+	String[] names = new String[labels.size()];
+	labels.toArray(names);
+	return names;
+}
+
+/*
+ * The tab text is the full machine name.
+ */
+public String getTabText() {
+	return this.configBox;
+}
+
+/*
+ * Init the SWT resources
+ */
+private void initResources() {
+	// Fonts
+	String fontDataName = this.gc.getFont().getFontData()[0].toString();
+	FontData fdItalic = new FontData(fontDataName);
+	fdItalic.setStyle(SWT.ITALIC);
+	this.italicFont = new Font(this.display, fdItalic);
+	FontData fdBold = new FontData(fontDataName);
+	fdBold.setStyle(SWT.BOLD);
+	this.boldFont = new Font(this.display, fdBold);
+	FontData fdBoldItalic = new FontData(fontDataName);
+	fdBoldItalic.setStyle(SWT.BOLD | SWT.ITALIC);
+	this.boldItalicFont = new Font(this.display, fdBoldItalic);
+
+	// Colors
+	this.lightred = new Color(DEFAULT_DISPLAY, 220, 50, 50);
+	this.lightyellow = new Color(DEFAULT_DISPLAY, 255, 255, 160);
+	this.darkyellow = new Color(DEFAULT_DISPLAY, 160, 160, 0);
+	this.blueref = new Color(DEFAULT_DISPLAY, 200, 200, 255);
+}
+
+/*
+ * Select the line corresponding to the given build.
+ */
+void select(BuildResultsElement buildResultsElement) {
+	int count = this.table.getItemCount();
+	String buildName = buildResultsElement.getName();
+	TableItem[] items = this.table.getItems();
+	for (int i=0; i<count; i++) {
+		if (items[i].getText().endsWith(buildName)) {
+			this.table.deselect(this.table.getSelectionIndex());
+			this.table.select(i);
+			return;
+		}
+	}
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPerspective.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPerspective.java
new file mode 100644
index 0000000..193489b
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPerspective.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/**
+ *  Defines the 'Performances' perspective.
+ */
+public class PerformanceResultsPerspective implements IPerspectiveFactory {
+
+	private IPageLayout factory;
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IPerspectiveFactory#createInitialLayout(org.eclipse.ui.IPageLayout)
+ */
+public void createInitialLayout(IPageLayout layout) {
+	this.factory = layout;
+	addViews();
+}
+
+/*
+ * Add views to the perspective
+ */
+private void addViews() {
+
+	// Component results view put on bottom
+	IFolderLayout bottom =
+		this.factory.createFolder(
+			"bottomRight", //NON-NLS-1
+			IPageLayout.BOTTOM,
+			0.5f,
+			this.factory.getEditorArea());
+	bottom.addView("org.eclipse.test.internal.performance.results.ui.ComponentsResultsView");
+	bottom.addView("org.eclipse.test.internal.performance.results.ui.BuildsComparisonView");
+
+	// Components and Builds view put on perspective top left
+	IFolderLayout topLeft =
+		this.factory.createFolder(
+			"topLeft", //NON-NLS-1
+			IPageLayout.LEFT,
+			0.5f,
+			this.factory.getEditorArea());
+	topLeft.addView("org.eclipse.test.internal.performance.results.ui.ComponentsView"); //NON-NLS-1
+	topLeft.addView("org.eclipse.test.internal.performance.results.ui.BuildsView"); //NON-NLS-1
+	topLeft.addView(IPageLayout.ID_PROJECT_EXPLORER); //NON-NLS-1
+
+	// Properties view put on perspective top right
+	IFolderLayout topRight =
+		this.factory.createFolder(
+			"topRight", //NON-NLS-1
+			IPageLayout.RIGHT,
+			0.5f,
+			this.factory.getEditorArea());
+	topRight.addView(IPageLayout.ID_PROP_SHEET); //NON-NLS-1
+
+	this.factory.setEditorAreaVisible(false);
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferenceInitializer.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferenceInitializer.java
new file mode 100644
index 0000000..d40edc3
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferenceInitializer.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.test.internal.performance.PerformanceTestPlugin;
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.performance.Dimension;
+
+/**
+ * Default performances preferences initializer.
+ */
+public class PerformanceResultsPreferenceInitializer extends AbstractPreferenceInitializer implements IPerformancesConstants {
+
+/*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences()
+ */
+public void initializeDefaultPreferences() {
+	IEclipsePreferences defaultPreferences = ((IScopeContext) new DefaultScope()).getNode(PLUGIN_ID);
+
+	// Eclipse version
+	defaultPreferences.putInt(PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
+
+	// Database
+	defaultPreferences.putBoolean(PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
+	defaultPreferences.putBoolean(PRE_DATABASE_LOCAL, IPerformancesConstants.DEFAULT_DATABASE_LOCAL);
+	defaultPreferences.put(PRE_DATABASE_LOCATION, IPerformancesConstants.NETWORK_DATABASE_LOCATION);
+
+	// Status
+	defaultPreferences.putInt(PRE_WRITE_STATUS, IPerformancesConstants.DEFAULT_WRITE_STATUS);
+
+	// Comparison
+	defaultPreferences.putInt(PRE_COMPARISON_THRESHOLD_FAILURE, IPerformancesConstants.DEFAULT_COMPARISON_THRESHOLD_FAILURE);
+	defaultPreferences.putInt(PRE_COMPARISON_THRESHOLD_ERROR, IPerformancesConstants.DEFAULT_COMPARISON_THRESHOLD_ERROR);
+	defaultPreferences.putInt(PRE_COMPARISON_THRESHOLD_IMPROVEMENT, IPerformancesConstants.DEFAULT_COMPARISON_THRESHOLD_IMPROVEMENT);
+
+	// Config descriptors
+	String[][] configDescriptors = PerformanceTestPlugin.getConfigDescriptors();
+	int cdLength = configDescriptors.length;
+	for (int i = 0; i < cdLength; i++) {
+		String[] descriptor = configDescriptors[i];
+		defaultPreferences.put(PRE_CONFIG_DESCRIPTOR_NAME + "." + i, descriptor[0]);
+		defaultPreferences.put(PRE_CONFIG_DESCRIPTOR_DESCRIPTION + "." + i, descriptor[1]);
+	}
+
+	// Default dimension
+	defaultPreferences.put(PRE_DEFAULT_DIMENSION, ((Dim) PerformanceTestPlugin.getDefaultDimension()).getName());
+
+	// Result dimensions
+	Dimension[] dimensions = PerformanceTestPlugin.getResultsDimensions();
+	int length = dimensions.length;
+	for (int i = 0; i < length; i++) {
+		Dim dim = (Dim) dimensions[i];
+		defaultPreferences.put(PRE_RESULTS_DIMENSION + "." + i, dim.getName());
+	}
+
+	// Filters
+	defaultPreferences.putBoolean(PRE_FILTER_ADVANCED_SCENARIOS, IPerformancesConstants.DEFAULT_FILTER_ADVANCED_SCENARIOS);
+	defaultPreferences.putBoolean(PRE_FILTER_OLD_BUILDS, IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS);
+	defaultPreferences.putBoolean(PRE_FILTER_NIGHTLY_BUILDS, IPerformancesConstants.DEFAULT_FILTER_NIGHTLY_BUILDS);
+
+	// Milestones
+	String[] milestones = IPerformancesConstants.V36_MILESTONES;
+	String prefix = PRE_MILESTONE_BUILDS + "." + ECLIPSE_MAINTENANCE_VERSION;
+	length = milestones.length;
+	for (int i = 0; i < length; i++) {
+		defaultPreferences.put(prefix + i, milestones[i]);
+	}
+	milestones = IPerformancesConstants.V37_MILESTONES;
+	prefix = PRE_MILESTONE_BUILDS + "." + ECLIPSE_DEVELOPMENT_VERSION;
+	length = milestones.length;
+	for (int i = 0; i < length; i++) {
+		defaultPreferences.put(prefix + i, milestones[i]);
+	}
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferencePage.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferencePage.java
new file mode 100644
index 0000000..72e691e
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferencePage.java
@@ -0,0 +1,1182 @@
+/*******************************************************************************
+ * 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.util.Iterator;
+
+import org.osgi.service.prefs.BackingStoreException;
+
+import org.eclipse.test.internal.performance.PerformanceTestPlugin;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.test.performance.ui.UiPlugin;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Text;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+/**
+ * Defines the 'Performances' preferences page.
+ */
+public class PerformanceResultsPreferencePage extends PreferencePage
+	implements IWorkbenchPreferencePage, SelectionListener, ModifyListener, IPerformancesConstants {
+
+	private Button mVersionRadioButton;
+	private Button dVersionRadionButton;
+	private CCombo databaseLocationCombo;
+	private Button dbConnectionCheckBox;
+	private Button dbLocalBrowseButton;
+	private Button dbRelengRadioButton;
+	private Button dbLocalRadioButton;
+	private CCombo defaultDimensionCombo;
+//	private CCombo lastBuildCombo;
+	private List resultsDimensionsList;
+	private CCombo milestonesCombo;
+	private Label dbLocationLabel;
+
+	// Status SWT objects
+	private Button statusValuesCheckBox;
+	private Button statusErrorNoneRadioButton;
+	private Button statusErrorNoticeableRadioButton;
+	private Button statusErrorSuspiciousRadioButton;
+	private Button statusErrorWeirdRadioButton;
+	private Button statusErrorInvalidRadioButton;
+	private Button statusSmallBuildValueCheckBox;
+	private Button statusSmallDeltaValueCheckBox;
+	private Button statusStatisticNoneRadioButton;
+	private Button statusStatisticErraticRadioButton;
+	private Button statusStatisticUnstableRadioButton;
+	private Text statusBuildsToConfirm;
+	private Text comparisonThresholdFailure;
+	private Text comparisonThresholdError;
+	private Text comparisonThresholdImprovement;
+
+	// TODO See whether config descriptors need to be set as preferences or not...
+	// private Table configDescriptorsTable;
+
+	private BuildsView buildsView;
+
+/**
+ * Utility method that creates a push button instance and sets the default
+ * layout data.
+ *
+ * @param parent
+ *            the parent for the new button
+ * @param label
+ *            the label for the new button
+ * @return the newly-created button
+ */
+private Button createCheckBox(Composite parent, String label) {
+	Button button = new Button(parent, SWT.CHECK);
+	button.setText(label);
+	button.addSelectionListener(this);
+	GridData data = new GridData();
+	data.horizontalAlignment = GridData.FILL;
+	data.horizontalSpan = 5;
+	button.setLayoutData(data);
+	return button;
+}
+
+/**
+ * Create a text field specific for this application
+ *
+ * @param parent
+ *            the parent of the new text field
+ * @return the new text field
+ */
+private CCombo createCombo(Composite parent) {
+	CCombo combo= new CCombo(parent, SWT.BORDER);
+	combo.addModifyListener(this);
+	GridData data = new GridData();
+	data.horizontalSpan = 3;
+	data.horizontalAlignment = GridData.FILL;
+	data.grabExcessHorizontalSpace = true;
+	data.verticalAlignment = GridData.CENTER;
+	data.grabExcessVerticalSpace = false;
+	combo.setLayoutData(data);
+	return combo;
+}
+
+
+/**
+ * Creates composite control and sets the default layout data.
+ *
+ * @param parent
+ *            the parent of the new composite
+ * @param numColumns
+ *            the number of columns for the new composite
+ * @param hSpan TODO
+ * @return the newly-created coposite
+ */
+private Composite createComposite(Composite parent, int numColumns, int hSpan) {
+	Composite composite = new Composite(parent, SWT.NULL);
+
+	// GridLayout
+	GridLayout layout = new GridLayout();
+	layout.numColumns = numColumns;
+	composite.setLayout(layout);
+
+	// GridData
+	GridData data = new GridData();
+	data.verticalAlignment = GridData.FILL;
+	data.horizontalAlignment = GridData.FILL;
+	data.horizontalSpan = hSpan;
+	composite.setLayoutData(data);
+	return composite;
+}
+
+/**
+ * (non-Javadoc) Method declared on PreferencePage
+ */
+protected Control createContents(Composite parent) {
+
+	this.buildsView = (BuildsView) PerformancesView.getWorkbenchView("org.eclipse.test.internal.performance.results.ui.BuildsView");
+	if (this.buildsView == null) {
+		Label errorLabel = createLabel(parent, "No performances preferences can be set because the build view has not been created yet!", false);
+		errorLabel.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_RED));
+	} else {
+		// Eclipse version choice
+		Composite composite_eclipseVersion = createComposite(parent, 5, 1);
+		createLabel(composite_eclipseVersion, "Eclipse version", false);
+		Composite composite_versionChoice = createComposite(composite_eclipseVersion, 5, 1);
+		this.mVersionRadioButton = createRadioButton(composite_versionChoice, "v"+ECLIPSE_MAINTENANCE_VERSION);
+		this.dVersionRadionButton = createRadioButton(composite_versionChoice, "v"+ECLIPSE_DEVELOPMENT_VERSION);
+
+		// Database location
+		Composite compositeDatabase = createComposite(parent, 5, 1);
+		Group databaseGroup = createGroup(compositeDatabase, "Database", 5);
+		Composite compositeDatabaseConnection = createComposite(databaseGroup, 3, 5);
+		this.dbConnectionCheckBox = createCheckBox(compositeDatabaseConnection, "Connected");
+		this.dbRelengRadioButton = createRadioButton(compositeDatabaseConnection, "Releng");
+		this.dbLocalRadioButton = createRadioButton(compositeDatabaseConnection, "Local");
+		this.dbLocationLabel = createLabel(databaseGroup, "Location", false);
+		this.databaseLocationCombo = createCombo(databaseGroup);
+		this.databaseLocationCombo.setEditable(false);
+	    this.dbLocalBrowseButton = createPushButton(databaseGroup, "Browse");
+
+		// Status
+		Composite compositeStatus = createComposite(parent, 1, 3);
+		Group statusGroup = createGroup(compositeStatus, "Status", 1);
+		this.statusValuesCheckBox = createCheckBox(statusGroup, "Values");
+		this.statusValuesCheckBox.setToolTipText("Include numbers while writing status");
+		Group statusErrorGroup = createGroup(statusGroup, "Error level", 5);
+		statusErrorGroup.setToolTipText("Exclude from the written status failures depending on their build result error...");
+		this.statusErrorNoneRadioButton = createRadioButton(statusErrorGroup, "None");
+		this.statusErrorNoneRadioButton.setToolTipText("Do not exclude failures if they have a noticeable error");
+		this.statusErrorInvalidRadioButton = createRadioButton(statusErrorGroup, "Invalid");
+		this.statusErrorInvalidRadioButton.setToolTipText("Exclude all invalid failures (i.e. result error is over 100%)");
+		this.statusErrorWeirdRadioButton = createRadioButton(statusErrorGroup, "Weird");
+		this.statusErrorWeirdRadioButton.setToolTipText("Exclude all weird failures (i.e. result error is over 50%)");
+		this.statusErrorSuspiciousRadioButton = createRadioButton(statusErrorGroup, "Suspicious");
+		this.statusErrorSuspiciousRadioButton.setToolTipText("Exclude all suspicious failures (i.e. result error is over 25%)");
+		this.statusErrorNoticeableRadioButton = createRadioButton(statusErrorGroup, "Noticeable");
+		this.statusErrorNoticeableRadioButton.setToolTipText("Exclude all failures which have a noticeable error (i.e result error is over 3%)");
+		Group statusSmallGroup = createGroup(statusGroup, "Small value", 5);
+		statusErrorGroup.setToolTipText("Exclude from the written status failures depending on their value");
+		this.statusSmallBuildValueCheckBox = createCheckBox(statusSmallGroup, "Build value");
+		this.statusSmallBuildValueCheckBox.setToolTipText("Exclude all failures which have a build result value smaller than 100ms");
+		this.statusSmallDeltaValueCheckBox = createCheckBox(statusSmallGroup, "Delta value");
+		this.statusSmallDeltaValueCheckBox.setToolTipText("Exclude all failures which have a delta result value smaller than 100ms");
+		Group statusStatisticsGroup = createGroup(statusGroup, "Statistics", 5);
+		statusStatisticsGroup.setToolTipText("Exclude from the written status failures depending on build results statistics...");
+		this.statusStatisticNoneRadioButton = createRadioButton(statusStatisticsGroup, "None");
+		this.statusStatisticNoneRadioButton.setToolTipText("Do not exclude failures which have bad baseline results statistics (i.e. variation is over 10%)");
+		this.statusStatisticUnstableRadioButton = createRadioButton(statusStatisticsGroup, "Unstable");
+		this.statusStatisticUnstableRadioButton.setToolTipText("Exclude all failures which have unstable baseline results statistics (i.e. variation is between 10% and 20%)");
+		this.statusStatisticErraticRadioButton = createRadioButton(statusStatisticsGroup, "Erratic");
+		this.statusStatisticErraticRadioButton.setToolTipText("Exclude all failures which have erratic baseline results statistics (i.e. variation is over 20%)");
+		createLabel(statusGroup, "Builds to confirm:", false);
+		this.statusBuildsToConfirm = createTextField(statusGroup);
+		this.statusBuildsToConfirm.setToolTipText("The number of previous builds to take into account to confirm a regression");
+
+		// Comparison
+		Composite compositeComparison = createComposite(parent, 1, 3);
+		Group comparisonGroup = createGroup(compositeComparison, "Comparison", 1);
+		Group thresholdsGroup = createGroup(comparisonGroup, "Thresholds", 6);
+//		Composite compositeFailureThreshold = createComposite(comparisonGroup, 2, 2);
+		createLabel(thresholdsGroup, "Failure:", false);
+		this.comparisonThresholdFailure = createTextField(thresholdsGroup);
+		this.comparisonThresholdFailure.setToolTipText("The threshold in percentage to report a failure");
+		createLabel(thresholdsGroup, "Error:", false);
+		this.comparisonThresholdError = createTextField(thresholdsGroup);
+		this.comparisonThresholdError.setToolTipText("The threshold in percentage to report an error");
+		createLabel(thresholdsGroup, "Improvement:", false);
+		this.comparisonThresholdImprovement = createTextField(thresholdsGroup);
+		this.comparisonThresholdImprovement.setToolTipText("The threshold in percentage to report an improvement");
+
+		// Milestones
+		Composite compositeMilestones = createComposite(parent, 3, 1);
+		createLabel(compositeMilestones, "Milestones", false);
+		this.milestonesCombo = createCombo(compositeMilestones);
+		this.milestonesCombo.setToolTipText("Enter the date of the milestone as yyyymmddHHMM");
+
+		// Default dimension layout
+		StringBuffer tooltip = new StringBuffer("Select the default dimension which will be used for performance results\n");
+		tooltip.append("When changed, the new selected dimension is automatically added to the dimensions list below...");
+		String tooltipText = tooltip.toString();
+		Composite compositeDefaultDimension = createComposite(parent, 3, 1);
+		createLabel(compositeDefaultDimension, "Default dimension: ", false);
+		this.defaultDimensionCombo = createCombo(compositeDefaultDimension);
+		this.defaultDimensionCombo.setEditable(false);
+		this.defaultDimensionCombo.setToolTipText(tooltipText);
+
+		// Results dimensions layout
+		tooltip = new StringBuffer("Select the dimensions which will be used while generating performance results\n");
+		tooltip.append("When changed, the default dimension above is automatically added to the new list...");
+		tooltipText = tooltip.toString();
+		Composite compositeResultsDimensions = createComposite(parent, 3, 1);
+		createLabel(compositeResultsDimensions, "Results dimensions: ", true/*beginning*/);
+		this.resultsDimensionsList = createList(compositeResultsDimensions);
+		this.resultsDimensionsList.setToolTipText(tooltipText);
+
+		// Config descriptors layout
+		/* TODO See whether config descriptors need to be set as preferences or not...
+		Composite compositeConfigDescriptors = createComposite(parent, 3);
+		createLabel(compositeConfigDescriptors, "Config descriptors: ", false);
+		this.configDescriptorsTable = createTable(compositeConfigDescriptors);
+		TableColumn firstColumn = new TableColumn(this.configDescriptorsTable, SWT.LEFT);
+		firstColumn.setText ("Name");
+		firstColumn.setWidth(50);
+		TableColumn secondColumn = new TableColumn(this.configDescriptorsTable, SWT.FILL | SWT.LEFT);
+		secondColumn.setText ("Description");
+		secondColumn.setWidth(300);
+		*/
+
+		// init values
+		initializeValues();
+	}
+
+	// font = null;
+	Composite contents = new Composite(parent, SWT.NULL);
+	contents.pack(true);
+	return contents;
+}
+
+/**
+ * Utility method that creates a label instance and sets the default layout
+ * data.
+ *
+ * @param parent
+ *            the parent for the new label
+ * @param text
+ *            the text for the new label
+ * @return the new label
+ */
+private Group createGroup(Composite parent, String text, int columns) {
+	Group group = new Group(parent, SWT.NONE);
+	group.setLayout(new GridLayout(columns, false));
+	group.setText(text);
+	GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+//	data.horizontalSpan = 1;
+	group.setLayoutData(data);
+	return group;
+}
+
+/**
+ * Utility method that creates a label instance and sets the default layout
+ * data.
+ *
+ * @param parent
+ *            the parent for the new label
+ * @param text
+ *            the text for the new label
+ * @param beginning TODO
+ * @return the new label
+ */
+private Label createLabel(Composite parent, String text, boolean beginning) {
+	Label label = new Label(parent, SWT.BEGINNING|SWT.LEFT);
+	label.setText(text);
+	GridData data = new GridData();
+	data.horizontalAlignment = GridData.FILL;
+	data.verticalAlignment = beginning ? GridData.BEGINNING : GridData.CENTER;
+	label.setLayoutData(data);
+	return label;
+}
+
+/**
+ * Create a text field specific for this application
+ *
+ * @param parent
+ *            the parent of the new text field
+ * @return the new text field
+ */
+private List createList(Composite parent) {
+	List list = new List(parent, SWT.MULTI | SWT.BORDER);
+	list.addSelectionListener(this);
+	GridData data = new GridData();
+	data.horizontalSpan = 2;
+	data.horizontalAlignment = GridData.FILL;
+	data.grabExcessHorizontalSpace = true;
+	data.verticalAlignment = GridData.CENTER;
+	data.grabExcessVerticalSpace = false;
+	list.setLayoutData(data);
+	return list;
+}
+
+/**
+ * Utility method that creates a push button instance and sets the default
+ * layout data.
+ *
+ * @param parent
+ *            the parent for the new button
+ * @param label
+ *            the label for the new button
+ * @return the newly-created button
+ */
+private Button createPushButton(Composite parent, String label) {
+	Button button = new Button(parent, SWT.PUSH);
+	button.setText(label);
+	button.addSelectionListener(this);
+	GridData data = new GridData();
+	data.horizontalAlignment = SWT.LEFT;
+	data.grabExcessHorizontalSpace = true;
+//	data.horizontalSpan = 2;
+	data.minimumWidth = 100;
+	button.setLayoutData(data);
+	return button;
+}
+
+/**
+ * Utility method that creates a radio button instance and sets the default
+ * layout data.
+ *
+ * @param parent
+ *            the parent for the new button
+ * @param label
+ *            the label for the new button
+ * @return the newly-created button
+ */
+private Button createRadioButton(Composite parent, String label) {
+	Button button = new Button(parent, SWT.RADIO | SWT.LEFT);
+	button.setText(label);
+	button.addSelectionListener(this);
+	GridData data = new GridData();
+	button.setLayoutData(data);
+	return button;
+}
+
+/*
+ * Create a text field specific for this application
+ *
+ * @param parent
+ *            the parent of the new text field
+ * @return the new text field
+ *
+private Table createTable(Composite parent) {
+	Table table = new Table(parent, SWT.BORDER);
+	table.setLinesVisible (true);
+	table.setHeaderVisible (true);
+	GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+	gridData.heightHint = 150;
+	table.setLayoutData(gridData);
+	return table;
+}
+*/
+
+/*
+ * Create a text field specific for this application
+ *
+ * @param parent
+ *            the parent of the new text field
+ * @return the new text field
+ */
+private Text createTextField(Composite parent) {
+	Text text = new Text(parent, SWT.SINGLE | SWT.BORDER);
+	text.addModifyListener(this);
+	GridData data = new GridData();
+	data.horizontalAlignment = GridData.FILL;
+	data.grabExcessHorizontalSpace = true;
+	data.verticalAlignment = GridData.CENTER;
+	data.grabExcessVerticalSpace = false;
+	text.setLayoutData(data);
+	return text;
+}
+
+/**
+ * The <code>ReadmePreferencePage</code> implementation of this
+ * <code>PreferencePage</code> method returns preference store that belongs to
+ * the our plugin. This is important because we want to store our preferences
+ * separately from the workbench.
+ */
+protected IPreferenceStore doGetPreferenceStore() {
+	return UiPlugin.getDefault().getPreferenceStore();
+}
+
+String getDialogTitle() {
+	String title = DB_Results.getDbTitle();
+	if (title == null) {
+		// DB is not connected
+		int version;
+		if (this.mVersionRadioButton.getSelection()) {
+			version = ECLIPSE_MAINTENANCE_VERSION;
+		} else {
+			version = ECLIPSE_DEVELOPMENT_VERSION;
+		}
+		title = "Eclipse " + version + " - DB not connected";
+	}
+	return title;
+}
+
+/*
+ * Get the directory path using the given location as default.
+ */
+private String getDirectoryPath(String location) {
+	DirectoryDialog dialog = new DirectoryDialog(getShell(), SWT.OPEN);
+	dialog.setText(getDialogTitle());
+	dialog.setMessage("Select local database directory:");
+	dialog.setFilterPath(location);
+	String path = dialog.open();
+	if (path != null) {
+		File dir = new File(path);
+		if (dir.exists() && dir.isDirectory()) {
+			return dir.getAbsolutePath();
+		}
+	}
+	return null;
+}
+
+/*
+ * (non-Javadoc) Method declared on IWorkbenchPreferencePage
+ */
+public void init(IWorkbench workbench) {
+	// do nothing
+}
+
+/*
+ * Init he contents of the dimensions list controls.
+ */
+void initDimensionsLists() {
+	// Dimensions lists
+	java.util.List dimensions = PerformanceTestPlugin.getDimensions();
+	Iterator names = dimensions.iterator();
+	while (names.hasNext()) {
+		String name = (String) names.next();
+		this.defaultDimensionCombo.add(name);
+		this.resultsDimensionsList.add(name);
+	}
+}
+
+/**
+ * Initializes states of the controls using default values in the preference
+ * store.
+ */
+private void initializeDefaults() {
+	IPreferenceStore store = getPreferenceStore();
+
+	// Init default database values
+	this.dbConnectionCheckBox.setSelection(store.getDefaultBoolean(PRE_DATABASE_CONNECTION));
+	this.dbRelengRadioButton.setSelection(false);
+	this.dbLocalRadioButton.setSelection(false);
+	final boolean dbLocal = store.getDefaultBoolean(PRE_DATABASE_LOCAL);
+	if (dbLocal) {
+		this.dbLocalRadioButton.setSelection(true);
+	} else {
+		this.dbRelengRadioButton.setSelection(true);
+	}
+	this.databaseLocationCombo.removeAll();
+	this.databaseLocationCombo.setText(store.getDefaultString(PRE_DATABASE_LOCATION));
+	updateDatabaseGroup();
+
+	// Init default status values
+	int writeStatus = store.getDefaultInt(PRE_WRITE_STATUS);
+	initStatusValues(writeStatus);
+
+	// Init comparison thresholds
+	this.comparisonThresholdFailure.setText(String.valueOf(store.getDefaultInt(PRE_COMPARISON_THRESHOLD_FAILURE)));
+	this.comparisonThresholdError.setText(String.valueOf(store.getDefaultInt(PRE_COMPARISON_THRESHOLD_ERROR)));
+	this.comparisonThresholdImprovement.setText(String.valueOf(store.getDefaultInt(PRE_COMPARISON_THRESHOLD_IMPROVEMENT)));
+
+	// Init eclipse version
+	this.mVersionRadioButton.setSelection(false);
+	this.dVersionRadionButton.setSelection(false);
+	int version = store.getDefaultInt(PRE_ECLIPSE_VERSION);
+	if (version == ECLIPSE_MAINTENANCE_VERSION) {
+		this.mVersionRadioButton.setSelection(true);
+	} else {
+		this.dVersionRadionButton.setSelection(true);
+	}
+    updateBrowseButtonToolTip(version);
+
+	// Milestones
+	this.milestonesCombo.removeAll();
+	String prefix = PRE_MILESTONE_BUILDS + "." + version;
+	String milestone = store.getDefaultString(prefix + "0");
+	int index = 0;
+	while (milestone != null && milestone.length() > 0) {
+		this.milestonesCombo.add(milestone);
+		milestone = store.getDefaultString(prefix + ++index);
+	}
+
+	// Init default default dimension
+	String defaultDimension = store.getDefaultString(PRE_DEFAULT_DIMENSION);
+	this.defaultDimensionCombo.setText(defaultDimension);
+
+	// Init default generated dimensions
+	this.resultsDimensionsList.add(store.getDefaultString(PRE_RESULTS_DIMENSION+".0"));
+	this.resultsDimensionsList.add(store.getDefaultString(PRE_RESULTS_DIMENSION+".1"));
+}
+
+/**
+ * Initializes states of the controls from the preference store.
+ */
+private void initializeValues() {
+	IPreferenceStore store = getPreferenceStore();
+
+	// Init database info
+	this.dbConnectionCheckBox.setSelection(store.getBoolean(PRE_DATABASE_CONNECTION));
+	final boolean dbLocal = store.getBoolean(PRE_DATABASE_LOCAL);
+	if (dbLocal) {
+		this.dbLocalRadioButton.setSelection(true);
+		this.dbRelengRadioButton.setToolTipText("");
+	} else {
+		this.dbRelengRadioButton.setSelection(true);
+		this.dbRelengRadioButton.setToolTipText(NETWORK_DATABASE_LOCATION);
+	}
+	this.databaseLocationCombo.removeAll();
+	this.databaseLocationCombo.setText(store.getString(PRE_DATABASE_LOCATION));
+	for (int i = 0; i < 3; i++) {
+		String history = store.getString(PRE_DATABASE_LOCATION + "." + i);
+		if (history.length() == 0)
+			break;
+		this.databaseLocationCombo.add(history);
+	}
+	updateDatabaseGroup();
+
+	// Init status values
+	int writeStatus = store.getInt(PRE_WRITE_STATUS);
+	initStatusValues(writeStatus);
+
+	// Init comparison thresholds
+	this.comparisonThresholdFailure.setText(String.valueOf(store.getInt(PRE_COMPARISON_THRESHOLD_FAILURE)));
+	this.comparisonThresholdError.setText(String.valueOf(store.getInt(PRE_COMPARISON_THRESHOLD_ERROR)));
+	this.comparisonThresholdImprovement.setText(String.valueOf(store.getInt(PRE_COMPARISON_THRESHOLD_IMPROVEMENT)));
+
+	// Init eclipse version
+	int version = store.getInt(PRE_ECLIPSE_VERSION);
+	if (version == ECLIPSE_MAINTENANCE_VERSION) {
+		this.mVersionRadioButton.setSelection(true);
+	} else {
+		this.dVersionRadionButton.setSelection(true);
+	}
+    updateBrowseButtonToolTip(version);
+
+	// Milestones
+	String prefix = PRE_MILESTONE_BUILDS + "." + version;
+	int index = 0;
+	String milestone = store.getString(prefix + index);
+	while (milestone != null && milestone.length() > 0) {
+		this.milestonesCombo.add(milestone);
+		milestone = store.getString(prefix + ++index);
+	}
+
+	// Init composite lists
+	initDimensionsLists();
+
+	// Init default dimension
+	String defaultDimension = store.getString(PRE_DEFAULT_DIMENSION);
+	this.defaultDimensionCombo.setText(defaultDimension);
+
+	// Init generated dimensions
+	int count = this.resultsDimensionsList.getItemCount();
+	int[] indices = new int[count];
+	int n = 0;
+	String resultsDimension = store.getString(PRE_RESULTS_DIMENSION + "." + n);
+	while (resultsDimension.length() > 0) {
+		indices[n++] = this.resultsDimensionsList.indexOf(resultsDimension);
+		resultsDimension = store.getString(PRE_RESULTS_DIMENSION + "." + n);
+	}
+	if (n < count) {
+		System.arraycopy(indices, 0, indices = new int[n], 0, n);
+	}
+	this.resultsDimensionsList.select(indices);
+
+	// Init config descriptors
+	/* TODO See whether config descriptors need to be set as preferences or not...
+	this.configDescriptorsTable.clearAll();
+	int d = 0;
+	String descriptorName = store.getString(PRE_CONFIG_DESCRIPTOR_NAME + "." + d);
+	String descriptorDescription = store.getString(PRE_CONFIG_DESCRIPTOR_DESCRIPTION + "." + d++);
+	while (descriptorName.length() > 0) {
+		TableItem tableItem = new TableItem (this.configDescriptorsTable, SWT.NONE);
+		tableItem.setText (0, descriptorName);
+		tableItem.setText (1, descriptorDescription);
+		descriptorName = store.getString(PRE_CONFIG_DESCRIPTOR_NAME + "." + d);
+		descriptorDescription = store.getString(PRE_CONFIG_DESCRIPTOR_DESCRIPTION + "." + d++);
+	}
+	*/
+}
+
+private void initStatusValues(int writeStatus) {
+	this.statusValuesCheckBox.setSelection((writeStatus & STATUS_VALUES) != 0);
+	this.statusErrorNoneRadioButton.setSelection(false);
+	this.statusErrorNoticeableRadioButton.setSelection(false);
+	this.statusErrorSuspiciousRadioButton.setSelection(false);
+	this.statusErrorWeirdRadioButton.setSelection(false);
+	this.statusErrorInvalidRadioButton.setSelection(false);
+	switch (writeStatus & STATUS_ERROR_LEVEL_MASK) {
+		case STATUS_ERROR_NONE:
+			this.statusErrorNoneRadioButton.setSelection(true);
+			break;
+		case STATUS_ERROR_NOTICEABLE:
+			this.statusErrorNoticeableRadioButton.setSelection(true);
+			break;
+		case STATUS_ERROR_SUSPICIOUS:
+			this.statusErrorSuspiciousRadioButton.setSelection(true);
+			break;
+		case STATUS_ERROR_WEIRD:
+			this.statusErrorWeirdRadioButton.setSelection(true);
+			break;
+		case STATUS_ERROR_INVALID:
+			this.statusErrorInvalidRadioButton.setSelection(true);
+			break;
+	}
+	this.statusSmallBuildValueCheckBox.setSelection(false);
+	this.statusSmallDeltaValueCheckBox.setSelection(false);
+	switch (writeStatus & STATUS_SMALL_VALUE_MASK) {
+		case STATUS_SMALL_VALUE_BUILD:
+			this.statusSmallBuildValueCheckBox.setSelection(true);
+			break;
+		case STATUS_SMALL_VALUE_DELTA:
+			this.statusSmallDeltaValueCheckBox.setSelection(true);
+			break;
+	}
+	this.statusStatisticNoneRadioButton.setSelection(false);
+	this.statusStatisticErraticRadioButton.setSelection(false);
+	this.statusStatisticUnstableRadioButton.setSelection(false);
+	switch (writeStatus & STATUS_STATISTICS_MASK) {
+		case 0:
+			this.statusStatisticNoneRadioButton.setSelection(true);
+			break;
+		case STATUS_STATISTICS_ERRATIC:
+			this.statusStatisticErraticRadioButton.setSelection(true);
+			break;
+		case STATUS_STATISTICS_UNSTABLE:
+			this.statusStatisticUnstableRadioButton.setSelection(true);
+			break;
+	}
+	this.statusBuildsToConfirm.setText(String.valueOf(writeStatus & STATUS_BUILDS_NUMBER_MASK));
+}
+
+/**
+ * (non-Javadoc) Method declared on ModifyListener
+ */
+public void modifyText(ModifyEvent event) {
+
+	// Add default dimension to results if necessary
+	if (event.getSource() == this.defaultDimensionCombo) {
+		String[] resultsDimensions = this.resultsDimensionsList.getSelection();
+		int length = resultsDimensions.length;
+		String defaultDimension = this.defaultDimensionCombo.getText();
+		for (int i = 0; i < length; i++) {
+			if (resultsDimensions[i].equals(defaultDimension)) {
+				// Default dim is already set as a results dimension, hence nothing has to be done
+				return;
+			}
+		}
+		System.arraycopy(resultsDimensions, 0, resultsDimensions = new String[length + 1], 0, length);
+		resultsDimensions[length] = defaultDimension;
+		this.resultsDimensionsList.setSelection(resultsDimensions);
+	}
+
+	// Add default dimension to results if necessary
+	if (event.getSource() == this.milestonesCombo) {
+
+		// Verify the only digits are entered
+		String milestoneDate = this.milestonesCombo.getText();
+		final int mLength = milestoneDate.length();
+		if (mLength > 0) {
+			for (int i=0; i<mLength; i++) {
+				if (!Character.isDigit(milestoneDate.charAt(i))) {
+					String[] items = this.milestonesCombo.getItems();
+					int length = items.length;
+					for (int j=0; j<length; j++) {
+						if (items[j].equals(milestoneDate)) {
+							return;
+						}
+					}
+					openMilestoneErrorMessage(milestoneDate);
+					return;
+				}
+			}
+		}
+
+		// Do not verify further until a complete milestone date is entered
+		if (mLength < 12) return;
+
+		// Verify the digits
+		try {
+			String str = milestoneDate.substring(0, 4);
+			int year = Integer.parseInt(str);
+			if (year < 2009 || year > 2020) { // 2020 should be enough!
+				MessageDialog.openError(getShell(), getDialogTitle(), milestoneDate+": "+str+" is an invalid year, only value between 2009 and 2020 is accepted!");
+				return;
+			}
+			str = milestoneDate.substring(4, 6);
+			int month = Integer.parseInt(str);
+			if (month <= 0 || month > 12) {
+				MessageDialog.openError(getShell(), getDialogTitle(), milestoneDate+": "+str+" is an invalid month, it should be only from 01 to 12!");
+				return;
+			}
+			str = milestoneDate.substring(6, 8);
+			int day = Integer.parseInt(str);
+			if (day <= 0 || day > 31) {
+				// TODO improve this verification
+				MessageDialog.openError(getShell(), getDialogTitle(), milestoneDate+": "+str+" is an invalid day, it should be only from 01 to 31!");
+				return;
+			}
+			str = milestoneDate.substring(8, 10);
+			int hour = Integer.parseInt(str);
+			if (hour < 0 || hour > 23) {
+				MessageDialog.openError(getShell(), getDialogTitle(), milestoneDate+": "+str+" is an invalid hour, it should be only from 00 to 23!");
+				return;
+			}
+			str = milestoneDate.substring(10, 12);
+			int min = Integer.parseInt(str);
+			if (min < 0 || min > 59) {
+				MessageDialog.openError(getShell(), getDialogTitle(), milestoneDate+": "+str+" is invalid minutes, it should be only from 00 to 59!");
+				return;
+			}
+		}
+		catch (NumberFormatException nfe) {
+			openMilestoneErrorMessage(milestoneDate);
+		}
+
+		// Get combo info
+		String[] milestones = this.milestonesCombo.getItems();
+		int length = milestones.length;
+		String lastMilestone = length == 0 ? null : milestones[length-1];
+
+		// Verify that the added milestone is valid
+		char version = (char) ('0' + (this.mVersionRadioButton.getSelection()
+			? ECLIPSE_MAINTENANCE_VERSION
+			: ECLIPSE_DEVELOPMENT_VERSION) - 30);
+
+		// Verify that the milestone follow the last one
+		String milestoneName;
+		if (lastMilestone == null) {
+			// No previous last milestone
+			milestoneName = "M1";
+		} else {
+			// Compare with last milestone
+			if (lastMilestone.charAt(0) == 'M') {
+				char digit = lastMilestone.charAt(1);
+				if (digit == '6') {
+					// M6 is the last dvpt milestone
+					milestoneName = "RC1";
+				} else {
+					milestoneName = "M" +((char)(digit+1));
+				}
+			} else if (lastMilestone.startsWith("RC")) {
+				char digit = lastMilestone.charAt(2);
+				if (digit == '4') {
+					// RC4 is the last release candidate milestone
+					milestoneName = "R3_"+version;
+				} else {
+					milestoneName = "RC" +((char)(digit+1));
+				}
+			} else if (lastMilestone.startsWith("R3_"+version+"-")) {
+				milestoneName = "R3_" + version + "_1";
+			} else if (lastMilestone.startsWith("R3_"+version+"_")) {
+				char digit = lastMilestone.charAt(5);
+				milestoneName = "R3_" + version + "_" + ((char)(digit+1));
+			} else {
+				MessageDialog.openError(getShell(), getDialogTitle(), "Unexpected last milestone name: "+lastMilestone+"!");
+				return;
+			}
+
+			// Verify the date of the new milestone
+			int lastMilestoneDash = lastMilestone.indexOf('-');
+			final String lastMilestoneDate = lastMilestone.substring(lastMilestoneDash+1);
+			if (milestoneDate.compareTo(lastMilestoneDate) <= 0) {
+				// TODO improve this verification
+				MessageDialog.openError(getShell(), getDialogTitle(), "Milestone "+milestoneDate+" should be after the last milestone: "+lastMilestoneDate+"!");
+				return;
+			}
+		}
+
+		// Verification are ok, ask to add the milestone
+		final String milestone = milestoneName + "-" + milestoneDate;
+		if (MessageDialog.openConfirm(getShell(), getDialogTitle(), milestoneDate+" is a valid milestone date.\n\nDo you want to add the milestone '"+milestone+"' to the preferences?")) {
+			this.milestonesCombo.add(milestone);
+			this.milestonesCombo.setText("");
+		}
+	}
+
+	// Verify the 'builds to confirm' number
+	if (event.getSource() == this.statusBuildsToConfirm) {
+		try {
+			int number = Integer.parseInt(this.statusBuildsToConfirm.getText());
+			if (number < 0) {
+				this.statusBuildsToConfirm.setText("0");
+			} else {
+				int buildsNumber = DB_Results.getBuildsNumber();
+				if (number > buildsNumber) {
+					this.statusBuildsToConfirm.setText(String.valueOf(buildsNumber));
+				}
+			}
+		}
+		catch (NumberFormatException nfe) {
+			this.statusBuildsToConfirm.setText("1");
+		}
+	}
+}
+
+
+/**
+ * @param milestone
+ */
+void openMilestoneErrorMessage(String milestone) {
+	MessageDialog.openError(getShell(), getDialogTitle(), milestone+" is an invalid milestone date. Only 'yyyymmddHHMM' format is accepted!");
+}
+
+/*
+ * (non-Javadoc) Method declared on PreferencePage
+ */
+protected void performDefaults() {
+	super.performDefaults();
+	initializeDefaults();
+}
+
+/*
+ * (non-Javadoc) Method declared on PreferencePage
+ */
+public boolean performOk() {
+	final boolean hasBuildsView = this.buildsView != null;
+	if (hasBuildsView) {
+		storeValues();
+		try {
+			IEclipsePreferences preferences = new InstanceScope().getNode(PLUGIN_ID);
+			preferences.flush();
+			this.buildsView.resetView();
+		} catch (BackingStoreException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+	return true;
+}
+
+/**
+ * Stores the values of the controls back to the preference store.
+ */
+private void storeValues() {
+	IPreferenceStore store = getPreferenceStore();
+
+	// Set version
+	int version;
+	if (this.mVersionRadioButton.getSelection()) {
+		version = ECLIPSE_MAINTENANCE_VERSION;
+	} else {
+		version = ECLIPSE_DEVELOPMENT_VERSION;
+	}
+	store.setValue(PRE_ECLIPSE_VERSION, version);
+
+	// Set database values
+	store.setValue(PRE_DATABASE_CONNECTION, this.dbConnectionCheckBox.getSelection());
+	final boolean dbLocal = this.dbLocalRadioButton.getSelection();
+	store.setValue(PRE_DATABASE_LOCAL, dbLocal);
+	String location = this.databaseLocationCombo.getText();
+	if (dbLocal) {
+		store.setValue(PRE_DATABASE_LOCATION, location);
+	} else {
+		store.setValue(PRE_DATABASE_LOCATION, NETWORK_DATABASE_LOCATION);
+	}
+	int count = this.databaseLocationCombo.getItemCount();
+	for (int i=0; i<count; i++) {
+		String item = this.databaseLocationCombo.getItem(i);
+		if (item.equals(location)) {
+			this.databaseLocationCombo.remove(i);
+			break;
+		}
+	}
+	if (dbLocal) {
+		this.databaseLocationCombo.add(location, 0);
+	}
+	int i=0;
+	for (; i<count; i++) {
+		String item = this.databaseLocationCombo.getItem(i);
+		if (item.length() == 0) break;
+		store.setValue(PRE_DATABASE_LOCATION+"."+i, item);
+	}
+	while (store.getString(PRE_DATABASE_LOCATION+"."+i).length() > 0) {
+		store.setToDefault(PRE_DATABASE_LOCATION+"."+i);
+		i++;
+	}
+
+	// Set status values
+	int writeStatus = 0;
+	if (this.statusValuesCheckBox.getSelection()) {
+		writeStatus |= STATUS_VALUES;
+	}
+	if (this.statusErrorNoneRadioButton.getSelection()) {
+		writeStatus |= STATUS_ERROR_NONE;
+	} else if (this.statusErrorNoticeableRadioButton.getSelection()) {
+		writeStatus |= STATUS_ERROR_NOTICEABLE;
+	} else if (this.statusErrorSuspiciousRadioButton.getSelection()) {
+		writeStatus |= STATUS_ERROR_SUSPICIOUS;
+	} else if (this.statusErrorWeirdRadioButton.getSelection()) {
+		writeStatus |= STATUS_ERROR_WEIRD;
+	} else if (this.statusErrorInvalidRadioButton.getSelection()) {
+		writeStatus |= STATUS_ERROR_INVALID;
+	}
+	if (this.statusSmallBuildValueCheckBox.getSelection()) {
+		writeStatus |= STATUS_SMALL_VALUE_BUILD;
+	}
+	if (this.statusSmallDeltaValueCheckBox.getSelection()) {
+		writeStatus |= STATUS_SMALL_VALUE_DELTA;
+	}
+	if (this.statusStatisticNoneRadioButton.getSelection()) {
+		writeStatus &= ~STATUS_STATISTICS_MASK;
+	} else if (this.statusStatisticErraticRadioButton.getSelection()) {
+		writeStatus |= STATUS_STATISTICS_ERRATIC;
+	} else if (this.statusStatisticUnstableRadioButton.getSelection()) {
+		writeStatus |= STATUS_STATISTICS_UNSTABLE;
+	}
+	writeStatus += Integer.parseInt(this.statusBuildsToConfirm.getText());
+	store.setValue(PRE_WRITE_STATUS, writeStatus);
+
+	// Init comparison thresholds
+	store.setValue(PRE_COMPARISON_THRESHOLD_FAILURE, Integer.parseInt(this.comparisonThresholdFailure.getText()));
+	store.setValue(PRE_COMPARISON_THRESHOLD_ERROR, Integer.parseInt(this.comparisonThresholdError.getText()));
+	store.setValue(PRE_COMPARISON_THRESHOLD_IMPROVEMENT, Integer.parseInt(this.comparisonThresholdImprovement.getText()));
+
+	// Set milestones
+	String prefix = PRE_MILESTONE_BUILDS + "." + version;
+	count  = this.milestonesCombo.getItemCount();
+	for (i=0; i<count; i++) {
+		store.putValue(prefix + i, this.milestonesCombo.getItem(i));
+	}
+	Util.setMilestones(this.milestonesCombo.getItems());
+
+	// Unset previous additional milestones
+	String milestone = store.getString(prefix + count);
+	while (milestone != null && milestone.length() > 0) {
+		store.putValue(prefix + count++, "");
+		milestone = store.getString(prefix + count);
+	}
+
+	// Set default dimension
+	String defaultDimension = this.defaultDimensionCombo.getText();
+	store.putValue(PRE_DEFAULT_DIMENSION, defaultDimension);
+	DB_Results.setDefaultDimension(defaultDimension);
+
+	// Set generated dimensions
+	int[] indices = this.resultsDimensionsList.getSelectionIndices();
+	int length = indices.length;
+	String[] dimensions = new String[length];
+	if (length > 0) {
+		for (i = 0; i < indices.length; i++) {
+			dimensions[i] = this.resultsDimensionsList.getItem(indices[i]);
+			store.putValue(PRE_RESULTS_DIMENSION + "." + i, dimensions[i]);
+		}
+	}
+	int currentLength = DB_Results.getResultsDimensions().length;
+	if (currentLength > length) {
+		for (i = currentLength - 1; i >= length; i--) {
+			store.putValue(PRE_RESULTS_DIMENSION + "." + i, ""); // reset extra dimensions
+		}
+	}
+	DB_Results.setResultsDimensions(dimensions);
+
+	// Set config descriptors
+	/* TODO See whether config descriptors need to be set as preferences or not...
+	TableItem[] items = this.configDescriptorsTable.getItems();
+	length = items.length;
+	for (int i = 0; i < length; i++) {
+		TableItem item = items[i];
+		store.putValue(PRE_CONFIG_DESCRIPTOR_NAME + "." + i, item.getText(0));
+		store.putValue(PRE_CONFIG_DESCRIPTOR_DESCRIPTION + "." + i, item.getText(1));
+	}
+	*/
+}
+
+/**
+ * (non-Javadoc) Method declared on SelectionListener
+ */
+public void widgetDefaultSelected(SelectionEvent event) {
+}
+
+/**
+ * (non-Javadoc) Method declared on SelectionListener
+ */
+public void widgetSelected(SelectionEvent event) {
+
+	// As for directory when 'Local' button is pushed
+	final Object source = event.getSource();
+	if (source == this.dbLocalBrowseButton) {
+		String location = this.databaseLocationCombo.getText();
+		String path = getDirectoryPath(location);
+		if (path != null) {
+			// First verify that the selected dir was correct
+			int version;
+			if (this.mVersionRadioButton.getSelection()) {
+				version = ECLIPSE_MAINTENANCE_VERSION;
+			} else {
+				version = ECLIPSE_DEVELOPMENT_VERSION;
+			}
+			File dbDir = new File(path, "perfDb"+version);
+			if (!dbDir.exists() || !dbDir.isDirectory()) {
+				StringBuffer message = new StringBuffer("Invalid performance database directory\n");
+				message.append(path+" should contain 'perfDb");
+				message.append(version);
+				message.append("' directory and none was found!");
+				MessageDialog.openError(getShell(), getDialogTitle(), message.toString());
+				return;
+			}
+
+			// Look for selected dir in combo box list
+			int count = this.databaseLocationCombo.getItemCount();
+			int index = -1;
+			for (int i = 0; i < count; i++) {
+				String item = this.databaseLocationCombo.getItem(i);
+				if (item.length() == 0) { // nothing in the combo-box list
+					break;
+				}
+				if (item.equals(path)) {
+					index = i;
+					break;
+				}
+			}
+			// Set the selected dir the more recent in the previous dirs list
+			if (index !=  0) {
+				if (index > 0) {
+					// the dir was used before, but not recently => remove it from previous dirs list
+					this.databaseLocationCombo.remove(index);
+				}
+				// add the selected dir on the top of the previous dirs list
+				this.databaseLocationCombo.add(path, 0);
+			}
+			// Set combo box text
+			this.databaseLocationCombo.setText(path);
+			updateLocalDb();
+		}
+	}
+
+	// Reset dabase location when 'Releng' button is pushed
+	if (source == this.dbConnectionCheckBox) {
+		updateDatabaseGroup();
+	}
+
+	// Reset dabase location when 'Releng' check-box is checked
+	if (source == this.dbLocalRadioButton) {
+		updateLocalDb();
+	}
+
+	// Add default dimension to results if necessary
+	if (source == this.resultsDimensionsList) {
+		String[] resultsDimensions = this.resultsDimensionsList.getSelection();
+		int length = resultsDimensions.length;
+		String defaultDimension = this.defaultDimensionCombo.getText();
+		for (int i = 0; i < length; i++) {
+			if (resultsDimensions[i].equals(defaultDimension)) {
+				// Default dim is already set as a results dimension, hence nothing has to be done
+				return;
+			}
+		}
+		System.arraycopy(resultsDimensions, 0, resultsDimensions = new String[length + 1], 0, length);
+		resultsDimensions[length] = defaultDimension;
+		this.resultsDimensionsList.setSelection(resultsDimensions);
+	}
+
+//	if (source == this.lastBuildCheckBox) {
+//		this.lastBuildCombo.setEnabled(this.lastBuildCheckBox.getSelection());
+//	}
+
+	if (source == this.mVersionRadioButton) {
+		if (this.mVersionRadioButton.getSelection()) {
+		    updateBrowseButtonToolTip(ECLIPSE_MAINTENANCE_VERSION);
+		}
+	}
+
+	if (source == this.dVersionRadionButton) {
+		if (this.dVersionRadionButton.getSelection()) {
+		    updateBrowseButtonToolTip(ECLIPSE_DEVELOPMENT_VERSION);
+		}
+	}
+}
+
+/*
+ * Update browse tooltip
+ */
+void updateBrowseButtonToolTip(int version) {
+	this.dbLocalBrowseButton.setToolTipText("Select the directory where the database was unzipped\n(i.e. should contain the perfDb"+version+" subdirectory)");
+}
+
+/*
+ * Update database group controls.
+ */
+void updateDatabaseGroup() {
+	if (this.dbConnectionCheckBox.getSelection()) {
+		this.dbRelengRadioButton.setEnabled(true);
+		this.dbLocalRadioButton.setEnabled(true);
+		updateLocalDb();
+	} else {
+		this.dbRelengRadioButton.setEnabled(false);
+		this.dbLocalRadioButton.setEnabled(false);
+		this.databaseLocationCombo.setEnabled(false);
+		this.dbLocalBrowseButton.setEnabled(false);
+		setValid(true);
+	}
+}
+
+/*
+ * Update database location controls.
+ */
+void updateLocalDb() {
+	if (this.dbLocalRadioButton.getSelection()) {
+		this.databaseLocationCombo.setEnabled(true);
+		this.dbLocalBrowseButton.setEnabled(true);
+		if (this.databaseLocationCombo.getItemCount() == 0) {
+			this.databaseLocationCombo.setText("");
+			setValid(false);
+		} else {
+			this.databaseLocationCombo.select(0);
+			setValid(true);
+		}
+		this.dbRelengRadioButton.setToolTipText("");
+		this.dbLocationLabel.setEnabled(true);
+	} else {
+		this.dbRelengRadioButton.setToolTipText(NETWORK_DATABASE_LOCATION);
+		this.databaseLocationCombo.setText("");
+		this.databaseLocationCombo.setEnabled(false);
+		this.dbLocalBrowseButton.setEnabled(false);
+		setValid(true);
+		this.dbLocationLabel.setEnabled(false);
+	}
+}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformancesView.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformancesView.java
new file mode 100644
index 0000000..bd2d9bc
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformancesView.java
@@ -0,0 +1,636 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.lang.reflect.InvocationTargetException;
+import java.text.NumberFormat;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+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.PerformanceResultsElement;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.ui.views.properties.IPropertySheetPage;
+import org.eclipse.ui.views.properties.PropertySheetPage;
+import org.osgi.service.prefs.BackingStoreException;
+
+
+/**
+ * Abstract view for performance results.
+ */
+public abstract class PerformancesView extends ViewPart implements ISelectionChangedListener, IPreferenceChangeListener {
+
+	// Format
+	static final NumberFormat DOUBLE_FORMAT = NumberFormat.getNumberInstance(Locale.US);
+	static {
+		DOUBLE_FORMAT.setMaximumFractionDigits(3);
+	}
+
+	// Graphic constants
+	static final Display DEFAULT_DISPLAY = Display.getDefault();
+	static final Color BLACK= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLACK);
+	static final Color BLUE= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLUE);
+	static final Color GREEN= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GREEN);
+	static final Color RED = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_RED);
+	static final Color GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GRAY);
+	static final Color DARK_GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_DARK_GRAY);
+	static final Color YELLOW = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_YELLOW);
+	static final Color WHITE = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_WHITE);
+
+	// Viewer filters
+	static final ViewerFilter[] NO_FILTER = new ViewerFilter[0];
+	final static ViewerFilter FILTER_BASELINE_BUILDS = new ViewerFilter() {
+		public boolean select(Viewer v, Object parentElement, Object element) {
+			if (element instanceof BuildResultsElement) {
+				BuildResultsElement buildElement = (BuildResultsElement) element;
+				return !buildElement.getName().startsWith(DB_Results.getDbBaselinePrefix());
+			}
+			return true;
+		}
+	};
+	public final static ViewerFilter FILTER_NIGHTLY_BUILDS = new ViewerFilter() {
+		public boolean select(Viewer v, Object parentElement, Object element) {
+			if (element instanceof BuildResultsElement) {
+				BuildResultsElement buildElement = (BuildResultsElement) element;
+				return buildElement.getName().charAt(0) != 'N';
+			}
+			return true;
+		}
+	};
+	final static ViewerFilter FILTER_OLD_BUILDS = new ViewerFilter() {
+		public boolean select(Viewer v, Object parentElement, Object element) {
+			if (element instanceof BuildResultsElement) {
+				BuildResultsElement buildElement = (BuildResultsElement) element;
+				return buildElement.isImportant();
+			}
+	        return true;
+        }
+	};
+	Set viewFilters = new HashSet();
+
+	// SWT resources
+	Shell shell;
+	Display display;
+	TreeViewer viewer;
+	IPropertySheetPage propertyPage;
+
+	// Directories
+	File dataDir;
+	File resultsDir = null;
+
+	// Views
+	IMemento viewState;
+
+	// Results model information
+	PerformanceResultsElement results;
+
+	// Actions
+	Action changeDataDir;
+	Action filterBaselineBuilds;
+	Action filterNightlyBuilds;
+	Action filterOldBuilds;
+//	Action dbConnection;
+
+	// Eclipse preferences
+	IEclipsePreferences preferences;
+
+/**
+ * Get a view from its ID.
+ *
+ * @param viewId The ID of the view
+ * @return The found view or <code>null</null> if not found.
+ */
+static IViewPart getWorkbenchView(String viewId) {
+	IWorkbench workbench = PlatformUI.getWorkbench();
+	IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
+	int length = windows.length;
+	for (int i=0; i<length; i++) {
+		IWorkbenchWindow window = windows[i];
+		IWorkbenchPage[] pages = window.getPages();
+		int pLength = pages.length;
+		for (int j=0; j<pLength; j++) {
+			IWorkbenchPage page = pages[i];
+			IViewPart view = page.findView(viewId);
+			if (view != null) {
+				return view;
+			}
+		}
+	}
+	return null;
+}
+
+/**
+ * The constructor.
+ */
+public PerformancesView() {
+
+	// Get preferences
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+
+	// Init db constants
+	int eclipseVersion = this.preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
+	String databaseLocation = this.preferences.get(IPerformancesConstants.PRE_DATABASE_LOCATION, IPerformancesConstants.NETWORK_DATABASE_LOCATION);
+	boolean connected = this.preferences.getBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
+	DB_Results.updateDbConstants(connected, eclipseVersion, databaseLocation);
+	this.preferences.addPreferenceChangeListener(this);
+
+	// Init tool tip
+	setTitleToolTip();
+
+	// Init milestones
+	Util.initMilestones(this.preferences);
+}
+
+File changeDataDir(String lastBuild) {
+	String localDataDir = this.preferences.get(IPerformancesConstants.PRE_LOCAL_DATA_DIR, "");
+	String filter = (this.dataDir == null) ? localDataDir : this.dataDir.getPath();
+	File dir = this.dataDir;
+	this.dataDir = changeDir(filter, "Select directory for data local files");
+	boolean refresh = false;
+	if (this.dataDir != null) {
+		this.preferences.put(IPerformancesConstants.PRE_LOCAL_DATA_DIR, this.dataDir.getAbsolutePath());
+		if (dir != null && dir.getPath().equals(this.dataDir.getPath())) {
+			refresh = MessageDialog.openQuestion(this.shell, getTitleToolTip(), "Do you want to read local file again?");
+		} else {
+			refresh = true;
+		}
+		if (refresh) {
+			// Confirm the read when there's a last build set
+			if (lastBuild != null) {
+				if (!MessageDialog.openConfirm(PerformancesView.this.shell, getTitleToolTip(), "Only builds before "+lastBuild+" will be taken into account!\nDo you want to continue?")) {
+					return null;
+				}
+			}
+			refresh(lastBuild);
+			return getSiblingView().dataDir = this.dataDir;
+		}
+	}
+	return null;
+}
+
+/*
+ * Select a directory.
+ */
+File changeDir(String filter, String msg) {
+    DirectoryDialog dialog = new DirectoryDialog(getSite().getShell(), SWT.OPEN);
+    dialog.setText(getTitleToolTip());
+    dialog.setMessage(msg);
+    if (filter != null) {
+    	dialog.setFilterPath(filter);
+    }
+    String path = dialog.open();
+    if (path != null) {
+	    File dir = new File(path);
+	    if (dir.exists() && dir.isDirectory()) {
+    		return dir;
+	    }
+    }
+    return null;
+}
+
+/*
+ * Contribute actions to bars.
+ */
+void contributeToActionBars() {
+	IActionBars bars = getViewSite().getActionBars();
+	fillLocalPullDown(bars.getMenuManager());
+	fillLocalToolBar(bars.getToolBarManager());
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+public void createPartControl(Composite parent) {
+	// Cache the shell and display.
+	this.shell = parent.getShell ();
+	this.display = this.shell.getDisplay ();
+}
+
+/*
+ * Fill the context menu.
+ */
+void fillContextMenu(IMenuManager manager) {
+	// no default contextual action
+}
+
+/*
+ * Fill the filters drop-down menu.
+ */
+void fillFiltersDropDown(IMenuManager manager) {
+	manager.add(this.filterBaselineBuilds);
+	manager.add(this.filterNightlyBuilds);
+}
+
+/*
+ * Fill the local data drop-down menu
+ */
+void fillLocalDataDropDown(IMenuManager manager) {
+	manager.add(this.changeDataDir);
+}
+
+/*
+ * Fill the local pull down menu.
+ */
+void fillLocalPullDown(IMenuManager manager) {
+
+	// Filters menu
+	MenuManager filtersManager= new MenuManager("Filters");
+	fillFiltersDropDown(filtersManager);
+	manager.add(filtersManager);
+
+	// Local data menu
+	MenuManager localDataManager= new MenuManager("Local data");
+	fillLocalDataDropDown(localDataManager);
+	manager.add(localDataManager);
+}
+
+/*
+ * Fill the local toolbar.
+ */
+void fillLocalToolBar(IToolBarManager manager) {
+	// no default toolbar action
+}
+
+/*
+ * Filter non milestone builds action run.
+ */
+void filterNightlyBuilds(boolean filter, boolean updatePreference) {
+	if (filter) {
+		this.viewFilters.add(FILTER_NIGHTLY_BUILDS);
+	} else {
+		this.viewFilters.remove(FILTER_NIGHTLY_BUILDS);
+	}
+	this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS, filter);
+	updateFilters();
+}
+
+/*
+ * Filter non milestone builds action run.
+ */
+void filterOldBuilds(boolean filter, boolean updatePreference) {
+	if (filter) {
+		this.viewFilters.add(FILTER_OLD_BUILDS);
+	} else {
+		this.viewFilters.remove(FILTER_OLD_BUILDS);
+	}
+	this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_OLD_BUILDS, filter);
+	updateFilters();
+}
+
+/*
+ * Finalize the viewer creation
+ */
+void finalizeViewerCreation() {
+	makeActions();
+	hookContextMenu();
+	contributeToActionBars();
+	restoreState();
+	updateFilters();
+	this.viewer.setInput(getViewSite());
+	this.viewer.addSelectionChangedListener(this);
+}
+
+/* (non-Javadoc)
+ * Method declared on IAdaptable
+ */
+public Object getAdapter(Class adapter) {
+    if (adapter.equals(IPropertySheetPage.class)) {
+        return getPropertySheet();
+    }
+    return super.getAdapter(adapter);
+}
+
+/**
+ * Returns the property sheet.
+ */
+protected IPropertySheetPage getPropertySheet() {
+	if (this.propertyPage == null) {
+		this.propertyPage = new PropertySheetPage();
+	}
+    return this.propertyPage;
+}
+
+/*
+ * Get the sibling view (see subclasses).
+ */
+abstract PerformancesView getSiblingView();
+
+/*
+ * Hook the context menu.
+ */
+void hookContextMenu() {
+	MenuManager menuMgr = new MenuManager("#PopupMenu");
+	menuMgr.setRemoveAllWhenShown(true);
+	menuMgr.addMenuListener(new IMenuListener() {
+		public void menuAboutToShow(IMenuManager manager) {
+			fillContextMenu(manager);
+		}
+	});
+	Menu menu = menuMgr.createContextMenu(this.viewer.getControl());
+	this.viewer.getControl().setMenu(menu);
+	getSite().registerContextMenu(menuMgr, this.viewer);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
+ */
+public void init(IViewSite site, IMemento memento) throws PartInitException {
+	super.init(site, memento);
+	this.viewState = memento;
+}
+
+/*
+ * Init results
+ */
+void initResults() {
+	this.results = PerformanceResultsElement.PERF_RESULTS_MODEL;
+	if (this.results.isInitialized()) {
+		this.dataDir = getSiblingView().dataDir;
+	} else {
+		String localDataDir = this.preferences.get(IPerformancesConstants.PRE_LOCAL_DATA_DIR, null);
+		if (localDataDir != null) {
+			File dir = new File(localDataDir);
+			if (dir.exists() && dir.isDirectory()) {
+				this.dataDir = dir;
+				readLocalFiles(null);
+			}
+		}
+	}
+}
+
+/*
+ * Make common actions to performance views.
+ */
+void makeActions() {
+
+	// Change data dir action
+	this.changeDataDir = new Action("&Read...") {
+		public void run() {
+			changeDataDir(null);
+		}
+	};
+	this.changeDataDir.setToolTipText("Change the directory of the local data files");
+//	this.changeDataDir.setImageDescriptor(ResultsElement.FOLDER_IMAGE_DESCRIPTOR);
+
+	// Filter baselines action
+	this.filterBaselineBuilds = new Action("&Baselines", IAction.AS_CHECK_BOX) {
+		public void run() {
+			if (isChecked()) {
+				PerformancesView.this.viewFilters.add(FILTER_BASELINE_BUILDS);
+			} else {
+				PerformancesView.this.viewFilters.remove(FILTER_BASELINE_BUILDS);
+			}
+			updateFilters();
+        }
+	};
+	this.filterBaselineBuilds.setToolTipText("Filter baseline builds");
+
+	// Filter baselines action
+	this.filterNightlyBuilds = new Action("&Nightly", IAction.AS_CHECK_BOX) {
+		public void run() {
+			filterNightlyBuilds(isChecked(), true/*update preference*/);
+		}
+	};
+	this.filterNightlyBuilds.setToolTipText("Filter nightly builds");
+
+	// Filter non-important builds action
+	this.filterOldBuilds = new Action("&Old Builds", IAction.AS_CHECK_BOX) {
+		public void run() {
+			filterOldBuilds(isChecked(), true/*update preference*/);
+		}
+	};
+	this.filterOldBuilds.setChecked(false);
+	this.filterOldBuilds.setToolTipText("Filter old builds (i.e. before last milestone) but keep all previous milestones)");
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
+ */
+public void preferenceChange(PreferenceChangeEvent event) {
+	// do nothing
+}
+
+/*
+ * Read local files
+ */
+void readLocalFiles(final String lastBuild) {
+
+	// Create runnable to read local files
+	IRunnableWithProgress runnable = new IRunnableWithProgress() {
+		public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+			try {
+				monitor.beginTask("Read local files", 1000);
+				PerformancesView.this.results.readLocal(PerformancesView.this.dataDir, monitor, lastBuild);
+				monitor.done();
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+	};
+
+	// Execute the runnable with progress
+	ProgressMonitorDialog readProgress = new ProgressMonitorDialog(getSite().getShell());
+	try {
+		readProgress.run(true, true, runnable);
+	} catch (InvocationTargetException e) {
+		// skip
+	} catch (InterruptedException e) {
+		// skip
+	}
+}
+
+/*
+ * Refresh the views.
+ */
+void refresh(String lastBuild) {
+
+	// Read local files
+	readLocalFiles(lastBuild);
+
+	// Refresh views
+	refreshInput();
+	getSiblingView().refreshInput();
+}
+
+/*
+ * Refresh the entire view by resetting its input.
+ */
+void refreshInput() {
+	this.viewer.setInput(getViewSite());
+	this.viewer.refresh();
+}
+
+/*
+ * Clear view content.
+ */
+void resetInput() {
+	this.results.reset(null);
+	this.viewer.setInput(getViewSite());
+	this.viewer.refresh();
+}
+
+/*
+ * Restore the view state from the memento information.
+ */
+void restoreState() {
+
+	// Filter baselines action state
+	if (this.viewState != null) {
+		Boolean filterBaselinesState = this.viewState.getBoolean(IPerformancesConstants.PRE_FILTER_BASELINE_BUILDS);
+		boolean filterBaselinesValue = filterBaselinesState == null ? false : filterBaselinesState.booleanValue();
+		this.filterBaselineBuilds.setChecked(filterBaselinesValue);
+		if (filterBaselinesValue) {
+			this.viewFilters.add(FILTER_BASELINE_BUILDS);
+		}
+		String dir = this.viewState.getString(IPerformancesConstants.PRE_WRITE_RESULTS_DIR);
+		if (dir != null) {
+			this.resultsDir = new File(dir);
+		}
+	}
+
+	// Filter nightly builds action
+	boolean checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_NIGHTLY_BUILDS, IPerformancesConstants.DEFAULT_FILTER_NIGHTLY_BUILDS);
+	this.filterNightlyBuilds.setChecked(checked);
+	if (checked) {
+		this.viewFilters.add(FILTER_NIGHTLY_BUILDS);
+	}
+
+	// Filter non important builds action state
+	checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_OLD_BUILDS, IPerformancesConstants.DEFAULT_FILTER_OLD_BUILDS);
+	this.filterOldBuilds.setChecked(checked);
+	if (checked) {
+		this.viewFilters.add(FILTER_OLD_BUILDS);
+	}
+}
+
+public void saveState(IMemento memento) {
+	super.saveState(memento);
+	memento.putBoolean(IPerformancesConstants.PRE_FILTER_BASELINE_BUILDS, this.filterBaselineBuilds.isChecked());
+	try {
+		this.preferences.flush();
+	} catch (BackingStoreException e) {
+		// ignore
+	}
+	if (this.resultsDir != null) {
+		memento.putString(IPerformancesConstants.PRE_WRITE_RESULTS_DIR, this.resultsDir.getPath());
+	}
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
+public void selectionChanged(SelectionChangedEvent event) {
+	if (this.propertyPage != null) {
+		this.propertyPage.selectionChanged(this, event.getSelection());
+	}
+}
+
+/**
+ * Passing the focus request to the viewer's control.
+ */
+public void setFocus() {
+	this.viewer.getControl().setFocus();
+}
+
+/*
+ * Set the view tooltip to reflect the DB connection kind.
+ */
+void setTitleToolTip() {
+	String title = DB_Results.getDbTitle();
+	if (title == null) {
+		// DB is not connected
+		int version = this.preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
+		title = "Eclipse v" + version + " - DB not connected";
+	}
+	setTitleToolTip(title);
+}
+
+/*
+ * Set/unset the database connection.
+ *
+void toogleDbConnection() {
+
+	// Toogle DB connection and store new state
+	boolean dbConnected = this.preferences.getBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
+	DB_Results.DB_CONNECTION = !dbConnected;
+	getSiblingView().dbConnection.setChecked(DB_Results.DB_CONNECTION);
+	this.preferences.putBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, DB_Results.DB_CONNECTION);
+
+	// First close DB connection
+	if (!DB_Results.DB_CONNECTION) {
+		DB_Results.shutdown();
+	}
+
+	// Read local files if any
+	if (this.dataDir != null) {
+		readLocalFiles();
+	}
+
+	// Refresh views
+	refreshInput();
+	getSiblingView().refreshInput();
+}
+*/
+
+/*
+ * Update the filters from the stored list and apply them to the view.
+ */
+final void updateFilters() {
+	ViewerFilter[] filters = new ViewerFilter[this.viewFilters.size()];
+	this.viewFilters.toArray(filters);
+	this.viewer.setFilters(filters);
+}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/filter_ps.gif b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/filter_ps.gif
new file mode 100644
index 0000000..a4c9e60
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/filter_ps.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/help.gif b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/help.gif
new file mode 100644
index 0000000..ae2c4c0
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/help.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/icallout_obj.gif b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/icallout_obj.gif
new file mode 100644
index 0000000..f52d86a
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/icallout_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/IPerformancesConstants.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/IPerformancesConstants.java
new file mode 100644
index 0000000..4891202
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/IPerformancesConstants.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * 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.utils;
+
+/**
+ * Interface to define all constants used for performances.
+ */
+public interface IPerformancesConstants {
+    public static final String PLUGIN_ID = "org.eclipse.test.performance.ui"; //$NON-NLS-1$
+
+	public static final String PREFIX = PLUGIN_ID + "."; //$NON-NLS-1$
+
+	// State constants
+    public static final String PRE_FILTER_BASELINE_BUILDS = PREFIX + "filter.baseline.builds"; //$NON-NLS-1$
+    public static final String PRE_FULL_LINE_SELECTION  = PREFIX + "full.line.selection"; //$NON-NLS-1$
+    public static final String PRE_WRITE_RESULTS_DIR = PREFIX + "write.results.dir"; //$NON-NLS-1$
+
+	// Preference constants
+    public static final String PRE_ECLIPSE_VERSION = PREFIX + "eclipse.version"; //$NON-NLS-1$
+    public static final String PRE_DATABASE_CONNECTION = PREFIX + "database.connection"; //$NON-NLS-1$
+    public static final String PRE_DATABASE_LOCAL = PREFIX + "local"; //$NON-NLS-1$
+    public static final String PRE_DATABASE_LOCATION = PREFIX + "database.location"; //$NON-NLS-1$
+    public static final String PRE_LOCAL_DATA_DIR = PREFIX + "local.data.dir"; //$NON-NLS-1$
+    public static final String PRE_RESULTS_GENERATION_DIR = PREFIX + "results.generation.dir"; //$NON-NLS-1$
+    public static final String PRE_CONFIG_DESCRIPTOR_NAME = PREFIX + "config.descriptor.name"; //$NON-NLS-1$
+    public static final String PRE_CONFIG_DESCRIPTOR_DESCRIPTION = PREFIX + "config.descriptor.description"; //$NON-NLS-1$
+    public static final String PRE_DEFAULT_DIMENSION = PREFIX + "default.dimension"; //$NON-NLS-1$
+    public static final String PRE_RESULTS_DIMENSION = PREFIX + "results.dimension"; //$NON-NLS-1$
+    public static final String PRE_MILESTONE_BUILDS = PREFIX + "milestone.builds"; //$NON-NLS-1$
+    public static final String PRE_STATUS_COMMENT_PREFIX = PREFIX + "status.comment"; //$NON-NLS-1$
+    public static final String PRE_FILTER_ADVANCED_SCENARIOS = PREFIX + "filter.non.fingerprints.scenarios"; //$NON-NLS-1$
+    public static final String PRE_FILTER_OLD_BUILDS = PREFIX + "filter.non.milestones.builds"; //$NON-NLS-1$
+    public static final String PRE_FILTER_NIGHTLY_BUILDS = PREFIX + "filter.nightly.builds"; //$NON-NLS-1$
+
+	// Other constants
+	public static final int ECLIPSE_MAINTENANCE_VERSION = 36;
+	public static final int ECLIPSE_DEVELOPMENT_VERSION = 37;
+
+	// Default values
+	public static final String DATABASE_NAME_PREFIX = "perfDb";
+	public static final String NETWORK_DATABASE_LOCATION = "net://minsky.ottawa.ibm.com:1528";
+	public static final int DEFAULT_ECLIPSE_VERSION = ECLIPSE_DEVELOPMENT_VERSION;
+	public static final boolean DEFAULT_FILTER_ADVANCED_SCENARIOS = true;
+	public static final boolean DEFAULT_FILTER_OLD_BUILDS = false;
+	public static final boolean DEFAULT_FILTER_NIGHTLY_BUILDS = false;
+	public static final boolean DEFAULT_DATABASE_CONNECTION = false;
+	public static final boolean DEFAULT_DATABASE_LOCAL = false;
+
+	// Status
+    public static final String PRE_WRITE_STATUS = PREFIX + "write.status"; //$NON-NLS-1$
+	public static final int STATUS_BUILDS_NUMBER_MASK= 0x00FF;
+	public static final int DEFAULT_BUILDS_NUMBER = 3;
+	public static final int STATUS_VALUES = 0x0100;
+	public static final int STATUS_ERROR_NONE = 0x0200;
+	public static final int STATUS_ERROR_NOTICEABLE = 0x0400;
+	public static final int STATUS_ERROR_SUSPICIOUS = 0x0600;
+	public static final int STATUS_ERROR_WEIRD = 0x0800;
+	public static final int STATUS_ERROR_INVALID = 0x0A00;
+	public static final int STATUS_ERROR_LEVEL_MASK = 0x0E00;
+	public static final int STATUS_SMALL_VALUE_BUILD = 0x1000;
+	public static final int STATUS_SMALL_VALUE_DELTA = 0x2000;
+	public static final int STATUS_SMALL_VALUE_MASK = 0x3000;
+	public static final int STATUS_STATISTICS_ERRATIC = 0x4000;
+	public static final int STATUS_STATISTICS_UNSTABLE = 0x8000;
+	public static final int STATUS_STATISTICS_MASK = 0xC000;
+	public static final int DEFAULT_WRITE_STATUS = STATUS_ERROR_NONE | DEFAULT_BUILDS_NUMBER;
+
+	// Comparison
+    public static final String PRE_COMPARISON_THRESHOLD_FAILURE = PREFIX + "comparison.threshold.failure"; //$NON-NLS-1$
+	public static final int DEFAULT_COMPARISON_THRESHOLD_FAILURE = 10;
+    public static final String PRE_COMPARISON_THRESHOLD_ERROR = PREFIX + "comparison.threshold.error"; //$NON-NLS-1$
+	public static final int DEFAULT_COMPARISON_THRESHOLD_ERROR = 3;
+    public static final String PRE_COMPARISON_THRESHOLD_IMPROVEMENT = PREFIX + "comparison.threshold.imporvement"; //$NON-NLS-1$
+	public static final int DEFAULT_COMPARISON_THRESHOLD_IMPROVEMENT = 10;
+
+	// Default milestones nowadays
+	public static final String[] V37_MILESTONES = new String[] {
+	};
+	public static final String[] V36_MILESTONES = new String[] {
+		"M1-200908060100",
+		"M2-200909170100",
+		"M3-200910301201",
+		"M4-200912101301",
+		"M5-201001291300",
+		"M6-201003121448",
+	};
+	/** @deprecated */
+	public static final String[] V35_MILESTONES = new String[] {
+		        "M1-200808071402",
+		        "M2-200809180100",
+		        "M3-200810301917",
+		        "M4-200812111908",
+		        "M5-200902021535",
+		        "M6-200903130100",
+		        "M7-200904302300",
+		        "RC1-200905151143",
+		        "RC2-200905221710",
+		        "RC3-200905282000",
+		        "RC4-200906051444",
+		        "R3_5-200906111540"
+	};
+	/** @deprecated */
+	public static final String[] V34_MILESTONES = new String[] {
+		        "M1-200708091105",
+		        "M2-200709210919",
+		        "M3-200711012000",
+		        "M4-200712131700",
+		        "M5-200802071530",
+		        "M6a-200804091425",
+		        "M7-200805020100",
+		        "RC1-200805161333",
+		        "RC2-200805230100",
+		        "RC3-200805301730",
+		        "RC4-200806091311",
+		        "R3_4-200806172000"
+	};
+	/** @deprecated */
+	public static final String[] V33_MILESTONES = new String[] {
+		        "M1-200608101230",
+		        "M2-200609220010",
+		        "M3-200611021715",
+		        "M4-200612141445",
+		        "M5-200702091006",
+		        "M5eh-200702220951",
+		        "M6-200703231616",
+		        "M7-200705031400",
+		        "RC1-200705171700",
+		        "RC2-200705251350",
+		        "RC3-200706011539",
+		        "RC4-200706081718",
+		        "R3_3-200706251500",
+		        "R3_3_1-200709211145",
+		        "R3_3_2-200802211800"
+	};
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/Util.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/Util.java
new file mode 100644
index 0000000..4bddd41
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/Util.java
@@ -0,0 +1,639 @@
+/*******************************************************************************
+ * 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.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+
+/**
+ * Utility methods for statistics. Got from org.eclipse.test.performance
+ * framework
+ */
+public final class Util implements IPerformancesConstants {
+
+	// Percentages
+	public static final NumberFormat PERCENTAGE_FORMAT = NumberFormat.getPercentInstance(Locale.US);
+	static {
+		PERCENTAGE_FORMAT.setMaximumFractionDigits(2);
+	}
+	public static final NumberFormat DOUBLE_FORMAT = NumberFormat.getNumberInstance(Locale.US);
+	static {
+		DOUBLE_FORMAT.setMaximumFractionDigits(2);
+	}
+
+	// Strings
+	public static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+	// Build prefixes
+	public static final List ALL_BUILD_PREFIXES = new ArrayList(3);
+	static {
+		ALL_BUILD_PREFIXES.add("I");
+		ALL_BUILD_PREFIXES.add("N");
+		ALL_BUILD_PREFIXES.add("M");
+	}
+	public static final List BUILD_PREFIXES = new ArrayList(2);
+	static {
+		BUILD_PREFIXES.add("I");
+		BUILD_PREFIXES.add("N");
+	}
+	public static final List MAINTENANCE_BUILD_PREFIXES = new ArrayList(2);
+	static {
+		MAINTENANCE_BUILD_PREFIXES.add("I");
+		MAINTENANCE_BUILD_PREFIXES.add("M");
+	}
+	public static final List BASELINE_BUILD_PREFIXES = new ArrayList(1);
+	static {
+		BASELINE_BUILD_PREFIXES.add(DB_Results.getDbBaselinePrefix());
+	}
+
+	// Milestones constants
+	private static String[] MILESTONES;
+	public static final BuildDateComparator BUILD_DATE_COMPARATOR = new BuildDateComparator();
+
+	// Components constants
+	public static final String ORG_ECLIPSE = "org.eclipse.";
+
+static class BuildDateComparator implements Comparator {
+	public int compare(Object o1, Object o2) {
+		String s1 = (String) o1;
+		String s2 = (String) o2;
+		return getBuildDate(s1).compareTo(getBuildDate(s2));
+	}
+}
+
+private static void initMilestones() {
+	String version = DB_Results.getDbVersion();
+
+	// Initialize reference version and database directory
+	char mainVersion = version.charAt(1);
+	char minorVersion = version.charAt(2);
+
+	// Initialize milestones
+	if (mainVersion == '3') {
+		switch (minorVersion) {
+			case '3':
+			case '4':
+			case '5':
+				throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is no longer supported!");
+			case '6':
+				MILESTONES = V36_MILESTONES;
+				break;
+			case '7':
+				MILESTONES = V37_MILESTONES;
+				break;
+			default:
+				throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is not supported yet!");
+		}
+	} else {
+		throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is not supported yet!");
+	}
+}
+
+// Static information for time and date
+public static final int ONE_MINUTE = 60000;
+public static final long ONE_HOUR = 3600000L;
+public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); //$NON-NLS-1$
+
+/**
+ * Return the name to display for the given component.
+ * <p>
+ * This name is built from the name of the component selected in the Components view.
+ * The rules to build the name are:
+ * <ol>
+ * <li>If the component name does not start with "org.eclipse" then the part name is just
+ * 		"<b>component name</b>"</li>
+ * <li>Otherwise, remove "org.eclipse." form the component name and count
+ * 		the tokens separated by a dot ('.')
+ * 		<ul>
+ * 		<li>If there's only one remaining token, then the part name is "<b>Platform/</b>"
+ * 			followed by:
+ * 			<ul>
+ * 			<li>"<b><i>TOKEN</i></b>" if token is less than 3 characters,</li>
+ * 			<li>"<b><i>Token</i></b>" otherwise</li>
+ * 			</ul>
+ * 		</li>
+ * 		<li>Otherwise then the part name is "<b><i>FIRST_TOKEN</i></b>" followed by:
+ * 			<ul>
+ * 			<li>for each followed additional token:
+ * 				<ul>
+ * 				<li>"<b><i>TOKEN</i></b>" if token is less than 3 characters,</li>
+ * 				<li>"<b><i>Token</i></b>" otherwise</li>
+ * 				</ul>
+ * 			</li>
+ * 			</ul>
+ * 		</li>
+ * 		</ul>
+ * </ol>
+ * E.g.
+ * 	- org.eclipse.ui -> "Platform/UI"
+ * 	- org.eclipse.swt -> "Platform/SWT"
+ * 	- org.eclipse.team -> "Platform/Team"
+ * 	- org.eclipse.jdt.ui -> "JDT/UI"
+ * 	- org.eclipse.jdt.core -> "JDT/Core"
+ * 	- org.eclipse.pde.api.tools -> "PDE/API Tools"
+ */
+public static String componentDisplayName(String componentName) {
+	String partName;
+	StringBuffer buffer = null;
+	if (componentName.startsWith(ORG_ECLIPSE)) {
+		partName = componentName.substring(ORG_ECLIPSE.length());
+		StringTokenizer tokenizer = new StringTokenizer(partName, ".");
+		while (tokenizer.hasMoreTokens()) {
+			String token = tokenizer.nextToken();
+			if (buffer == null) {
+				if (tokenizer.hasMoreTokens()) {
+					buffer = new StringBuffer("'"+token.toUpperCase());
+					buffer.append('/');
+				} else {
+					buffer = new StringBuffer("'Platform/");
+					if (token.length() > 3) {
+						buffer.append(Character.toUpperCase(token.charAt(0)));
+						buffer.append(token.substring(1));
+					} else {
+						buffer.append(token.toUpperCase());
+					}
+				}
+			} else {
+				if (token.length() > 3) {
+					buffer.append(Character.toUpperCase(token.charAt(0)));
+					buffer.append(token.substring(1));
+				} else {
+					buffer.append(token.toUpperCase());
+				}
+				if (tokenizer.hasMoreTokens()) buffer.append(' ');
+			}
+		}
+	} else {
+		buffer = new StringBuffer("'");
+		buffer.append(componentName);
+		buffer.append("'");
+	}
+	buffer.append("' results");
+	return buffer.toString();
+}
+
+/**
+ * Compute the student t-test values.
+ *
+ * @see "http://en.wikipedia.org/wiki/Student's_t-test"
+ *
+ * @param baselineResults The baseline build
+ * @param buildResults The current build
+ * @return The student t-test value as a double.
+ */
+public static double computeTTest(BuildResults baselineResults, BuildResults buildResults) {
+
+	double ref = baselineResults.getValue();
+	double val = buildResults.getValue();
+
+	double delta = ref - val;
+	long dfRef = baselineResults.getCount() - 1;
+	double sdRef = baselineResults.getDeviation();
+	long dfVal = buildResults.getCount() - 1;
+	double sdVal = buildResults.getDeviation();
+	// TODO if the stdev's are not sufficiently similar, we have to take a
+	// different approach
+
+	if (!Double.isNaN(sdRef) && !Double.isNaN(sdVal) && dfRef > 0 && dfVal > 0) {
+		long df = dfRef + dfVal;
+		double sp_square = (dfRef * sdRef * sdRef + dfVal * sdVal * sdVal) / df;
+
+		double se_diff = Math.sqrt(sp_square * (1.0 / (dfRef + 1) + 1.0 / (dfVal + 1)));
+		double t = Math.abs(delta / se_diff);
+		return t;
+	}
+
+	return -1;
+}
+
+/**
+ * Copy a file to another location.
+ *
+ * @param src the source file.
+ * @param dest the destination.
+ * @return <code>true</code> if the file was successfully copied,
+ * 	<code>false</code> otherwise.
+ */
+public static boolean copyFile(File src, File dest) {
+
+	try {
+		InputStream in = new FileInputStream(src);
+		OutputStream out = new FileOutputStream(dest);
+		byte[] buf = new byte[1024];
+		int len;
+		while ((len = in.read(buf)) > 0) {
+			out.write(buf, 0, len);
+		}
+		in.close();
+		out.close();
+	} catch (FileNotFoundException e) {
+		e.printStackTrace();
+		return false;
+	} catch (IOException e) {
+		e.printStackTrace();
+		return false;
+	}
+	return true;
+}
+
+/**
+ * Copy a file content to another location.
+ *
+ * @param in the input stream.
+ * @param dest the destination.
+ * @return <code>true</code> if the file was successfully copied,
+ * 	<code>false</code> otherwise.
+ */
+public static boolean copyStream(InputStream in, File dest) {
+
+	try {
+		OutputStream out = new FileOutputStream(dest);
+		byte[] buf = new byte[1024];
+		int len;
+		while ((len = in.read(buf)) > 0) {
+			out.write(buf, 0, len);
+		}
+		in.close();
+		out.close();
+	} catch (FileNotFoundException e) {
+		e.printStackTrace();
+		return false;
+	} catch (IOException e) {
+		e.printStackTrace();
+		return false;
+	}
+	return true;
+}
+
+/**
+ * Return the build date as yyyyMMddHHmm.
+ *
+ * @param buildName The build name (e.g. I20090806-0100)
+ * @return The date as a string.
+ */
+public static String getBuildDate(String buildName) {
+	return getBuildDate(buildName, DB_Results.getDbBaselinePrefix());
+}
+
+/**
+ * Return the build date as yyyyMMddHHmm.
+ *
+ * @param buildName The build name (e.g. I20090806-0100)
+ * @param baselinePrefix The baseline prefix (e.g. {@link DB_Results#getDbBaselinePrefix()})
+ * @return The date as a string.
+ */
+public static String getBuildDate(String buildName, String baselinePrefix) {
+
+	// Baseline name
+	if (baselinePrefix != null && buildName.startsWith(baselinePrefix)) {
+		int length = buildName.length();
+		return buildName.substring(length-12, length);
+	}
+
+	// Build name
+	char first = buildName.charAt(0);
+	if (first == 'N' || first == 'I' || first == 'M') { // TODO (frederic) should be buildIdPrefixes...
+		return buildName.substring(1, 9)+buildName.substring(10, 14);
+	}
+
+	// Try with date format
+	int length = buildName.length() - 12 /* length of date */;
+	for (int i=0; i<=length; i++) {
+		try {
+			String substring = i == 0 ? buildName : buildName.substring(i);
+			Util.DATE_FORMAT.parse(substring);
+			return substring; // if no exception is raised then the substring has a correct date format => return it
+		} catch(ParseException ex) {
+			// skip
+		}
+	}
+	return null;
+}
+
+/**
+ * Returns the date of the milestone corresponding at the given index.
+ *
+ * @param index The index of the milestone
+ * @return The date as a YYYYMMDD-hhmm string.
+ */
+public static String getMilestoneDate(int index) {
+	int length = getMilestonesLength();
+	if (index >= length) return null;
+	int dash = MILESTONES[index].indexOf('-');
+	return MILESTONES[index].substring(dash+1);
+}
+
+/**
+ * Returns the milestone matching the given build name.
+ *
+ * @param buildName The name of the build
+ * @return The milestone as a string (e.g. M1)
+ */
+public static String getMilestone(String buildName) {
+	if (buildName != null && buildName.length() >= 12) {
+		int length = getMilestonesLength();
+		String buildDate = getBuildDate(buildName, DB_Results.getDbBaselinePrefix());
+		for (int i=0; i<length; i++) {
+			int start = MILESTONES[i].indexOf(buildDate);
+			if (start > 0) {
+				return MILESTONES[i];
+			}
+		}
+	}
+	return null;
+}
+
+/**
+ * Returns the name the milestone matching the given build name.
+ *
+ * @param buildName The name of the build
+ * @return The milestone name as a string (e.g. M1)
+ */
+public static String getMilestoneName(String buildName) {
+	if (buildName != null && buildName.length() >= 12) {
+		int length = getMilestonesLength();
+		String buildDate = getBuildDate(buildName, DB_Results.getDbBaselinePrefix());
+		for (int i=0; i<length; i++) {
+			int start = MILESTONES[i].indexOf(buildDate);
+			if (start > 0) {
+				return MILESTONES[i].substring(0, start - 1);
+			}
+		}
+	}
+	return null;
+}
+
+/**
+ * Returns whether the given build name is a milestone or not.
+ *
+ * @param buildName The build name
+ * @return <code>true</code> if the build name matches a milestone one,
+ * 	<code>false</code> otherwise.
+ */
+public static boolean isMilestone(String buildName) {
+	return getMilestoneName(buildName) != null;
+}
+
+/**
+ * Returns the name of the milestone which run after the given build name
+ * or <code>null</code> if there's no milestone since the build has run.
+ *
+ * @param buildName The build name
+ * @return <code>true</code> if the build name matches a milestone one,
+ * 	<code>false</code> otherwise.
+ */
+public static String getNextMilestone(String buildName) {
+	int length = getMilestonesLength();
+	String buildDate = getBuildDate(buildName);
+	for (int i=0; i<length; i++) {
+		String milestoneDate = MILESTONES[i].substring(MILESTONES[i].indexOf('-')+1);
+		if (milestoneDate.compareTo(buildDate) > 0) {
+			return milestoneDate;
+		}
+	}
+	return null;
+}
+
+/**
+ * Return the number of milestones.
+ *
+ * @return The number as an int
+ */
+public static int getMilestonesLength() {
+	if (MILESTONES == null) initMilestones();
+	int length = MILESTONES.length;
+	return length;
+}
+
+/**
+ * @deprecated
+ */
+public static boolean matchPattern(String name, String pattern) {
+	if (pattern.equals("*")) return true; //$NON-NLS-1$
+	if (pattern.indexOf('*') < 0 && pattern.indexOf('?') < 0) {
+		pattern += "*"; //$NON-NLS-1$
+	}
+	StringTokenizer tokenizer = new StringTokenizer(pattern, "*?", true); //$NON-NLS-1$
+	int start = 0;
+	String previous = ""; //$NON-NLS-1$
+	while (tokenizer.hasMoreTokens()) {
+		String token = tokenizer.nextToken();
+		if (!token.equals("*") && !token.equals("?")) { //$NON-NLS-1$ //$NON-NLS-2$
+			if (previous.equals("*")) { //$NON-NLS-1$
+				int idx = name.substring(start).indexOf(token);
+				if (idx < 0) return false;
+				start += idx;
+			} else {
+				if (previous.equals("?")) start++; //$NON-NLS-1$
+				if (!name.substring(start).startsWith(token)) return false;
+			}
+			start += token.length();
+		}
+		previous = token;
+	}
+	if (previous.equals("*")) { //$NON-NLS-1$
+		return true;
+	} else if (previous.equals("?")) { //$NON-NLS-1$
+		return name.length() == start;
+	}
+	return name.endsWith(previous);
+}
+
+/**
+ * @deprecated
+ */
+public static double round(double value) {
+	return Math.round(value * 10000) / 10000.0;
+}
+
+/**
+ * @deprecated
+ */
+public static double round(double value, int precision) {
+	if (precision < 0) {
+		throw new IllegalArgumentException("Should have a precision at least greater than 0!");
+	}
+	if (precision == 0) return (long) Math.floor(value);
+	double factor = 10;
+	int n = 1;
+	while (n++ < precision)
+		factor *= 10;
+	return Math.round(value * factor) / factor;
+}
+
+/**
+ * Returns a string to display the given time as a duration
+ * formatted as "hh:mm:ss".
+ *
+ * @param time The time to format as a long.
+ * @return The formatted string.
+ */
+public static String timeChrono(long time) {
+	if (time < 1000) { // less than 1s
+		return "00:00:00"; //$NON-NLS-1$
+	}
+	StringBuffer buffer = new StringBuffer();
+	int seconds = (int) (time / 1000);
+	if (seconds < 60) {
+		buffer.append("00:00:"); //$NON-NLS-1$
+		if (seconds < 10) buffer.append('0');
+		buffer.append(seconds);
+	} else {
+		int minutes = seconds / 60;
+		if (minutes < 60) {
+			buffer.append("00:"); //$NON-NLS-1$
+			if (minutes < 10) buffer.append('0');
+			buffer.append(minutes);
+			buffer.append(':');
+			seconds = seconds % 60;
+			if (seconds < 10) buffer.append('0');
+			buffer.append(seconds);
+		} else {
+			int hours = minutes / 60;
+			if (hours < 10) buffer.append('0');
+			buffer.append(hours);
+			buffer.append(':');
+			minutes = minutes % 60;
+			if (minutes < 10) buffer.append('0');
+			buffer.append(minutes);
+			buffer.append(':');
+			seconds = seconds % 60;
+			if (seconds < 10) buffer.append('0');
+			buffer.append(seconds);
+		}
+	}
+	return buffer.toString();
+}
+
+/**
+ * Returns a string to display the given time as the hour of the day
+ * formatted as "hh:mm:ss".
+ *
+ * @param time The time to format as a long.
+ * @return The formatted string.
+ */
+public static String timeEnd(long time) {
+	GregorianCalendar calendar = new GregorianCalendar();
+	calendar.add(Calendar.SECOND, (int)(time/1000));
+	Date date = calendar.getTime();
+	SimpleDateFormat dateFormat = new SimpleDateFormat("KK:mm:ss"); //$NON-NLS-1$
+	return dateFormat.format(date);
+}
+
+/**
+ * Returns a string to display the given time as a duration
+ * formatted as:
+ *	<ul>
+ *	<li>"XXXms" if the duration is less than 0.1s (e.g. "543ms")</li>
+ *	<li>"X.YYs" if the duration is less than 1s (e.g. "5.43s")</li>
+ *	<li>"XX.Ys" if the duration is less than 1mn (e.g. "54.3s")</li>
+ *	<li>"XXmn XXs" if the duration is less than 1h (e.g. "54mn 3s")</li>
+ *	<li>"XXh XXmn XXs" if the duration is over than 1h (e.g. "5h 4mn 3s")</li>
+ *	</ul>
+ *
+ * @param time The time to format as a long.
+ * @return The formatted string.
+ */
+public static String timeString(long time) {
+	NumberFormat format = NumberFormat.getInstance();
+	format.setMaximumFractionDigits(1);
+	StringBuffer buffer = new StringBuffer();
+	if (time == 0) {
+		// print nothing
+	} if (time < 100) { // less than 0.1s
+		buffer.append(time);
+		buffer.append("ms"); //$NON-NLS-1$
+	} else if (time < 1000) { // less than 1s
+		if ((time%100) != 0) {
+			format.setMaximumFractionDigits(2);
+		}
+		buffer.append(format.format(time/1000.0));
+		buffer.append("s"); //$NON-NLS-1$
+	} else if (time < Util.ONE_MINUTE) {  // less than 1mn
+		if ((time%1000) == 0) {
+			buffer.append(time/1000);
+		} else {
+			buffer.append(format.format(time/1000.0));
+		}
+		buffer.append("s"); //$NON-NLS-1$
+	} else if (time < Util.ONE_HOUR) {  // less than 1h
+		buffer.append(time/Util.ONE_MINUTE).append("mn "); //$NON-NLS-1$
+		long seconds = time%Util.ONE_MINUTE;
+		buffer.append(seconds/1000);
+		buffer.append("s"); //$NON-NLS-1$
+	} else {  // more than 1h
+		long h = time / Util.ONE_HOUR;
+		buffer.append(h).append("h "); //$NON-NLS-1$
+		long m = (time % Util.ONE_HOUR) / Util.ONE_MINUTE;
+		buffer.append(m).append("mn "); //$NON-NLS-1$
+		long seconds = m%Util.ONE_MINUTE;
+		buffer.append(seconds/1000);
+		buffer.append("s"); //$NON-NLS-1$
+	}
+	return buffer.toString();
+}
+
+private Util() {
+	// don't instantiate
+}
+
+/**
+ * Set the milestones.
+ *
+ * @param items The milestones list (e.g. {@link IPerformancesConstants#V37_MILESTONES}).
+ */
+public static void setMilestones(String[] items) {
+	MILESTONES = items;
+}
+
+/**
+ * Init the milestones from preferences
+ *
+ * @param preferences The preferences from which got milestones list
+ */
+public static void initMilestones(IEclipsePreferences preferences) {
+	int eclipseVersion = preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
+	String prefix = IPerformancesConstants.PRE_MILESTONE_BUILDS + "." + eclipseVersion;
+	int index = 0;
+	String milestone = preferences.get(prefix + index, null);
+	String[] milestones = new String[20];
+	while (milestone != null) {
+		milestones[index] = milestone;
+		index++;
+		milestone = preferences.get(prefix + index, null);
+	}
+	int length = milestones.length;
+	if (index < length) {
+		System.arraycopy(milestones, 0, milestones = new String[index], 0, index);
+	}
+	MILESTONES = milestones;
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/BarGraph.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/BarGraph.java
new file mode 100644
index 0000000..438909c
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/BarGraph.java
@@ -0,0 +1,294 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+
+public class BarGraph {
+
+	private static final int MARGIN= 5; // margin on all four sides
+	private static final int BARHEIGHT= 8; // height of bar
+	private static final int GAP= 10; // gap between bars
+	private static final int TGAP= 5; // gap between lines and labels
+
+	private static final boolean NO_SCALE= true; //
+
+	// if NO_SCALE is true the following values are used:
+	private static final double RATIO= 0.6; // fraction of width reserved for bar graph; needs tweaking
+	private static final int FROM_END= 20; // a break (//) is shown this far from end of bar
+	private static final int SLANT= 8; // slant of break
+	private static final int GAP2= 5; // width of break
+
+	private StringBuffer fAreaBuffer;
+
+	private static class BarItem {
+
+		String title;
+		double value;
+		String url;
+		String slowdownExpected;
+		boolean significant;
+
+		BarItem(String t, double[] stats, String u, String slow, boolean sig) {
+			this.title= t;
+			this.value= stats[0]==0 ? 0 : -stats[0] * 100;
+			this.url= u;
+			this.slowdownExpected= slow;
+			this.significant= sig;
+		}
+	}
+
+	private String fTitle;
+	private List fItems;
+
+	BarGraph(String title) {
+		this.fTitle= title;
+		this.fItems= new ArrayList();
+	}
+
+	public void addItem(String name, double[] stats, String url, String slow, boolean significant) {
+		this.fItems.add(new BarItem(name, stats, url, slow, significant));
+	}
+
+	public int getHeight() {
+		int n= this.fItems.size();
+		int textHeight= 16;
+		int titleHeight= 0;
+		if (this.fTitle != null)
+			titleHeight= textHeight + GAP;
+		return MARGIN + titleHeight + n * (GAP + BARHEIGHT) + GAP + textHeight + MARGIN;
+	}
+
+	public void paint(Display display, int width, int height, GC gc) {
+
+		NumberFormat nf= NumberFormat.getInstance();
+
+		BarItem[] bars= (BarItem[]) this.fItems.toArray(new BarItem[this.fItems.size()]);
+
+		// draw white background
+		Color bg= display.getSystemColor(SWT.COLOR_WHITE);
+		gc.setBackground(bg);
+		gc.fillRectangle(0, 0, width, height);
+
+		// determine the widths of the bar and the label areas
+		int w;
+		if (NO_SCALE) {
+			// we use a fixed width
+			w= (int) (RATIO * width);
+		} else {
+			// we calculate the max width
+			int maxNameLength= 0;
+			for (int i= 0; i < bars.length; i++) {
+				Point es= gc.stringExtent(bars[i].title);
+				maxNameLength= Math.max(maxNameLength, es.x);
+			}
+			w= width - maxNameLength - TGAP - 2 * MARGIN;
+		}
+
+		Color fg= display.getSystemColor(SWT.COLOR_BLACK);
+
+		int vstart= 0; // start rows here
+		if (this.fTitle != null) {
+			vstart= gc.stringExtent(this.fTitle).y + GAP;
+			gc.drawString(this.fTitle, MARGIN, MARGIN, true); // draw title left aligned
+		}
+
+		int center= MARGIN + w / 2;
+		int w2= w / 2 - gc.stringExtent("-999.9").x - TGAP; // reserve space //$NON-NLS-1$
+
+		// determine maximum of values
+		double max= 0.0;
+		for (int i= 0; i < bars.length; i++)
+			max= Math.max(max, Math.abs(bars[i].value));
+
+		double d;
+		if (NO_SCALE) {
+			d= 25;
+			max= 125;
+		} else {
+			if (max > 400.0) {
+				d= 200;
+			} else if (max > 200.0) {
+				d= 100;
+			} else if (max > 100.0) {
+				d= 50;
+			} else if (max > 50) {
+				d= 25;
+			} else if (max > 25) {
+				d= 10;
+			} else if (max > 10) {
+				d= 5;
+			} else if (max > 5) {
+				d= 2.5;
+			} else {
+				d= 1.0;
+			}
+		}
+
+		// draw striped background
+		int y= MARGIN + vstart;
+		Color lightblue= new Color(display, 237, 243, 254);
+		gc.setBackground(lightblue);
+		for (int i= 0; i < bars.length; i++)
+			if (i % 2 == 0)
+				gc.fillRectangle(0, y + i * (BARHEIGHT + GAP), width, BARHEIGHT + GAP);
+
+		// draw grid
+		int yy= y + bars.length * (BARHEIGHT + GAP);
+		gc.drawLine(center, y, center, yy + TGAP);
+		Color grey= display.getSystemColor(SWT.COLOR_GRAY);
+		for (int i= 1; d * i < max; i++) {
+
+			double xx= d * i;
+			int x= (int) ((xx / max) * w2);
+
+			gc.setForeground(grey);
+			gc.drawLine(center - x, y, center - x, yy + TGAP);
+			gc.drawLine(center + x, y, center + x, yy + TGAP);
+
+			gc.setForeground(fg);
+
+			String s3= nf.format(-xx) + "%"; //$NON-NLS-1$
+			Point es3= gc.stringExtent(s3);
+			gc.drawString(s3, center - x - es3.x / 2, yy + TGAP, true);
+
+			String s4= nf.format(xx) + "%"; //$NON-NLS-1$
+			Point es4= gc.stringExtent(s4);
+			gc.drawString(s4, center + x - es4.x / 2, yy + TGAP, true);
+		}
+		gc.drawLine(0, yy, w, yy);
+
+		nf.setMaximumFractionDigits(1);
+
+		// link color
+		Color blue= display.getSystemColor(SWT.COLOR_BLUE);
+		// draw bars
+//		Color green= display.getSystemColor(SWT.COLOR_GREEN);
+//		Color red= display.getSystemColor(SWT.COLOR_RED);
+		Color green = new Color(display, 95, 191, 95);
+		Color red = new Color(display, 225, 50, 50);
+		Color gray= display.getSystemColor(SWT.COLOR_GRAY);
+		Color yellow= display.getSystemColor(SWT.COLOR_YELLOW);
+		Color white= display.getSystemColor(SWT.COLOR_WHITE);
+		for (int i= 0; i < bars.length; i++) {
+
+			BarItem bar= bars[i];
+			double delta = bar.value;
+			double orgDelta= delta;
+
+			boolean clamped= false;
+			if (NO_SCALE) {
+				if (delta > max) {
+					delta= max;
+					clamped= true;
+				} else if (delta < -max) {
+					delta= -max;
+					clamped= true;
+				}
+			}
+
+			int barLength= (int) (delta / max * w2);
+
+			if (delta < 0) {
+				if (bar.slowdownExpected != null) {
+					gc.setBackground(gray);
+				} else if (!bar.significant) {
+					gc.setBackground(yellow);
+				} else  {
+					gc.setBackground(red);
+				}
+			} else if (!bar.significant) {
+				gc.setBackground(yellow);
+			} else {
+				gc.setBackground(green);
+			}
+
+			if (barLength > 0) {
+				gc.fillRectangle(center, y + (GAP / 2), barLength, BARHEIGHT);
+				gc.drawRectangle(center, y + (GAP / 2), barLength, BARHEIGHT);
+			} else if (barLength < 0) {
+				gc.fillRectangle(center+barLength, y + (GAP / 2), -barLength, BARHEIGHT);
+				gc.drawRectangle(center+barLength, y + (GAP / 2), -barLength, BARHEIGHT);
+			}
+
+			if (clamped) {
+
+				int h2= (BARHEIGHT + GAP);
+				int x= center + barLength;
+				if (barLength > 0)
+					x-= FROM_END;
+				else
+					x+= FROM_END - GAP2 - SLANT;
+				int[] pts= new int[] { x, y + h2 - 1, x + SLANT, y + 1, x + SLANT + GAP2, y + 1, x + GAP2, y + h2 - 1};
+				if (i % 2 == 0)
+					gc.setBackground(lightblue);
+				else
+					gc.setBackground(white);
+				gc.fillPolygon(pts);
+				gc.drawLine(pts[0], pts[1], pts[2], pts[3]);
+				gc.drawLine(pts[4], pts[5], pts[6], pts[7]);
+			}
+
+			String label= nf.format(orgDelta);
+			Point labelExtent= gc.stringExtent(label);
+			int labelxpos= center + barLength;
+			int labelvpos= y + (BARHEIGHT + GAP - labelExtent.y) / 2;
+			if (orgDelta > 0.0) {
+				gc.drawString(label, labelxpos + TGAP, labelvpos, true);
+			} else {
+				gc.drawString(label, labelxpos - TGAP - labelExtent.x, labelvpos, true);
+			}
+
+			int x= MARGIN + w + TGAP;
+			String title= bar.title;
+			boolean hasURL= bar.url != null;
+			Color oldfg= gc.getForeground();
+			if (hasURL) {
+				gc.setForeground(blue);
+				Point e= gc.stringExtent(title);
+				gc.drawLine(x, labelvpos + e.y - 1, x + e.x, labelvpos + e.y - 1);
+			}
+			gc.drawString(title, x, labelvpos, true);
+			if (hasURL)
+				gc.setForeground(oldfg);
+
+			int y0= y;
+			y+= BARHEIGHT + GAP;
+
+			if (hasURL) {
+				if (this.fAreaBuffer == null)
+					this.fAreaBuffer= new StringBuffer();
+				this.fAreaBuffer.append("		echo '<area shape=\"RECT\" coords=\"0," + y0 + ',' + width + ',' + y + "\" href=\"" + bar.url + "\">';\n");
+			}
+		}
+
+		lightblue.dispose();
+		red.dispose();
+		green.dispose();
+	}
+
+	public String getAreas() {
+		if (this.fAreaBuffer != null) {
+			String s= this.fAreaBuffer.toString();
+			this.fAreaBuffer= null;
+			return s;
+		}
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrint.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrint.java
new file mode 100644
index 0000000..c9002a3
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrint.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+import org.eclipse.test.internal.performance.results.db.PerformanceResults;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+
+/**
+ * Class used to create scenario fingerprint.
+ */
+public class FingerPrint {
+
+private static final int GRAPH_WIDTH = 1000;
+
+	String component;
+	PrintStream stream;
+	File outputDir;
+
+public FingerPrint(String name, PrintStream ps, File outputDir) {
+	if (!name.startsWith("global")) this.component = name;
+	this.stream = ps;
+	this.outputDir = outputDir;
+}
+
+/**
+ * Create and save fingerprints as image and print their reference in the current stream.
+ *
+ * @param performanceResults The performance results used to print the fingerprints
+ */
+public void print(final PerformanceResults performanceResults) {
+	String buildName = performanceResults.getName();
+
+	// Compute fingerprint output file name prefix
+	int currentUnderscoreIndex = buildName.indexOf('_');
+	if  (currentUnderscoreIndex != -1){
+		buildName = buildName.substring(0, currentUnderscoreIndex);
+	}
+	StringBuffer buffer = new StringBuffer("FP_");
+	if (this.component != null) {
+		buffer.append(this.component);
+		buffer.append('_');
+	}
+	buffer.append(DB_Results.getDbBaselineRefVersion());
+	buffer.append('_');
+	buffer.append(buildName);
+	String filePrefix = buffer.toString();
+
+	// Print the legend
+	this.stream.print("The following fingerprints show results for the most representative tests of the ");
+	if (this.component == null) {
+		this.stream.print("current build.<br>\n");
+	} else {
+		this.stream.print(this.component);
+		this.stream.print(" component.<br>\n");
+	}
+	this.stream.print("<table border=\"0\">\n");
+	this.stream.print("<tr><td valign=\"top\">Select which kind of scale you want to use:</td>\n");
+	this.stream.print("<td valign=\"top\">\n");
+	this.stream.print("  <form>\n");
+	this.stream.print("    <select onChange=\"toggleFingerprints();\">\n");
+	this.stream.print("      <option>percentage</option>\n");
+	this.stream.print("      <option>time (linear)</option>\n");
+	this.stream.print("      <option>time (log)</option>\n");
+	this.stream.print("    </select>\n");
+	this.stream.print("  </form>\n");
+	this.stream.print("</td>\n");
+//	this.stream.print("<td valign=\"top\">\n");
+//	this.stream.print("<a href=\"help.html\"><img hspace=\"10\" border=\"0\" src=\""+Utils.LIGHT+"\" title=\"Some tips on fingerprints\"/></a>\n");
+//	this.stream.print("</td></tr></table>\n");
+	this.stream.print("</tr></table>\n");
+	this.stream.print("<img hspace=\"10\" border=\"0\" src=\""+Utils.LIGHT+"\"><a href=\""+Utils.HELP+"\">Help on fingerprints</a>\n");
+
+	// Print script to reset dropdown list selection
+	this.stream.print("<script type=\"text/javascript\">\n");
+	this.stream.print("	setFingerprintsType();\n");
+	this.stream.print("</script>\n");
+
+	// Create each fingerprint and save it
+	String[] configNames = performanceResults.getConfigNames(false/* not sorted*/);
+	String[] configBoxes = performanceResults.getConfigBoxes(false/* not sorted*/);
+	int length = configNames.length;
+	for (int c=0; c<length; c++) {
+		String configName  = configNames[c];
+		List scenarios = performanceResults.getComponentSummaryScenarios(this.component, configName);
+		if (scenarios == null) continue;
+
+		// Create BarGraph
+		// TODO use FingerPrintGraph instead
+		BarGraph barGraph = null;
+		List allResults = new ArrayList();
+		String defaultDimName = DB_Results.getDefaultDimension().getName();
+		for (int i=0, size=scenarios.size(); i<size; i++) {
+			ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
+			ConfigResults configResults = scenarioResults.getConfigResults(configName);
+			if (configResults == null || !configResults.isValid()) continue;
+			double[] results = configResults.getCurrentBuildDeltaInfo();
+			double percent = -results[0] * 100.0;
+			if (results != null && Math.abs(percent) < 200) {
+				String name = scenarioResults.getLabel() + " (" + defaultDimName + ")";
+				if (!configResults.getCurrentBuildName().equals(buildName)) {
+					continue; // the test didn't run on last build, skip it
+				}
+				if (!configResults.isBaselined()) {
+					name = "*" + name + " (" + configResults.getBaselineBuildName() + ")";
+				}
+				if (barGraph == null) {
+					barGraph = new BarGraph(null);
+				}
+				barGraph.addItem(name,
+				    results,
+				    configName + "/" + scenarioResults.getFileName() + ".html",
+				    configResults.getCurrentBuildResults().getComment(),
+				    (Utils.confidenceLevel(results) & Utils.ERR) == 0);
+
+				// add results
+				allResults.add(configResults);
+			}
+		}
+		if (barGraph == null) continue;
+
+		// Save image file
+		String fileName = filePrefix + '.' + configName ;
+		File outputFile = new File(this.outputDir, fileName+".gif");
+		save(barGraph, outputFile);
+
+		// Print image file reference in stream
+		String boxName = configBoxes[c];
+		if (outputFile.exists()) {
+			String areas = barGraph.getAreas();
+			if (areas == null) areas = "";
+			this.stream.print("<h4>");
+			this.stream.print(boxName);
+			this.stream.print("</h4>\n");
+			this.stream.print("<?php\n");
+			this.stream.print("	$type=$_SERVER['QUERY_STRING'];\n");
+			this.stream.print("	if ($type==\"\" || $type==\"fp_type=0\") {\n");
+			this.stream.print("		echo '<img src=\"");
+			this.stream.print(fileName);
+			this.stream.print(".gif\" usemap=\"#");
+			this.stream.print(fileName);
+			this.stream.print("\" name=\"");
+			this.stream.print(configName);
+			this.stream.print("\">';\n");
+			this.stream.print("		echo '<map name=\"");
+			this.stream.print(fileName);
+			this.stream.print("\">';\n");
+			this.stream.print(areas);
+			this.stream.print("		echo '</map>';\n");
+			this.stream.print("	}\n");
+		} else {
+			this.stream.print("<br><br>There is no fingerprint for ");
+			this.stream.print(boxName);
+			this.stream.print("<br><br>\n");
+		}
+
+		// Create, paint and print the time bars graph
+		FingerPrintGraph graph = new FingerPrintGraph(this.outputDir, fileName, GRAPH_WIDTH, allResults);
+		graph.paint(this.stream);
+		this.stream.print("?>\n");
+	}
+}
+
+/*
+ * Save the computed bar graph.
+ */
+private void save(BarGraph barGraph, File outputFile) {
+
+	// Create and paint image
+	Display display = Display.getDefault();
+	int height = barGraph.getHeight();
+	Image image = new Image(display, GRAPH_WIDTH, height);
+	GC gc = new GC(image);
+	barGraph.paint(display, GRAPH_WIDTH, height, gc);
+	gc.dispose();
+
+	saveImage(outputFile, image);
+}
+
+/**
+ * @param outputFile
+ * @param image
+ */
+private void saveImage(File outputFile, Image image) {
+	// Save image
+	ImageData data = Utils.downSample(image);
+	ImageLoader imageLoader = new ImageLoader();
+	imageLoader.data = new ImageData[] { data };
+
+	OutputStream out = null;
+	try {
+		out = new BufferedOutputStream(new FileOutputStream(outputFile));
+		imageLoader.save(out, SWT.IMAGE_GIF);
+	} catch (FileNotFoundException e) {
+		e.printStackTrace();
+	} finally {
+		image.dispose();
+		if (out != null) {
+			try {
+				out.close();
+			} catch (IOException e1) {
+				// silently ignored
+			}
+		}
+	}
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrintGraph.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrintGraph.java
new file mode 100644
index 0000000..7fec6d6
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrintGraph.java
@@ -0,0 +1,672 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+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.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Resource;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+/**
+ * Abstract class to build graph with bars
+ */
+public class FingerPrintGraph {
+
+	// Sizes
+	static final int MARGIN= 5; // margin on all four sides
+	static final int BAR_HEIGHT= 6; // height of bar
+	static final int GAP= 10; // gap between bars
+	static final int TGAP= 5; // gap between lines and labels
+	static final int LINE_HEIGHT = 2*BAR_HEIGHT + GAP;
+
+	// fraction of width reserved for bar graph
+	static final double RATIO= 0.6;
+
+	// Formatting constants
+	static final NumberFormat NUMBER_FORMAT;
+	static {
+		NUMBER_FORMAT = NumberFormat.getInstance();
+		NUMBER_FORMAT.setMaximumFractionDigits(1);
+	}
+
+	// Graphic constants
+	static final Display DEFAULT_DISPLAY = Display.getDefault();
+	static final Color BLACK= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLACK);
+	static final Color BLUE= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLUE);
+	static final Color GREEN= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GREEN);
+	static final Color RED = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_RED);
+	static final Color GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GRAY);
+	static final Color DARK_GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_DARK_GRAY);
+	static final Color YELLOW = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_YELLOW);
+	static final Color WHITE = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_WHITE);
+
+	// Bar graph kinds
+	static final int NO_TIME = 0; // i.e. percentage
+	static final int TIME_LINEAR = 1;
+	static final int TIME_LOG = 2;
+	static final int[] SUPPORTED_GRAPHS = {
+//		NO_TIME,
+		TIME_LINEAR,
+		TIME_LOG,
+	};
+
+	// Graphic fields
+	GC gc;
+	Image image;
+	int imageWidth;
+	int imageHeight;
+	int graphWidth;
+	int graphHeight;
+	Map resources = new HashMap();
+
+	// Data fields
+	int count = 0;
+	ConfigResults[] results = new ConfigResults[10];
+	BarGraphArea[] areas;
+
+	// Values
+	double maxValue = 0.0;
+	double minValue = Double.MAX_VALUE;
+
+	// File info
+	File outputDir;
+	String imageName;
+	private final String defaultDimName = DB_Results.getDefaultDimension().getName();
+
+	/*
+	 * Member class defining a bar graph area.
+	 * This area applies to a configuration results and is made of several zones.
+	 */
+	class BarGraphArea {
+		List zones;
+		private ConfigResults configResults;
+
+		/*
+		 * Member class defining a zone inside a bar graph area.
+		 * Typically made of a rectangle and an associated text used as tooltip.
+		 */
+		class AreaZone {
+			Rectangle zone;
+			String title;
+
+			AreaZone(Rectangle zone, String tooltip) {
+	            super();
+	            this.zone = zone;
+	            this.title = tooltip;
+            }
+
+			void print(String url, PrintStream stream) {
+				stream.print("		echo '<area shape=\"RECT\"");
+				if (this.title != null) {
+					stream.print(" title=\""+this.title+"\"");
+				}
+				stream.print("coords=\"");
+				stream.print(this.zone.x);
+				stream.print(',');
+				stream.print(this.zone.y);
+				stream.print(',');
+				stream.print(this.zone.x+this.zone.width);
+				stream.print(',');
+				stream.print(this.zone.y+this.zone.height);
+				stream.print('"');
+				if (url != null) {
+					stream.print(" href=\"");
+					stream.print(url);
+					stream.print('"');
+				}
+				stream.print(">';\n");
+			}
+		}
+
+		 BarGraphArea(ConfigResults results) {
+			this.configResults = results;
+			this.zones = new ArrayList();
+        }
+
+		void print(PrintStream stream) {
+			String url = this.configResults.getName() + "/" + ((ScenarioResults) this.configResults.getParent()).getFileName() + ".html";
+			int size = this.zones.size();
+			for (int i=0; i<size; i++) {
+				AreaZone zone = (AreaZone) this.zones.get(i);
+				zone.print(url, stream);
+			}
+		}
+
+		void addArea(Rectangle rec, String tooltip) {
+			AreaZone zone = new AreaZone(rec, tooltip);
+			this.zones.add(zone);
+		}
+
+	}
+
+
+FingerPrintGraph(File dir, String fileName, int width, List results) {
+    super();
+    this.imageWidth = width;
+    this.count = results.size();
+    this.results = new ConfigResults[this.count];
+    results.toArray(this.results);
+    this.outputDir = dir;
+    this.imageName = fileName;
+}
+
+/**
+ */
+void drawBars(int kind) {
+
+	// Get/Set graphical resources
+	Font italicFont = (Font) this.resources.get("italicFont");
+	if (italicFont == null) {
+		String fontDataName = this.gc.getFont().getFontData()[0].toString();
+		FontData fdItalic = new FontData(fontDataName);
+		fdItalic.setStyle(SWT.ITALIC);
+		italicFont = new Font(DEFAULT_DISPLAY, fdItalic);
+		this.resources.put("italicFont", italicFont);
+	}
+	Color blueref = (Color) this.resources.get("blueref");
+	if (blueref == null) {
+		blueref = new Color(DEFAULT_DISPLAY, 200, 200, 255);
+		this.resources.put("blueref", blueref);
+	}
+	Color lightyellow= (Color) this.resources.get("lightyellow");
+	if (lightyellow == null) {
+		lightyellow = new Color(DEFAULT_DISPLAY, 255, 255, 160);
+		this.resources.put("lightyellow", lightyellow);
+	}
+	Color darkyellow= (Color) this.resources.get("darkyellow");
+	if (darkyellow == null) {
+		darkyellow = new Color(DEFAULT_DISPLAY, 160, 160, 0);
+		this.resources.put("darkyellow", darkyellow);
+	}
+	Color okColor= (Color) this.resources.get("lightgreen");
+	if (okColor == null) {
+		okColor = new Color(DEFAULT_DISPLAY, 95, 191, 95);
+		this.resources.put("lightgreen", okColor);
+	}
+	Color failureColor = (Color) this.resources.get("lightred");
+	if (failureColor == null) {
+		failureColor = new Color(DEFAULT_DISPLAY, 220, 50, 50);
+		this.resources.put("lightred", failureColor);
+	}
+
+	// Build each scenario bar graph
+	this.areas = new BarGraphArea[this.count];
+	double max = kind == TIME_LOG ? Math.log(this.maxValue) : this.maxValue;
+	for (int i=0, y=MARGIN; i < this.count; i++, y+=LINE_HEIGHT) {
+
+		// get builds info
+		ConfigResults configResults = this.results[i];
+		this.areas[i] = new BarGraphArea(configResults);
+		BarGraphArea graphArea = this.areas[i];
+		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
+		double currentValue = currentBuildResults.getValue();
+		double currentError = currentBuildResults.getError();
+		double error = configResults.getError();
+		boolean singleTest = Double.isNaN(error);
+		boolean isSignificant = singleTest || error < Utils.STANDARD_ERROR_THRESHOLD;
+		boolean isCommented = currentBuildResults.getComment() != null;
+		BuildResults baselineBuildResults = configResults.getBaselineBuildResults();
+		double baselineValue = baselineBuildResults.getValue();
+		double baselineError = baselineBuildResults.getError();
+
+		// draw baseline build bar
+		Color whiteref = (Color) this.resources.get("whiteref");
+		if (whiteref == null) {
+			whiteref = new Color(DEFAULT_DISPLAY, 240, 240, 248);
+			this.resources.put("whiteref", whiteref);
+		}
+		this.gc.setBackground(whiteref);
+		double baselineGraphValue = kind == TIME_LOG ? Math.log(baselineValue) : baselineValue;
+		int baselineBarLength= (int) (baselineGraphValue / max * this.graphWidth);
+		int baselineErrorLength= (int) (baselineError / max * this.graphWidth / 2);
+		int labelxpos = MARGIN + baselineBarLength;
+		if (kind == TIME_LOG || baselineErrorLength <= 1) {
+			this.gc.fillRectangle(MARGIN, y + (GAP/2), baselineBarLength, BAR_HEIGHT);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2), baselineBarLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			graphArea.addArea(rec, "Time for baseline build "+baselineBuildResults.getName()+": "+Util.timeString((long)baselineValue));
+		} else {
+			int wr = baselineBarLength - baselineErrorLength;
+			Rectangle recValue = new Rectangle(MARGIN, y + (GAP/2), wr, BAR_HEIGHT);
+			this.gc.fillRectangle(recValue);
+			this.gc.setBackground(YELLOW);
+			Rectangle recError = new Rectangle(MARGIN+wr, y + (GAP/2), baselineErrorLength*2, BAR_HEIGHT);
+			this.gc.fillRectangle(recError);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2), baselineBarLength+baselineErrorLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			StringBuffer tooltip = new StringBuffer("Time for baseline build ");
+			tooltip.append(baselineBuildResults.getName());
+			tooltip.append(": ");
+			tooltip.append(Util.timeString((long)baselineValue));
+			tooltip.append(" [&#177;");
+			tooltip.append(Util.timeString((long)baselineError));
+			tooltip.append(']');
+			graphArea.addArea(rec, tooltip.toString());
+			labelxpos += baselineErrorLength;
+		}
+
+		// set current build bar color
+		if (baselineValue < currentValue) {
+			if (isCommented) {
+				this.gc.setBackground(GRAY);
+			} else  {
+				this.gc.setBackground(failureColor);
+			}
+		} else {
+			this.gc.setBackground(okColor);
+		}
+
+		// draw current build bar
+		double currentGraphValue = kind == TIME_LOG ? Math.log(currentValue) : currentValue;
+		int currentBarLength= (int) (currentGraphValue / max * this.graphWidth);
+		int currentErrorLength= (int) (currentError / max * this.graphWidth / 2);
+		if (kind == TIME_LOG || currentErrorLength <= 1) {
+			this.gc.fillRectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength, BAR_HEIGHT);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			String tooltip = "Time for current build "+currentBuildResults.getName()+": "+Util.timeString((long)currentValue);
+			if (isCommented) {
+				tooltip += ".		" + currentBuildResults.getComment();
+			}
+			graphArea.addArea(rec, tooltip);
+			if (labelxpos < (MARGIN+currentBarLength)) {
+				labelxpos = MARGIN + currentBarLength;
+			}
+		} else {
+			int wr = currentBarLength - currentErrorLength;
+			Rectangle recValue = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, wr, BAR_HEIGHT);
+			this.gc.fillRectangle(recValue);
+			this.gc.setBackground(YELLOW);
+			Rectangle recError = new Rectangle(MARGIN+wr, y + (GAP/2) + BAR_HEIGHT, currentErrorLength*2, BAR_HEIGHT);
+			this.gc.fillRectangle(recError);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength+currentErrorLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			StringBuffer tooltip = new StringBuffer("Time for current build ");
+			tooltip.append(currentBuildResults.getName());
+			tooltip.append(": ");
+			tooltip.append(Util.timeString((long)currentValue));
+			tooltip.append(" [&#177;");
+			tooltip.append(Util.timeString((long)currentError));
+			tooltip.append(']');
+			if (isCommented) {
+				tooltip.append(".		");
+				tooltip.append(currentBuildResults.getComment());
+			}
+			graphArea.addArea(rec, tooltip.toString());
+			if (labelxpos < (MARGIN+currentBarLength+currentErrorLength)) {
+				labelxpos = MARGIN + currentBarLength+currentErrorLength;
+			}
+		}
+
+		// set delta value style and color
+		boolean hasFailure = currentBuildResults.getFailure() != null;
+		if (hasFailure) {
+			if (isCommented) {
+				this.gc.setForeground(DARK_GRAY);
+			} else  {
+				this.gc.setForeground(RED);
+			}
+		} else {
+			this.gc.setForeground(BLACK);
+		}
+
+		// draw delta value
+		double delta = -configResults.getDelta();
+		String label = delta > 0 ? "+" : "";
+		label += NUMBER_FORMAT.format(delta*100) + "%";
+		Point labelExtent= this.gc.stringExtent(label);
+		int labelvpos= y + (LINE_HEIGHT - labelExtent.y) / 2;
+		this.gc.drawString(label, labelxpos+TGAP, labelvpos, true);
+		this.gc.setForeground(BLACK);
+		this.gc.setFont(null);
+		int titleStart = (int) (RATIO * this.imageWidth);
+		if (singleTest || !isSignificant) {
+			String deltaTooltip = null;
+			if (singleTest) {
+				deltaTooltip = "This test performed only one iteration; hence its reliability cannot be assessed";
+			} else if (!isSignificant) {
+				deltaTooltip = "This test has a bad reliability: error is "+NUMBER_FORMAT.format(error*100)+"% (> 3%)!";
+			}
+			Image warning = (Image) this.resources.get("warning");
+			int xi = labelxpos+TGAP+labelExtent.x;
+			this.gc.drawImage(warning, xi, labelvpos);
+			ImageData imageData = warning.getImageData();
+			// Set zones
+			// - first one is between end of bar and warning image beginning
+			Rectangle deltaZone = new Rectangle(labelxpos, labelvpos-2, xi-labelxpos, labelExtent.y+4);
+			graphArea.addArea(deltaZone, null);
+			// - second one is the warning image
+			Rectangle warningZone = new Rectangle(xi, labelvpos, imageData.width, imageData.height);
+			graphArea.addArea(warningZone, deltaTooltip);
+			// - last one is between end of the warning image and the scenario title beginning
+			int warningImageEnd = xi+imageData.width;
+			Rectangle emptyZone = new Rectangle(warningImageEnd, labelvpos, titleStart-warningImageEnd, imageData.height);
+			graphArea.addArea(emptyZone, deltaTooltip);
+		} else {
+			// No tooltip => delta zone is between end of bar and the scenario title beginning
+			Rectangle deltaZone = new Rectangle(labelxpos, labelvpos-2, titleStart-labelxpos, labelExtent.y+4);
+			graphArea.addArea(deltaZone, null);
+		}
+
+		// set title style
+		Color oldfg= this.gc.getForeground();
+		this.gc.setForeground(BLUE);
+
+		// draw scenario title
+		int x= titleStart;
+		ScenarioResults scenarioResults = (ScenarioResults) configResults.getParent();
+		String title = scenarioResults.getLabel() + " (" + this.defaultDimName + ")";
+		Point e= this.gc.stringExtent(title);
+		this.gc.drawLine(x, labelvpos + e.y - 1, x + e.x, labelvpos + e.y - 1);
+		this.gc.drawString(title, x, labelvpos, true);
+		this.gc.setForeground(oldfg);
+		this.gc.setFont(null);
+		Rectangle titleZone = new Rectangle(x, labelvpos, e.x, e.y);
+		graphArea.addArea(titleZone, null/*no tooltip*/);
+		if (!configResults.isBaselined()) {
+			Image warning = (Image) this.resources.get("warning");
+			this.gc.drawImage(warning, x+e.x, labelvpos);
+			ImageData imageData = warning.getImageData();
+			Rectangle warningZone = new Rectangle(x+e.x, labelvpos, imageData.width, imageData.height);
+			String titleTooltip =  "This test has no baseline result, hence use build "+configResults.getBaselineBuildName()+" for reference!";
+			graphArea.addArea(warningZone, titleTooltip);
+		}
+	}
+}
+
+void drawLinearScale() {
+
+	// Draw scale background
+	drawScaleBackground();
+
+	// Draw scale grid lines
+	int gridValue = 100;
+	int n = (int) (this.maxValue / gridValue);
+	while (n > 10) {
+		switch (gridValue) {
+			case 100:
+				gridValue = 200;
+				break;
+			case 200:
+				gridValue = 500;
+				break;
+			case 500:
+				gridValue = 1000;
+				break;
+			default:
+				gridValue += 1000;
+				break;
+		}
+		n = (int) (this.maxValue / gridValue);
+	}
+	int gridWidth = (int) (this.graphWidth * gridValue / this.maxValue);
+	int x = MARGIN;
+	long value = 0; // TODO use minValue instead
+	while (x < this.graphWidth) {
+
+		// draw line
+		this.gc.setForeground(GRAY);
+		if (x > 0) {
+			this.gc.setLineStyle(SWT.LINE_DOT);
+			this.gc.drawLine(x, MARGIN, x, this.graphHeight + TGAP);
+		}
+
+		// draw value
+		this.gc.setForeground(BLACK);
+		String val= Util.timeString(value);
+		Point point= this.gc.stringExtent(val);
+		this.gc.drawString(val, x - point.x / 2, this.graphHeight + TGAP, true);
+
+		// compute next grid position
+		x += gridWidth;
+		value += gridValue; // value is expressed in seconds
+	}
+	this.gc.setLineStyle(SWT.LINE_SOLID);
+	this.gc.drawLine(0, this.graphHeight, this.graphWidth, this.graphHeight);
+}
+
+void drawLogarithmScale() {
+
+	// Draw scale background
+	drawScaleBackground();
+
+	// Draw scale grid lines
+	double max = Math.log(this.maxValue);
+	int gridValue = 100;
+	int x = MARGIN;
+	long value = 0; // TODO use minValue instead
+	while (x < this.graphWidth) {
+
+		// draw line
+		this.gc.setForeground(GRAY);
+		if (x > MARGIN) {
+			this.gc.setLineStyle(SWT.LINE_DOT);
+			this.gc.drawLine(x, MARGIN, x, this.graphHeight + TGAP);
+		}
+
+		// draw value
+		this.gc.setForeground(BLACK);
+		String str = Util.timeString(value);
+		Point point= this.gc.stringExtent(str);
+		this.gc.drawString(str, x - point.x / 2, this.graphHeight + TGAP, true);
+
+		// compute next grid position
+		value += gridValue;
+		int v = (int) (value / 100);
+		int c = 1;
+		while (v > 10) {
+			v = v / 10;
+			c *= 10;
+		}
+		switch (v) {
+			case 3:
+				gridValue = 200*c;
+				break;
+			case 5:
+				gridValue = 500*c;
+				break;
+			case 10:
+				gridValue = 1000*c;
+				break;
+		}
+		x = MARGIN + (int) (this.graphWidth * Math.log(value) / max);
+	}
+	this.gc.setLineStyle(SWT.LINE_SOLID);
+	this.gc.drawLine(0, this.graphHeight, this.graphWidth, this.graphHeight);
+}
+
+/**
+ * Draw the scale depending on the bar time graph kind.
+ */
+void drawScale(int kind) {
+	switch (kind) {
+		case TIME_LINEAR:
+			drawLinearScale();
+			break;
+		case TIME_LOG:
+			drawLogarithmScale();
+			break;
+	}
+}
+
+private void drawScaleBackground() {
+
+	// Draw striped background
+	Color lightblue = (Color) this.resources.get("lightblue");
+	if (lightblue == null) {
+		lightblue = new Color(DEFAULT_DISPLAY, 237, 243, 254);
+		this.resources.put("lightblue", lightblue);
+	}
+	this.gc.setBackground(lightblue);
+	for (int i= 0; i<this.count; i++) {
+		if (i % 2 == 0) {
+	        this.gc.fillRectangle(0, MARGIN + i * LINE_HEIGHT, this.imageWidth, LINE_HEIGHT);
+        }
+	}
+
+	// Draw bottom vertical line
+	int yy= MARGIN + this.count * LINE_HEIGHT;
+	this.gc.drawLine(MARGIN, MARGIN, MARGIN, yy + TGAP);
+}
+
+String getImageName(int kind) {
+	switch (kind) {
+		case TIME_LINEAR:
+			return this.imageName+"_linear";
+		case TIME_LOG:
+			return this.imageName+"_log";
+	}
+	return this.imageName;
+}
+
+void paint(int kind) {
+
+	// Set image
+	this.graphHeight = MARGIN + this.count * LINE_HEIGHT;
+	this.imageHeight = this.graphHeight + GAP + 16 + MARGIN;
+	this.image = new Image(DEFAULT_DISPLAY, this.imageWidth, this.imageHeight);
+	this.gc = new GC(this.image);
+
+	// draw white background
+	this.gc.setBackground(WHITE);
+	this.gc.fillRectangle(0, 0, this.imageWidth, this.imageHeight);
+
+	// Set widths and heights
+	int width= (int) (RATIO * this.imageWidth); // width for results bar
+	this.graphWidth= width - this.gc.stringExtent("-999.9%").x - TGAP - MARGIN; // reserve space //$NON-NLS-1$
+
+	// Get warning image width
+	Image warning = (Image) this.resources.get("warning");
+	if (warning == null) {
+		warning = new Image(this.gc.getDevice(), new File(this.outputDir, Utils.WARNING_OBJ).toString());
+		this.resources.put("warning", warning);
+	}
+	this.graphWidth -= warning.getImageData().width;
+
+	// Set maximum of values
+	this.maxValue = 0.0;
+	this.minValue = Double.MAX_VALUE;
+	for (int i= 0; i<this.count; i++) {
+		BuildResults baselineBuildResults = this.results[i].getBaselineBuildResults();
+		double value = baselineBuildResults.getValue();
+		double error = baselineBuildResults.getError();
+		if (!Double.isNaN(error)) value += Math.abs(error);
+		if (value < 1000000 && value > this.maxValue) {
+			this.maxValue = value;
+		}
+		if (value < this.minValue) {
+			this.minValue = value;
+		}
+		BuildResults currentBuildResults = this.results[i].getCurrentBuildResults();
+		value = currentBuildResults.getValue();
+		error = currentBuildResults.getError();
+		if (!Double.isNaN(error)) value += Math.abs(error);
+		if (value < 1000000 && value > this.maxValue) {
+			this.maxValue = value;
+		}
+		if (value < this.minValue) {
+			this.minValue = value;
+		}
+	}
+	this.minValue = 0; // do not use minValue for now...
+
+	// Draw the scale
+	drawScale(kind);
+
+	// Draw the bars
+	drawBars(kind);
+
+	// Dispose
+	this.gc.dispose();
+}
+
+/**
+ * Create, paint and save all supported bar graphs and add the corresponding
+ * image and map references in the given stream.
+ *
+ * @param stream
+ */
+final public void paint(PrintStream stream) {
+
+	// Paint supported graphs
+	int length = SUPPORTED_GRAPHS.length;
+	for (int i=0; i<length; i++) {
+		int kind = SUPPORTED_GRAPHS[i];
+		paint(kind);
+		save(kind, stream);
+	}
+
+	// Dispose created graphic resources
+	Iterator iterator = this.resources.values().iterator();
+	while (iterator.hasNext()) {
+		Resource resource = (Resource) iterator.next();
+		resource.dispose();
+	}
+	this.resources.clear();
+}
+
+void print(int kind, PrintStream stream) {
+	String imgName = getImageName(kind);
+	stream.print("	if ($type==\"fp_type="+kind+"\") {\n");
+	stream.print("		echo '<img src=\"");
+	stream.print(imgName);
+	stream.print(".gif\" usemap=\"#");
+	stream.print(imgName);
+	stream.print("\" name=\"");
+	stream.print(imgName.substring(imgName.lastIndexOf('.')));
+	stream.print("\">';\n");
+	stream.print("		echo '<map name=\"");
+	stream.print(imgName);
+	stream.print("\">';\n");
+	if (this.areas != null) {
+		for (int i=0; i<this.count; i++) {
+			this.areas[i].print(stream);
+		}
+	}
+	stream.print("		echo '</map>';\n");
+	stream.print("	}\n");
+}
+
+void save(int kind, PrintStream stream) {
+	File file = new File(this.outputDir, getImageName(kind)+".gif");
+	Utils.saveImage(file, this.image);
+	if (file.exists()) {
+		print(kind, stream);
+	} else {
+		stream.print("<br><br>There is no fingerprint for ");
+		stream.print(this.imageName);
+		stream.print(" (kind=");
+		stream.print(kind);
+		stream.print(")<br><br>\n");
+	}
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/GenerateResults.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/GenerateResults.java
new file mode 100644
index 0000000..b225af7
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/GenerateResults.java
@@ -0,0 +1,1057 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.performance.ui;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.PerformanceResults;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.osgi.framework.Bundle;
+
+/**
+ * Main class to generate performance results of all scenarios matching a given pattern
+ * in one HTML page per component.
+ *
+ * @see #printUsage() method to see a detailed parameters usage
+ */
+public class GenerateResults {
+
+/**
+ * Prefix of baseline builds displayed in data graphs.
+ * This field is set using <b>-baseline.prefix</b> argument.
+ * <p>
+ * Example:
+ *		<pre>-baseline.prefix 3.2_200606291905</pre>
+ *
+ * @see #currentBuildPrefixes
+ */
+String baselinePrefix = null;
+
+/**
+ * Root directory where all files are generated.
+ * This field is set using <b>-output</b> argument.
+ * <p>
+ * Example:
+ * 	<pre>-output /releng/results/I20070615-1200/performance</pre>
+ */
+File outputDir;
+
+/**
+ * Root directory where all data are locally stored to speed-up generation.
+ * This field is set using <b>-dataDir</b> argument.
+ * <p>
+ * Example:
+ * 	<pre>-dataDir /tmp</pre>
+ */
+File dataDir;
+
+/**
+ * Arrays of 2 strings which contains config information: name and description.
+ * This field is set using <b>-config</b> and/or <b>-config.properties</b> arguments.
+ * <p>
+ * Example:
+ * <pre>
+ * 	-config eclipseperflnx3_R3.3,eclipseperfwin2_R3.3,eclipseperflnx2_R3.3,eclipseperfwin1_R3.3,eclipseperflnx1_R3.3
+ * 	-config.properties
+ * 		"eclipseperfwin1_R3.3,Win XP Sun 1.4.2_08 (2 GHz 512 MB);
+ * 		eclipseperflnx1_R3.3,RHEL 3.0 Sun 1.4.2_08 (2 GHz 512 MB);
+ * 		eclipseperfwin2_R3.3,Win XP Sun 1.4.2_08 (3 GHz 2 GB);
+ * 		eclipseperflnx2_R3.3,RHEL 3.0 Sun 1.4.2_08 (3 GHz 2 GB);
+ * 		eclipseperflnx3_R3.3,RHEL 4.0 Sun 1.4.2_08 (3 GHz 2.5 GB)"
+ * </pre>
+ * Note that:
+ * <ul>
+ * <li>if only <b>-config</b> is set, then configuration name is used for description </li>
+ * <li>if only <b>-config.properties</b> is set, then all configurations defined with this argument are generated
+ * <li>if both arguments are defined, then only configurations defined by <b>-config</b> argument are generated,
+ * 		<b>-config.properties</b> argument is only used to set the configuration description.</li>
+ * </ul>
+ */
+String[][] configDescriptors;
+
+/**
+ * Scenario pattern used to generate performance results.
+ * This field is set using <b>-scenarioPattern</b> argument.
+ * <p>
+ * Note that this pattern uses SQL conventions, not RegEx ones,
+ * which means that '%' is used to match several consecutive characters
+ * and '_' to match a single character.
+ * <p>
+ * Example:
+ * 	<pre>-scenario.pattern org.eclipse.%.test</pre>
+ */
+String scenarioPattern;
+
+/**
+ * A list of prefixes for builds displayed in data graphs.
+ * This field is set using <b>-currentPrefix</b> argument.
+ * <p>
+ * Example:
+ * 	<pre>-current.prefix N, I</pre>
+ *
+ * @see #baselinePrefix
+ */
+List currentBuildPrefixes;
+
+/**
+ * A list of prefixes of builds to highlight in displayed data graphs.
+ * This field is set using <b>-highlight</b> and/or <b>-highlight.latest</b> arguments.
+ * <p>
+ * Example:
+ * 	<pre>-higlight 3_2</pre>
+ */
+List pointsOfInterest;
+
+/**
+ * Tells whether only fingerprints has to be generated.
+ * This field is set to <code>true</code> if <b>-fingerprints</b> argument is specified.
+ * <p>
+ * Default is <code>false</code> which means that scenario data
+ * will also be generated.
+ *
+ * @see #genData
+ * @see #genAll
+ */
+boolean genFingerPrints = false;
+
+/**
+ * Tells whether only fingerprints has to be generated.
+ * This field is set to <code>true</code> if <b>-data</b> argument is specified.
+ * <p>
+ * Default is <code>false</code> which means that fingerprints
+ * will also be generated.
+ *
+ * @see #genFingerPrints
+ * @see #genAll
+ */
+boolean genData = false;
+
+/**
+ * Tells whether only fingerprints has to be generated.
+ * This field is set to <code>false</code>
+ * if <b>-fingerprints</b> or <b>-data</b> argument is specified.
+ * <p>
+ * Default is <code>true</code> which means that scenario data
+ * will also be generated.
+ *
+ * @see #genData
+ * @see #genFingerPrints
+ */
+boolean genAll = true;
+
+/**
+ * Tells whether information should be displayed in the console while generating.
+ * This field is set to <code>true</code> if <b>-print</b> argument is specified.
+ * <p>
+ * Default is <code>false</code> which means that nothing is print during the generation.
+ */
+PrintStream printStream = null;
+
+/**
+ * Tells what should be the failure percentage threshold.
+ * <p>
+ * Default is 10%.
+ */
+int failure_threshold = 10; // PerformanceTestPlugin.getDBLocation().startsWith("net://");
+
+PerformanceResults performanceResults;
+
+public GenerateResults() {
+}
+
+public GenerateResults(boolean fingerprints, File data) {
+	this.dataDir = data;
+	this.genFingerPrints = fingerprints;
+	this.genAll = !fingerprints;
+	this.printStream = System.out;
+}
+
+/*
+ * Parse the command arguments and create corresponding performance
+ * results object.
+ */
+private void parse(String[] args) {
+	StringBuffer buffer = new StringBuffer("Parameters used to generate performance results (");
+	buffer.append(new SimpleDateFormat().format(new Date(System.currentTimeMillis())));
+	buffer.append("):\n");
+	int i = 0;
+	int argsLength = args.length;
+	if (argsLength == 0) {
+		printUsage();
+	}
+
+	String currentBuildId = null;
+	String baseline = null;
+	String jvm = null;
+	this.configDescriptors = null;
+
+	while (i < argsLength) {
+		String arg = args[i];
+		if (!arg.startsWith("-")) {
+			i++;
+			continue;
+		}
+		if (argsLength == i + 1 && i != argsLength - 1) {
+			System.out.println("Missing value for last parameter");
+			printUsage();
+		}
+		if (arg.equals("-baseline")) {
+			baseline = args[i + 1];
+			if (baseline.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	-baseline = "+baseline+'\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-baseline.prefix")) {
+			this.baselinePrefix = args[i + 1];
+			if (this.baselinePrefix.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ").append(this.baselinePrefix).append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-current.prefix")) {
+			String idPrefixList = args[i + 1];
+			if (idPrefixList.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ");
+			String[] ids = idPrefixList.split(",");
+			this.currentBuildPrefixes = new ArrayList();
+			for (int j = 0; j < ids.length; j++) {
+				this.currentBuildPrefixes.add(ids[j]);
+				buffer.append(ids[j]);
+			}
+			buffer.append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-highlight") || arg.equals("-highlight.latest")) {
+			if (args[i + 1].startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ");
+			String[] ids = args[i + 1].split(",");
+			this.pointsOfInterest = new ArrayList();
+			for (int j = 0; j < ids.length; j++) {
+				this.pointsOfInterest.add(ids[j]);
+				buffer.append(ids[j]);
+			}
+			buffer.append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-current")) {
+			currentBuildId  = args[i + 1];
+			if (currentBuildId.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ").append(currentBuildId).append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-jvm")) {
+			jvm = args[i + 1];
+			if (jvm.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ").append(jvm).append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-output")) {
+			String dir = args[++i];
+			if (dir.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			this.outputDir = new File(dir);
+			if (!this.outputDir.exists() && !this.outputDir.mkdirs()) {
+				System.err.println("Cannot create directory "+dir+" to write results in!");
+				System.exit(2);
+			}
+			buffer.append("	").append(arg).append(" = ").append(dir).append('\n');
+			continue;
+		}
+		if (arg.equals("-dataDir")) {
+			String dir = args[++i];
+			if (dir.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			this.dataDir = new File(dir);
+			if (!this.dataDir.exists() && !this.dataDir.mkdirs()) {
+				System.err.println("Cannot create directory "+dir+" to save data locally!");
+				System.exit(2);
+			}
+			buffer.append("	").append(arg).append(" = ").append(dir).append('\n');
+			continue;
+		}
+		if (arg.equals("-config")) {
+			String configs = args[i + 1];
+			if (configs.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			String[] names = configs.split(",");
+			int length = names.length;
+			buffer.append("	").append(arg).append(" = ");
+			for (int j=0; j<length; j++) {
+				if (j>0) buffer.append(',');
+				buffer.append(names[j]);
+			}
+			if (this.configDescriptors == null) {
+				this.configDescriptors = new String[length][2];
+				for (int j=0; j<length; j++) {
+					this.configDescriptors[j][0] = names[j];
+					this.configDescriptors[j][1] = names[j];
+				}
+			} else {
+				int confLength = this.configDescriptors[0].length;
+				int newLength = confLength;
+				mainLoop: for (int j=0; j<confLength; j++) {
+					for (int k=0; k<length; k++) {
+						if (this.configDescriptors[j][0].equals(names[k])) {
+							continue mainLoop;
+						}
+					}
+					this.configDescriptors[j][0] = null;
+					this.configDescriptors[j][1] = null;
+					newLength--;
+				}
+				if (newLength < confLength) {
+					String[][] newDescriptors = new String[newLength][2];
+					for (int j=0, c=0; j<newLength; j++) {
+						if (this.configDescriptors[c] != null) {
+							newDescriptors[j][0] = this.configDescriptors[c][0];
+							newDescriptors[j][1] = this.configDescriptors[c][1];
+						} else {
+							c++;
+						}
+					}
+					this.configDescriptors = newDescriptors;
+				}
+			}
+			buffer.append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-config.properties")) {
+			String configProperties = args[i + 1];
+			if (configProperties.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			if (this.configDescriptors == null) {
+				System.out.println("Missing -config parameter");
+				printUsage();
+			}
+			int length = this.configDescriptors.length;
+			StringTokenizer tokenizer = new StringTokenizer(configProperties, ";");
+			buffer.append('\t').append(arg).append(" = '").append(configProperties).append("' splitted in ").append(length).append(" configs:");
+			while (tokenizer.hasMoreTokens()) {
+				String labelDescriptor = tokenizer.nextToken();
+				String[] elements = labelDescriptor.trim().split(",");
+				for (int j=0; j<length; j++) {
+					if (elements[0].equals(this.configDescriptors[j][0])) {
+						this.configDescriptors[j][1] = elements[1];
+						buffer.append("\n\t\t+ ");
+						buffer.append(elements[0]);
+						buffer.append(" -> ");
+						buffer.append(elements[1]);
+					}
+				}
+			}
+			buffer.append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-scenario.filter") || arg.equals("-scenario.pattern")) {
+			this.scenarioPattern= args[i + 1];
+			if (this.scenarioPattern.startsWith("-")) {
+				System.out.println("Missing value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ").append(this.scenarioPattern).append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-fingerprints")) {
+			this.genFingerPrints = true;
+			this.genAll = false;
+			buffer.append("	").append(arg).append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-data")) {
+			this.genData = true;
+			this.genAll = false;
+			buffer.append("	").append(arg).append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-print")) {
+			this.printStream = System.out; // default is to print to console
+			buffer.append("	").append(arg);
+			i++;
+			String printFile = i==argsLength ? null : args[i];
+			if (printFile==null ||printFile.startsWith("-")) {
+				buffer.append(" (to the console)").append('\n');
+			} else {
+				try {
+					this.printStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(printFile)));
+				}
+				catch (FileNotFoundException fnfe) {
+					// use the console if the output file cannot be created
+				}
+				buffer.append(" (to file: ").append(printFile).append(")\n");
+			}
+			continue;
+		}
+		if (arg.equals("-failure.threshold")) {
+			String value = args[i + 1];
+			try {
+				this.failure_threshold = Integer.parseInt(value);
+				if (this.failure_threshold < 0) {
+					System.out.println("Value for "+arg+" parameter must be positive.");
+					printUsage();
+				}
+			}
+			catch (NumberFormatException nfe) {
+				System.out.println("Invalid value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ").append(value).append('\n');
+			i++;
+			continue;
+		}
+		i++;
+	}
+	if (this.printStream != null) {
+		this.printStream.print(buffer.toString());
+	}
+
+	// Stop if some mandatory parameters are missing
+	if (this.outputDir == null || this.configDescriptors == null || jvm == null) {
+		printUsage();
+	}
+
+	// Set performance results
+	setPerformanceResults(currentBuildId, baseline);
+}
+
+/*
+ * Print component PHP file
+ */
+private void printComponent(/*PerformanceResults performanceResults, */String component) throws FileNotFoundException {
+	if (this.printStream != null) this.printStream.print(".");
+	File outputFile = new File(this.outputDir, component + ".php");
+	PrintStream stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
+
+	// Print header
+	boolean isGlobal = component.startsWith("global");
+	if (isGlobal) {
+		File globalFile = new File(this.outputDir, "global.php");
+		PrintStream gStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(globalFile)));
+		gStream.print(Utils.HTML_OPEN);
+		gStream.print("</head>\n");
+		gStream.print("<body>\n");
+		gStream.print("<?php\n");
+		gStream.print("	include(\"global_fp.php\");\n");
+		gStream.print("?>\n");
+		gStream.print("<table border=0 cellpadding=2 cellspacing=5 width=\"100%\">\n");
+		gStream.print("<tbody><tr> <td colspan=3 align=\"left\" bgcolor=\"#0080c0\" valign=\"top\"><b><font color=\"#ffffff\" face=\"Arial,Helvetica\">\n");
+		gStream.print("Detailed performance data grouped by scenario prefix</font></b></td></tr></tbody></table>\n");
+		gStream.print("<a href=\"org.eclipse.ant.php?\">org.eclipse.ant*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.compare.php?\">org.eclipse.compare*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.core.php?\">org.eclipse.core*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.core.php?\">org.eclipse.jdt.core*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.debug.php?\">org.eclipse.jdt.debug*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.text.php?\">org.eclipse.jdt.text*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.ui.php?\">org.eclipse.jdt.ui*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jface.php?\">org.eclipse.jface*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.osgi.php?\">org.eclipse.osgi*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.pde.api.tools.php?\">org.eclipse.pde.api.tools*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.pde.ui.php?\">org.eclipse.pde.ui*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.swt.php?\">org.eclipse.swt*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.team.php?\">org.eclipse.team*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.ua.php?\">org.eclipse.ua*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.ui.php?\">org.eclipse.ui*</a><br><p><br><br>\n");
+		gStream.print("</body>\n");
+		gStream.print(Utils.HTML_CLOSE);
+		gStream.close();
+	} else {
+		stream.print(Utils.HTML_OPEN);
+	}
+	stream.print("<link href=\""+Utils.TOOLTIP_STYLE+"\" rel=\"stylesheet\" type=\"text/css\">\n");
+	stream.print("<script src=\""+Utils.TOOLTIP_SCRIPT+"\"></script>\n");
+	stream.print("<script src=\""+Utils.FINGERPRINT_SCRIPT+"\"></script>\n");
+	stream.print(Utils.HTML_DEFAULT_CSS);
+
+	// Print title
+	stream.print("<body>");
+	printComponentTitle(/*performanceResults, */component, isGlobal, stream);
+
+	// print the html representation of fingerprint for each config
+	Display display = Display.getDefault();
+	if (this.genFingerPrints || this.genAll) {
+		final FingerPrint fingerprint = new FingerPrint(component, stream, this.outputDir);
+		display.syncExec(
+			new Runnable() {
+				public void run(){
+					try {
+						fingerprint.print(GenerateResults.this.performanceResults);
+					} catch (Exception ex) {
+						ex.printStackTrace();
+					}
+				}
+			}
+		);
+	}
+//	FingerPrint fingerprint = new FingerPrint(component, stream, this.outputDir);
+//	fingerprint.print(performanceResults);
+
+	// print scenario status table
+	if (!isGlobal) {
+		// print the component scenario status table beneath the fingerprint
+		final ScenarioStatusTable sst = new ScenarioStatusTable(component, stream);
+		display.syncExec(
+			new Runnable() {
+				public void run(){
+					try {
+						sst.print(GenerateResults.this.performanceResults);
+					} catch (Exception ex) {
+						ex.printStackTrace();
+					}
+				}
+			}
+		);
+//		ScenarioStatusTable sst = new ScenarioStatusTable(component, stream);
+//		sst.print(performanceResults);
+	}
+
+	stream.print(Utils.HTML_CLOSE);
+	stream.close();
+}
+
+private void printComponentTitle(/*PerformanceResults performanceResults, */String component, boolean isGlobal, PrintStream stream) {
+	String baselineName = this.performanceResults.getBaselineName();
+	String currentName = this.performanceResults.getName();
+
+	// Print title line
+	stream.print("<h3>Performance of ");
+	if (!isGlobal) {
+		stream.print(component);
+		stream.print(": ");
+	}
+	stream.print(currentName);
+	stream.print(" relative to ");
+	int index = baselineName.indexOf('_');
+	if (index > 0) {
+		stream.print(baselineName.substring(0, index));
+		stream.print(" (");
+		index = baselineName.lastIndexOf('_');
+		stream.print(baselineName.substring(index+1, baselineName.length()));
+		stream.print(')');
+	} else {
+		stream.print(baselineName);
+	}
+		stream.print("</h3>\n");
+
+	// Print reference to global results
+	if (!isGlobal) {
+		stream.print("<?php\n");
+		stream.print("	$type=$_SERVER['QUERY_STRING'];\n");
+		stream.print("	if ($type==\"\") {\n");
+		stream.print("		$type=\"fp_type=0\";\n");
+		stream.print("	}\n");
+		stream.print("	$href=\"<a href=\\\"performance.php?\";\n");
+		stream.print("	$href=$href . $type . \"\\\">Back to global results</a><br><br>\";\n");
+		stream.print("	echo $href;\n");
+		stream.print("?>\n");
+	}
+}
+
+/*
+ * Print summary of coefficient of variation for each scenario of the given pattern
+ * both for baseline and current builds.
+ */
+private void printSummary(/*PerformanceResults performanceResults*/) {
+	long start = System.currentTimeMillis();
+	if (this.printStream != null) this.printStream.print("Print scenarios variations summary...");
+	File outputFile = new File(this.outputDir, "cvsummary.html");
+	PrintStream stream = null;
+	try {
+		stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
+		printSummaryPresentation(stream);
+//		List scenarioNames = DB_Results.getScenarios();
+//		int size = scenarioNames.size();
+		String[] components = this.performanceResults.getComponents();
+		int componentsLength = components.length;
+		printSummaryColumnsTitle(stream/*, performanceResults*/);
+		String[] configs = this.performanceResults.getConfigNames(true/*sorted*/);
+		int configsLength = configs.length;
+		for (int i=0; i<componentsLength; i++) {
+			String componentName = components[i];
+			List scenarioNames = this.performanceResults.getComponentScenarios(componentName);
+			int size = scenarioNames.size();
+			for (int s=0; s<size; s++) {
+				String scenarioName = ((ScenarioResults) scenarioNames.get(s)).getName();
+				if (scenarioName == null) continue;
+				ScenarioResults scenarioResults = this.performanceResults.getScenarioResults(scenarioName);
+				if (scenarioResults != null) {
+					stream.print("<tr>\n");
+					for (int j=0; j<2; j++) {
+						for (int c=0; c<configsLength; c++) {
+							printSummaryScenarioLine(j, configs[c], scenarioResults, stream);
+						}
+					}
+					stream.print("<td>");
+					stream.print(scenarioName);
+					stream.print("</td></tr>\n");
+				}
+			}
+		}
+	} catch (Exception e) {
+		e.printStackTrace();
+	} finally {
+		stream.print("</table></body></html>\n");
+		stream.flush();
+		stream.close();
+	}
+	if (this.printStream != null) this.printStream.println("done in "+(System.currentTimeMillis()-start)+"ms");
+}
+
+/*
+ * Print summary presentation (eg. file start and text presenting the purpose of this file contents)..
+ */
+private void printSummaryPresentation(PrintStream stream) {
+	stream.print(Utils.HTML_OPEN);
+	stream.print(Utils.HTML_DEFAULT_CSS);
+	stream.print("<title>Summary of Elapsed Process Variation Coefficients</title></head>\n");
+	stream.print("<body><h3>Summary of Elapsed Process Variation Coefficients</h3>\n");
+	stream.print("<p> This table provides a bird's eye view of variability in elapsed process times\n");
+	stream.print("for baseline and current build stream performance scenarios.");
+	stream.print(" This summary is provided to facilitate the identification of scenarios that should be examined due to high variability.");
+	stream.print("The variability for each scenario is expressed as a <a href=\"http://en.wikipedia.org/wiki/Coefficient_of_variation\">coefficient\n");
+	stream.print("of variation</a> (CV). The CV is calculated by dividing the <b>standard deviation\n");
+	stream.print("of the elapse process time over builds</b> by the <b>average elapsed process\n");
+	stream.print("time over builds</b> and multiplying by 100.\n");
+	stream.print("</p><p>High CV values may be indicative of any of the following:<br></p>\n");
+	stream.print("<ol><li> an unstable performance test. </li>\n");
+	stream.print("<ul><li>may be evidenced by an erratic elapsed process line graph.<br><br></li></ul>\n");
+	stream.print("<li>performance regressions or improvements at some time in the course of builds.</li>\n");
+	stream.print("<ul><li>may be evidenced by plateaus in elapsed process line graphs.<br><br></li></ul>\n");
+	stream.print("<li>unstable testing hardware.\n");
+	stream.print("<ul><li>consistent higher CV values for one test configuration as compared to others across");
+	stream.print(" scenarios may be related to hardward problems.</li></ul></li></ol>\n");
+	stream.print("<p> Scenarios are listed in alphabetical order in the far right column. A scenario's\n");
+	stream.print("variation coefficients (CVs) are in columns to the left for baseline and current\n");
+	stream.print("build streams for each test configuration. Scenarios with CVs > 10% are highlighted\n");
+	stream.print("in yellow (10%<CV>&lt;CV<20%) and orange(CV>20%). </p>\n");
+	stream.print("<p> Each CV value links to the scenario's detailed results to allow viewers to\n");
+	stream.print("investigate the variability.</p>\n");
+}
+
+/*
+ * Print columns titles of the summary table.
+ */
+private void printSummaryColumnsTitle(PrintStream stream/*, PerformanceResults performanceResults*/) {
+	String[] configBoxes = this.performanceResults.getConfigBoxes(true/*sorted*/);
+	int length = configBoxes.length;
+	stream.print("<table border=\"1\"><tr><td colspan=\"");
+	stream.print(length);
+	stream.print("\"><b>Baseline CVs</b></td><td colspan=\"");
+	stream.print(length);
+	stream.print("\"><b>Current Build Stream CVs</b></td><td rowspan=\"2\"><b>Scenario Name</b></td></tr>\n");
+	stream.print("<tr>");
+	for (int n=0; n<2; n++) {
+		for (int c=0; c<length; c++) {
+			stream.print("<td>");
+			stream.print(configBoxes[c]);
+			stream.print("</td>");
+		}
+	}
+	stream.print("</tr>\n");
+}
+
+/*
+ * Print a scenario line in the summary table.
+ */
+private void printSummaryScenarioLine(int i, String config, ScenarioResults scenarioResults, PrintStream stream) {
+	ConfigResults configResults = scenarioResults.getConfigResults(config);
+	if (configResults == null || !configResults.isValid()) {
+		stream.print("<td>n/a</td>");
+		return;
+	}
+	String url = config + "/" + scenarioResults.getFileName()+".html";
+	double[] stats = null;
+	if (i==0) { // baseline results
+		List baselinePrefixes;
+		if (this.baselinePrefix == null) {
+			baselinePrefixes = Util.BASELINE_BUILD_PREFIXES;
+		} else {
+			baselinePrefixes = new ArrayList();
+			baselinePrefixes.add(this.baselinePrefix);
+		}
+		stats = configResults.getStatistics(baselinePrefixes);
+	} else {
+		stats = configResults.getStatistics(this.currentBuildPrefixes);
+	}
+	double variation = stats[3];
+	if (variation > 0.1 && variation < 0.2) {
+		stream.print("<td bgcolor=\"yellow\">");
+	} else if (variation >= 0.2) {
+		stream.print("<td bgcolor=\"FF9900\">");
+	} else {
+		stream.print("<td>");
+	}
+	stream.print("<a href=\"");
+	stream.print(url);
+	stream.print("\"/>");
+	stream.print(Util.PERCENTAGE_FORMAT.format(variation));
+	stream.print("</a></td>");
+}
+
+/*
+ * Print usage in case one of the argument of the line was incorrect.
+ * Note that calling this method ends the program run due to final System.exit()
+ */
+private void printUsage() {
+	System.out.println(
+		"Usage:\n\n" +
+		"-baseline\n" +
+		"	Build id against which to compare results.\n" +
+		"	Same as value specified for the \"build\" key in the eclipse.perf.config system property.\n\n" +
+
+		"[-baseline.prefix]\n" +
+		"	Optional.  Build id prefix used in baseline test builds and reruns.  Used to plot baseline historical data.\n" +
+		"	A common prefix used for the value of the \"build\" key in the eclipse.perf.config system property when rerunning baseline tests.\n\n" +
+
+		"-current\n" +
+		"	build id for which to generate results.  Compared to build id specified in -baseline parameter above.\n" +
+		"	Same as value specified for the \"build\" key in the eclipse.perf.config system property. \n\n" +
+
+		"[-current.prefix]\n" +
+		"	Optional.  Comma separated list of build id prefixes used in current build stream.\n" +
+		"	Used to plot current build stream historical data.  Defaults to \"N,I\".\n" +
+		"	Prefixes for values specified for the \"build\" key in the eclipse.perf.config system property. \n\n" +
+
+		"-jvm\n" +
+		"	Value specified in \"jvm\" key in eclipse.perf.config system property for current build.\n\n" +
+
+		"-config\n" +
+		"	Comma separated list of config names for which to generate results.\n" +
+		"	Same as values specified in \"config\" key in eclipse.perf.config system property.\n\n" +
+
+		"-output\n" +
+		"	Path to default output directory.\n\n" +
+
+		"[-config.properties]\n" +
+		"	Optional.  Used by scenario status table to provide the following:\n" +
+		"		alternate descriptions of config values to use in columns.\n" +
+		"	The value should be specified in the following format:\n" +
+		"	name1,description1;name2,description2;etc..\n\n" +
+
+		"[-highlight]\n" +
+		"	Optional.  Comma-separated list of build Id prefixes used to find most recent matching for each entry.\n" +
+		"	Result used to highlight points in line graphs.\n\n" +
+
+		"[-scenario.pattern]\n" +
+		"	Optional.  Scenario prefix pattern to query database.  If not specified,\n" +
+		"	default of % used in query.\n\n" +
+
+		"[-fingerprints]\n" +
+		"	Optional.  Use to generate fingerprints only.\n\n" +
+
+		"[-data]\n" +
+		"	Optional.  Generates table of scenario reference and current data with line graphs.\n\n" +
+
+		"[-print]\n" +
+		"	Optional.  Display output in the console while generating.\n" +
+
+		"[-nophp]\n" +
+		"	Optional.  Generate files for non-php server.\n" +
+
+		"[-failure.threshold]\n" +
+		"	Optional.  Set the failure percentage threshold (default is 10%).\n"
+	);
+
+	System.exit(1);
+}
+
+/**
+ * Run the generation from a list of arguments.
+ * Typically used to generate results from an application.
+ */
+public IStatus run(String[] args) {
+	parse(args);
+	return generate(null);
+}
+
+/**
+ * Run the generation.
+ */
+public IStatus run(PerformanceResults results, String buildName, String baseline, File output, final IProgressMonitor monitor) {
+	this.performanceResults = results;
+	this.outputDir = output;
+	setDefaults(buildName, baseline);
+	return generate(monitor);
+}
+
+/*
+ * Note that all necessary information to generate properly must be set before
+ * calling this method
+ */
+private IStatus generate(final IProgressMonitor monitor) {
+	long begin = System.currentTimeMillis();
+	int work = 1100;
+    int dataWork = 1000 * this.performanceResults.getConfigBoxes(false).length;
+	if (this.genAll || this.genData) {
+	    work += dataWork;
+    }
+	SubMonitor subMonitor = SubMonitor.convert(monitor, work);
+	subMonitor.setTaskName("Generate perf results for build "+this.performanceResults.getName());
+	try {
+
+		// Print whole scenarios summary
+		if (this.printStream != null) this.printStream.println();
+		printSummary(/*performanceResults*/);
+
+		// Copy images and scripts to output dir
+		Bundle bundle = UiPlugin.getDefault().getBundle();
+//		URL images = bundle.getEntry("images");
+//		if (images != null) {
+//			images = FileLocator.resolve(images);
+//			Utils.copyImages(new File(images.getPath()), this.outputDir);
+//		}
+		/* New way to get images
+		File content = FileLocator.getBundleFile(bundle);
+		BundleFile bundleFile;
+		if (content.isDirectory()) {
+			bundleFile = new DirBundleFile(content);
+			Utils.copyImages(bundleFile.getFile("images", true), this.outputDir);
+		} else {
+			bundleFile = new ZipBundleFile(content, null);
+			Enumeration imageFiles = bundle.findEntries("images", "*.gif", false);
+			while (imageFiles.hasMoreElements()) {
+				URL url = (URL) imageFiles.nextElement();
+				Utils.copyFile(bundleFile.getFile("images"+File.separator+, true), this.outputDir);
+			}
+		}
+		*/
+		// Copy bundle files
+		Utils.copyBundleFiles(bundle, "images", "*.gif", this.outputDir); // images
+		Utils.copyBundleFiles(bundle, "scripts", "*.js", this.outputDir); // java scripts
+		Utils.copyBundleFiles(bundle, "scripts", "*.css", this.outputDir); // styles
+		Utils.copyBundleFiles(bundle, "doc", "*.html", this.outputDir); // doc
+		Utils.copyBundleFiles(bundle, "doc/images", "*.png", this.outputDir); // images for doc
+		/*
+		URL doc = bundle.getEntry("doc");
+		if (doc != null) {
+			doc = FileLocator.resolve(doc);
+			File docDir = new File(doc.getPath());
+			FileFilter filter = new FileFilter() {
+				public boolean accept(File pathname) {
+		            return !pathname.getName().equals("CVS");
+	            }
+			};
+			File[] docFiles = docDir.listFiles(filter);
+			for (int i=0; i<docFiles.length; i++) {
+				File file = docFiles[i];
+				if (file.isDirectory()) {
+					File subdir = new File(this.outputDir, file.getName());
+					subdir.mkdir();
+					File[] subdirFiles = file.listFiles(filter);
+					for (int j=0; j<subdirFiles.length; j++) {
+						if (subdirFiles[i].isDirectory()) {
+							// expect only one sub-directory
+						} else {
+							Util.copyFile(subdirFiles[j], new File(subdir, subdirFiles[j].getName()));
+						}
+					}
+				} else {
+					Util.copyFile(file, new File(this.outputDir, file.getName()));
+				}
+			}
+		}
+		*/
+
+		// Print HTML pages and all linked files
+		if (this.printStream != null) {
+			this.printStream.println("Print performance results HTML pages:");
+			this.printStream.print("	- components main page");
+		}
+		long start = System.currentTimeMillis();
+//		subMonitor.setTaskName("Write fingerprints: 0%");
+//		subMonitor.subTask("Global...");
+		subMonitor.subTask("Write fingerprints: global (0%)...");
+		printComponent(/*performanceResults, */"global_fp");
+		subMonitor.worked(100);
+		if (subMonitor.isCanceled()) throw new OperationCanceledException();
+		String[] components = this.performanceResults.getComponents();
+		int length = components.length;
+		int step = 1000 / length;
+		int progress = 0;
+		for (int i=0; i<length; i++) {
+			int percentage = (int) ((progress / ((double) length)) * 100);
+//			subMonitor.setTaskName("Write fingerprints: "+percentage+"%");
+//			subMonitor.subTask(components[i]+"...");
+			subMonitor.subTask("Write fingerprints: "+components[i]+" ("+percentage+"%)...");
+			printComponent(/*performanceResults, */components[i]);
+			subMonitor.worked(step);
+			if (subMonitor.isCanceled()) throw new OperationCanceledException();
+			progress++;
+		}
+		if (this.printStream != null) {
+			String duration = Util.timeString(System.currentTimeMillis()-start);
+			this.printStream.println(" done in "+duration);
+		}
+
+		// Print the scenarios data
+		if (this.genData || this.genAll) {
+			start = System.currentTimeMillis();
+			if (this.printStream != null) this.printStream.println("	- all scenarios data:");
+			ScenarioData data = new ScenarioData(this.baselinePrefix, this.pointsOfInterest, this.currentBuildPrefixes, this.outputDir);
+			try {
+				data.print(this.performanceResults, this.printStream, subMonitor.newChild(dataWork));
+			} catch (Exception ex) {
+				ex.printStackTrace();
+			}
+			if (this.printStream != null) {
+				String duration = Util.timeString(System.currentTimeMillis()-start);
+				this.printStream.println("	=> done in "+duration);
+			}
+		}
+		if (this.printStream != null) {
+			long time = System.currentTimeMillis();
+			this.printStream.println("End of generation: "+new SimpleDateFormat("H:mm:ss").format(new Date(time)));
+			String duration = Util.timeString(System.currentTimeMillis()-begin);
+			this.printStream.println("=> done in "+duration);
+		}
+		return new Status(IStatus.OK, UiPlugin.getDefault().toString(), "Everything is OK");
+	}
+	catch (OperationCanceledException oce) {
+		return new Status(IStatus.OK, UiPlugin.getDefault().toString(), "Generation was cancelled!");
+	}
+	catch (Exception ex) {
+		return new Status(IStatus.ERROR, UiPlugin.getDefault().toString(), "An unexpected exception occurred!", ex);
+	}
+	finally {
+		if (this.printStream != null) {
+			this.printStream.flush();
+			if (this.printStream != System.out) {
+				this.printStream.close();
+			}
+		}
+	}
+}
+
+private void setDefaults(String buildName, String baseline) {
+	if (buildName == null) {
+		buildName = this.performanceResults.getName();
+	}
+
+	// Set default output dir if not set
+	if (this.outputDir.getPath().indexOf(buildName) == -1) {
+		File dir = new File(this.outputDir, buildName);
+		if (dir.exists() || dir.mkdir()) {
+			this.outputDir = dir;
+			if (this.printStream != null) {
+				this.printStream.println("	+ changed output dir to: "+dir.getPath());
+			}
+		}
+	}
+
+	// Verify that build is known
+	String[] builds = this.performanceResults.getAllBuildNames();
+	if (builds == null || builds.length == 0) {
+		System.err.println("Cannot connect to database to generate results build '"+buildName+"'");
+		System.exit(1);
+	}
+	if (Arrays.binarySearch(builds, buildName, Util.BUILD_DATE_COMPARATOR) < 0) {
+		throw new RuntimeException("No results in database for build '"+buildName+"'");
+	}
+	if (this.printStream != null) {
+		this.printStream.println();
+		this.printStream.flush();
+	}
+
+	// Init baseline prefix if not set
+	if (this.baselinePrefix == null) {
+		int index = baseline.lastIndexOf('_');
+		if (index > 0) {
+			this.baselinePrefix = baseline.substring(0, index);
+		} else {
+//			this.baselinePrefix = DB_Results.getDbBaselinePrefix();
+			this.baselinePrefix = baseline;
+		}
+	}
+
+	// Init current build prefixes if not set
+	if (this.currentBuildPrefixes == null) {
+		this.currentBuildPrefixes = new ArrayList();
+		if (buildName.charAt(0) == 'M') {
+			this.currentBuildPrefixes.add("M");
+		} else {
+			this.currentBuildPrefixes.add("N");
+		}
+		this.currentBuildPrefixes.add("I");
+	}
+}
+
+private void setPerformanceResults(String buildName, String baselineName) {
+
+	// Set performance results
+	this.performanceResults = new PerformanceResults(buildName, baselineName, this.baselinePrefix, this.printStream);
+
+	// Set defaults
+	setDefaults(buildName, this.performanceResults.getBaselineName());
+
+	// Read performance results data
+	this.performanceResults.readAll(buildName, this.configDescriptors, this.scenarioPattern, this.dataDir, this.failure_threshold, null);
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#stop()
+ */
+public void stop() {
+	// Do nothing
+}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/LineGraph.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/LineGraph.java
new file mode 100644
index 0000000..b7e7eeb
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/LineGraph.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.util.*;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.test.internal.performance.data.Dim;
+
+public class LineGraph {
+
+    StringBuffer fAreaBuffer;
+
+    private static class GraphItem {
+
+        String title;
+        String description=null;
+        double value;
+        Color color;
+        boolean displayDescription=false;
+
+        GraphItem(String title, String description,double value, Color color,boolean display) {
+        	this(title, description, value, color);
+        	this.displayDescription=display;
+        }
+
+        GraphItem(String title, String description, double value, Color color) {
+            this.title= title;
+            this.value= value;
+            this.color= color;
+            this.description= description;
+        }
+
+        Point getSize(GC g) {
+            Point e1= g.stringExtent(this.description);
+            Point e2= g.stringExtent(this.title);
+            return new Point(Math.max(e1.x, e2.x), e1.y+e2.y);
+        }
+    }
+
+    static final int PADDING= 15;
+
+
+    String fTitle;
+    List fItems;
+    Dim fDimension;
+
+
+    public LineGraph(String title, Dim dim) {
+        this.fTitle= title;
+        this.fItems= new ArrayList();
+        this.fDimension= dim;
+    }
+
+    public void paint(Image im) {
+
+        Rectangle bounds= im.getBounds();
+
+        GC g= new GC(im);
+
+        Point ee= g.stringExtent(this.fTitle);
+        int titleHeight= ee.y;
+
+        double maxItem= getMaxItem();
+        double minItem= getMinItem();
+
+        int max= (int) (Math.ceil(maxItem * (maxItem < 0 ? 0.9 : 1.2)));
+        int min= (int) (Math.floor(minItem * (minItem < 0 ? 1.2 : 0.9)));
+
+        String smin= this.fDimension.getDisplayValue(min);
+        Point emin= g.stringExtent(smin);
+
+        String smax= this.fDimension.getDisplayValue(max);
+        Point emax= g.stringExtent(smax);
+
+        int labelWidth= Math.max(emin.x, emax.x) + 2;
+
+        int top= PADDING;
+        int bottom= bounds.height - titleHeight - PADDING;
+        int left= PADDING + labelWidth;
+
+        GraphItem lastItem= (GraphItem) this.fItems.get(this.fItems.size()-1);
+        int right= bounds.width - lastItem.getSize(g).x - PADDING/2;
+
+        // draw the title
+        //g.drawString(fTitle, (bounds.width - titleWidth) / 2, titleHeight, true);
+
+        // draw the max and min values
+        g.drawString(smin, PADDING/2+labelWidth-emin.x, bottom-titleHeight, true);
+        g.drawString(smax, PADDING/2+labelWidth-emax.x, top, true);
+
+        // draw the vertical and horizontal lines
+        g.drawLine(left, top, left, bottom);
+        g.drawLine(left, bottom, right, bottom);
+
+        Color oldbg= g.getBackground();
+        Color oldfg= g.getForeground();
+
+        int n= this.fItems.size();
+        int xincrement= n > 1 ? (right-left) / (n-1) : 0;
+
+        int graduations= max - min;
+        if (graduations == 0)
+            graduations= 1;
+
+        int lastx= 0;
+        int lasty= 0;
+
+        int xposition= left;
+
+        for (int i= 0; i < n; i++) {
+            GraphItem thisItem= (GraphItem) this.fItems.get(i);
+
+            int yposition= (int) (bottom - (((thisItem.value-min) * (bottom-top)) / graduations));
+
+            if (i > 0)	// don't draw for first segment
+                g.drawLine(lastx, lasty, xposition, yposition);
+
+            g.setBackground(thisItem.color);
+            g.setForeground(thisItem.color);
+            g.fillOval(xposition-2, yposition-2, 5, 5);
+
+            if (this.fAreaBuffer == null)
+                this.fAreaBuffer= new StringBuffer();
+
+            this.fAreaBuffer.append("\r<area shape=\"CIRCLE\" coords=\""+(xposition-2)+','+(yposition-2)+','+5+" alt=\""+ thisItem.title+": "+thisItem.description+"\""+ " title=\""+ thisItem.title+": "+thisItem.description+"\">");
+
+
+            int shift;
+            if (i > 0 && yposition < lasty)
+                shift= 3;	 // below dot
+            else
+                shift= -(2*titleHeight+3);	// above dot
+            if (thisItem.displayDescription){
+            	g.drawString(thisItem.title, xposition+2, yposition+shift, true);
+            	g.drawString(thisItem.description, xposition+2, yposition+shift+titleHeight, true);
+            }
+            g.setBackground(oldbg);
+            g.setForeground(oldfg);
+
+            lastx= xposition;
+            lasty= yposition;
+            xposition+= xincrement;
+        }
+
+        g.dispose();
+    }
+
+    public void addItem(String name, String description, double value, Color col) {
+    	addItem(name, description, value, col,false);
+    }
+
+    public void addItem(String name, String description, double value, Color col, boolean display) {
+        this.fItems.add(new GraphItem(name, description, value, col,display));
+    }
+
+    public double getMaxItem() {
+        double maxItem= 0;
+        for (int i= 0; i < this.fItems.size(); i++) {
+            GraphItem graphItem= (GraphItem) this.fItems.get(i);
+            if (graphItem.value > maxItem)
+                maxItem= graphItem.value;
+        }
+        if (maxItem == 0)
+            return 1;
+        return maxItem;
+    }
+
+    public double getMinItem() {
+        double minItem= getMaxItem();
+        for (int i= 0; i < this.fItems.size(); i++) {
+            GraphItem graphItem= (GraphItem) this.fItems.get(i);
+            if (graphItem.value < minItem)
+                minItem= graphItem.value;
+        }
+        if (minItem == 0)
+            return -1;
+        return minItem;
+    }
+    public String getAreas() {
+        if (this.fAreaBuffer != null) {
+            String s= this.fAreaBuffer.toString();
+            this.fAreaBuffer= null;
+            return s;
+        }
+        return null;
+    }
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Main.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Main.java
new file mode 100644
index 0000000..dfe3312
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Main.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+
+/**
+ * Main class to generate performance results of all scenarios matching a given pattern
+ * in one HTML page per component.
+ *
+ * @see GenerateResults for the complete implementation
+ */
+public class Main implements IApplication {
+
+/**
+ * Generate the performance results for a specified build regarding to a specific reference.
+ * This action generates following HTML files:
+ * <ul>
+ * <li>A summary table to see the variations for all the concerned scenarios</li>
+ * <li>A global php file including global scenario fingerprints and links for all concerned components results php files</li>
+ * <li>A php file for each component including scenario fingerprints and status table with links to a scenario data file</li>
+ * <li>A data HTML file for each config of each scenario included in status table</li>
+ * </ul>
+ * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext)
+ */
+public Object start(IApplicationContext context) throws Exception {
+	DB_Results.DB_CONNECTION = true; // force DB connection while running the application
+	GenerateResults generation = new GenerateResults();
+	String[] args = (String[]) context.getArguments().get("application.args");
+	generation.run(args);
+	return null;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#stop()
+ */
+public void stop() {
+	// Do nothing
+}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Messages.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Messages.java
new file mode 100644
index 0000000..615d686
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Messages.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.performance.ui;
+
+import java.text.MessageFormat;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+
+	private static final String BUNDLE_NAME = "org.eclipse.test.performance.ui.messages";//$NON-NLS-1$
+
+	private Messages() {
+		// Do not instantiate
+	}
+
+	public static String standardError;
+
+	static {
+		NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+	}
+	
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @return the manipulated String
+	 */
+	public static String bind(String message) {
+		return bind(message, null);
+	}
+	
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param binding the object to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object binding) {
+		return bind(message, new Object[] {binding});
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param binding1 An object to be inserted into the message
+	 * @param binding2 A second object to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object binding1, Object binding2) {
+		return bind(message, new Object[] {binding1, binding2});
+	}
+
+	/**
+	 * Bind the given message's substitution locations with the given string values.
+	 * 
+	 * @param message the message to be manipulated
+	 * @param bindings An array of objects to be inserted into the message
+	 * @return the manipulated String
+	 */
+	public static String bind(String message, Object[] bindings) {
+		return MessageFormat.format(message, bindings);
+	}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/RawDataTable.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/RawDataTable.java
new file mode 100644
index 0000000..124bdfa
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/RawDataTable.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+/**
+ * Class used to fill details file of scenario builds data.
+ * @see ScenarioData
+ */
+public class RawDataTable {
+
+	private ConfigResults configResults;
+	private List buildPrefixes;
+	private PrintStream stream;
+	private Dim[] dimensions = DB_Results.getResultsDimensions();
+	private boolean debug = false;
+
+private RawDataTable(ConfigResults results, PrintStream ps) {
+	this.configResults = results;
+	this.stream = ps;
+}
+
+public RawDataTable(ConfigResults results, List prefixes, PrintStream ps) {
+	this(results, ps);
+	this.buildPrefixes = prefixes;
+}
+public RawDataTable(ConfigResults results, String baselinePrefix, PrintStream ps) {
+	this(results, ps);
+	this.buildPrefixes = new ArrayList();
+	this.buildPrefixes.add(baselinePrefix);
+}
+
+/**
+ * Print all build data to the current stream.
+ */
+public void print(){
+	this.stream.print("<table border=\"1\">");
+	printSummary();
+	printDetails();
+	this.stream.print("</table>\n");
+}
+
+/*
+ * Print table columns headers.
+ */
+private void printColumnHeaders() {
+	StringBuffer buffer = new StringBuffer();
+	int length = this.dimensions.length;
+	for (int i=0; i<length; i++) {
+		buffer.append("<td><b>");
+		buffer.append(this.dimensions[i].getName());
+		buffer.append("</b></td>");
+	}
+	this.stream.print(buffer.toString());
+}
+
+/*
+ * Print all build results in the table.
+ */
+private void printDetails() {
+	this.stream.print("<tr><td><b>Build ID</b></td>");
+	printColumnHeaders();
+	this.stream.print("</tr>\n");
+
+	List builds = this.configResults.getBuildsMatchingPrefixes(this.buildPrefixes);
+	Collections.reverse(builds);
+	int size = builds.size();
+	for (int i=0; i<size; i++) {
+		BuildResults buildResults = (BuildResults) builds.get(i);
+		this.stream.print("<tr><td>");
+		this.stream.print(buildResults.getName());
+		this.stream.print("</td>");
+		int dimLength = this.dimensions.length;
+		for (int d=0; d<dimLength; d++) {
+			Dim dimension = this.dimensions[d];
+			int dim_id = dimension.getId();
+			double value = buildResults.getValue(dim_id);
+			printDimTitle(dimension.getName());
+			String displayValue = dimension.getDisplayValue(value);
+			this.stream.print(displayValue);
+			if (this.debug) System.out.print("\t"+displayValue);
+			this.stream.print("</td>");
+		}
+		if (this.debug) System.out.println();
+		this.stream.print("</tr>\n");
+	}
+	if (this.debug) System.out.println("\n");
+}
+
+/*
+ * Print summary on top of the table.
+ */
+private void printSummary() {
+	this.stream.print("<tr><td><b>Stats</b></td>");
+	printColumnHeaders();
+	this.stream.print("</tr>\n");
+
+	int length = this.dimensions.length;
+	double[][] dimStats = new double[length][];
+	for (int i=0; i<length; i++) {
+		dimStats[i] = this.configResults.getStatistics(this.buildPrefixes, this.dimensions[i].getId());
+	}
+
+	this.stream.print("<tr><td>#BUILDS SAMPLED</td>");
+	for (int i=0; i<length; i++) {
+		String dimName = this.dimensions[i].getName();
+		printDimTitle(dimName);
+		this.stream.print((int)dimStats[i][0]);
+		this.stream.print("</td>");
+	}
+	this.stream.print("</tr>\n");
+	this.stream.print("<tr><td>MEAN</td>");
+	printRowDoubles(dimStats, 1);
+	this.stream.print("</tr>\n");
+	this.stream.print("<tr><td>STD DEV</td>");
+	printRowDoubles(dimStats, 2);
+	this.stream.print("</tr>\n");
+	this.stream.print("<tr><td>COEF. VAR</td>");
+	printRowDoubles(dimStats, 3);
+	this.stream.print("</tr>\n");
+
+	// Blank line
+	this.stream.print("<tr>");
+	for (int i=0; i<length+1;	i++){
+		this.stream.print("<td>&nbsp;</td>");
+	}
+	this.stream.print("</tr>\n");
+}
+
+/*
+ * Print values in table row.
+ */
+private void printRowDoubles(double[][] stats, int idx) {
+	int length = this.dimensions.length;
+	for (int i=0; i<length; i++) {
+		double value = stats[i][idx];
+		String dimName = this.dimensions[i].getName();
+		if (idx == 3) {
+			if (value > 0.1 && value < 0.2) {
+				this.stream.print("<td bgcolor=\"yellow\" title=\"");
+			} else if (value >= 0.2) {
+				this.stream.print("<td bgcolor=\"FF9900\" title=\"");
+			} else {
+				this.stream.print("<td title=\"");
+			}
+			this.stream.print(dimName);
+			this.stream.print("\">");
+			this.stream.print(Util.PERCENTAGE_FORMAT.format(value));
+			this.stream.print("</td>");
+		} else {
+			printDimTitle(dimName);
+			this.stream.print(this.dimensions[i].getDisplayValue(value));
+			this.stream.print("</td>");
+		}
+	}
+}
+
+/*
+ * Print dim title inside value reference.
+ * TODO (frederic) See if this title is really necessary
+ */
+private void printDimTitle(String dimName) {
+    this.stream.print("<td title=\"");
+    this.stream.print(dimName);
+    this.stream.print("\">");
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java
new file mode 100644
index 0000000..3cb5a78
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java
@@ -0,0 +1,472 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.AssertionFailedError;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.test.internal.performance.data.Dim;
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.ComponentResults;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+import org.eclipse.test.internal.performance.results.db.PerformanceResults;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+/**
+ * Class used to print scenario all builds data.
+ */
+public class ScenarioData {
+	private String baselinePrefix = null;
+	private List pointsOfInterest;
+	private List buildIDStreamPatterns;
+	private File rootDir;
+	private static final int GRAPH_WIDTH = 600;
+	private static final int GRAPH_HEIGHT = 200;
+	private Dim[] dimensions = DB_Results.getResultsDimensions();
+
+/**
+ * Summary of results for a scenario for a given build compared to a
+ * reference.
+ *
+ * @param baselinePrefix The prefix of the baseline build names
+ * @param pointsOfInterest A list of buildId's to highlight on line graphs
+ * @param buildIDPatterns
+ * @param outputDir The directory root where the files are generated
+ *
+*/
+public ScenarioData(String baselinePrefix, List pointsOfInterest, List buildIDPatterns, File outputDir) {
+	this.baselinePrefix = baselinePrefix;
+	this.pointsOfInterest = pointsOfInterest;
+	this.buildIDStreamPatterns = buildIDPatterns;
+	this.rootDir = outputDir;
+}
+
+/*
+ * Create a file handle verifying that its name does not go over
+ * the maximum authorized length.
+ */
+private File createFile(File outputDir, String subdir, String name, String extension) {
+	File dir = outputDir;
+	if (subdir != null) {
+		dir = new File(outputDir, subdir);
+		if (!dir.exists()) {
+			dir.mkdir();
+		}
+	}
+	return new File(dir, name + '.' + extension);
+}
+
+/*
+ * Returns a LineGraph object representing measurements for a scenario over builds.
+ */
+private TimeLineGraph getLineGraph(ScenarioResults scenarioResults, ConfigResults configResults, Dim dim, List highlightedPoints, List currentBuildIdPrefixes) {
+	Display display = Display.getDefault();
+
+	Color black = display.getSystemColor(SWT.COLOR_BLACK);
+	Color yellow = display.getSystemColor(SWT.COLOR_DARK_YELLOW);
+	Color magenta = display.getSystemColor(SWT.COLOR_MAGENTA);
+
+	String scenarioName = scenarioResults.getName();
+	TimeLineGraph graph = new TimeLineGraph(scenarioName + ": " + dim.getName(), dim);
+	String baseline = configResults.getBaselineBuildName();
+	String current = configResults.getCurrentBuildName();
+
+	final String defaultBaselinePrefix = DB_Results.getDbBaselinePrefix();
+	Iterator builds = configResults.getResults();
+	List lastSevenNightlyBuilds = configResults.lastNightlyBuildNames(7);
+	buildLoop: while (builds.hasNext()) {
+		BuildResults buildResults = (BuildResults) builds.next();
+		String buildID = buildResults.getName();
+		int underscoreIndex = buildID.indexOf('_');
+		String label = (underscoreIndex != -1 && buildID.equals(current)) ? buildID.substring(0, underscoreIndex) : buildID;
+		if (buildID.startsWith(defaultBaselinePrefix)) {
+			label = defaultBaselinePrefix+buildID.charAt(defaultBaselinePrefix.length())+buildID.substring(underscoreIndex);
+		}
+
+		double value = buildResults.getValue(dim.getId());
+
+		if (buildID.equals(current)) {
+			Color color = black;
+			if (buildID.startsWith("N"))
+				color = yellow;
+
+			graph.addItem("main", label, dim.getDisplayValue(value), value, color, true, Utils.getDateFromBuildID(buildID), true);
+			continue;
+		}
+		if (highlightedPoints.contains(buildID)) {
+			graph.addItem("main", label, dim.getDisplayValue(value), value, black, false, Utils.getDateFromBuildID(buildID, false), true);
+			continue;
+		}
+		if (buildID.charAt(0) == 'N') {
+			if (lastSevenNightlyBuilds.contains(buildID)) {
+				graph.addItem("main", buildID, dim.getDisplayValue(value), value, yellow, false, Utils.getDateFromBuildID(buildID), false);
+			}
+			continue;
+		}
+		for (int i=0;i<currentBuildIdPrefixes.size();i++){
+			if (buildID.startsWith(currentBuildIdPrefixes.get(i).toString())) {
+				graph.addItem("main", buildID, dim.getDisplayValue(value), value, black, false, Utils.getDateFromBuildID(buildID), false);
+				continue buildLoop;
+			}
+		}
+		if (buildID.equals(baseline)) {
+			boolean drawBaseline = (this.baselinePrefix != null) ? false : true;
+			graph.addItem("reference", label, dim.getDisplayValue(value), value, magenta, true, Utils.getDateFromBuildID(buildID, true), true, drawBaseline);
+			continue;
+		}
+		if (this.baselinePrefix != null) {
+			if (buildID.startsWith(this.baselinePrefix) && !buildID.equals(baseline) && Utils.getDateFromBuildID(buildID, true) <= Utils.getDateFromBuildID(baseline, true)) {
+				graph.addItem("reference", label, dim.getDisplayValue(value), value, magenta, false, Utils.getDateFromBuildID(buildID, true), false);
+				continue;
+			}
+		}
+	}
+	return graph;
+}
+
+/**
+ * Print the scenario all builds data from the given performance results.
+ *
+ * @param performanceResults The needed information to generate scenario data
+ */
+public void print(PerformanceResults performanceResults, PrintStream printStream, final SubMonitor subMonitor) {
+	String[] configNames = performanceResults.getConfigNames(false/*not sorted*/);
+	String[] configBoxes = performanceResults.getConfigBoxes(false/*not sorted*/);
+	int length = configNames.length;
+	int size = performanceResults.size();
+	double total = length * size;
+	subMonitor.setWorkRemaining(length*size);
+	int progress = 0;
+	for (int i=0; i<length; i++) {
+		final String configName = configNames[i];
+		final String configBox = configBoxes[i];
+
+		// Manage monitor
+//		subMonitor.setTaskName("Generating data for "+configBox);
+		final String subTaskPrefix = "Generating data: "+configBox;
+		subMonitor.subTask(subTaskPrefix);
+		if (subMonitor.isCanceled()) throw new OperationCanceledException();
+
+		long start = System.currentTimeMillis();
+		if (printStream != null) printStream.print("		+ "+configName);
+		final File outputDir = new File(this.rootDir, configName);
+		outputDir.mkdir();
+		Iterator components = performanceResults.getResults();
+		while (components.hasNext()) {
+			if (printStream != null) printStream.print(".");
+			final ComponentResults componentResults = (ComponentResults) components.next();
+
+			// Manage monitor
+			int percentage = (int) ((progress++ / total) * 100);
+//			subMonitor.setTaskName("Generating data for "+configBox+": "+percentage+"%");
+//			subMonitor.subTask("Component "+componentResults.getName()+"...");
+			subMonitor.subTask(subTaskPrefix + " (" +componentResults.getName()+ ") "+ percentage + "%...");
+
+			Display display = Display.getDefault();
+		     display.syncExec(
+				new Runnable() {
+					public void run(){
+						printSummary(configName, configBox, componentResults, outputDir, subMonitor);
+					}
+				}
+			);
+//			printSummary(configName, configBox, componentResults, outputDir, monitor);
+			printDetails(configName, configBoxes[i], componentResults, outputDir);
+
+			subMonitor.worked(1);
+			if (subMonitor.isCanceled()) throw new OperationCanceledException();
+		}
+		if (printStream != null) {
+			String duration = Util.timeString(System.currentTimeMillis()-start);
+			printStream.println(" done in "+duration);
+		}
+	}
+}
+
+/*
+ * Print the summary file of the builds data.
+ */
+void printSummary(String configName, String configBox, ComponentResults componentResults, File outputDir, SubMonitor subMonitor) {
+	Iterator scenarios = componentResults.getResults();
+	while (scenarios.hasNext()) {
+		List highlightedPoints = new ArrayList();
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.next();
+		ConfigResults configResults = scenarioResults.getConfigResults(configName);
+		if (configResults == null || !configResults.isValid()) continue;
+
+		// get latest points of interest matching
+		if (this.pointsOfInterest != null) {
+			Iterator buildPrefixes = this.pointsOfInterest.iterator();
+			while (buildPrefixes.hasNext()) {
+				String buildPrefix = (String) buildPrefixes.next();
+				List builds = configResults.getBuilds(buildPrefix);
+				if (buildPrefix.indexOf('*') <0 && buildPrefix.indexOf('?') < 0) {
+					if (builds.size() > 0) {
+						highlightedPoints.add(builds.get(builds.size()-1));
+					}
+				} else {
+					highlightedPoints.addAll(builds);
+				}
+			}
+		}
+
+		String scenarioFileName = scenarioResults.getFileName();
+		File outputFile = new File(outputDir, scenarioFileName+".html");
+		PrintStream stream = null;
+		try {
+			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
+		} catch (FileNotFoundException e) {
+			System.err.println("can't create output file" + outputFile); //$NON-NLS-1$
+		}
+		if (stream == null) {
+			stream = System.out;
+		}
+		stream.print(Utils.HTML_OPEN);
+		stream.print(Utils.HTML_DEFAULT_CSS);
+
+		stream.print("<title>" + scenarioResults.getName() + "(" + configBox + ")" + "</title></head>\n"); //$NON-NLS-1$
+		stream.print("<h4>Scenario: " + scenarioResults.getName() + " (" + configBox + ")</h4><br>\n"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		String failureMessage = Utils.failureMessage(configResults.getCurrentBuildDeltaInfo(), true);
+ 		if (failureMessage != null){
+   			stream.print("<table><tr><td><b>"+failureMessage+"</td></tr></table>\n");
+ 		}
+
+ 		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
+ 		String comment = currentBuildResults.getComment();
+		if (comment != null) {
+			stream.print("<p><b>Note:</b><br>\n");
+			stream.print(comment + "</p>\n");
+		}
+
+		// Print link to raw data.
+		String rawDataFile = "raw/" + scenarioFileName+".html";
+		stream.print("<br><br><b><a href=\""+rawDataFile+"\">Raw data and Stats</a></b><br><br>\n");
+		stream.print("<b>Click measurement name to view line graph of measured values over builds.</b><br><br>\n");
+		if (subMonitor.isCanceled()) throw new OperationCanceledException();
+
+		try {
+			// Print build result table
+			stream.print("<table border=\"1\">\n"); //$NON-NLS-1$
+			stream.print("<tr><td><b>Build Id</b></td>"); //$NON-NLS-1$
+			int dimLength = this.dimensions.length;
+			for (int d=0; d<dimLength; d++) {
+				Dim dim = this.dimensions[d];
+				stream.print("<td><a href=\"#" + dim.getLabel() + "\"><b>" + dim.getName() + "</b></a></td>");
+			}
+			stream.print("</tr>\n");
+
+			// Write build lines
+			printTableLine(stream, currentBuildResults);
+			printTableLine(stream, configResults.getBaselineBuildResults());
+
+			// Write difference line
+			printDifferenceLine(stream, configResults);
+
+			// End of table
+			stream.print("</table>\n");
+			stream.print("*Delta values in red and green indicate degradation > 10% and improvement > 10%,respectively.<br><br>\n");
+			stream.print("<br><hr>\n\n");
+
+			// print text legend.
+			stream.print("Black and yellow points plot values measured in integration and last seven nightly builds.<br>\n" + "Magenta points plot the repeated baseline measurement over time.<br>\n"
+					+ "Boxed points represent previous releases, milestone builds, current reference and current build.<br><br>\n"
+					+ "Hover over any point for build id and value.\n");
+
+			// print image maps of historical
+			for (int d=0; d<dimLength; d++) {
+				Dim dim = this.dimensions[d];
+				TimeLineGraph lineGraph = getLineGraph(scenarioResults, configResults, dim, highlightedPoints, this.buildIDStreamPatterns);
+				if (subMonitor.isCanceled()) throw new OperationCanceledException();
+
+				String dimShortName = dim.getLabel();
+				String imgFileName = scenarioFileName + "_" + dimShortName;
+				File imgFile = createFile(outputDir, "graphs", imgFileName, "gif");
+				saveGraph(lineGraph, imgFile);
+				stream.print("<br><a name=\"" + dimShortName + "\"></a>\n");
+				stream.print("<br><b>" + dim.getName() + "</b><br>\n");
+				stream.print(dim.getDescription() + "<br><br>\n");
+				stream.print("<img src=\"graphs/");
+				stream.print(imgFile.getName());
+				stream.print("\" usemap=\"#" + lineGraph.fTitle + "\">");
+				stream.print("<map name=\"" + lineGraph.fTitle + "\">");
+				stream.print(lineGraph.getAreas());
+				stream.print("</map>\n");
+				if (subMonitor.isCanceled()) throw new OperationCanceledException();
+			}
+			stream.print("<br><br></body>\n");
+			stream.print(Utils.HTML_CLOSE);
+			if (stream != System.out)
+				stream.close();
+
+		} catch (AssertionFailedError e) {
+			e.printStackTrace();
+			continue;
+		}
+	}
+}
+
+/*
+ * Print the data for a build results.
+ */
+private void printTableLine(PrintStream stream, BuildResults buildResults) {
+	stream.print("<tr><td>");
+	stream.print(buildResults.getName());
+	if (buildResults.isBaseline()) stream.print(" (reference)");
+	stream.print("</td>");
+	int dimLength = this.dimensions.length;
+	for (int d=0; d<dimLength; d++) {
+		Dim dim = this.dimensions[d];
+		int dim_id = dim.getId();
+		double stddev = buildResults.getDeviation(dim_id);
+		String displayValue = dim.getDisplayValue(buildResults.getValue(dim_id));
+		stream.print("<td>");
+		stream.print(displayValue);
+		if (stddev < 0) {
+			stream.print(" [n/a]\n");
+		} else if (stddev > 0) {
+			stream.print(" [");
+			stream.print(dim.getDisplayValue(stddev));
+			stream.print("]");
+		}
+		stream.print( "</td>");
+	}
+	stream.print("</tr>\n");
+}
+
+/*
+ * Print the line showing the difference between current and baseline builds.
+ */
+private void printDifferenceLine(PrintStream stream, ConfigResults configResults) {
+	stream.print("<tr><td>*Delta</td>");
+	int dimLength = this.dimensions.length;
+	for (int d=0; d<dimLength; d++) {
+		Dim currentDim = this.dimensions[d];
+		int dim_id = currentDim.getId();
+		BuildResults currentBuild = configResults.getCurrentBuildResults();
+		BuildResults baselineBuild = configResults.getBaselineBuildResults();
+
+		// Compute difference values
+		double baselineValue = baselineBuild.getValue(dim_id);
+		double diffValue = baselineValue - currentBuild.getValue(dim_id);
+		double diffPercentage =  baselineValue == 0 ? 0 : Math.round(diffValue / baselineValue * 1000) / 10.0;
+		String diffDisplayValue = currentDim.getDisplayValue(diffValue);
+
+		// Set colors
+		String fontColor = "";
+		if (diffPercentage > 10) {
+			fontColor = "#006600";	// green
+		}
+		if (diffPercentage < -10) {
+			fontColor = "#FF0000";	// red
+		}
+
+		// Print line
+		String percentage = (diffPercentage == 0) ? "" : "<br>" + diffPercentage + " %";
+		if (diffPercentage > 10 || diffPercentage < -10) {
+			stream.print("<td><FONT COLOR=\"" + fontColor + "\"><b>" + diffDisplayValue + percentage + "</b></FONT></td>");
+		} else {
+			stream.print("<td>" + diffDisplayValue + percentage + "</td>");
+		}
+	}
+	stream.print("</tr></font>");
+}
+
+/*
+ * Print details file of the scenario builds data.
+ */
+private void printDetails(String configName, String configBox, ComponentResults componentResults, File outputDir) {
+	Iterator scenarios = componentResults.getResults();
+	while (scenarios.hasNext()) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.next();
+		ConfigResults configResults = scenarioResults.getConfigResults(configName);
+		if (configResults == null || !configResults.isValid()) continue;
+		String scenarioName= scenarioResults.getName();
+		String scenarioFileName = scenarioResults.getFileName();
+		File outputFile = createFile(outputDir, "raw", scenarioFileName, "html");
+		PrintStream stream = null;
+		try {
+			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
+		} catch (FileNotFoundException e) {
+			System.err.println("can't create output file" + outputFile); //$NON-NLS-1$
+		}
+		if (stream == null) stream = System.out;
+		RawDataTable currentResultsTable = new RawDataTable(configResults, this.buildIDStreamPatterns, stream);
+		RawDataTable baselineResultsTable = new RawDataTable(configResults, this.baselinePrefix, stream);
+		stream.print(Utils.HTML_OPEN);
+		stream.print(Utils.HTML_DEFAULT_CSS);
+		stream.print("<title>" + scenarioName + "(" + configBox + ")" + " - Details</title></head>\n"); //$NON-NLS-1$
+		stream.print("<h4>Scenario: " + scenarioName + " (" + configBox + ")</h4>\n"); //$NON-NLS-1$
+		stream.print("<a href=\"../"+scenarioFileName+".html\">VIEW GRAPH</a><br><br>\n"); //$NON-NLS-1$
+		stream.print("<table><td><b>Current Stream Test Runs</b></td><td><b>Baseline Test Runs</b></td></tr>\n");
+		stream.print("<tr valign=\"top\">\n");
+		stream.print("<td>");
+		currentResultsTable.print();
+		stream.print("</td>\n");
+		stream.print("<td>");
+		baselineResultsTable.print();
+		stream.print("</td>\n");
+		stream.print("</tr>\n");
+		stream.print("</table>\n");
+		stream.close();
+	}
+}
+
+/*
+ * Prints a LineGraph object as a gif file.
+ */
+private void saveGraph(LineGraph p, File outputFile) {
+	Image image = new Image(Display.getDefault(), GRAPH_WIDTH, GRAPH_HEIGHT);
+	p.paint(image);
+
+	/* Downscale to 8 bit depth palette to save to gif */
+	ImageData data = Utils.downSample(image);
+	ImageLoader il = new ImageLoader();
+	il.data = new ImageData[] { data };
+	OutputStream out = null;
+	try {
+		out = new BufferedOutputStream(new FileOutputStream(outputFile));
+		il.save(out, SWT.IMAGE_GIF);
+
+	} catch (FileNotFoundException e) {
+		e.printStackTrace();
+	} finally {
+		image.dispose();
+		if (out != null) {
+			try {
+				out.close();
+			} catch (IOException e1) {
+				// silently ignored
+			}
+		}
+	}
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioStatusTable.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioStatusTable.java
new file mode 100644
index 0000000..2a39253
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioStatusTable.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.io.PrintStream;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.test.internal.performance.results.db.BuildResults;
+import org.eclipse.test.internal.performance.results.db.ConfigResults;
+import org.eclipse.test.internal.performance.results.db.PerformanceResults;
+import org.eclipse.test.internal.performance.results.db.ScenarioResults;
+
+/**
+ * Class used to print a scenario status table.
+ */
+public class ScenarioStatusTable {
+
+	private String component;
+	private PrintStream stream;
+	private int jsIdCount;
+
+/**
+ * Creates an HTML table of red x/green check for a scenario for each
+ * configuration.
+ */
+public ScenarioStatusTable(String  name, PrintStream stream) {
+    this.component = name;
+    this.stream = stream;
+}
+
+/**
+ * Prints the HTML representation of scenario status table into the given stream.
+ */
+public void print(PerformanceResults performanceResults) {
+
+	String baselineName = performanceResults.getBaselineName();
+	List scenarios = performanceResults.getComponentScenarios(this.component);
+	int size = scenarios.size();
+
+	// Print titles
+	printTitle();
+	this.stream.print("<table border=\"1\">\n");
+	this.stream.print("<tr>\n");
+	this.stream.print("<td><h4>All ");
+	this.stream.print(computeSize(scenarios));
+	this.stream.print(" scenarios</h4></td>\n");
+	printColumnsTitle(size, performanceResults);
+
+	// Print one line per scenario results
+	this.jsIdCount = 0;
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
+		if (!scenarioResults.isValid()) continue;
+		this.stream.print("<tr>\n");
+		this.stream.print("<td>");
+		boolean hasSummary = scenarioResults.hasSummary();
+		if (hasSummary) this.stream.print("<b>");
+		String scenarioBaseline = scenarioResults.getBaselineBuildName();
+		boolean hasBaseline = baselineName.equals(scenarioBaseline);
+		if (!hasBaseline) {
+			this.stream.print("*");
+			this.stream.print(scenarioResults.getShortName());
+			this.stream.print(" <small>(vs.&nbsp;");
+			this.stream.print(scenarioBaseline);
+			this.stream.print(")</small>");
+		} else {
+			this.stream.print(scenarioResults.getShortName());
+		}
+		if (hasSummary) this.stream.print("</b>");
+		this.stream.print("\n");
+		String[] configs = performanceResults.getConfigNames(true/*sort*/);
+		int length = configs.length;
+		for (int j=0; j<length; j++) {
+			printConfigStats(scenarioResults, configs[j]);
+		}
+	}
+	this.stream.print("</table>\n");
+}
+
+private int computeSize(List scenarios) {
+	int size = scenarios.size();
+	int n = 0;
+	for (int i=0; i<size; i++) {
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
+		if (scenarioResults.isValid()) n++;
+	}
+	return n;
+}
+
+/*
+ * Print the table columns title.
+ */
+private void printColumnsTitle(int size, PerformanceResults performanceResults) {
+	String[] configNames = performanceResults.getConfigNames(true/*sort*/);
+	String[] configBoxes = performanceResults.getConfigBoxes(true/*sort*/);
+	int length = configNames.length;
+	for (int i=0; i<length; i++) {
+		String columnTitle = configNames[i];
+		String boxName = configBoxes[i];
+		int idx = boxName.indexOf('(');
+		if (idx < 0) {
+			columnTitle = boxName;
+		} else {
+			// first line
+			StringTokenizer tokenizer = new StringTokenizer(boxName.substring(0, idx).trim(), " ");
+			StringBuffer buffer = new StringBuffer(tokenizer.nextToken());
+			while (tokenizer.hasMoreTokens()) {
+				buffer.append("&nbsp;");
+				buffer.append(tokenizer.nextToken());
+			}
+			buffer.append(' ');
+			// second line
+			tokenizer = new StringTokenizer(boxName.substring(idx).trim(), " ");
+			buffer.append(tokenizer.nextToken());
+			while (tokenizer.hasMoreTokens()) {
+				buffer.append("&nbsp;");
+				buffer.append(tokenizer.nextToken());
+			}
+			columnTitle = buffer.toString();
+		}
+		this.stream.print("<td><h5>");
+		this.stream.print(columnTitle);
+		this.stream.print("</h5>\n");
+	}
+}
+
+/*
+ * Print the scenario statistics value for the given configuration.
+ */
+private void printConfigStats(ScenarioResults scenarioResults, String config) {
+	ConfigResults configResults = scenarioResults.getConfigResults(config);
+	if (configResults == null || !configResults.isValid()) {
+		this.stream.print("<td>n/a</td>");
+		return;
+	}
+	BuildResults currentBuildResults = configResults.getCurrentBuildResults();
+	String failure = currentBuildResults.getFailure();
+	double[] deviation = configResults.getCurrentBuildDeltaInfo();
+	int confidence = Utils.confidenceLevel(deviation);
+	boolean hasFailure = failure != null;
+	String comment = currentBuildResults.getComment();
+	String image = Utils.getImage(confidence, hasFailure, comment != null);
+	this.stream.print("<td><a ");
+	if (!hasFailure|| (confidence & Utils.NAN) != 0 || failure.length() == 0){
+		// write deviation with error in table when test pass
+		this.stream.print("href=\"");
+		this.stream.print(configResults.getName());
+		this.stream.print('/');
+		this.stream.print(scenarioResults.getFileName());
+		this.stream.print(".html\">\n");
+		this.stream.print("<img hspace=\"10\" border=\"0\" src=\"");
+		this.stream.print(image);
+		this.stream.print("\"/></a>\n");
+	} else {
+		// create message with tooltip text including deviation with error plus failure message
+		this.jsIdCount+=1;
+		this.stream.print("class=\"tooltipSource\" onMouseover=\"show_element('toolTip");
+		this.stream.print(this.jsIdCount);
+		this.stream.print("')\" onMouseout=\"hide_element('toolTip");
+		this.stream.print(this.jsIdCount);
+		this.stream.print("')\" \nhref=\"");
+		this.stream.print(configResults.getName());
+		this.stream.print('/');
+		this.stream.print(scenarioResults.getFileName());
+		this.stream.print(".html\">\n");
+		this.stream.print("<img hspace=\"10\" border=\"0\" src=\"");
+		this.stream.print(image);
+		this.stream.print("\"/>\n");
+		this.stream.print("<span class=\"hidden_tooltip\" id=\"toolTip");
+		this.stream.print(this.jsIdCount);
+		this.stream.print("\">");
+		this.stream.print(failure);
+		this.stream.print("</span></a>\n");
+	}
+	String result = Utils.failureMessage(deviation, false);
+	this.stream.print(result);
+	this.stream.print("\n");
+}
+
+/*
+ * Print the status table explanationtitle.
+ */
+private void printTitle() {
+	this.stream.print("<br><h4>Scenario Status</h4>\n");
+	this.stream.print("The following table gives a complete but compact view of performance results for the component.<br>\n");
+	this.stream.print("Each line of the table shows the results for one scenario on all machines.<br><br>\n");
+	this.stream.print("The name of the scenario is in <b>bold</b> when its results are also displayed in the fingerprints<br>\n");
+	this.stream.print("and starts with an '*' when the scenario has no results in the last baseline run.<br><br>\n");
+	this.stream.print("Here are information displayed for each test (ie. in each cell):\n");
+	this.stream.print("<ul>\n");
+	this.stream.print("<li>an icon showing whether the test fails or passes and whether it's reliable or not.<br>\n");
+	this.stream.print("The legend for this icon is:\n");
+	this.stream.print("<ul>\n");
+	this.stream.print("<li>Green (<img src=\"");
+	this.stream.print(Utils.OK_IMAGE);
+	this.stream.print("\">): mark a <b>successful result</b>, which means this test has neither significant performance regression nor significant standard error</li>");
+	this.stream.print("<li>Red (<img src=\"");
+	this.stream.print(Utils.FAIL_IMAGE);
+	this.stream.print("\">): mark a <b>failing result</b>, which means this test shows a significant performance regression (more than 10%)</li>\n");
+	this.stream.print("<li>Gray (<img src=\"");
+	this.stream.print(Utils.FAIL_IMAGE_EXPLAINED);
+	this.stream.print("\">): mark a <b>failing result</b> (see above) with a comment explaining this degradation.</li>\n");
+	this.stream.print("<li>Yellow (<img src=\"");
+	this.stream.print(Utils.FAIL_IMAGE_WARN);
+	this.stream.print("\"> or <img src=\"");
+	this.stream.print(Utils.OK_IMAGE_WARN);
+	this.stream.print("\">): mark a <b>failing or successful result</b> with a significant standard error (more than ");
+	this.stream.print(Utils.STANDARD_ERROR_THRESHOLD_STRING);
+	this.stream.print(")</li>\n");
+	this.stream.print("<li>Black (<img src=\"");
+	this.stream.print(Utils.UNKNOWN_IMAGE);
+	this.stream.print("\">): mark an <b>undefined result</b>, which means that deviation on this test is not a number (<code>NaN</code>) or is infinite (happens when the reference value is equals to 0!)</li>");
+	this.stream.print("<li>\"n/a\": mark a test for with <b>no</b> performance results</li>\n");
+	this.stream.print("</ul></li>\n");
+	this.stream.print("<li>the value of the deviation from the baseline as a percentage (ie. formula is: <code>(build_test_time - baseline_test_time) / baseline_test_time</code>)</li>\n");
+	this.stream.print("<li>the value of the standard error of this deviation as a percentage (ie. formula is: <code>sqrt(build_test_stddev^2 / N + baseline_test_stddev^2 / N) / baseline_test_time</code>)<br>\n");
+	this.stream.print("When test only has one measure, the standard error cannot be computed and is replaced with a '<font color=\"#CCCC00\">[n/a]</font>'.</li>\n");
+	this.stream.print("</ul>\n");
+	this.stream.print("<u>Hints</u>:<ul>\n");
+	this.stream.print("<li>fly over image of failing tests to see the complete error message</li>\n");
+	this.stream.print("<li>to look at the complete and detailed test results, click on its image</li>\n");
+	this.stream.print("</ul>\n");
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/TimeLineGraph.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/TimeLineGraph.java
new file mode 100644
index 0000000..467bdd5
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/TimeLineGraph.java
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.util.*;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.test.internal.performance.data.Dim;
+
+public class TimeLineGraph extends LineGraph{
+
+    Hashtable fItemGroups;
+
+    public TimeLineGraph (String title, Dim dim) {
+        super(title, dim);
+        this.fItemGroups=new Hashtable();
+    }
+
+    public void paint(Image im) {
+
+        Rectangle bounds= im.getBounds();
+
+        GC g= new GC(im);
+
+        Point ee= g.stringExtent(this.fTitle);
+        int titleHeight= ee.y;
+
+        double maxItem= getMaxItem();
+        double minItem= getMinItem();
+
+        int max= (int) (Math.ceil(maxItem * (maxItem < 0 ? 0.8 : 1.2)));
+        int min= (int) (Math.floor(minItem * (minItem < 0 ? 1.2 : 0.8)));
+
+        String smin= this.fDimension.getDisplayValue(min);
+        Point emin= g.stringExtent(smin);
+
+        String smax= this.fDimension.getDisplayValue(max);
+        Point emax= g.stringExtent(smax);
+
+        int labelWidth= Math.max(emin.x, emax.x) + 2;
+
+        int top= PADDING;
+        int bottom= bounds.height - titleHeight - PADDING;
+        int left= PADDING + labelWidth;
+
+        //getMostRecent
+        TimeLineGraphItem lastItem= getMostRecent(this.fItemGroups);
+        int right=bounds.width - PADDING/2;
+        if (lastItem!=null)
+        	right= bounds.width - lastItem.getSize(g).x - PADDING/2;
+
+        // draw the max and min values
+        g.drawString(smin, PADDING/2+labelWidth-emin.x, bottom-titleHeight, true);
+        g.drawString(smax, PADDING/2+labelWidth-emax.x, top, true);
+        g.drawString("TIME (not drawn to scale)", (right-left)/3+PADDING+titleHeight,bottom-PADDING+(titleHeight*2), true);
+
+        // draw the vertical and horizontal lines
+        g.drawLine(left, top, left, bottom);
+        g.drawLine(left, bottom, right, bottom);
+
+        Color oldbg= g.getBackground();
+        Color oldfg= g.getForeground();
+
+        setCoordinates(right-left,left,bottom-top,bottom,max-min);
+
+        Enumeration _enum=this.fItemGroups.elements();
+        Comparator comparator=new TimeLineGraphItem.GraphItemComparator();
+
+        while (_enum.hasMoreElements()) {
+ 			List items = (List) _enum.nextElement();
+			Object[] fItemsArray=items.toArray();
+			Arrays.sort(fItemsArray,comparator);
+			int lastx = 0;
+			int lasty = 0;
+
+			int n = fItemsArray.length;
+
+			for (int i = 0; i < n; i++) {
+				TimeLineGraphItem thisItem = (TimeLineGraphItem) fItemsArray[i];
+
+				int yposition = thisItem.y;
+				int xposition = thisItem.x;
+				g.setLineWidth(1);
+
+				g.setBackground(thisItem.color);
+				g.setForeground(thisItem.color);
+
+				if (thisItem.drawAsBaseline){
+					g.setLineWidth(0);
+					g.drawLine(xposition, yposition,right,yposition);
+					g.drawLine(left,yposition,xposition, yposition);
+    		    }
+
+				if (i > 0) // don't draw for first segment
+					g.drawLine(lastx, lasty, xposition, yposition);
+
+				g.setBackground(thisItem.color);
+				g.setForeground(thisItem.color);
+			//	g.fillOval(xposition - 2, yposition - 2, 6, 6);
+				g.fillRectangle(xposition - 2, yposition - 2, 5, 5);
+
+				if (thisItem.isSpecial)
+					g.drawRectangle(xposition -4, yposition - 4, 8, 8);
+
+				if (this.fAreaBuffer == null)
+					this.fAreaBuffer = new StringBuffer();
+
+				this.fAreaBuffer.append("\r<area shape=\"circle\" coords=\""
+						+ (xposition - 2) + ',' + (yposition - 2) + ',' + 5
+						+ " alt=\"" + thisItem.title + ": "
+						+ thisItem.description + "\"" + " title=\""
+						+ thisItem.title + ": " + thisItem.description + "\">");
+
+				int shift;
+				if (i > 0 && yposition < lasty)
+					shift = 3; // below dot
+				else
+					shift = -(2 * titleHeight + 3); // above dot
+				if (thisItem.displayDescription) {
+					g.drawString(thisItem.title, xposition + 2, yposition
+							+ shift, true);
+					g.drawString(thisItem.description, xposition + 2, yposition
+							+ shift + titleHeight, true);
+				}
+				g.setBackground(oldbg);
+				g.setForeground(oldfg);
+
+				lastx = xposition;
+				lasty = yposition;
+			}
+		}
+
+        g.dispose();
+    }
+
+    public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp) {
+    	addItem(groupName, name, description, value, col, display,	timestamp,false);
+    }
+
+    public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp,boolean isSpecial) {
+ 		addItem(groupName, name,description, value, col, display,
+ 				timestamp,isSpecial,false);
+	}
+
+    public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp,boolean isSpecial,boolean drawBaseline) {
+      	List items = (List) this.fItemGroups.get(groupName);
+  		if (this.fItemGroups.get(groupName) == null) {
+  			items=new ArrayList();
+  			this.fItemGroups.put(groupName, items);
+  		}
+  		items.add(new TimeLineGraphItem(name, description, value, col, display,
+  				timestamp,isSpecial,drawBaseline));
+    }
+
+    public double getMaxItem() {
+    	Enumeration _enum=this.fItemGroups.elements();
+        double maxItem= 0;
+    	while (_enum.hasMoreElements()) {
+			List items = (List) _enum.nextElement();
+			for (int i = 0; i < items.size(); i++) {
+				TimeLineGraphItem graphItem = (TimeLineGraphItem) items.get(i);
+				if (graphItem.value > maxItem)
+					maxItem = graphItem.value;
+			}
+		}
+        if (maxItem == 0)
+            return 1;
+        return maxItem;
+    }
+
+    public double getMinItem() {
+       	Enumeration _enum = this.fItemGroups.elements();
+		double minItem = getMaxItem();
+
+		while (_enum.hasMoreElements()) {
+			List items = (List) _enum.nextElement();
+			for (int i = 0; i < items.size(); i++) {
+				TimeLineGraphItem graphItem = (TimeLineGraphItem) items.get(i);
+				if (graphItem.value < minItem)
+					minItem = graphItem.value;
+			}
+		}
+        if (minItem == 0)
+            return -1;
+        return minItem;
+    }
+
+    private TimeLineGraphItem getMostRecent(Hashtable lineGraphGroups) {
+		Enumeration _enum = lineGraphGroups.elements();
+		long mostRecentTimestamp = 0;
+		TimeLineGraphItem mostRecentItem = null;
+
+		while (_enum.hasMoreElements()) {
+			List items = (List) _enum.nextElement();
+			for (int i = 0; i < items.size(); i++) {
+				if (items.size() == 1)
+					return (TimeLineGraphItem) items.get(i);
+				else {
+					TimeLineGraphItem graphItem = (TimeLineGraphItem) items.get(i);
+					if (graphItem.timestamp > mostRecentTimestamp) {
+						mostRecentTimestamp = graphItem.timestamp;
+						mostRecentItem = (TimeLineGraphItem) items.get(i);
+					}
+				}
+			}
+		}
+		return mostRecentItem;
+	}
+
+    private void setCoordinates(int width, int xOffset, int height, int yOffset, int yValueRange){
+
+        List mainGroup=(ArrayList)this.fItemGroups.get("main");
+        List referenceGroup=(ArrayList)this.fItemGroups.get("reference");
+
+        Comparator comparator=new TimeLineGraphItem.GraphItemComparator();
+
+ 		Object[] fItemsArray=mainGroup.toArray();
+		Arrays.sort(fItemsArray,comparator);
+
+		int n = mainGroup.size();
+		int xIncrement=width/n;
+		double max=getMaxItem()*1.2;
+//		double min=getMinItem()*0.8;
+
+		for (int i = 0; i < n; i++) {
+			TimeLineGraphItem thisItem = (TimeLineGraphItem) fItemsArray[i];
+			thisItem.setX(xOffset + (i * xIncrement));
+			thisItem.setY((int)(PADDING+((max-thisItem.value) * (height)/(yValueRange))));
+
+			}
+
+		if (referenceGroup==null)
+			return;
+
+		n = referenceGroup.size();
+		for (int i = 0; i < n; i++) {
+			 TimeLineGraphItem thisItem = (TimeLineGraphItem) referenceGroup.get(i);
+			 if (thisItem.timestamp==-1)
+				 thisItem.setX(xOffset + (i * (width/n)));
+			 else
+				 setRelativeXPosition(thisItem,mainGroup);
+
+			 thisItem.setY((int)(PADDING+((max-thisItem.value) * (height)/(yValueRange))));
+
+		}
+    }
+
+
+	private void setRelativeXPosition (TimeLineGraphItem thisItem, List items){
+			Comparator comparator=new TimeLineGraphItem.GraphItemComparator();
+			Object[] fItemsArray=items.toArray();
+			Arrays.sort(fItemsArray,comparator);
+
+			TimeLineGraphItem closestPrecedingItem=null;
+			long minimumTimeDiffPreceding=thisItem.timestamp;
+
+			TimeLineGraphItem closestFollowingItem=null;
+			long minimumTimeDiffFollowing=thisItem.timestamp;
+
+			for (int i=0;i<fItemsArray.length;i++){
+				TimeLineGraphItem anItem=(TimeLineGraphItem)fItemsArray[i];
+				long timeDiff=thisItem.timestamp-anItem.timestamp;
+
+				 if (timeDiff>0&&timeDiff<minimumTimeDiffPreceding){
+					 closestPrecedingItem=anItem;
+				 	minimumTimeDiffPreceding=thisItem.timestamp-anItem.timestamp;
+				 }
+				 if (timeDiff<=0&&Math.abs(timeDiff)<=minimumTimeDiffFollowing){
+					 closestFollowingItem=anItem;
+					 minimumTimeDiffFollowing=thisItem.timestamp-anItem.timestamp;
+				 }
+			}
+			if (closestFollowingItem==null && closestPrecedingItem!=null)
+				thisItem.setX(closestPrecedingItem.x);
+
+			else if (closestFollowingItem!=null && closestPrecedingItem==null)
+				thisItem.setX(closestFollowingItem.x);
+			else{
+				long timeRange=closestFollowingItem.timestamp-closestPrecedingItem.timestamp;
+
+				int xRange=closestFollowingItem.x-closestPrecedingItem.x;
+				double increments=(xRange*1.0)/timeRange;
+
+				thisItem.setX((int)(Math.round((thisItem.timestamp-closestPrecedingItem.timestamp)*increments)+closestPrecedingItem.x));
+			}
+	}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/TimeLineGraphItem.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/TimeLineGraphItem.java
new file mode 100644
index 0000000..c52d0a9
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/TimeLineGraphItem.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.util.Comparator;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+
+public class TimeLineGraphItem {
+
+
+    String title;
+    String description=null;
+    double value;
+    Color color;
+    boolean displayDescription=false;
+    long timestamp;
+    boolean isSpecial=false;
+    boolean drawAsBaseline=false;
+    int x;
+    int y;
+
+    TimeLineGraphItem(String title, String description,double value,Color color,boolean display, long timestamp, boolean isSpecial,boolean isBaseline) {
+    	this(title, description, value, color,display, timestamp,isSpecial);
+    	this.drawAsBaseline=isBaseline;
+    }
+
+    TimeLineGraphItem(String title, String description,double value,Color color,boolean display, long timestamp, boolean isSpecial) {
+    	this(title, description, value, color,display, timestamp);
+    	this.isSpecial=isSpecial;
+    }
+
+    TimeLineGraphItem(String title, String description,double value,Color color,boolean display, long timestamp) {
+    	this(title, description, value, color,timestamp);
+    	this.displayDescription=display;
+    }
+
+    TimeLineGraphItem(String title, String description, double value, Color color,long timestamp) {
+        this.title= title;
+        this.value= value;
+        this.color= color;
+        this.description= description;
+        this.timestamp=timestamp;
+    }
+
+    Point getSize(GC g) {
+        Point e1= g.stringExtent(this.description);
+        Point e2= g.stringExtent(this.title);
+        return new Point(Math.max(e1.x, e2.x), e1.y+e2.y);
+    }
+
+    public static class GraphItemComparator implements Comparator{
+		public int compare(Object o1, Object o2) {
+			long ts1=((TimeLineGraphItem)o1).timestamp;
+			long ts2=((TimeLineGraphItem)o2).timestamp;
+
+			if (ts1>ts2)
+				return 1;
+			if (ts1<ts2)
+				return -1;
+
+			return 0;
+		}
+    }
+
+	public int getX() {
+		return this.x;
+	}
+
+	public void setX(int x) {
+		this.x = x;
+	}
+
+	public int getY() {
+		return this.y;
+	}
+
+	public void setY(int y) {
+		this.y = y;
+	}
+
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/UiPlugin.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/UiPlugin.java
new file mode 100644
index 0000000..58462dd
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/UiPlugin.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.ui.preferences.ScopedPreferenceStore;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public class UiPlugin extends Plugin {
+	//The shared instance.
+	private static UiPlugin plugin;
+	private IPreferenceStore preferenceStore;
+
+	/**
+	 * The constructor.
+	 */
+	public UiPlugin() {
+		super();
+		if (plugin == null) {
+			plugin = this;
+		}
+	}
+
+	/**
+	 * This method is called upon plug-in activation
+	 */
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+	}
+
+	/**
+	 * This method is called when the plug-in is stopped
+	 */
+	public void stop(BundleContext context) throws Exception {
+		super.stop(context);
+		plugin = null;
+	}
+
+	/**
+	 * Returns the shared instance.
+	 */
+	public static UiPlugin getDefault() {
+		return plugin;
+	}
+
+	public IPreferenceStore getPreferenceStore() {
+		if (this.preferenceStore == null) {
+			this.preferenceStore = new ScopedPreferenceStore(new InstanceScope(), getBundle().getSymbolicName());
+
+		}
+		return this.preferenceStore;
+	}
+
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Utils.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Utils.java
new file mode 100644
index 0000000..b4feffd
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Utils.java
@@ -0,0 +1,416 @@
+/*******************************************************************************
+ * 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.performance.ui;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.HashMap;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.test.internal.performance.PerformanceTestPlugin;
+import org.eclipse.test.internal.performance.db.Variations;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.osgi.framework.Bundle;
+
+
+public class Utils {
+
+	public final static double STANDARD_ERROR_THRESHOLD = 0.03; // 3%
+	static final NumberFormat PERCENT_FORMAT = NumberFormat.getPercentInstance();
+	static {
+		PERCENT_FORMAT.setMaximumFractionDigits(1);
+	}
+	static final DecimalFormat DEVIATION_FORMAT = (DecimalFormat) NumberFormat.getPercentInstance();
+	static {
+		DEVIATION_FORMAT.setMaximumFractionDigits(1);
+		DEVIATION_FORMAT.setMinimumFractionDigits(1);
+		DEVIATION_FORMAT.setPositivePrefix("+");
+		DEVIATION_FORMAT.setNegativePrefix("- ");
+	}
+	static final DecimalFormat STDERR_FORMAT = (DecimalFormat) NumberFormat.getNumberInstance();
+	static {
+		STDERR_FORMAT.setMaximumFractionDigits(1);
+		STDERR_FORMAT.setMinimumFractionDigits(1);
+		STDERR_FORMAT.setMultiplier(100);
+	}
+	public final static String STANDARD_ERROR_THRESHOLD_STRING = PERCENT_FORMAT.format(STANDARD_ERROR_THRESHOLD);
+
+	// Image files
+	public final static String UNKNOWN_IMAGE="images/Unknown.gif";
+	public final static String OK_IMAGE="images/OK.gif";
+	public final static String OK_IMAGE_WARN="images/OK_caution.gif";
+	public final static String FAIL_IMAGE="images/FAIL.gif";
+	public final static String FAIL_IMAGE_WARN="images/FAIL_caution.gif";
+	public final static String FAIL_IMAGE_EXPLAINED="images/FAIL_greyed.gif";
+	public final static String LIGHT="images/light.gif";
+	public final static String WARNING_OBJ="images/warning_obj.gif";
+
+	// Java script files
+	public final static String TOOLTIP_SCRIPT = "scripts/ToolTip.js";
+	public final static String TOOLTIP_STYLE = "scripts/ToolTip.css";
+	public final static String FINGERPRINT_SCRIPT = "scripts/Fingerprints.js";
+
+	// Doc files
+	public final static String HELP = "doc/help.html";
+
+	// Status
+	public final static int OK = 0;
+	public final static int NAN = 0x1;
+	public final static int ERR = 0x2;
+
+	/**
+	 * Return &lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type"
+	 *         content="text/html; charset=iso-8859-1"&gt;
+	 */
+	public final static String HTML_OPEN = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
+
+	/**
+	 * Return "&lt;/html&gt;".
+	 */
+	public final static String HTML_CLOSE = "</html>\n";
+
+	/**
+	 * Default style-sheet used on eclipse.org
+	 */
+	public final static String HTML_DEFAULT_CSS = "<style type=\"text/css\">" + "p, table, td, th {  font-family: arial, helvetica, geneva; font-size: 10pt}\n"
+			+ "pre {  font-family: \"Courier New\", Courier, mono; font-size: 10pt}\n" + "h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}\n"
+			+ "code {  font-family: \"Courier New\", Courier, mono; font-size: 10pt}\n" + "sup {  font-family: arial,helvetica,geneva; font-size: 10px}\n"
+			+ "h3 {  font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}\n" + "li {  font-family: arial, helvetica, geneva; font-size: 10pt}\n"
+			+ "h1 {  font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}\n"
+			+ "body {  font-family: arial, helvetica, geneva; font-size: 10pt; clip:   rect(   ); margin-top: 5mm; margin-left: 3mm}\n"
+			+ ".indextop { font-size: x-large;; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold}\n"
+			+ ".indexsub { font-size: xx-small;; font-family: Arial, Helvetica, sans-serif; color: #8080FF}\n" + "</style>\n\n";
+
+	/**
+	 * Creates a Variations object using build id pattern, config and jvm.
+	 *
+	 * @param buildIdPattern
+	 * @param config
+	 * @param jvm
+	 */
+	public static Variations getVariations(String buildIdPattern, String config, String jvm) {
+		String buildIdPatterns = buildIdPattern.replace(',', '%');
+		Variations variations = new Variations();
+		variations.put(PerformanceTestPlugin.CONFIG, config);
+		variations.put(PerformanceTestPlugin.BUILD, buildIdPatterns);
+		variations.put("jvm", jvm);
+		return variations;
+	}
+
+	/**
+	 * Copy all bundle files contained in the given path
+	 */
+	public static void copyBundleFiles(Bundle bundle, String path, String pattern, File output) {
+		Enumeration imageFiles = bundle.findEntries(path, pattern, false);
+		while (imageFiles.hasMoreElements()) {
+			URL url = (URL) imageFiles.nextElement();
+			try {
+				File outputFile = new File(output, url.getFile());
+				if (!outputFile.getParentFile().exists()) {
+					outputFile.getParentFile().mkdirs();
+				}
+				Util.copyStream(url.openStream(), outputFile);
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+	}
+
+	/**
+	 * Downsample Image to 8 bit depth format so that the resulting image data
+	 * can be saved to GIF. Note. If the source image contains photo quality
+	 * content with more than 256 colours, resulting data will look very poor.
+	 */
+	static int closest(RGB[] rgbs, int n, RGB rgb) {
+		int minDist = 256 * 256 * 3;
+		int minIndex = 0;
+		for (int i = 0; i < n; ++i) {
+			RGB rgb2 = rgbs[i];
+			int da = rgb2.red - rgb.red;
+			int dg = rgb2.green - rgb.green;
+			int db = rgb2.blue - rgb.blue;
+			int dist = da * da + dg * dg + db * db;
+			if (dist < minDist) {
+				minDist = dist;
+				minIndex = i;
+			}
+		}
+		return minIndex;
+	}
+
+	static class ColorCounter implements Comparable {
+		RGB rgb;
+
+		int count;
+
+		public int compareTo(Object o) {
+			return ((ColorCounter) o).count - this.count;
+		}
+	}
+
+	public static ImageData downSample(Image image) {
+		ImageData data = image.getImageData();
+		if (!data.palette.isDirect && data.depth <= 8)
+			return data;
+
+		// compute a histogram of color frequencies
+		HashMap freq = new HashMap();
+		int width = data.width;
+		int[] pixels = new int[width];
+		int[] maskPixels = new int[width];
+		for (int y = 0, height = data.height; y < height; ++y) {
+			data.getPixels(0, y, width, pixels, 0);
+			for (int x = 0; x < width; ++x) {
+				RGB rgb = data.palette.getRGB(pixels[x]);
+				ColorCounter counter = (ColorCounter) freq.get(rgb);
+				if (counter == null) {
+					counter = new ColorCounter();
+					counter.rgb = rgb;
+					freq.put(rgb, counter);
+				}
+				counter.count++;
+			}
+		}
+
+		// sort colors by most frequently used
+		ColorCounter[] counters = new ColorCounter[freq.size()];
+		freq.values().toArray(counters);
+		Arrays.sort(counters);
+
+		// pick the most frequently used 256 (or fewer), and make a palette
+		ImageData mask = null;
+		if (data.transparentPixel != -1 || data.maskData != null) {
+			mask = data.getTransparencyMask();
+		}
+		int n = Math.min(256, freq.size());
+		RGB[] rgbs = new RGB[n + (mask != null ? 1 : 0)];
+		for (int i = 0; i < n; ++i)
+			rgbs[i] = counters[i].rgb;
+		if (mask != null) {
+			rgbs[rgbs.length - 1] = data.transparentPixel != -1 ? data.palette.getRGB(data.transparentPixel) : new RGB(255, 255, 255);
+		}
+		PaletteData palette = new PaletteData(rgbs);
+
+		// create a new image using the new palette:
+		// for each pixel in the old image, look up the best matching
+		// index in the new palette
+		ImageData newData = new ImageData(width, data.height, 8, palette);
+		if (mask != null)
+			newData.transparentPixel = rgbs.length - 1;
+		for (int y = 0, height = data.height; y < height; ++y) {
+			data.getPixels(0, y, width, pixels, 0);
+			if (mask != null)
+				mask.getPixels(0, y, width, maskPixels, 0);
+			for (int x = 0; x < width; ++x) {
+				if (mask != null && maskPixels[x] == 0) {
+					pixels[x] = rgbs.length - 1;
+				} else {
+					RGB rgb = data.palette.getRGB(pixels[x]);
+					pixels[x] = closest(rgbs, n, rgb);
+				}
+			}
+			newData.setPixels(0, y, width, pixels, 0);
+		}
+		return newData;
+	}
+
+	/**
+	 * Returns the date/time from the build id in format yyyymmddhm
+	 *
+	 * @param buildId
+	 * @return date/time in format YYYYMMDDHHMM, ie. 200504060010
+	 */
+	public static long getDateFromBuildID(String buildId) {
+		return getDateFromBuildID(buildId, false);
+	}
+
+	public static long getDateFromBuildID(String buildId, boolean matchLast) {
+		Calendar calendar = Calendar.getInstance();
+
+		if (buildId.indexOf('_') != -1) {
+			String[] buildIdParts = buildId.split("_");
+
+			int buildIdSegment = 1;
+			if (matchLast)
+				buildIdSegment = buildIdParts.length - 1;
+			// if release build, expect <release>_<release date and
+			// timestamp>_<date and timestamp test ran>
+			// use test date and time for plotting
+			int year = Integer.parseInt(buildIdParts[buildIdSegment].substring(0, 4));
+			int month = Integer.parseInt(buildIdParts[buildIdSegment].substring(4, 6)) - 1;
+			int date = Integer.parseInt(buildIdParts[buildIdSegment].substring(6, 8));
+			int hours = Integer.parseInt(buildIdParts[buildIdSegment].substring(8, 10));
+			int min = Integer.parseInt(buildIdParts[buildIdSegment].substring(10, 12));
+
+			calendar.set(year, month, date, hours, min);
+			return calendar.getTimeInMillis();
+
+		} else if (buildId.indexOf('-') != -1) {
+			// if regular build, expect <buildType><date>-<time> format
+			String[] buildIdParts = buildId.split("-");
+			int year = Integer.parseInt(buildIdParts[0].substring(1, 5));
+			int month = Integer.parseInt(buildIdParts[0].substring(5, 7)) - 1;
+			int date = Integer.parseInt(buildIdParts[0].substring(7, 9));
+			int hours = Integer.parseInt(buildIdParts[1].substring(0, 2));
+			int min = Integer.parseInt(buildIdParts[1].substring(2, 4));
+			calendar.set(year, month, date, hours, min);
+
+			return calendar.getTimeInMillis();
+		}
+
+		return -1;
+	}
+
+	/**
+	 * Returns a message corresponding to given statistics.
+	 *
+	 * @param resultStats The value with its standard error
+	 * @param full
+	 * @return The failure message. May be empty if stats are good...
+	 */
+	public static String failureMessage(double[] resultStats, boolean full) {
+		StringBuffer buffer = new StringBuffer();
+		int level = confidenceLevel(resultStats);
+//		boolean isWarn = (level & WARN) != 0;
+		boolean isErr = (level & ERR) != 0;
+		if (full) {
+			if (isErr) {
+				buffer.append("*** WARNING ***  ");
+	 			buffer.append(Messages.bind(Messages.standardError, PERCENT_FORMAT.format(resultStats[1]), STANDARD_ERROR_THRESHOLD_STRING));
+			}
+			return buffer.toString();
+		}
+		if (resultStats != null) {
+			double deviation = resultStats[0];
+			buffer.append("<font color=\"#0000FF\" size=\"1\">");
+			if (Double.isNaN(deviation) || Double.isInfinite(deviation)) {
+	 			buffer.append(" [n/a]");
+ 			} else {
+				double stderr = resultStats[1];
+				deviation = Math.abs(deviation)<0.001 ? 0 : -deviation;
+	 			if (Double.isNaN(stderr) || Double.isInfinite(stderr)) {
+		 			buffer.append(DEVIATION_FORMAT.format(deviation));
+					buffer.append("</font><font color=\"#DDDD00\" size=\"1\"> ");
+		 			buffer.append(" [n/a]");
+	 			} else {
+		 			buffer.append(DEVIATION_FORMAT.format(deviation));
+	 				buffer.append(" [&#177;");
+	 				buffer.append(STDERR_FORMAT.format(Math.abs(stderr)));
+	 				buffer.append(']');
+	 			}
+ 			}
+			buffer.append("</font>");
+		}
+		return buffer.toString();
+	}
+
+	/**
+	 * Returns the confidence level for given statistics:
+	 * <ul>
+	 * <li>{@link #NAN}: if the value is infinite or not a number</li>
+	 * <li>{@link #ERR}: if the standard error is over the expected threshold ({@link #STANDARD_ERROR_THRESHOLD})</li>
+	 * <li>{@link #OK}: in all other cases</li>
+	 * </ul>
+	 *
+	 * @param resultStats array of 2 doubles, the former is the average value and
+	 * 	the latter is the standard error made while computing the average.
+	 * @return a value telling caller the level of confidence of the provided value
+	 */
+	public static int confidenceLevel(double[] resultStats) {
+		int level = OK;
+ 		if (resultStats != null){
+			if (Double.isNaN(resultStats[0]) || Double.isInfinite(resultStats[0])) {
+				level = NAN;
+ 			} else {
+//	 			if (resultStats[1] >= (STANDARD_ERROR_THRESHOLD/2)) { // warns standard error higher than the half of authorized threshold
+//	 				level |= WARN;
+//	 			}
+	 			if (resultStats[1] >= STANDARD_ERROR_THRESHOLD) { // standard error higher than the authorized threshold
+	 				level = ERR;
+	 			}
+ 			}
+ 		}
+		return level;
+	}
+
+	/**
+	 * Get an icon image corresponding to a given level of confidence and explanation.
+	 *
+	 * @param confidence the confiden level
+	 * @param hasExplanation flags indicates whether the confidence may be tempered by an explanation
+	 * @return Corresponding image
+	 */
+	public static String getImage(int confidence, boolean scenarioFailed, boolean hasExplanation) {
+	    String image = null;
+
+	    if (scenarioFailed) {
+	    	if (hasExplanation) {
+		    	image = FAIL_IMAGE_EXPLAINED;
+		    } else if ((confidence & ERR) != 0) {
+    			image = FAIL_IMAGE_WARN;
+		    } else {
+    			image = FAIL_IMAGE;
+		    }
+	    } else if ((confidence & NAN) != 0) {
+			image = UNKNOWN_IMAGE;
+	    } else if ((confidence & ERR) != 0) {
+	   		image = OK_IMAGE_WARN;
+	    } else {
+   			image = OK_IMAGE;
+	    }
+	    return image;
+    }
+
+/**
+ * @param outputFile
+ * @param image
+ */
+public static void saveImage(File outputFile, Image image) {
+	// Save image
+	ImageData data = downSample(image);
+	ImageLoader imageLoader = new ImageLoader();
+	imageLoader.data = new ImageData[] { data };
+
+	OutputStream out = null;
+	try {
+		out = new BufferedOutputStream(new FileOutputStream(outputFile));
+		imageLoader.save(out, SWT.IMAGE_GIF);
+	} catch (FileNotFoundException e) {
+		e.printStackTrace();
+	} finally {
+		image.dispose();
+		if (out != null) {
+			try {
+				out.close();
+			} catch (IOException e1) {
+				// silently ignored
+			}
+		}
+	}
+}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/messages.properties b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/messages.properties
new file mode 100644
index 0000000..a486ba1
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/messages.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2000, 2007 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
+###############################################################################
+
+### Performance Tests Messages.
+
+standardError = Standard error on this test is {0} (should be less than {1} to become reliable!)
diff --git a/bundles/org.eclipse.test.performance.ui/toc.xml b/bundles/org.eclipse.test.performance.ui/toc.xml
new file mode 100644
index 0000000..32d0d3f
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/toc.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?NLS TYPE="org.eclipse.help.toc"?>
+
+<toc label="Performance Results Guide">
+	<topic label="Overview"  href="html/overview.html"> 
+		<topic label="Preferences" href="html/preferences.html"/> 
+		<topic label="Local data" href="html/local_data.html"/> 
+	</topic>
+	<topic label="Views">
+		<topic label="Components" href="html/components.html"/> 
+		<topic label="Properties" href="html/properties.html"/> 
+		<topic label="Builds" href="html/builds.html"/> 
+		<topic label="Results" href="html/results.html"/>
+		<topic href="html/builds-comparison.html" label="Builds Comparison"/>
+	</topic>
+	<topic label="Verification" href="html/verification.html"/> 
+</toc>