3.6.2rc1
diff --git a/bundles/org.eclipse.build.tools/.classpath b/bundles/org.eclipse.build.tools/.classpath
new file mode 100644
index 0000000..49c14c9
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="src_rss"/>
+	<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.util_3.2.100.v20100503.jar"/>
+	<classpathentry kind="var" path="ECLIPSE_HOME/plugins/org.apache.ant_1.7.1.v20090120-1145/lib/ant.jar"/>
+	<classpathentry kind="var" path="ECLIPSE_HOME/plugins/org.apache.ant_1.7.1.v20090120-1145/lib/ant-launcher.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.build.tools/.cvsignore b/bundles/org.eclipse.build.tools/.cvsignore
new file mode 100644
index 0000000..b274aa8
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/.cvsignore
@@ -0,0 +1,3 @@
+bin
+logs
+*.settings
diff --git a/bundles/org.eclipse.build.tools/.project b/bundles/org.eclipse.build.tools/.project
new file mode 100644
index 0000000..4cec12b
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.build.tools</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.build.tools/about.html b/bundles/org.eclipse.build.tools/about.html
new file mode 100644
index 0000000..9db411a
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/about.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<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>20th June, 2002</p>	
+<h3>License</h3>
+<p>Eclipse.org 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
+Common Public License Version 1.0 (&quot;CPL&quot;).  A copy of the CPL is available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>.
+For purposes of the CPL, &quot;Program&quot; will mean the Content.</p>
+
+<h3>Contributions</h3>
+
+<p>If this Content is licensed to you under the terms and conditions of the CPL, any Contributions, as defined in the CPL, uploaded, submitted, or otherwise
+made available to Eclipse.org, members of Eclipse.org and/or the host of Eclipse.org web site, by you that relate to such
+Content are provided under the terms and conditions of the CPL and can be made available to others under the terms of the CPL.</p>
+
+<p>If this Content is licensed to you under license terms and conditions other than the CPL (&quot;Other License&quot;), any modifications, enhancements and/or
+other code and/or documentation (&quot;Modifications&quot;) uploaded, submitted, or otherwise made available to Eclipse.org, members of Eclipse.org and/or the
+host of Eclipse.org, by you that relate to such Content are provided under terms and conditions of the Other License and can be made available
+to others under the terms of the Other License.  In addition, with regard to Modifications for which you are the copyright holder, you are also
+providing the Modifications under the terms and conditions of the CPL and such Modifications can be made available to others under the terms of
+the CPL.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/bugTools.jar b/bundles/org.eclipse.build.tools/bugTools.jar
new file mode 100644
index 0000000..1e1f2d6
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/bugTools.jar
Binary files differ
diff --git a/bundles/org.eclipse.build.tools/buildTools.jar b/bundles/org.eclipse.build.tools/buildTools.jar
new file mode 100644
index 0000000..1f753eb
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/buildTools.jar
Binary files differ
diff --git a/bundles/org.eclipse.build.tools/data/_README.txt b/bundles/org.eclipse.build.tools/data/_README.txt
new file mode 100644
index 0000000..ab54702
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/data/_README.txt
@@ -0,0 +1,2 @@
+These files are sample data to get you started, generated using 
+the feedPublish.*.properties files in the properties/ folder.
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/data/builds-eclipse-3.3.xml b/bundles/org.eclipse.build.tools/data/builds-eclipse-3.3.xml
new file mode 100644
index 0000000..3ee1147
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/data/builds-eclipse-3.3.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?>
+
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <title>Eclipse Builds</title>
+  <link href="http://download.eclipse.org/eclipse/downloads/builds-eclipse.xml" rel="self" type="application/atom+xml"/>
+  <updated>2006-11-14T23:54:27Z</updated>
+  <author>
+    <name>Eclipse Build Team</name>
+  </author>
+  <id>http://download.eclipse.org/eclipse/downloads/builds-eclipse.xml</id>
+  <entry>
+    <title>[announce] Eclipse I20060922-0010 (3.3.0.I20060922-0010) is available</title>
+    <link href="http://download.eclipse.org/eclipse/downloads/drops/I20060922-0010"/>
+    <id>http://download.eclipse.org/eclipse/downloads/drops/I20060922-0010</id>
+    <updated>2006-11-14T23:54:27Z</updated>
+    <summary>
+      <build xmlns="http://www.eclipse.org/2006/BuildFeed" branch="HEAD" datetime="20060922-0010" href="http://download.eclipse.org/eclipse/downloads/drops/I20060922-0010" type="I" version="3.3.0">
+        <detail href="http://www.eclipse.org/downloads/download.php?r=1&amp;file=/eclipse/downloads/drops/I20060922-0010/" map="directory.txt"/>
+        <downloads>http://download.eclipse.org/eclipse/downloads/</downloads>
+        <releasenotes>http://download.eclipse.org/eclipse/downloads/drops/I20060922-0010/buildNotes.php
+          </releasenotes>
+        <releases>
+          <release arch="x86" os="win32" type="SDK" ws="win">eclipse-SDK-I20060922-0010-win32.zip</release>
+          <release arch="x86" os="linux" type="SDK" ws="gtk">eclipse-SDK-I20060922-0010-linux-gtk.tar.gz</release>
+        </releases>
+        <tests>
+          <test href="http://download.eclipse.org/eclipse/downloads/drops/I20060922-0010/testResults.php" type="junit">
+            <result arch="x86" os="win32" ws="win">UNKNOWN</result>
+            <result arch="x86" os="linux" ws="gtk">UNKNOWN</result>
+            <result arch="x86_64" os="linux" ws="gtk">UNKNOWN</result>
+            <result arch="ppc" os="linux" ws="gtk">UNKNOWN</result>
+            <result arch="x86" os="linux" ws="motif">UNKNOWN</result>
+            <result arch="x86" os="solaris8" ws="gtk">UNKNOWN</result>
+            <result arch="x86" os="aix" ws="motif">UNKNOWN</result>
+            <result arch="x86" os="hp-ux" ws="motif">UNKNOWN</result>
+            <result arch="ppc" os="macosx" ws="carbon">UNKNOWN</result>
+          </test>
+          <test href="http://download.eclipse.org/eclipse/downloads/drops/I20060922-0010/performance/performance.php" type="performance">
+            <result arch="x86" id="RHEL4-3GHz-2.5GB" os="linux" ws="gtk">PENDING</result>
+            <result arch="x86" id="winxp-3GHz-2GB" os="win32" ws="win">UNKNOWN</result>
+            <result arch="x86" id="RHEL3-3GHz-2GB" os="linux" ws="gtk">UNKNOWN</result>
+            <result arch="x86" id="winxp-2GHz-512MB" os="win32" ws="win">UNKNOWN</result>
+          </test>
+        </tests>
+        <coordinated status="COMPLETE"/>
+      </build>
+    </summary>
+  </entry>
+</feed>
diff --git a/bundles/org.eclipse.build.tools/data/builds-emf-2.3.xml b/bundles/org.eclipse.build.tools/data/builds-emf-2.3.xml
new file mode 100644
index 0000000..4b3f599
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/data/builds-emf-2.3.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?>
+
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <title>EMF Builds</title>
+  <link href="http://download.eclipse.org/tools/emf/feeds/builds.xml" rel="self" type="application/atom+xml"/>
+  <updated>2006-11-14T23:07:56Z</updated>
+  <author>
+    <name>EMF Build Team</name>
+  </author>
+  <id>http://download.eclipse.org/tools/emf/feeds/builds.xml</id>
+  <entry>
+    <title>[announce] EMF 2.3.0M3 (S200611091546) is available</title>
+    <link href="http://www.eclipse.org/emf/downloads/?showAll=1&amp;hlbuild=S200611091546#S200611091546"/>
+    <id>http://www.eclipse.org/emf/downloads/?showAll=1&amp;hlbuild=S200611091546#S200611091546</id>
+    <updated>2006-11-14T23:07:56Z</updated>
+    <summary>
+      <build xmlns="http://www.eclipse.org/2006/BuildFeed" branch="HEAD" datetime="200611091546" href="http://www.eclipse.org/emf/downloads/?showAll=1&amp;hlbuild=S200611091546#S200611091546" type="S" version="2.3.0">
+        <detail config="build.cfg" href="http://www.eclipse.org/downloads/download.php?r=1&amp;file=/tools/emf/downloads/drops/2.3.0/S200611091546/" log="buildlog.txt" map="directory.txt"/>
+        <update>http://download.eclipse.org/tools/emf/updates/</update>
+        <downloads>http://www.eclipse.org/emf/downloads/</downloads>
+        <releasenotes>http://www.eclipse.org/emf/news/release-notes.php?version=2.3.0
+          </releasenotes>
+        <releases>
+          <release arch="ALL" os="ALL" type="SDK" ws="ALL">emf-sdo-xsd-SDK-2.3.0M3.zip</release>
+        </releases>
+        <tests>
+          <test href="http://www.eclipse.org/downloads/download.php?r=1&amp;file=/tools/emf/downloads/drops/2.3.0/S200611091546/testResults.php" type="junit">
+            <result arch="ALL" os="ALL" ws="ALL">PASS</result>
+          </test>
+        </tests>
+        <dependencies>
+          <dependency>http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/M20060919-1045/eclipse-SDK-M20060919-1045-linux-gtk.tar.gz</dependency>
+        </dependencies>
+        <coordinated status="COMPLETE"/>
+      </build>
+    </summary>
+  </entry>
+</feed>
diff --git a/bundles/org.eclipse.build.tools/data/builds-uml2-uml-2.1.xml b/bundles/org.eclipse.build.tools/data/builds-uml2-uml-2.1.xml
new file mode 100644
index 0000000..0ae3ea4
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/data/builds-uml2-uml-2.1.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?>
+
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <title>UML2 Builds</title>
+  <link href="http://download.eclipse.org/modeling/mdt/feeds/builds-uml2-uml-2.0.xml" rel="self" type="application/atom+xml"/>
+  <updated>2006-11-14T23:12:21Z</updated>
+  <author>
+    <name>UML2 Build Team</name>
+  </author>
+  <id>http://download.eclipse.org/modeling/mdt/feeds/builds-uml2-uml-2.0.xml</id>
+  <entry>
+    <title>[announce] UML2 2.0.1 M200609210850 is available</title>
+    <link href="http://www.eclipse.org/modeling/mdt/downloads/?project=uml2-uml&amp;showAll=1&amp;hlbuild=M200609210850#M200609210850"/>
+    <id>http://www.eclipse.org/modeling/mdt/downloads/?project=uml2-uml&amp;showAll=1&amp;hlbuild=M200609210850#M200609210850</id>
+    <updated>2006-11-14T23:12:21Z</updated>
+    <summary>
+      <build xmlns="http://www.eclipse.org/2006/BuildFeed" branch="HEAD" datetime="200609210850" href="http://www.eclipse.org/modeling/mdt/downloads/?project=uml2-uml&amp;showAll=1&amp;hlbuild=M200609210850#M200609210850" type="M" version="2.0.1">
+        <detail config="build.cfg" href="http://www.eclipse.org/downloads/download.php?r=1&amp;file=/modeling/mdt/uml2-uml/downloads/drops/2.0.1/M200609210850/" log="buildlog.txt" map="directory.txt"/>
+        <update>http://download.eclipse.org/modeling/mdt/updates/?project=uml2-uml</update>
+        <downloads>http://www.eclipse.org/modeling/mdt/downloads/?project=uml2-uml#uml2-uml</downloads>
+        <releasenotes>http://www.eclipse.org/modeling/mdt/news/release-notes.php?project=uml2-uml&amp;amp;version=2.0.1
+          </releasenotes>
+        <releases>
+          <release arch="ALL" os="ALL" type="SDK" ws="ALL">uml2-SDK-.zip</release>
+        </releases>
+        <tests>
+          <test href="http://www.eclipse.org/downloads/download.php?r=1&amp;file=/modeling/mdt/uml2-uml/downloads/drops/2.0.1/M200609210850/testResults.php" type="junit">
+            <result arch="ALL" os="ALL" ws="ALL">PASS</result>
+          </test>
+        </tests>
+        <dependencies>
+          <dependency>http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/M20060919-1045/eclipse-SDK-M20060919-1045-linux-gtk.tar.gz</dependency>
+          <dependency>http://www.eclipse.org/downloads/download.php?file=/tools/emf/downloads/drops/2.2.1/S200609210005/emf-sdo-xsd-SDK-2.2.1RC2.zip</dependency>
+        </dependencies>
+        <coordinated status="COMPLETE"/>
+      </build>
+    </summary>
+  </entry>
+</feed>
diff --git a/bundles/org.eclipse.build.tools/feedTools.jar b/bundles/org.eclipse.build.tools/feedTools.jar
new file mode 100644
index 0000000..e10ea43
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/feedTools.jar
Binary files differ
diff --git a/bundles/org.eclipse.build.tools/plugin.xml b/bundles/org.eclipse.build.tools/plugin.xml
new file mode 100644
index 0000000..29b7f74
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/plugin.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin
+   id="org.eclipse.build.tools"
+   name="org.eclipse.build.tools"
+   version="1.0.0">
+
+   <runtime>
+   </runtime>
+
+   <extension
+         point="org.eclipse.ant.core.extraClasspathEntries">
+      <extraClasspathEntry
+            library="buildTools.jar">
+      </extraClasspathEntry>
+      <extraClasspathEntry
+            library="feedTools.jar">
+      </extraClasspathEntry>
+   </extension>
+	<!-- Tasks -->
+   <extension point="org.eclipse.ant.core.antTasks">
+       <antTask
+            library="buildTools.jar"
+            name="sysProp"
+            class="org.eclipse.releng.SystemProperty">
+      </antTask>
+      <antTask
+            library="buildTools.jar"
+            name="generateBaseFetchScript"
+            class="org.eclipse.releng.generators.FetchBaseTask">
+      </antTask>
+       <antTask
+            library="buildTools.jar"
+            name="generateTestProperties"
+            class="org.eclipse.releng.generators.TestVersionTracker">
+      </antTask>
+      <antTask
+            library="buildTools.jar"
+            name="verifyCompile"
+            class="org.eclipse.releng.CompileErrorCheck">
+       </antTask>
+       <antTask
+            library="buildTools.jar"
+            name="findMachine"
+            class="org.eclipse.releng.FindMachineTask">
+      </antTask>
+      <antTask
+            library="buildTools.jar"
+            name="modifiedMaps"
+            class="org.eclipse.releng.CvsDiffParser">
+      </antTask>
+      <antTask
+            library="buildTools.jar"
+            name="unpackUpdateJars"
+            class="org.eclipse.releng.UnpackUpdateJars">
+      </antTask>
+      <antTask
+            library="buildTools.jar"
+            name="generateExcludeList"
+            class="org.eclipse.releng.GenerateExcludeListTask">
+      </antTask>
+      
+      
+    <!--   <antTask
+            library="feedTools.jar"
+            name="CreateFeed"
+            class="org.eclipse.releng.generators.rss.RSSFeedCreateFeedTask">
+      </antTask> 
+      <antTask
+            library="feedTools.jar"
+            name="AddEntry"
+            class="org.eclipse.releng.generators.rss.RSSFeedAddEntryTask">
+      </antTask> 
+       <antTask
+            library="feedTools.jar"
+            name="GetProperty"
+            class="org.eclipse.releng.generators.rss.RSSFeedGetPropertyTask">
+      </antTask>
+       <antTask
+            library="feedTools.jar"
+            name="UpdateEntry"
+            class="org.eclipse.releng.generators.rss.RSSFeedUpdateEntryTask">
+      </antTask>
+       <antTask
+            library="feedTools.jar"
+            name="PublishFeed"
+            class="org.eclipse.releng.services.rss.RSSFeedPublisherTask">
+      </antTask>
+       <antTask
+            library="feedTools.jar"
+            name="WatchFeed"
+            class="org.eclipse.releng.services.rss.RSSFeedWatcherTask">
+      </antTask> -->
+    </extension>
+</plugin>
diff --git a/bundles/org.eclipse.build.tools/properties/UpdateBugStateTask.properties b/bundles/org.eclipse.build.tools/properties/UpdateBugStateTask.properties
new file mode 100644
index 0000000..b6c21ad
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/properties/UpdateBugStateTask.properties
@@ -0,0 +1,6 @@
+#this file should be loaded by UpdateBugStateTask.xml when running
+#it is expected to contain login and logincookie, run BugzillaLogin.sh to generate these
+#see below for sample syntax
+
+login=8130
+logincookie=982276000
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/properties/_README.txt b/bundles/org.eclipse.build.tools/properties/_README.txt
new file mode 100644
index 0000000..959c36c
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/properties/_README.txt
@@ -0,0 +1,13 @@
+These files are examples to get you started. Since you cannot commit 
+your files into CVS here, just point the scripts at your own properties
+files, similar in content to these, and run accordingly. 
+
+You can also run the Ant scripts without properties files if you set 
+the attributes statically in your ant script.
+
+Or, you can call the Tasks themselves within another Java class. 
+RSSFeedAddEntryTask is an example of this, in that it optionally 
+calls RSSFeedCreateFeedTask if no existing feed file already exists. 
+RSSFeedGetPropertyTask is a wrapper for RSSFeedUpdateEntryTask, which 
+also queries for an attribute value, but instead of changing it, 
+simply returns it.
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/properties/feedPublish.eclipse.properties b/bundles/org.eclipse.build.tools/properties/feedPublish.eclipse.properties
new file mode 100644
index 0000000..3629240
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/properties/feedPublish.eclipse.properties
@@ -0,0 +1,108 @@
+###############################################################################
+# 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
+###############################################################################
+# properties for: RSSFeedCreateFeedTask.java, RSSFeedAddEntryTask.java, RSSFeedGetPropertyTask.java, RSSFeedUpdateEntryTask.java, RSSFeedPublisherTask.java
+
+# NOTE: if values are commented out (#), they must be passed in to Ant via commandline instead (eg., from emf's promoteToEclipse.sh when promoting a build)
+
+# one of 0|1|2
+debug=1
+
+# if file does not exist, it will be created; if it does, it will be modified
+#file=/builds/transfer/files/master/downloads/builds-eclipse-3.3.xml
+file=../data/builds-eclipse-3.4.xml
+
+# required
+project=Eclipse
+branch=HEAD
+version=3.3.0
+buildID=I20060922-0010
+feedURL=http://download.eclipse.org/eclipse/downloads/builds-eclipse.xml
+buildURL=http://download.eclipse.org/eclipse/downloads/drops/%%buildID%%
+
+# optional build alias, like 3.3M3
+buildAlias=I20060922-0010
+
+# optional build type; one of: N, I, M, S, R; if not provided, will be pulled from buildID above
+buildType=
+
+# optional
+buildDetailURL=http://www.eclipse.org/downloads/download.php?r=1&file=/eclipse/downloads/drops/%%buildID%%/
+buildConfigURL=
+buildLogURL=
+buildMapURL=directory.txt
+
+# optional
+releaseNotesURL=http://download.eclipse.org/eclipse/downloads/drops/%%buildID%%/buildNotes.php
+updateManagerURL=
+downloadsURL=http://download.eclipse.org/eclipse/downloads/
+
+# optional: one of NONE|UNSIGNED|SIGNREADY|BUILDREADY|SIGNED 
+jarSigningStatus=
+
+# optional: one of NONE|BUILDCOMPLETE|UMSITEREADY|CALLISTOSITEREADY|COMPLETE, or 
+coordinatedStatus=
+# optional: timestamp when will be ready (eg., 2006-05-02T20:50:00Z), or 
+# project on which you're waiting (eg., TPTP)
+coordinatedDetails=
+
+# comma or space separated values
+dependencyURLs=
+
+#optional
+JUnitTestURL=http://download.eclipse.org/eclipse/downloads/drops/%%buildID%%/testResults.php
+performanceTestURL=http://download.eclipse.org/eclipse/downloads/drops/%%buildID%%/performance/performance.php
+APITestURL=
+
+# values to set for test results in quints of id,os,ws,arch,status,id,os,ws,arch,status,... 
+JUnitTestResults=\
+0,win32,win,x86,UNKNOWN,\
+0,linux,gtk,x86,UNKNOWN,\
+0,linux,gtk,x86_64,UNKNOWN,\
+0,linux,gtk,ppc,UNKNOWN,\
+0,solaris8,gtk,x86,UNKNOWN,\
+0,aix,motif,x86,UNKNOWN,\
+0,hp-ux,motif,x86,UNKNOWN,\
+0,macosx,carbon,ppc,UNKNOWN
+performanceTestResults=\
+RHEL4-3GHz-2.5GB,linux,gtk,x86,UNKNOWN,\
+winxp-3GHz-2GB,win32,win,x86,UNKNOWN,\
+RHEL3-3GHz-2GB,linux,gtk,x86,UNKNOWN,\
+winxp-2GHz-512MB,win32,win,x86,UNKNOWN
+APITestResults=
+
+# values to set for release bundles in quints of os,ws,arch,name/type,filename,os,ws,arch,name/type,filename... 
+# only need the filename; the full http URL can be assembled from buildURL + "/download.php?dropFile=" + filename or buildURL (switched to ftp://) + "/" + filename
+# eg., win32,win,SDK,eclipse-SDK-%%buildAlias%%-win32.zip,linux,gtk,SDK,eclipse-SDK-%%buildAlias%%-linux-gtk.tar.gz
+Releases=\
+win32,win,x86,SDK,eclipse-SDK-%%buildAlias%%-win32.zip,\
+linux,gtk,x86,SDK,eclipse-SDK-%%buildAlias%%-linux-gtk.tar.gz
+
+########################################
+
+# options for publishing files - need either an scp target or a cvs root/path, or both
+# file (above) is required too
+
+# /path/to/cvs/executable
+cvsExec=/usr/bin/cvs
+# user@cvsServer:/path/to/cvs/root
+cvsRoot=
+# cvs/module/path
+cvsPath=www/news
+# /tmp/folder/path/for/checkout
+cvsTemp=
+
+# /path/to/scp/executable
+scpExec=/usr/bin/scp
+# user@server:/path/to/destination/file.xml - destintion folder MUST ALREADY EXIST
+scpTarget=
+
+# optionally, if the target folder might not exist, you can use ssh to create it before scp'ing the file - only runs if sshMakeDirCommand is defined
+sshExec=/usr/bin/ssh
diff --git a/bundles/org.eclipse.build.tools/properties/feedPublish.emf.properties b/bundles/org.eclipse.build.tools/properties/feedPublish.emf.properties
new file mode 100644
index 0000000..314e027
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/properties/feedPublish.emf.properties
@@ -0,0 +1,100 @@
+###############################################################################
+# 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
+###############################################################################
+# properties for: RSSFeedCreateFeedTask.java, RSSFeedAddEntryTask.java, RSSFeedGetPropertyTask.java, RSSFeedUpdateEntryTask.java, RSSFeedPublisherTask.java
+
+# NOTE: if values are commented out (#), they must be passed in to Ant via commandline instead (eg., from emf's promoteToEclipse.sh when promoting a build)
+
+# one of 0|1|2
+debug=0
+
+# if file does not exist, it will be created; if it does, it will be modified
+#file=/var/www/emf/feeds/builds-emf-2.3.xml
+file=../data/builds-emf-2.3.xml
+
+# required
+project=EMF
+branch=HEAD
+version=2.3.0
+buildID=S200611091546
+feedURL=http://download.eclipse.org/tools/emf/feeds/builds-emf-2.3.xml
+buildURL=http://www.eclipse.org/emf/downloads/?showAll=1&hlbuild=%%buildID%%#%%buildID%%
+
+# optional build alias, like 2.2.1RC2
+buildAlias=2.3.0M3
+
+# optional build type; one of: N, I, M, S, R; if not provided, will be pulled from buildID above
+buildType=
+
+# optional
+buildDetailURL=http://www.eclipse.org/downloads/download.php?r=1&file=/tools/emf/downloads/drops/%%version%%/%%buildID%%/
+buildConfigURL=build.cfg
+buildLogURL=buildlog.txt
+buildMapURL=directory.txt
+
+# optional
+releaseNotesURL=http://www.eclipse.org/emf/news/release-notes.php?version=%%version%%
+updateManagerURL=http://download.eclipse.org/tools/emf/updates/
+downloadsURL=http://www.eclipse.org/emf/downloads/
+
+# optional: one of NONE|UNSIGNED|SIGNREADY|BUILDREADY|SIGNED 
+jarSigningStatus=
+
+# optional: one of NONE|BUILDCOMPLETE|UMSITEREADY|CALLISTOSITEREADY|COMPLETE 
+coordinatedStatus=CALLISTOSITEREADY
+# optional: timestamp when will be ready (eg., 2006-05-02T20:50:00Z), or 
+# project on which you're waiting (eg., TPTP)
+coordinatedDetails=
+
+# comma or space separated values
+dependencyURLs=http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/M20060919-1045/eclipse-SDK-M20060919-1045-linux-gtk.tar.gz
+
+# optional
+JUnitTestURL=http://www.eclipse.org/downloads/download.php?r=1&file=/tools/emf/downloads/drops/%%version%%/%%buildID%%/testResults.php
+performanceTestURL=
+APITestURL=
+
+# values to set for test results in quints of id,os,ws,arch,status,id,os,ws,arch,status,... 
+JUnitTestResults=0,ALL,ALL,ALL,PASS
+performanceTestResults=
+APITestResults=
+
+# values to set for release bundles in quints of os,ws,arch,name/type,filename,os,ws,arch,name/type,filename... 
+# only need the filename; the full http URL can be assembled from buildURL + "/download.php?dropFile=" + filename or buildURL (switched to ftp://) + "/" + filename
+# eg., ALL,ALL,ALL,SDK,emf-sdo-xsd-SDK-%%buildAlias%%.zip
+Releases=ALL,ALL,ALL,SDK,emf-sdo-xsd-SDK-%%buildAlias%%.zip
+
+########################################
+
+# options for publishing files - need either an scp target or a cvs root/path, or both
+# file (above) is required too
+
+# /path/to/cvs/executable
+cvsExec=/usr/bin/cvs
+# user@cvsServer:/path/to/cvs/root
+cvsRoot=nickb@dev.eclipse.org:/cvsroot/org.eclipse
+# cvs/module/path
+cvsPath=www/emf/feeds
+# /tmp/folder/path/for/checkout
+cvsTemp=/tmp/tmp-RSSFeedPublisherTask-emf-nickb
+
+# /path/to/scp/executable
+scpExec=/usr/bin/scp
+# user@server:/path/to/destination/folder/ - destination folder MUST ALREADY EXIST
+scpTarget=nickb@download1.eclipse.org:/home/data/httpd/download.eclipse.org/tools/emf/feeds/
+
+# optionally, if the target folder might not exist, you can use ssh to create it before scp'ing the file
+sshExec=/usr/bin/ssh
+
+# for local testing purposes only
+#cvsRoot=nickb@dbzi:/home/cvs
+#cvsPath=tmp/foo
+#scpTarget=nickb@dbzi:/tmp/foo/
+
diff --git a/bundles/org.eclipse.build.tools/properties/feedPublish.uml2.properties b/bundles/org.eclipse.build.tools/properties/feedPublish.uml2.properties
new file mode 100644
index 0000000..7ca324f
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/properties/feedPublish.uml2.properties
@@ -0,0 +1,94 @@
+###############################################################################
+# 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
+###############################################################################
+# properties for: RSSFeedCreateFeedTask.java, RSSFeedAddEntryTask.java, RSSFeedGetPropertyTask.java, RSSFeedUpdateEntryTask.java, RSSFeedPublisherTask.java
+
+# NOTE: if values are commented out (#), they must be passed in to Ant via commandline instead (eg., from emf's promoteToEclipse.sh when promoting a build)
+
+# one of 0|1|2
+debug=0
+
+# if file does not exist, it will be created; if it does, it will be modified
+#file=/var/www/modeling/mdt/feeds/builds-uml2-uml-2.1.xml
+file=../data/builds-uml2-uml-2.1.xml
+
+# required
+project=UML2
+branch=HEAD
+version=2.1.0
+buildID=I200611141702
+feedURL=http://download.eclipse.org/modeling/mdt/feeds/builds-uml2-uml-2.1.xml
+buildURL=http://www.eclipse.org/modeling/mdt/downloads/?project=uml2-uml&showAll=1&hlbuild=%%buildID%%#%%buildID%%
+
+# optional build alias, like 2.0.1RC1
+buildAlias=
+
+# optional build type; one of: N, I, M, S, R; if not provided, will be pulled from buildID above
+buildType=
+
+# optional
+buildDetailURL=http://www.eclipse.org/downloads/download.php?r=1&file=/modeling/mdt/uml2-uml/downloads/drops/%%version%%/%%buildID%%/
+buildConfigURL=build.cfg
+buildLogURL=buildlog.txt
+buildMapURL=directory.txt
+
+# optional
+releaseNotesURL=http://www.eclipse.org/modeling/mdt/news/release-notes.php?project=uml2-uml&amp;version=%%version%%
+updateManagerURL=http://download.eclipse.org/modeling/mdt/updates/?project=uml2-uml
+downloadsURL=http://www.eclipse.org/modeling/mdt/downloads/?project=uml2-uml#uml2-uml
+
+# optional: one of NONE|UNSIGNED|SIGNREADY|BUILDREADY|SIGNED 
+jarSigningStatus=
+
+# optional: one of NONE|BUILDCOMPLETE|UMSITEREADY|CALLISTOSITEREADY|COMPLETE 
+coordinatedStatus=
+# optional: timestamp when will be ready (eg., 2006-05-02T20:50:00Z), or 
+# project on which you're waiting (eg., TPTP)
+coordinatedDetails=
+
+# comma or space separated values
+dependencyURLs=http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/M20060919-1045/eclipse-SDK-M20060919-1045-linux-gtk.tar.gz http://www.eclipse.org/downloads/download.php?file=/tools/emf/downloads/drops/2.2.1/S200609210005/emf-sdo-xsd-SDK-2.2.1RC2.zip
+
+# optional
+JUnitTestURL=http://www.eclipse.org/downloads/download.php?r=1&file=/modeling/mdt/uml2-uml/downloads/drops/%%version%%/%%buildID%%/testResults.php
+performanceTestURL=
+APITestURL=
+
+# values to set for test results in quints of id,os,ws,arch,status,id,os,ws,arch,status,... 
+JUnitTestResults=0,ALL,ALL,ALL,PASS
+performanceTestResults=
+APITestResults=
+
+# values to set for release bundles in quints of os,ws,arch,name/type,filename,os,ws,arch,name/type,filename... 
+# only need the filename; the full http URL can be assembled from buildURL + "/download.php?dropFile=" + filename or buildURL (switched to ftp://) + "/" + filename
+# eg., ALL,ALL,ALL,SDK,uml2-SDK-%%buildAlias%%.zip
+Releases=ALL,ALL,ALL,SDK,uml2-SDK-%%buildAlias%%.zip
+
+########################################
+
+# options for publishing files - need either an scp target or a cvs root/path, or both
+# file (above) is required too
+
+# /path/to/cvs/executable
+cvsExec=/usr/bin/cvs
+# user@cvsServer:/path/to/cvs/root
+cvsRoot=khussey@dev.eclipse.org:/cvsroot/org.eclipse
+# cvs/module/path
+cvsPath=www/uml2/feeds
+# /tmp/folder/path/for/checkout
+cvsTemp=/tmp/RSSFeedPublisherTask-uml2-khussey
+
+# /path/to/scp/executable
+scpExec=/usr/bin/scp
+# user@server:/path/to/destination/folder/ - destination folder MUST ALREADY EXIST
+scpTarget=khussey@download1.eclipse.org:/home/data/httpd/download.eclipse.org/tools/uml2/feeds/
+
+# optionally, if the target folder might not exist, you can use ssh to create it before scp'ing the file - only runs if sshMakeDirCommand is defined
+sshExec=/usr/bin/ssh
diff --git a/bundles/org.eclipse.build.tools/properties/feedWatch.emf.properties b/bundles/org.eclipse.build.tools/properties/feedWatch.emf.properties
new file mode 100644
index 0000000..ad8f528
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/properties/feedWatch.emf.properties
@@ -0,0 +1,96 @@
+###############################################################################
+# 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
+###############################################################################
+# properties for RSSFeedWatcherTask.java
+
+# one of 0|1|2
+debug=0
+
+# if file does not exist, it will be created; if it does, it will be modified
+file=../data/builds-eclipse-3.4.xml
+
+# required URL for the feed to check
+feedURL=http://download.eclipse.org/downloads/builds-eclipse.xml
+
+# Feed Watch Actions - must be defined as triplets of [xpath0]; [script0]; [commandline0]; [xpath1]; [script1]; [commandline1]; ...
+# must be separated by semi-colons. Line breaks are permitted for clarity and will be String.trim()'d
+feedWatchActions=\
+//*[name()='entry'][1]/*/*[name()='build']/@href;null;null;\
+//*[name()='entry'][1]/*/*[name()='build']/*[name()='releases']/*[@os='linux'][@ws='gtk'][@type='SDK']/text();null;null;\
+//*[name()='entry'][1]/*/*[name()='build']/@type;null;null;\
+
+# PASSING ANT TASK RESULTS TO EXTERNAL SCRIPT AS COMMANDLINE ARGUMENTS
+######################################################################
+
+# The <exec> task that is run within RSSFeedWatcherTask.java, eg.
+#   <exec executable="/path/to/executable"><arg line="-somearg value -someotherarg anothervalue"/></exec>
+# will be passed some default commandline arguments BEFORE any you might want to specify as well (so that you can override them if you wish). 
+
+# For example, if you have these properties set in this file:
+#   debug=1
+#   feedWatchActions=//*[name() = 'entry'][1]/*[name() = 'updated']/text(); ../scripts_rss/sendEmailAlert.sh; -debug 0
+# you will execute the following under the covers of the org.eclipse.releng.services.RSSFeedWatcherTask:
+#   <exec executable="../scripts_rss/sendEmailAlert.sh">
+#     <arg line="-debug 1 -feedURL http://www.eclipse.org/emf/feeds/builds.xml 
+#       -xpath "/*[name() = 'feed']/*[name() = 'updated']/text()" 
+#       -oldvalue "2006-05-04T12:14:33Z" -newvalue "2006-05-11T12:14:33Z" -debug 0"/>
+#   </exec>
+
+# Therefore, you can use these values in the script that you execute (be it sending an email or starting a build or whatever). 
+# If you do NOT want to pass arguments to your executable, use the word "null" instead. This will NOT be passed to your script.
+
+
+# CHECKING ANT TASK RESULTS WITHIN ANT SCRIPT
+#############################################
+
+# If you want to run some <exec> and then check the results of that execution, you can do so in the ant script that calls RSSFeedWatcherTask. 
+# The following properties will be set after you run the task:
+
+#			feedWatchAction.Result.0       - The result returned from the <exec> task; if set, something bad happened
+#			feedWatchAction.Error.0        - Details about the error that occurred, if set, something bad happened
+#			feedWatchAction.Output.0       - Console output from your <exec>, if any; 
+#				this will depend on what you run in response to a feed change, and if that process produces console output
+#			feedWatchAction.OldValue.0 - The original value of the node you asked for, from the previous cached version of the feed
+#			feedWatchAction.NewValue.0 - The changed value of the node you asked for, from the latest version of the feed; 
+#				if you asked for changes to the test status for performance tests, this could be "PASS"; 
+#				if you asked for any changes to the feed, this will be the XML datestamp of the last update, eg: 2006-05-04T12:14:33Z
+#				
+#			If you are watching for more than one condition, you can check for feedWatchAction.Result.0, feedWatchAction.Result.1, feedWatchAction.Result.2, etc.
+
+
+# RUNNING NO EXTERNAL <exec> 
+#############################################
+
+# If you only want to check for changes to the feed and handle the response yourself within Ant instead of an <exec>,
+# simply pass in the executable as "null" to prevent any shell execution from occurring. For example:
+
+# feedWatchActions=//*[name() = 'entry'][1]/*[name() = 'updated']/text(); null; null
+
+# If the node is found and the feed has been changed, the property feedWatchAction.NewValue.0 
+# (and/or feedWatchAction.NewValue.1, feedWatchAction.NewValue.2, etc.) will be set to the new changed value.
+
+
+# XPATH EXAMPLES - SAMPLE NODES TO WATCH FOR CHANGES
+####################################################
+
+# 1. to watch for ANY change in the feed, check the <feed>'s <updated> node (and respond with an email, passing -debug 1 to the mail script)
+# feedWatchActions=/*[name() = 'feed']/*[name() = 'updated']/text(); ../scripts_rss/sendEmailAlert.sh; -debug 1
+
+# 2. to watch for ANY changes in the current build, check the first <entry>'s <updated> node (and respond with an email, no commandline flags)
+# feedWatchActions=//*[name() = 'entry'][1]/*[name() = 'updated']/text(); ../scripts_rss/sendEmailAlert.sh; null
+
+# 3a. to watch for coordinated status changes, check the first <entry>'s <build> node's coordinated="" attribute (and respond using some custom Ant script)
+# feedWatchActions=//*[name() = 'entry'][1]/*/*/*[name() = 'coordinated']/@status; null; null
+
+# 3b. to watch for jar signing status changes, check the first <entry>'s <build> node's jars="" attribute (and respond using some custom Ant script)
+# feedWatchActions=//*[name() = 'entry'][1]/*/*[name() = 'build']/@jars; null; null
+
+# 3c. to watch for changes in the current build's performance test results on linux-gtk (and respond using some custom Ant script)
+# feedWatchActions=//*[name() = 'entry'][1]/*/*/*/*[@type = 'performance']/*[name() = 'result'][@os = 'linux'][@ws = 'gtk']/text(); null; null
diff --git a/bundles/org.eclipse.build.tools/properties/feedWatch.uml2.properties b/bundles/org.eclipse.build.tools/properties/feedWatch.uml2.properties
new file mode 100644
index 0000000..8f76d74
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/properties/feedWatch.uml2.properties
@@ -0,0 +1,93 @@
+###############################################################################
+# 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
+###############################################################################
+# properties for RSSFeedWatcherTask.java
+
+# one of 0|1|2
+debug=2
+
+# if file does not exist, it will be created; if it does, it will be modified
+file=../data/builds-emf-2.3.xml
+
+# required URL for the feed to check
+feedURL=http://www.eclipse.org/emf/feeds/builds-emf-2.3.xml
+
+# Feed Watch Actions - must be defined as triplets of [xpath0]; [script0]; [commandline0]; [xpath1]; [script1]; [commandline1]; ...
+# must be separated by semi-colons. Line breaks are permitted for clarity and will be String.trim()'d
+feedWatchActions=/*[name() = 'feed']/*[name() = 'updated']/text(); null; null
+
+# PASSING ANT TASK RESULTS TO EXTERNAL SCRIPT AS COMMANDLINE ARGUMENTS
+######################################################################
+
+# The <exec> task that is run within RSSFeedWatcherTask.java, eg.
+#   <exec executable="/path/to/executable"><arg line="-somearg value -someotherarg anothervalue"/></exec>
+# will be passed some default commandline arguments BEFORE any you might want to specify as well (so that you can override them if you wish). 
+
+# For example, if you have these properties set in this file:
+#   debug=1
+#   feedWatchActions=//*[name() = 'entry'][1]/*[name() = 'updated']/text(); ../scripts_rss/sendEmailAlert.sh; -debug 0
+# you will execute the following under the covers of the org.eclipse.releng.services.RSSFeedWatcherTask:
+#   <exec executable="../scripts_rss/sendEmailAlert.sh">
+#     <arg line="-debug 1 -feedURL http://www.eclipse.org/emf/feeds/builds.xml 
+#       -xpath "/*[name() = 'feed']/*[name() = 'updated']/text()" 
+#       -oldvalue "2006-05-04T12:14:33Z" -newvalue "2006-05-11T12:14:33Z" -debug 0"/>
+#   </exec>
+
+# Therefore, you can use these values in the script that you execute (be it sending an email or starting a build or whatever). 
+# If you do NOT want to pass arguments to your executable, use the word "null" instead. This will NOT be passed to your script.
+
+
+# CHECKING ANT TASK RESULTS WITHIN ANT SCRIPT
+#############################################
+
+# If you want to run some <exec> and then check the results of that execution, you can do so in the ant script that calls RSSFeedWatcherTask. 
+# The following properties will be set after you run the task:
+
+#			feedWatchAction.Result.0       - The result returned from the <exec> task; if set, something bad happened
+#			feedWatchAction.Error.0        - Details about the error that occurred, if set, something bad happened
+#			feedWatchAction.Output.0       - Console output from your <exec>, if any; 
+#				this will depend on what you run in response to a feed change, and if that process produces console output
+#			feedWatchAction.OldValue.0 - The original value of the node you asked for, from the previous cached version of the feed
+#			feedWatchAction.NewValue.0 - The changed value of the node you asked for, from the latest version of the feed; 
+#				if you asked for changes to the test status for performance tests, this could be "PASS"; 
+#				if you asked for any changes to the feed, this will be the XML datestamp of the last update, eg: 2006-05-04T12:14:33Z
+#				
+#			If you are watching for more than one condition, you can check for feedWatchAction.Result.0, feedWatchAction.Result.1, feedWatchAction.Result.2, etc.
+
+
+# RUNNING NO EXTERNAL <exec> 
+#############################################
+
+# If you only want to check for changes to the feed and handle the response yourself within Ant instead of an <exec>,
+# simply pass in the executable as "null" to prevent any shell execution from occurring. For example:
+
+# feedWatchActions=//*[name() = 'entry'][1]/*[name() = 'updated']/text(); null; null
+
+# If the node is found and the feed has been changed, the property feedWatchAction.NewValue.0 
+# (and/or feedWatchAction.NewValue.1, feedWatchAction.NewValue.2, etc.) will be set to the new changed value.
+
+
+## XPATH EXAMPLES - SAMPLE NODES TO WATCH FOR CHANGES
+####################################################
+
+# 1. to watch for ANY change in the feed, check the <feed>'s <updated> node (and respond with an email, passing -debug 1 to the mail script)
+# feedWatchActions=/*[name() = 'feed']/*[name() = 'updated']/text(); ../scripts_rss/sendEmailAlert.sh; -debug 1
+
+# 2. to watch for ANY changes in the current build, check the first <entry>'s <updated> node (and respond with an email, no commandline flags)
+# feedWatchActions=//*[name() = 'entry'][1]/*[name() = 'updated']/text(); ../scripts_rss/sendEmailAlert.sh; null
+
+# 3a. to watch for coordinated status changes, check the first <entry>'s <build> node's coordinated="" attribute (and respond using some custom Ant script)
+# feedWatchActions=//*[name() = 'entry'][1]/*/*/*[name() = 'coordinated']/@status; null; null
+
+# 3b. to watch for jar signing status changes, check the first <entry>'s <build> node's jars="" attribute (and respond using some custom Ant script)
+# feedWatchActions=//*[name() = 'entry'][1]/*/*[name() = 'build']/@jars; null; null
+
+# 3c. to watch for changes in the current build's performance test results on linux-gtk (and respond using some custom Ant script)
+# feedWatchActions=//*[name() = 'entry'][1]/*/*/*/*[@type = 'performance']/*[name() = 'result'][@os = 'linux'][@ws = 'gtk']/text(); null; null
diff --git a/bundles/org.eclipse.build.tools/schema/_README.txt b/bundles/org.eclipse.build.tools/schema/_README.txt
new file mode 100644
index 0000000..8da3676
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/schema/_README.txt
@@ -0,0 +1,2 @@
+These files are for reference, but are not required to run the 
+Ant scripts + tasks.
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/schema/atom10_build.xsd b/bundles/org.eclipse.build.tools/schema/atom10_build.xsd
new file mode 100644
index 0000000..76bc3e5
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/schema/atom10_build.xsd
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<xsd:schema xmlns:atom="http://www.w3.org/2005/Atom" xmlns:bf="http://www.eclipse.org/2006/BuildFeed" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.w3.org/2005/Atom">

+  <xsd:import namespace="http://www.eclipse.org/2006/BuildFeed" schemaLocation="build.xsd"/> 

+  <xsd:element name="feed">

+    <xsd:complexType>

+      <xsd:sequence>

+        <xsd:element ref="atom:title"/>

+        <xsd:element ref="atom:link"/>

+        <xsd:element ref="atom:updated"/>

+        <xsd:element ref="atom:author"/>

+        <xsd:element ref="atom:id"/>

+        <xsd:element maxOccurs="unbounded" ref="atom:entry"/>

+      </xsd:sequence>

+    </xsd:complexType>

+  </xsd:element>

+  <xsd:element name="author">

+    <xsd:complexType>

+      <xsd:sequence>

+        <xsd:element ref="atom:name"/>

+      </xsd:sequence>

+    </xsd:complexType>

+  </xsd:element>

+  <xsd:element name="name" type="xsd:string"/>

+  <xsd:element name="entry">

+    <xsd:complexType>

+      <xsd:sequence>

+        <xsd:element ref="atom:title"/>

+        <xsd:element ref="atom:link"/>

+        <xsd:element ref="atom:id"/>

+        <xsd:element ref="atom:updated"/>

+        <xsd:element ref="atom:summary"/>

+      </xsd:sequence>

+    </xsd:complexType>

+  </xsd:element>

+  <xsd:element name="summary">

+    <xsd:complexType>

+      <xsd:sequence>

+        <xsd:element maxOccurs="1" minOccurs="1" ref="bf:build"/>

+      </xsd:sequence>

+    </xsd:complexType>

+  </xsd:element>

+

+  <xsd:element name="title" type="xsd:string"/>

+  <xsd:element name="link">

+    <xsd:complexType>

+      <xsd:attribute name="href" type="xsd:anyURI" use="required"/>

+      <xsd:attribute name="rel" type="xsd:NCName"/>

+      <xsd:attribute name="type"/>

+    </xsd:complexType>

+  </xsd:element>

+  <xsd:element name="updated" type="xsd:dateTime"/>

+  <xsd:element name="id" type="xsd:anyURI"/>

+</xsd:schema>

diff --git a/bundles/org.eclipse.build.tools/schema/build.xsd b/bundles/org.eclipse.build.tools/schema/build.xsd
new file mode 100644
index 0000000..0a4909e
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/schema/build.xsd
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.eclipse.org/2006/BuildFeed" elementFormDefault="qualified" targetNamespace="http://www.eclipse.org/2006/BuildFeed">

+

+  <xs:element name="build">

+    <xs:complexType>

+      <xs:sequence>

+        <xs:element maxOccurs="1" minOccurs="0" ref="detail"/>

+        <xs:element maxOccurs="1" minOccurs="0" ref="update"/>

+        <xs:element maxOccurs="1" minOccurs="0" ref="downloads"/>

+        <xs:element maxOccurs="1" minOccurs="0" ref="releasenotes"/>

+        

+        <xs:element maxOccurs="1" minOccurs="0" ref="releases"/>

+        

+        <xs:element maxOccurs="1" minOccurs="0" ref="tests"/>

+        

+        <xs:element maxOccurs="1" minOccurs="0" ref="dependencies"/>

+        <xs:element maxOccurs="1" minOccurs="0" ref="coordinated"/>

+      </xs:sequence>

+      <xs:attribute name="version" type="buildVersion" use="required"/>

+      <xs:attribute name="type" type="enumBuildType" use="required"/>

+      <xs:attribute name="datetime" type="buildDatetime" use="required"/>

+      <xs:attribute name="href" type="xs:anyURI" use="required"/>

+      <xs:attribute name="branch" type="xs:token" use="optional"/>

+      <xs:attribute name="jars" type="enumJarSigningStatus" use="optional"/>

+    </xs:complexType>

+  </xs:element>

+

+  <xs:element name="detail">

+    <xs:complexType>

+      <xs:attribute name="href" type="xs:anyURI" use="required"/>

+      <xs:attribute name="config" type="xs:token" use="optional"/>

+      <xs:attribute name="log" type="xs:token" use="optional"/>

+      <xs:attribute name="map" type="xs:token" use="optional"/>

+	</xs:complexType>

+  </xs:element>

+

+  <xs:element name="update" type="xs:anyURI"/>

+  <xs:element name="downloads" type="xs:anyURI"/>

+  <xs:element name="releasenotes" type="xs:anyURI"/>

+

+  <xs:element name="releases">

+    <xs:complexType>

+      <xs:sequence>

+        <xs:element maxOccurs="unbounded" minOccurs="1" ref="release"/>

+      </xs:sequence>

+    </xs:complexType>

+  </xs:element>

+  <xs:element name="release">

+    <xs:complexType>

+      <xs:simpleContent>

+        <xs:extension base="xs:anyURI">

+          <xs:attribute name="os" type="enumOS" use="required"/>

+          <xs:attribute name="ws" type="enumWS" use="required"/>

+          <xs:attribute name="arch" type="enumArch" use="optional"/>

+          <xs:attribute name="type" type="enumReleaseType" use="required"/>

+        </xs:extension>

+      </xs:simpleContent>

+    </xs:complexType>

+  </xs:element>

+

+  <xs:element name="tests">

+    <xs:complexType>

+      <xs:sequence>

+        <xs:element maxOccurs="unbounded" minOccurs="1" ref="test"/>

+      </xs:sequence>

+    </xs:complexType>

+  </xs:element>

+  <xs:element name="test">

+    <xs:complexType>

+      <xs:sequence>

+        <xs:element maxOccurs="unbounded" minOccurs="1" ref="result"/>

+      </xs:sequence>

+      <xs:attribute name="href" type="xs:anyURI" use="optional"/>

+      <xs:attribute name="type" type="enumTestType" use="required"/>

+    </xs:complexType>

+  </xs:element>

+  <xs:element name="result">

+    <xs:complexType>

+      <xs:simpleContent>

+        <xs:extension base="enumTestStatus">

+          <xs:attribute name="id" type="xs:token" use="optional"/>

+          <xs:attribute name="os" type="enumOS" use="required"/>

+          <xs:attribute name="ws" type="enumWS" use="required"/>

+          <xs:attribute name="arch" type="enumArch" use="optional"/>

+        </xs:extension>

+      </xs:simpleContent>

+    </xs:complexType>

+  </xs:element>

+

+  <xs:element name="dependencies">

+    <xs:complexType>

+      <xs:sequence>

+        <xs:element maxOccurs="unbounded" minOccurs="1" ref="dependency"/>

+      </xs:sequence>

+    </xs:complexType>

+  </xs:element>

+  <xs:element name="dependency" type="xs:anyURI"/>

+

+  <xs:element name="coordinated">

+    <xs:complexType>

+      <xs:simpleContent>

+        <xs:extension base="xs:string">

+        <xs:attribute name="status" type="enumCoordinatedStatus" use="optional"/>      

+      </xs:extension>

+    </xs:simpleContent>

+    </xs:complexType>

+  </xs:element>

+

+  <xs:simpleType name="enumBuildType">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value="N"/>

+      <xs:enumeration value="I"/>

+      <xs:enumeration value="M"/>

+      <xs:enumeration value="S"/>

+      <xs:enumeration value="R"/>

+    </xs:restriction>

+  </xs:simpleType> 

+  

+  <xs:simpleType name="enumTestType">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value="junit"/>

+      <xs:enumeration value="performance"/>

+      <xs:enumeration value="api"/>

+    </xs:restriction>

+  </xs:simpleType> 

+  

+  <xs:simpleType name="enumJarSigningStatus">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value=""/>

+      <xs:enumeration value="NONE"/>

+      <xs:enumeration value="UNSIGNED"/>

+      <xs:enumeration value="SIGNREADY"/>

+      <xs:enumeration value="BUILDREADY"/>

+      <xs:enumeration value="SIGNED"/>

+    </xs:restriction>

+  </xs:simpleType> 

+  

+  <xs:simpleType name="enumCoordinatedStatus">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value=""/>

+      <xs:enumeration value="NONE"/>

+      <xs:enumeration value="BUILDCOMPLETE"/>

+      <xs:enumeration value="UMSITEREADY"/>

+      <xs:enumeration value="CALLISTOSITEREADY"/>

+      <xs:enumeration value="COMPLETE"/>

+    </xs:restriction>

+  </xs:simpleType> 

+  

+  <xs:simpleType name="enumTestStatus">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value=""/>

+      <xs:enumeration value="PASS"/>

+      <xs:enumeration value="PENDING"/>

+      <xs:enumeration value="FAIL"/>

+      <xs:enumeration value="AVAILABLE"/>

+      <xs:enumeration value="SKIPPED"/>

+      <xs:enumeration value="UNKNOWN"/>

+    </xs:restriction>

+  </xs:simpleType>

+

+  <xs:simpleType name="enumOS">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value="aix"/>

+      <xs:enumeration value="hp-ux"/>

+      <xs:enumeration value="linux"/>

+      <xs:enumeration value="macosx"/>

+      <xs:enumeration value="qnx"/>

+      <xs:enumeration value="solaris8"/>

+      <xs:enumeration value="wce"/>

+      <xs:enumeration value="win32"/>

+      <xs:enumeration value="ALL"/>

+      <xs:enumeration value="OTHER"/>

+    </xs:restriction>

+  </xs:simpleType>

+

+  <xs:simpleType name="enumWS">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value="carbon"/>

+      <xs:enumeration value="gtk"/>

+      <xs:enumeration value="motif"/>

+      <xs:enumeration value="photon"/>

+      <xs:enumeration value="win"/>

+      <xs:enumeration value="ALL"/>

+      <xs:enumeration value="OTHER"/>

+    </xs:restriction>

+  </xs:simpleType>

+

+  <xs:simpleType name="enumArch">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value="arm"/>

+      <xs:enumeration value="hp9000"/>

+      <xs:enumeration value="ppc"/>

+      <xs:enumeration value="sparc"/>

+      <xs:enumeration value="x86"/>

+      <xs:enumeration value="x86_64"/>

+      <xs:enumeration value="ALL"/>

+      <xs:enumeration value="OTHER"/>

+    </xs:restriction>

+  </xs:simpleType>

+

+  <xs:simpleType name="enumReleaseType">

+    <xs:restriction base="xs:token">

+      <xs:enumeration value="SDK"/>

+      <xs:enumeration value="runtime"/>

+      <xs:enumeration value="tests"/>

+      <xs:enumeration value="examples"/>

+      <xs:enumeration value="OTHER"/>

+    </xs:restriction>

+  </xs:simpleType>

+

+  <xs:simpleType name="buildDatetime">

+    <xs:restriction base="xs:token">

+      <xs:pattern value="\d{8}-{0,1}\d{0,4}"/>

+    </xs:restriction>

+  </xs:simpleType>

+

+  <xs:simpleType name="buildVersion">

+    <xs:restriction base="xs:token">

+      <xs:pattern value="\d{1,2}.\d{1,2}.\d{1,2}"/>

+    </xs:restriction>

+  </xs:simpleType>

+

+</xs:schema>

diff --git a/bundles/org.eclipse.build.tools/scripts/customTest.xml.template b/bundles/org.eclipse.build.tools/scripts/customTest.xml.template
new file mode 100644
index 0000000..4aba363
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts/customTest.xml.template
@@ -0,0 +1,16 @@
+<project name="Customized testing instructions" default="customSetup">

+

+<!--All properties here are set in test.properties, with the exception of ${tester}-->

+	<target name="customSetup">		

+		<!--copy the script which sets up environment and invokes the tests on test machine-->

+		<exec dir="." executable="${copyClient}">

+			<arg line="${tester}/testAll.bat ${testMachine}:${executionDir}" />

+		</exec>

+			

+		<!--copy in the properties file for testing with alternate vm args-->	

+		<exec dir="." executable="${copyClient}">

+			<arg line="${tester}/vm.properties ${testMachine}:${executionDir}" />

+		</exec>

+	</target>

+

+</project>
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/scripts/publish.xml b/bundles/org.eclipse.build.tools/scripts/publish.xml
new file mode 100644
index 0000000..11e9751
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts/publish.xml
@@ -0,0 +1,179 @@
+<project name="Publish Build" default="default">
+
+<!-- Properties that must be passed to this script:
+	buildDirectory: Path to perform the build in.  (A working directory)
+	buildType:		Type of build (nightly, integration etc.)
+	buildId:		Build name
+	buildLabel:		<buildType>-<buildName>-<timestamp>
+-->
+<property name="result" value="${buildDirectory}/${buildLabel}" />
+
+<!--name of generated index page-->
+<property name="indexFileName" value="index.html" />
+
+<target name="default">
+	<antcall target="countFiles" />
+	<antcall target="generateIndex" />
+	<antcall target="getStaticFiles" />
+</target>
+	
+<target name="generateIndex">
+ 
+  <property name="class" value="org.eclipse.releng.generators.TestResultsGenerator" />
+  <taskdef name="indexResults" classname="${class}" />
+   
+<!--
+	isBuildTested:  true|false  should JUnit plugin test results be used to generate index page
+	dropTokenList:  comma separated list of strings which should be replaced by the fileName attribute settings in the testManifest.xml.
+	xmlDirectoryName:  path to directory containing JUnit plugin test results in xml format (see doc is org.eclipse.test).
+	dropDirectoryName: path to directory containing the result of the build.
+	testResultsTemplateFileName:  path to template file used to generate page with links to JUnit test results	
+	testResultsHtmlFileName:  name of file which will be generated with links to JUnit test results
+	dropHtmlFileName:  name of generated index page
+	hrefTestResultsTargetPath:  relative path from index page to directory containing JUnit html test results
+	hrefCompileLogsTargetPath:  relative path from index page directory containing compilelogs
+	testManifestFileName:	name of xml file containing descriptions of zip types and log files
+
+	
+	
+-->
+	
+	<property name="xmlDirectoryName" value="${result}/testresults/xml" />
+	<property name="dropDirectoryName" value="${result}" />
+	<property name="testResultsTemplateFileName" value="${basedir}/templateFiles/testResults.php.template" />
+	<property name="dropTemplateFileName" value="${basedir}/templateFiles/index.html.template" />
+	<property name="testResultsHtmlFileName" value="testResults.php" />
+	<property name="hrefTestResultsTargetPath" value="testresults/html" />
+	<property name="hrefCompileLogsTargetPath" value="compilelogs" />
+	<property name="compileLogsDirectoryName" value="${result}/compilelogs" />
+	<property name="testManifestFileName" value="${basedir}/testManifest.xml" />
+
+	
+  <indexResults 
+    isBuildTested="${isBuildTested}"
+    buildType="${buildType}"
+    dropTokenList="${dropTokenList}"
+  	platformIdentifierToken="${platformIdentifierToken}"
+  	platformSpecificTemplateList="${platformSpecificTemplateList}"
+    dropHtmlFileName="${indexFileName}"
+    xmlDirectoryName="${xmlDirectoryName}"
+    dropDirectoryName="${dropDirectoryName}"
+    testResultsTemplateFileName="${testResultsTemplateFileName}"
+    dropTemplateFileName="${dropTemplateFileName}"
+    testResultsHtmlFileName="${testResultsHtmlFileName}"
+    hrefTestResultsTargetPath="${hrefTestResultsTargetPath}"
+    hrefCompileLogsTargetPath="${hrefCompileLogsTargetPath}"
+    compileLogsDirectoryName="${compileLogsDirectoryName}"
+    testManifestFileName="${testManifestFileName}"
+  />
+
+	<tstamp>
+		<format property="TODAY" pattern="MMMM d, yyyy"/>
+	</tstamp>
+	
+  	<!--  Insert Build Type descriptor  -->
+  	<antcall target="${buildType}" />
+  	
+  	<!--  Insert Build Date  -->
+	<replace file="${result}/${indexFileName}" token="@date@" value="${TODAY}"/>
+	<replace dir="${result}" value="${TODAY}">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@date@]]></replacetoken>
+	</replace>
+	
+  	<!--  Insert Build Name  -->
+	<replace file="${result}/${indexFileName}" token="@build@" value="${buildId}"/>
+	<replace dir="${result}" value="${buildId}">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@build@]]></replacetoken>
+	</replace>
+
+
+  	<!--  Update timestamp on file to permit overwrite through Ant copy task  -->
+	<touch file="${result}/${indexFileName}" />
+	<touch>
+	    <fileset dir="${result}">
+	    	<include name="**/*.php"/>
+	    </fileset>
+	</touch>
+		
+</target>
+
+
+<target name="getStaticFiles">
+	<!--get static files required in the buildLabel directory-->
+	<copy todir="${result}">
+		<fileset dir="staticDropFiles" />
+	</copy>
+	
+	<!--copy buildnotes from plugin directories-->
+	<mkdir dir="${result}/buildnotes" />
+	<copy todir="${result}/buildnotes" flatten="true">
+		<fileset dir="${buildDirectory}/plugins" includes="**/buildnotes_*.html" />
+	</copy>
+</target>
+
+<target name="countFiles">
+<!--	files.count is a file that should exist in the drop directory with a count of the zip files in the same directory.
+		It is required to generate a link to the build on the downloads page.
+ -->
+ <taskdef name="countFiles" classname="org.eclipse.releng.generators.FileCounter" />
+ 
+	<countFiles 
+		sourceDirectory="${result}"
+		filterString=".zip,.tar.gz"
+		outputFile="${result}/files.count"
+	/>
+	
+</target>
+
+<!--Build type descriptors-->
+<target name="I">
+	<replace file="${result}/${indexFileName}" token="@type@" value="Integration"/>
+	<replace dir="${result}" value="Integration">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@type@]]></replacetoken>
+	</replace>
+</target>
+	
+<target name="N">
+	<replace file="${result}/${indexFileName}" token="@type@" value="Nightly"/>
+	<replace dir="${result}" value="Nightly">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@type@]]></replacetoken>
+	</replace>
+</target>
+	
+<target name="M">
+	<replace file="${result}/${indexFileName}" token="@type@" value="Maintenance"/>
+	<replace dir="${result}" value="Maintenance">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@type@]]></replacetoken>
+	</replace>
+</target>
+
+<target name="R">
+	<replace file="${result}/${indexFileName}" token="@type@" value="Release"/>
+	<replace dir="${result}" value="Release">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@type@]]></replacetoken>
+	</replace>
+</target>
+	
+<target name="S">
+	<replace file="${result}/${indexFileName}" token="@type@" value="Stable"/>
+	<replace dir="${result}" value="Stable">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@type@]]></replacetoken>
+	</replace>
+</target>
+
+<target name="T">
+	<replace file="${result}/${indexFileName}" token="@type@" value="Test"/>
+	<replace dir="${result}" value="Test">
+	  <include name="**/*.php"/>
+	  <replacetoken><![CDATA[@type@]]></replacetoken>
+	</replace>
+</target>
+	
+</project>
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/scripts/test.xml b/bundles/org.eclipse.build.tools/scripts/test.xml
new file mode 100644
index 0000000..81762df
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts/test.xml
@@ -0,0 +1,145 @@
+<project name="Targets to run Automated Tests Locally and Remotely" default="main" basedir=".">
+
+<!--
+This script must be called with the ${tester} property set.
+
+The testing.properties file must contain definitions for all other properties in this script if they are not
+set in a calling script or command line.  See test.properties.template for property descriptions.
+-->
+
+<property name="customTest" value="${tester}/customTest.xml" />
+<property name="testing.properties" value="${tester}/testing.properties" />
+<property file="${testing.properties}" />
+<property name="dropLocation" value="${buildDirectory}" />
+	
+<target name="main">
+	<antcall target="${testTarget}" />
+</target>
+
+<!--
+Targets for setting up and running tests remotely
+It is assumed that keys are set up on test machines to permit connections without user name and password prompts.
+-->
+<target name="runtests-remote" depends="setRemoteLoginClient,setRemoteCopyClient">
+	<property name="testResults" value="${dropLocation}/${buildLabel}/testresults" />
+	
+	<exec dir="." executable="${loginClient}">
+		<arg line="${testMachine} mkdir ${testDir}" />
+	</exec>
+	
+	<!--install the vm used for testing-->
+	<antcall target="installVmForRemote" />
+	
+	<!--set up the automated testing framework-->
+	<exec dir="." executable="${copyClient}">
+		<arg line="${dropLocation}/${buildLabel}/${testFramework} ${testMachine}:${testDir}" />
+	</exec>
+	<exec dir="." executable="${loginClient}">
+		<arg line="${testMachine} unzip -o -qq ${testDir}/${testFramework} -d ${testDir}" />
+	</exec>
+	<exec dir="." executable="${copyClient}">
+		<arg line="${dropLocation}/${buildLabel}/${runtime} ${testMachine}:${executionDir}" />
+	</exec>
+	
+	<!--callback to custom script for post setup-->
+	<ant antfile="${customTest}" target="customSetup" dir="${basedir}"/>
+	
+	<exec dir="." executable="${loginClient}">
+		<arg line="${testMachine} ${testScript} ${args}" />
+	</exec>
+	
+	<!--${testResults} and ${testResults}/consolelogs must exist before rcp and scp copy operations.
+	  Directories contents are copied flattened if the destination directories don't exist.-->
+	<mkdir dir="${testResults}/consolelogs" />
+	
+	<exec dir="." executable="${copyClient}">
+		<arg line="-r ${testMachine}:${executionDir}/results/* ${testResults}"/>
+	</exec>
+	<!--  copy the console log from testing  -->
+	<exec dir="." executable="${copyClient}">
+		<arg line="-r ${testMachine}:${executionDir}/${consolelog} ${testResults}/consolelogs"/>
+	</exec>
+</target>
+
+<target name="setRemoteLoginClient">
+	<!--use rsh if rsh is set, otherwise use default, ssh-->
+	<condition property="loginClient" value="rsh">
+		<isset property="rsh" />
+	</condition>
+	<!--default remote login client-->
+	<property name="loginClient" value="ssh" />	
+</target>
+
+<target name="setRemoteCopyClient">
+	<!--use rcp if rsh is set, otherwise use default, scp-->
+	<condition property="copyClient" value="rcp">
+		<isset property="rsh" />
+	</condition>
+	<!--default remote copy client-->
+	<property name="copyClient" value="scp" />	
+</target>
+
+<target name="installVmForRemote" unless="skipVmInstall">
+	<available file="${vmDest}" property="vmExists" />
+	<antcall target="getVM" />
+	
+	<exec dir="." executable="${copyClient}">
+		<arg line="${vmDest} ${testMachine}:${testDir}" />
+	</exec>
+	
+	<exec dir="." executable="${loginClient}">
+		<arg line="${testMachine} ${vmInstallCommand}" />
+	</exec>
+</target>
+
+
+<!--
+
+Targets for setting up and running tests locally
+
+-->
+<target name="runtests-local">
+	<delete dir="${testDir}" quiet="true"/>
+	<mkdir dir="${testDir}" />
+	<property name="testResults" value="${dropLocation}/${buildLabel}/testresults" />
+	
+	<!--set up testing directory-->
+	<unzip src="${dropLocation}/${buildLabel}/${testFramework}" dest="${testDir}" />
+	
+	<!--install the vm used for testing-->
+	<antcall target="installVmForLocal" />
+	
+	<!--copy in the runtime to test-->
+	<copy todir="${executionDir}" file="${dropLocation}/${buildLabel}/${runtime}" />
+
+	<!--callback to custom script for additional setup-->
+	<ant antfile="${customTest}" target="customSetup" dir="${basedir}" />
+
+	<!--run the tests-->
+	<exec dir="${executionDir}" executable="${testExecutable}">
+  		<arg line="${args}" />
+	</exec>
+
+	<mkdir dir="${testResults}" />
+	<mkdir dir="${testResults}/consolelogs" />
+
+	<copy todir="${testResults}">
+		<fileset dir="${executionDir}/results" />
+	</copy>
+
+	<copy todir="${testResults}/consolelogs" file="${executionDir}/${consolelog}" />
+</target>
+
+<target name="installVmForLocal" unless="skipVmInstall">
+	<available file="${vmDest}" property="vmExists" />
+	<antcall target="getVM" />
+	<exec dir="${testDir}" executable="${vmInstallExecutable}">	
+		<arg line="${vmInstallCommand}" />
+	</exec>
+</target>
+
+<target name="getVM" unless="vmExists">
+	<get src="${vmUrl}" dest="${vmDest}" usetimestamp="true"/>
+</target>
+
+</project>
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/scripts/testing.properties.template b/bundles/org.eclipse.build.tools/scripts/testing.properties.template
new file mode 100644
index 0000000..3e9aea3
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts/testing.properties.template
@@ -0,0 +1,52 @@
+###############################################################################

+# Copyright (c) 2004, 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

+###############################################################################

+#target to call in test.xml:  runtests-local | runtests-remote

+testTarget=runtests-remote

+

+#use rsh as protocol for login and copying files.

+rsh=true

+

+#directory on test machine where automated testing framework will be installed.  ${buildLabel}

+#set by calling script

+testDir=c:\\buildtest\\${buildLabel}

+

+#name of zip file containing automated testing framework and JUnit test plug-ins

+testFramework=eclipse-Automated-Tests-${buildId}.zip

+

+#name of runtime to install and test

+runtime=eclipse-SDK-${buildId}-win32.zip

+

+#directory where test scripts are launched

+executionDir=${testDir}\\eclipse-testing

+

+#name of file that captures console output from running JUnit plug-in tests

+consolelog=win32_consolelog.txt

+

+#name of script to execute on remote machine

+testScript=start /min ${executionDir}\\testAll.bat

+

+#arguments to $(testScript)

+args=${executionDir} win32_consolelog.txt

+

+#name and path to downloaded vm.

+vmDest=jdk-1_4_2_14-fcs-bin-b03-windows-i586-22_jan_2007.zip

+

+#cvsroot

+cvsRoot=:pserver:anonymous@ottcvs1:/home/cvs/releng

+

+#path to tar.gz or zip of vm used to run tests

+vmDest=jdks/${vmDest}

+

+#command to execute to install the vm

+vmInstallCommand="unzip -o -qq ${testDir}\\${vmDest} -d ${testDir}"

+

+#skip default vm installation

+#skipVmInstall=true

diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/_README.txt b/bundles/org.eclipse.build.tools/scripts_bugzilla/_README.txt
new file mode 100644
index 0000000..760c397
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/_README.txt
@@ -0,0 +1,15 @@
+UpdateBugStateTask is an ant task used for updating the status of 1 or 
+more bugs that have been resolved with a particular build of any Eclipse 
+project. It can also be used for updating a specified list of bugs which 
+are all in a specific state.
+
+Before you can use this tool, you must run the UpdateBugStateTask.sh script 
+to generate a Bugzilla login session. This should take less than a minute, 
+and is a one time thing (although it is possible that the Bugzilla server 
+will clear the stored sessions, in this case simply run the 
+UpdateBugStateTask.sh script again).
+
+UpdateBugStateTask requires JDK 1.4 or greater.
+
+See the sample UpdateBugStateTask.xml build script for a full list and 
+description of the task options.
diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/bugzillaLogin.sh b/bundles/org.eclipse.build.tools/scripts_bugzilla/bugzillaLogin.sh
new file mode 100644
index 0000000..9edd9f5
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/bugzillaLogin.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+echo -n "Bugzilla login or email address: "
+read LOGIN
+echo -n "Bugzilla password: "
+stty -echo
+read PW
+stty echo
+echo
+echo "Logging in and fetching cookie values..."
+echo
+
+#older versions of curl (like the one on emf) don't have -k
+OUT=$(curl -k 2>&1| grep unknown)
+if [[ $OUT ]];
+then
+	CURLARG=""
+else
+	CURLARG="-k"
+fi
+HEADERS=$(mktemp)
+curl -s -S $CURLARG 'https://bugs.eclipse.org/bugs/index.cgi' -d "GoAheadAndLogIn=1&Bugzilla_login=$LOGIN&Bugzilla_password=$PW" -D $HEADERS >/dev/null
+PW="$RANDOM $RANDOM $RANDOM $RANDOM"
+VALUES=$(grep Set-Cookie $HEADERS | sed -e 's/.\{1,\}Bugzilla_\(login\(cookie\)\?=[0-9]\{1,\}\).\{1,\}/\1/')
+rm -fr $HEADERS
+
+if [[ $VALUES ]];
+then
+	#alternatively, you can do ./UpdateBugStateTask.sh 2>../properties/UpdateBugStateTask.properties
+	echo "Paste the following into UpdateBugStateTask.properties:"
+	echo "---- 8< ---- cut here ---- 8< ----"
+	echo ""
+	if [[ -e /proc/self/fd/2 ]];
+	then
+		echo "$VALUES" >/proc/self/fd/2
+	else
+		echo "$VALUES"
+	fi
+	echo ""
+	echo "---- 8< ---- cut here ---- 8< ----"
+else
+	echo "Bugzilla didn't send us any cookie values, this means that either:"
+	echo "   - you mistyped your login/password"
+	echo "   - you can't reach Bugzilla for some reason"
+	echo
+	echo "Make sure that you can reach <https://bugs.eclipse.org/bugs/index.cgi> and try again."
+fi
diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsJar.sh b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsJar.sh
new file mode 100644
index 0000000..0e395b8
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsJar.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+$ANT_HOME/bin/ant -f buildBugToolsJar.xml;
diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsJar.xml b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsJar.xml
new file mode 100644
index 0000000..1d1f63e
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsJar.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<project name="Build bugTools.jar" default="buildBugToolsJar" basedir="../">
+	<target name="buildBugToolsJar">
+	  <echo message="Compiling bugTools ..."/>
+	  <delete file="bugTools.jar"/>
+	  <delete dir="bin/"/>
+  	  <mkdir dir="bin"/>
+	  <javac 
+	  	source="1.4"
+	  	target="1.4"
+		srcdir="."
+	    destdir="bin/"
+	    classpath="../org.eclipse.releng.basebuilder/plugins/org.apache.ant_1.6.5/lib/ant.jar"
+		includes="src_bugzilla/**/*.java, src_bugzilla/**/messages.properties"/>
+	  <echo message="Jarring bugTools.jar ..."/>
+	  <jar 
+		destfile="bugTools.jar" 
+		includes="**/*.class" 
+		basedir="bin"
+	    update="true"/>
+	  <jar 
+		destfile="bugTools.jar" 
+		includes="**/messages.properties" 
+		basedir="src_bugzilla"
+	    update="true"/>
+	</target>
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsZip.sh b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsZip.sh
new file mode 100644
index 0000000..45fe1f9
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsZip.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+$ANT_HOME/bin/ant -f buildBugToolsZip.xml;
diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsZip.xml b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsZip.xml
new file mode 100644
index 0000000..918e2e6
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/buildBugToolsZip.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<project name="Build bugTools.zip" default="buildBugToolsZip" basedir="../">
+	<target name="buildBugToolsZip">
+		<property name="zipfile" value="org.eclipse.build.tools_bugTools.zip"/>
+		<ant antfile="scripts_bugzilla/buildBugToolsJar.xml" target="buildBugToolsJar" inheritall="true"/>
+		<echo message="Bundling ${zipfile} ..."/>
+		<delete file="${zipfile}"/>
+		<zip
+			destfile="${zipfile}"
+			includes=".classpath, .project, 
+					bugTools.jar, 
+					src_bugzilla/**/*, 
+					scripts_bugzilla/*,
+					properties/UpdateBugStateTask.properties"
+			basedir="."
+			update="true"/>
+	</target>
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/updateBugState.sh b/bundles/org.eclipse.build.tools/scripts_bugzilla/updateBugState.sh
new file mode 100644
index 0000000..e0411f4
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/updateBugState.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-1.4;
+export ANT_HOME=/opt/apache-ant-1.6;
+
+#aasemble the command and its classpath
+CLASSPATH="$JAVA_HOME/lib/rt.jar";
+# note that com.sun.org.apache.xerces (Sun JDK 1.5, rt.jar) != org.apache.xerces (Ant 1.6.5, xercesImpl.jar) so must remove from classpath
+#for f in `find $ANT_HOME/lib  -maxdepth 1 -name "*.jar" -type f -not -name "xercesImpl.jar"`; do CLASSPATH=$CLASSPATH":"$f; done
+CLASSPATH=$CLASSPATH":"$ANT_HOME/lib/ant.jar":"$ANT_HOME/lib/ant-launcher.jar;
+cmd="$JAVA_HOME/bin/java \
+  -Dant.home=$ANT_HOME \
+  -Dant.library.dir=$JAVA_HOME/lib \
+  -classpath $CLASSPATH:../bugTools.jar \
+  org.apache.tools.ant.launch.Launcher \
+  -buildfile updateBugState.xml";
+
+if [[ $debug -gt 0 ]]; then 
+	echo "Running ..."; echo ""; echo $cmd | sed -e "s/ \-/#  \-/g" -e "s/:/#    :/g" | tr "#" "\n"; echo "";
+fi
+
+# run the command
+$cmd;
diff --git a/bundles/org.eclipse.build.tools/scripts_bugzilla/updateBugState.xml b/bundles/org.eclipse.build.tools/scripts_bugzilla/updateBugState.xml
new file mode 100644
index 0000000..b26b905
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_bugzilla/updateBugState.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+
+<project name="Update Bug State" default="updateBugState">
+
+	<taskdef name="UpdateBugState" 
+	  classname="org.eclipse.releng.services.bugzilla.UpdateBugStateTask" 
+	  classpath="../bugTools.jar"/>
+
+	<!-- 1. Define property file -->
+	<target name="init">
+		<property file="../properties/UpdateBugStateTask.properties"/>
+	</target>
+
+	<!-- 2. Update Bugzilla state for specific criteria -->
+	<target name="updateBugState" depends="init" description="Update Bugzilla state for specific criteria">
+		<!--
+		UpdateBugStateTask takes a few parameters, some are required and some are optional, see below.
+		milestone, product and resolution correspond to the Bugzilla items of the same name
+
+		login - required, your userid for Bugzilla (can be generated by UpdateBugStateTask.sh)
+
+		logincookie - required, your logincookie for Bugzilla (can be generated by UpdateBugStateTask.sh)
+
+		status - required, only query for bugs in this state
+		one of UNCONFIRMED, NEW, ASSIGNED, or REOPENED
+
+		bugList - optional, specify the list of bugs to update
+		a non-digit (space, comma, semicolon, etc) delimited list of integers corresponding to Bugzilla bugIDs
+		if you specify this, you cannot specify milestone or product
+
+		product - required (if no bugList), only query for bugs on this product
+		this cargument annot be combined with bugList
+
+		buildAlias - optional label, the task adds a comment to Bugzilla of the form
+		"Fixed in $buildAlias ($buildID).", otherwise uses buildID or just "Fixed in latest build."
+
+		buildID - optional label, the task adds a comment to Bugzilla of the form
+		"Fixed in $buildAlias ($buildID).", otherwise uses buildAlias or just "Fixed in latest build."
+		of the format YYYYMMDDHHMM, [IMNRS]YYYYMMDDHHMM, [IMNRS]-YYYYMMDDHHMM
+		YYYYMMDD-HHMM, [IMNRS]YYYYMMDD-HHMM, or [IMNRS]-YYYYMMDD-HHMM
+
+		endDate - optional, only query for bugs last updated before this timestamp
+		(that is, bugs after this timestamp will be ignored). Must be in the form yyyymmddHHMM or yyyymmdd0000.
+		this argument cannot be combined with bugList
+
+		milestone - optional, only query for bugs that have this milestone
+		this argument cannot be combined with bugList
+
+		resolution - optional, this is what the bug's new state will be
+		one of FIXED, INVALID, WONTFIX, LATER, REMIND, or WORKSFORME (default: FIXED)
+
+		debug - optional, use this to control task verbosity
+		0 - only print a message if the task needs to abort
+		1 - show progress (and errors, if any) (default)
+		2 - maximum verbosity, probably only useful if something goes wrong
+		values < 0 are equivalent to 0
+		values > 2 are equivalent to 2
+		
+		Examples: -->
+		
+		<!--
+		1. find all bugs last updated before 2006/06/01 17:38 from product EMF in the ASSIGNED state;
+		   move to FIXED using buildID S200606051102 and buildAlias 2.2.0RC7. Comment will be:
+		     "Fixed in 2.2.0RC7 (S200606051102)."
+		-->
+		<UpdateBugState login="${login}" logincookie="${logincookie}" 
+			status="ASSIGNED" product="EMF" endDate="200606011738" 
+			resolution="FIXED" buildID="S200606051102" buildAlias="2.2.0RC7" 
+		/>
+		
+		<!--
+		2. find all bugs from product EMF targetted for milestone "2.2" which are in the ASSIGNED state; 
+		   move to FIXED. Comment will be:
+			 "Fixed in latest build."
+		-->
+		<UpdateBugState debug="2" login="${login}" logincookie="${logincookie}"
+              status="ASSIGNED" product="EMF"
+				resolution="FIXED" milestone="2.2"
+        />
+		
+		<!--
+		3. find all bugs in given list (131811 144877 144890 144989) which are still in the ASSIGNED state 
+		   (to avoid duplicate updates); move to FIXED using buildID I200606051102. Comment will be:
+		     "Fixed in I200606051102."
+		-->
+		<UpdateBugState login="${login}" logincookie="${logincookie}" 
+			status="ASSIGNED" bugList="131811 144877 144890 144989" 
+			resolution="FIXED" buildID="S200606051102" 
+		/> 
+		
+	</target>
+</project>
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/README.searchcvs.setup.mysql.txt b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/README.searchcvs.setup.mysql.txt
new file mode 100644
index 0000000..865ed36
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/README.searchcvs.setup.mysql.txt
@@ -0,0 +1,25 @@
+Search CVS is a tool for converting cvs commits into a mysql database, which 
+can be updated without worry of data duplication. The database is then searchable 
+via a web page run on www.eclipse.org.
+
+MySQL Server Quick Setup:
+
+1. Extract the searchcvs/ folder from cvs and place it in your $HOME folder 
+on some server running MySQL 5 and PHP 4 or 5 with the MySQL module for MySQL 5 compiled in. 
+
+2. Customize setup.sh for your needs and run it to extract your code from cvs.
+
+3. Load database tables using .dump files provided.
+
+4. Edit includes/parsecvs-dbaccess.php. Run parsecvs.sh.
+
+5. To make nightly updates to the data (reflecting the day's cvs commits), run parsecvs.sh as a cron like this:
+
+#Min    Hr      Mday    Month   Wday    Cmd
+00 22 * * * $HOME/searchcvs/parsecvs.sh 2>&1 1> $HOME/searchcvs/parsecvs.log.txt
+
+----
+
+Additional details on setup and on using this tool can be found here:
+
+http://wiki.eclipse.org/index.php/Search_CVS
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/README.searchcvs.setup.web.txt b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/README.searchcvs.setup.web.txt
new file mode 100644
index 0000000..2021731
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/README.searchcvs.setup.web.txt
@@ -0,0 +1,13 @@
+Search CVS is a tool for converting cvs commits into a mysql database, which 
+can be updated without worry of data duplication. The database is then searchable 
+via a web page run on www.eclipse.org.
+
+Web Server Quick Setup:
+
+1. See searchcvs/www/README.
+
+----
+
+Additional details on setup and on using this tool can be found here:
+
+http://wiki.eclipse.org/index.php/Search_CVS
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/includes/parsecvs-dbaccess.php b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/includes/parsecvs-dbaccess.php
new file mode 100644
index 0000000..ca5187f
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/includes/parsecvs-dbaccess.php
@@ -0,0 +1,5 @@
+<?php
+    $dbhost = "mysqlserver";
+    $dbuser = "dbaccessrw";
+    $dbpass = "dbaccessrwpassword";
+?>
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-modelingschema.dump b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-modelingschema.dump
new file mode 100644
index 0000000..74a3807
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-modelingschema.dump
@@ -0,0 +1,161 @@
+-- MySQL dump 10.10

+-- 

+-- You can load this file into mysql using: 

+-- $ mysql -u root -p < mysql-modelingschema.dump

+-- 

+-- Note that the database name for the stored CVS data is `modeling`, as in the Eclipse Modeling Project. 

+-- You'll probably want to change that for your project's needs.

+-- 

+-- Host: localhost    Database: modeling

+-- ------------------------------------------------------

+-- Server version	5.0.22-log

+

+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;

+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;

+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;

+/*!40101 SET NAMES utf8 */;

+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;

+/*!40103 SET TIME_ZONE='+00:00' */;

+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;

+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;

+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;

+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

+

+DROP DATABASE IF EXISTS `modeling`;

+CREATE DATABASE `modeling`;

+

+USE `modeling`;

+

+--

+-- Table structure for table `bugs`

+--

+

+DROP TABLE IF EXISTS `bugs`;

+CREATE TABLE `bugs` (

+  `fid` smallint(5) unsigned NOT NULL,

+  `revision` varchar(15) NOT NULL,

+  `bugid` mediumint(8) unsigned NOT NULL,

+  PRIMARY KEY  (`fid`,`revision`,`bugid`)

+) ENGINE=MyISAM DEFAULT CHARSET=utf8;

+

+--

+-- Dumping data for table `bugs`

+--

+

+

+/*!40000 ALTER TABLE `bugs` DISABLE KEYS */;

+LOCK TABLES `bugs` WRITE;

+UNLOCK TABLES;

+/*!40000 ALTER TABLE `bugs` ENABLE KEYS */;

+

+--

+-- Table structure for table `commits`

+--

+

+DROP TABLE IF EXISTS `commits`;

+CREATE TABLE `commits` (

+  `fid` smallint(5) unsigned NOT NULL,

+  `revision` varchar(15) NOT NULL,

+  `date` datetime NOT NULL,

+  `author` varchar(20) NOT NULL,

+  `state` enum('Exp','dead') NOT NULL,

+  `linesplus` smallint(5) unsigned default NULL,

+  `linesminus` smallint(5) unsigned default NULL,

+  `message` text NOT NULL,

+  PRIMARY KEY  (`fid`,`revision`),

+  KEY `author` (`author`),

+  FULLTEXT KEY `message` (`message`)

+) ENGINE=MyISAM DEFAULT CHARSET=utf8;

+

+--

+-- Dumping data for table `commits`

+--

+

+

+/*!40000 ALTER TABLE `commits` DISABLE KEYS */;

+LOCK TABLES `commits` WRITE;

+UNLOCK TABLES;

+/*!40000 ALTER TABLE `commits` ENABLE KEYS */;

+

+--

+-- Table structure for table `cvsfiles`

+--

+

+DROP TABLE IF EXISTS `cvsfiles`;

+CREATE TABLE `cvsfiles` (

+  `fid` int(11) NOT NULL auto_increment,

+  `cvsname` text NOT NULL,

+  `head` varchar(15) NOT NULL,

+  `keyword_subs` enum('b','k','kv','kvl','v') NOT NULL,

+  `project` varchar(50) NOT NULL,

+  PRIMARY KEY  (`fid`),

+  KEY `project` (`project`),

+  UNIQUE KEY `cvsname` (`cvsname`(333))

+) ENGINE=MyISAM DEFAULT CHARSET=utf8;

+

+--

+-- Dumping data for table `cvsfiles`

+--

+

+

+/*!40000 ALTER TABLE `cvsfiles` DISABLE KEYS */;

+LOCK TABLES `cvsfiles` WRITE;

+UNLOCK TABLES;

+/*!40000 ALTER TABLE `cvsfiles` ENABLE KEYS */;

+

+--

+-- Table structure for table `filetags`

+--

+

+DROP TABLE IF EXISTS `filetags`;

+CREATE TABLE `filetags` (

+  `fid` mediumint(8) unsigned NOT NULL,

+  `tid` smallint(5) unsigned NOT NULL,

+  `revision` varchar(15) NOT NULL,

+  PRIMARY KEY  (`fid`,`tid`),

+  KEY `tid` (`tid`)

+) ENGINE=MyISAM DEFAULT CHARSET=utf8;

+

+--

+-- Dumping data for table `filetags`

+--

+

+

+/*!40000 ALTER TABLE `filetags` DISABLE KEYS */;

+LOCK TABLES `filetags` WRITE;

+UNLOCK TABLES;

+/*!40000 ALTER TABLE `filetags` ENABLE KEYS */;

+

+--

+-- Table structure for table `tags`

+--

+

+DROP TABLE IF EXISTS `tags`;

+CREATE TABLE `tags` (

+  `tid` int(11) NOT NULL auto_increment,

+  `tagname` varbinary(50) NOT NULL,

+  `tagdate` datetime default NULL,

+  PRIMARY KEY  (`tid`),

+  UNIQUE KEY `tagname` (`tagname`),

+  KEY `tagdate` (`tagdate`)

+) ENGINE=MyISAM DEFAULT CHARSET=utf8;

+

+--

+-- Dumping data for table `tags`

+--

+

+

+/*!40000 ALTER TABLE `tags` DISABLE KEYS */;

+LOCK TABLES `tags` WRITE;

+UNLOCK TABLES;

+/*!40000 ALTER TABLE `tags` ENABLE KEYS */;

+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

+

+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;

+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;

+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;

+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;

+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

+

diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-modelingschema.truncate b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-modelingschema.truncate
new file mode 100644
index 0000000..a5b06ee
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-modelingschema.truncate
@@ -0,0 +1,9 @@
+-- You can load this file into mysql using:

+-- $ mysql -u root -p < mysql-modelingschema.truncate

+

+USE `modeling`;

+TRUNCATE TABLE `bugs`;

+TRUNCATE TABLE `commits`;

+TRUNCATE TABLE `cvsfiles`;

+TRUNCATE TABLE `filetags`;

+TRUNCATE TABLE `tags`;

diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-users.dump b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-users.dump
new file mode 100644
index 0000000..6a487fa
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/mysql-users.dump
@@ -0,0 +1,19 @@
+-- Note: use the same username/password values here as set in 

+--   includes/parsecvs-dbaccess.php and in

+--   www/projectName/includes/searchcvs-dbaccess.php

+--

+-- You can load this file into mysql using: 

+-- $ mysql -u root -p < mysql-users.dump

+

+-- Note that the database name for the stored CVS data is `modeling`, as in the Eclipse Modeling Project. 

+-- You'll probably want to change that for your project's needs.

+

+-- Local access (cvs parser) granted to read/write for user 'dbaccessrw' with password 'dbaccessrwpassword'

+CREATE USER 'dbaccessrw'@'localhost' IDENTIFIED BY 'dbaccessrwpassword';

+GRANT USAGE ON *.* TO 'dbaccessrw'@'localhost' IDENTIFIED BY 'dbaccessrwpassword' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;

+GRANT ALL PRIVILEGES ON `dbaccessrw`.* TO 'dbaccessrw'@'localhost';

+

+-- Remote access (website) granted to read only for user 'dbaccessro' with password 'dbaccessropassword'

+CREATE USER 'dbaccessro'@'%eclipse.org' IDENTIFIED BY 'dbaccessropassword';

+GRANT USAGE ON *.* TO 'dbaccessro'@'%eclipse.org' IDENTIFIED BY 'dbaccessropassword' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;

+GRANT SELECT ON `modeling`.* TO 'dbaccessro'@'%eclipse.org';

diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/parsecvs.php b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/parsecvs.php
new file mode 100644
index 0000000..9b9c4fd
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/parsecvs.php
@@ -0,0 +1,163 @@
+#!/usr/bin/php
+
+<?php
+/*
+RCS file: /cvsroot/tools/org.eclipse.emf/.cvsignore,v //store
+Working file: .cvsignore //don't need to store, basename(rcs file), minus ,v?
+head: 1.1 //maybe store, could be calculated based on tags table?
+branch: //probably store, what does this actually mean?, never used (always empty)?
+locks: strict //store, though not used (always strict)?
+access list: //store, though not used (always empty)?
+symbolic names: //do we actually need to store these? we'll see
+        build_200608030000: 1.1
+	...
+keyword substitution: kv //store, 'b', 'k', 'kv', 'kvl', 'v' are (currently) used
+total revisions: 2;     selected revisions: 2 //don't store, is count of commits to the file
+description: //is this a title for the revisions? probably
+----------------------------
+revision 1.1
+date: 2004/03/06 18:22:28;  author: marcelop;  state: Exp;
+branches:  1.1.2;
+Move the EMF, XSD and SDO source code to the Eclipse.org repository
+----------------------------
+revision 1.1.2.1
+date: 2005/06/02 16:09:17;  author: nickb;  state: Exp;  lines: +0 -0
+*** empty log message ***
+=============================================================================
+*/
+
+$db = "modeling"; /* database name. change this to match your project or can leave as is */
+
+$perfile_regex = "/^RCS\ file:\ (.+?$)\\n
+	^Working\ file:\ (.+?$)\\n
+	^head:\ (.+?$)\\n
+	^branch:\ ?(.*?$)\\n
+	^locks:\ (.+?$)\\n
+	^access\ list:\ ?(.*?$)\\n
+	(?:^symbolic\ names:\ ?(.*?$)\\n
+	((?:^\\t\S+:\ [0-9\.]+$\\n)+))?
+	^keyword\ substitution:\ (.+?$)\\n
+	^total\ revisions:\ (\d+);\\tselected\ revisions:\ (\d+)$\\n
+	^description:\ ?(.*?$)\\n
+	^((?:\-{28}.+$\\n)?
+	^={77})$/smx";
+
+$percommit_regex = "#^\-{28}$\\n
+	^revision\ ([0-9\.]+)$\\n
+	^date:\ (\d{4}/\d\d/\d\d\ \d\d:\d\d:\d\d);\ \ author:\ (\S+);\ \ state:\ (\S+);(?:\ \ lines:\ \+(\d+)\ \-(\d+))?$\\n
+	(?:^branches:(?:\ \ ([0-9\.]+);)+$\\n)?
+	^(.+?)$\\n
+	^(?:\-{28}|={77})#smx";
+
+$bugs_regex = "@(?:
+        \[\#?(\d+)\]
+        |
+        (?:Bugzilla)?\#(\d+)
+        |
+        https?\Q://bugs.eclipse.org/bugs/show_bug.cgi?id=\E(\d+)
+        )@x";
+
+include_once "includes/parsecvs-dbaccess.php";
+$connect = mysql_connect($dbhost, $dbuser, $dbpass) or die("Couldn't connect to database!\n");
+mysql_select_db($db, $connect) or die(mysql_error());
+$file = file_get_contents(($argv[1] ? $argv[1] : "php://stdin"));
+
+wmysql_query("CREATE TEMPORARY TABLE `tmptags` (`tagname` VARBINARY(255), `revision` VARCHAR(20), PRIMARY KEY (`tagname`)) ENGINE = memory");
+
+preg_match_all("/^(RCS file:.+?^={77}$)/sm", $file, $regs) or die("Couldn't find any cvs logs!\n");
+foreach ($regs[0] as $z)
+{
+	/* parse each file's info */
+	if (preg_match($perfile_regex, $z, $props))
+	{
+		$esc = array(1, 3, 9);
+		foreach ($esc as $y)
+		{
+			$props[$y] = mysql_real_escape_string($props[$y], $connect);
+		}
+		preg_match("/^\/cvsroot\/[^\/]+\/([^\/]+)\//", $props[1], $proj);
+		$q = "`project` = '$proj[1]', `head` = '$props[3]', `keyword_subs` = '$props[9]'";
+		wmysql_query("INSERT INTO `cvsfiles` SET `cvsname` = '$props[1]', $q ON DUPLICATE KEY UPDATE $q");
+		/* mysql_insert_id() won't work if we updated rather than inserted */
+		$result = wmysql_query("SELECT `fid` FROM `cvsfiles` WHERE `cvsname` = '$props[1]'");
+		$row = mysql_fetch_row($result);
+
+		/* parse symbolic names */
+		$tags = array();
+		$filetags = array();
+		$count = preg_match_all("/^\t(\S+): ([0-9\.]+)$\n/m", $props[8], $syms);
+		for ($i=0;$i<$count;$i++)
+		{
+			array_push($filetags, "('{$syms[1][$i]}', '{$syms[2][$i]}')");
+		}
+		if ($count > 0)
+		{
+			$syms[1] = preg_replace("/^(.+)$/e", "fixup('$1')", $syms[1]);
+			wmysql_query("INSERT INTO `tags` (`tagname`, `tagdate`) VALUES " . join($syms[1], ",") . " ON DUPLICATE KEY UPDATE `tid` = `tid`");
+			wmysql_query("INSERT INTO `tmptags` (`tagname`, `revision`) VALUES " . join($filetags, ","));
+			wmysql_query("INSERT INTO `filetags` SELECT $row[0], `tid`, `revision` FROM `tmptags` NATURAL JOIN `tags` ON DUPLICATE KEY UPDATE `filetags`.`revision` = `tmptags`.`revision`");
+			wmysql_query("TRUNCATE TABLE `tmptags`");
+		}
+
+		$commits = $props[13];
+		/* parse commits */
+		while (preg_match($percommit_regex, $commits, $revs))
+		{
+			$commits = substr($commits, strlen($revs[0]) - 28); //leave the \-{28} in tact
+			$revs[8] = mysql_real_escape_string($revs[8], $connect);
+			$q = "`date` = STR_TO_DATE('$revs[2]', '%Y/%m/%d %T'), `author` = '$revs[3]', `state` = '$revs[4]', `linesplus` = '$revs[5]', `linesminus` = '$revs[6]', `message` = '$revs[8]'";
+			wmysql_query("INSERT INTO `commits` SET `fid` = '$row[0]', `revision` = '$revs[1]', $q ON DUPLICATE KEY UPDATE $q");
+
+			/* parse bug numbers */
+			if (preg_match_all($bugs_regex, $revs[8], $ubugs))
+			{
+				unset($ubugs[0]);
+				$bugs = extract_bugs($ubugs);
+				$bugs = preg_replace("/^(.+)$/", "('$row[0]', '$revs[1]', '$1')", $bugs);
+				wmysql_query("INSERT INTO `bugs` (`fid`, `revision`, `bugid`) VALUES " . join($bugs, ",") . " ON DUPLICATE KEY UPDATE `bugid` = `bugid`");
+			}
+		}
+	}
+}
+wmysql_query("DROP TEMPORARY TABLE `tmptags`");
+
+$tables = array();
+mysql_select_db("INFORMATION_SCHEMA") or die(mysql_error());
+$result = wmysql_query("SELECT TABLE_NAME FROM TABLES WHERE `TABLE_SCHEMA` = '$db' AND `TABLE_TYPE` = 'BASE TABLE'");
+while ($row = mysql_fetch_row($result))
+{
+	array_push($tables, $row[0]);
+}
+
+mysql_select_db($db) or die(mysql_error());
+wmysql_query("OPTIMIZE TABLE " . join($tables, ","));
+wmysql_query("ANALYZE TABLE " . join($tables, ","));
+mysql_close($connect);
+
+function fixup($str)
+{
+	return "('$str', " . (preg_match("/^build_(\d{12})$/", $str, $regs) ? "STR_TO_DATE('$regs[1]', '%Y%m%d%k%i')" : "NULL") . ")";
+}
+
+function wmysql_query($sql)
+{
+	$res = mysql_query($sql) or die("$sql\n" . mysql_error() . "\n");
+	return $res;
+}
+
+function extract_bugs($regs)
+{
+        foreach ($regs as $z)
+        {
+                foreach ($z as $y)
+                {
+                        if (preg_match("/^\d+$/", $y))
+                        {
+                                $bugs[] = $y;
+                        }
+                }
+        }
+
+        return $bugs;
+}
+?>
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/parsecvs.sh b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/parsecvs.sh
new file mode 100755
index 0000000..a261e22
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/parsecvs.sh
@@ -0,0 +1,27 @@
+#/bin/bash
+
+CVSOPTS="-Q";
+
+cd ${0%/*}; # cd to directory where this script is located; cvssrc/ must be beneath it
+
+# pass in specific target folder(s) or do all in cvssrc/ folder
+dir="";
+if [ $# -gt 0 ]; then 
+  while [ $# -gt 0 ]; do
+    dir=$dir" $1"; shift 1;
+  done
+else 
+  dir="$(ls -d cvssrc/*)"; 
+fi
+
+for i in $dir; do
+  echo "[`date +%H:%M:%S`] Processing $i";
+  cd $i;
+  cvs $CVSOPTS up -Pd .;
+  f=$(mktemp)
+  cvs $CVSOPTS log > $f
+  echo $f | /usr/local/bin/php ../../parsecvs.php;
+  rm -f $f
+  cd ../..;
+  echo "[`date +%H:%M:%S`] done.";
+done
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/setup.sh b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/setup.sh
new file mode 100755
index 0000000..0fa1a4b
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/setup.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# setup script for use with Neil Skrypuch's Search CVS tool
+
+# used to check out cvs content so that it can be logged by parsecvs.sh
+
+checkoutdir="$HOME/searchcvs/cvssrc";
+quietcvs="-Q";
+
+mkdir -p $checkoutdir;
+
+checkoutProjects () 
+{
+  CVSROOT=$1;
+  project=$2;
+  cd $checkoutdir;
+  echo "[`date +%H:%M:%S`] Check out $checkoutdir/$project from $CVSROOT ...";
+  cvs -d :pserver:anonymous@dev.eclipse.org:$CVSROOT $quietcvs co $project;
+}
+
+# list projects to extract; add other cvs roots (eg. /cvsroot/modeling) as necessary below
+webprojects="emf emft uml2 mdt modeling";
+toolsprojects="org.eclipse.emf org.eclipse.xsd org.eclipse.emf.ecore.sdo org.eclipse.emf.releng.build org.eclipse.uml2 org.eclipse.uml2.releng";
+techprojects="org.eclipse.emft org.eclipse.gmf org.eclipse.corona";
+eclipseprojects="org.eclipse.releng org.eclipse.releng.basebuilder org.eclipse.releng.eclipsebuilder org.eclipse.releng.tools org.eclipse.releng.tests org.eclipse.pde org.eclipse.pde.core org.eclipse.pde.ui.tests org.eclipse.pde.runtime org.eclipse.pde.junit.runtime org.eclipse.pde.source org.eclipse.pde.build org.eclipse.pde.junit org.eclipse.pde.doc.user org.eclipse.pde.ui"
+modelingprojects="gmf emf org.eclipse.mdt org.eclipse.mdt.releng releng-common"
+
+# do checkouts for each cvs root's projects
+for f in $webprojects; do checkoutProjects /cvsroot/org.eclipse   www/$f; done
+for f in $toolsprojects; do checkoutProjects /cvsroot/tools       $f    ; done
+for f in $techprojects;  do checkoutProjects /cvsroot/technology  $f    ; done
+for f in $eclipseprojects;  do checkoutProjects /cvsroot/eclipse  $f    ; done
+for f in $modelingprojects; do checkoutProjects /cvsroot/modeling $f    ; done
+
+echo "[`date +%H:%M:%S`] Done.";
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/README.folder.contents.may.not.be.up.to.date.txt b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/README.folder.contents.may.not.be.up.to.date.txt
new file mode 100644
index 0000000..4ab1741
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/README.folder.contents.may.not.be.up.to.date.txt
@@ -0,0 +1,36 @@
+Search CVS is a tool for converting cvs commits into a mysql database, which 
+can be updated without worry of data duplication. The database is then searchable 
+via a web page run on www.eclipse.org.
+
+This folder is a copy of the contents of another cvs repository for example purposes only,
+and should not be considered up to date. It is provided here simply to make this code base 
+more complete and to provide a self-contained runnable example. 
+
+For the latest version of this folder's contents, see:
+
+:pserver:anonymous@dev.eclipse.org:/cvsroot/org.eclipse/www/emf/
+    searchcvs.php
+    includes/searchcvs.css
+    includes/db.php
+
+Additionally, there is a non-public file called includes/searchcvs-dbaccess.php (referenced by
+includes/db.php) which reads something like this:
+
+<?php
+    $dbhost = "mysqlserver";
+    $dbuser = "dbaccessro";
+    $dbpass = "dbaccessropassword";
+?>
+
+** WARNING: DO NOT COMMIT THIS FILE INTO YOUR PUBLIC CVS REPOSITORY! **
+
+To put this file on the www.eclipse.org server without it being available in CVS for public view, 
+place a copy of the file in your home directory on dev.eclipse.org, then open a bug 
+(https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Community) or send a note to webmaster@eclipse.org
+and ask to have the file placed for you. Example: https://bugs.eclipse.org/bugs/show_bug.cgi?id=156451#c8
+
+----
+
+Additional details on setup and on using this tool can be found here:
+
+http://wiki.eclipse.org/index.php/Search_CVS
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/db.php b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/db.php
new file mode 100644
index 0000000..7fafa85
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/db.php
@@ -0,0 +1,12 @@
+<?php
+require_once $_SERVER['DOCUMENT_ROOT'] . "/emf/includes/searchcvs-dbaccess.php";
+$connect = mysql_connect($dbhost, $dbuser, $dbpass);
+mysql_select_db($db ? $db : "modeling", $connect) or die(mysql_error());
+
+function wmysql_query($sql)
+{
+	#print $sql . "\n";
+	$res = mysql_query($sql) or die("$sql\n" . mysql_error());
+	return $res;
+}
+?>
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/searchcvs-dbaccess.php b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/searchcvs-dbaccess.php
new file mode 100644
index 0000000..c1de49a
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/searchcvs-dbaccess.php
@@ -0,0 +1,5 @@
+<?php
+    $dbhost = "mysqlserver";
+    $dbuser = "dbaccessro";
+    $dbpass = "dbaccessropassword";
+?>
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/searchcvs.css b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/searchcvs.css
new file mode 100644
index 0000000..c19bfa0
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/includes/searchcvs.css
@@ -0,0 +1,101 @@
+abbr

+{

+	border-bottom: 1px dotted gray;

+	/* removes konqueror's custom abbr style */

+	font: inherit;

+	letter-spacing: inherit;

+}

+

+#midcolumn .homeitem3col h3 span

+{

+	float: right;

+}

+

+#midcolumn .homeitem3col ul li div

+{

+	float: right;

+	padding-left: 2em;

+}

+

+#midcolumn .homeitem3col ul li

+{

+	clear: both;

+}

+

+#midcolumn .homeitem3col ul li ul li

+{

+	border-style: none;

+	padding: 0;

+}

+

+#midcolumn .homeitem3col .pager

+{

+	text-align: center;

+	padding: 1em 0 1em 0;

+}

+

+#midcolumn .homeitem3col .pager span

+{

+	margin: 0.5em;

+	padding: 0.3em;

+}

+

+#midcolumn .homeitem3col .pager span.selected

+{

+	border-bottom: 1px dotted black;

+	border-top: 1px dotted black;

+	font-weight: bold;

+}

+

+#midcolumn .homeitem3col #searchdiv

+{

+	text-align: center;

+	padding-top: 1em;

+}

+

+#midcolumn .homeitem3col ul li ul li span.empty

+{

+	color: gray;

+}

+

+#midcolumn .homeitem3col ul li ul li span.hl0,

+#midcolumn .homeitem3col ul li ul li span.hl7

+{

+	background-color: #FFFF66;

+}

+

+#midcolumn .homeitem3col ul li ul li span.hl1,

+#midcolumn .homeitem3col ul li ul li span.hl8

+{

+	background-color: #FF66FF;

+}

+

+#midcolumn .homeitem3col ul li ul li span.hl2,

+#midcolumn .homeitem3col ul li ul li span.hl9

+{

+	background-color: #66FFFF;

+}

+

+#midcolumn .homeitem3col ul li ul li span.hl3,

+#midcolumn .homeitem3col ul li ul li span.hl10

+{

+	background-color: #AAAAFF;

+}

+

+#midcolumn .homeitem3col ul li ul li span.hl4,

+#midcolumn .homeitem3col ul li ul li span.hl11

+{

+	background-color: #AAFFAA;

+}

+

+#midcolumn .homeitem3col ul li ul li span.hl5,

+#midcolumn .homeitem3col ul li ul li span.hl12

+{

+	background-color: #FFAAAA;

+}

+

+#midcolumn .homeitem3col ul li ul li span.hl6,

+#midcolumn .homeitem3col ul li ul li span.hl13

+{

+	background-color: #CCCCCC;

+}

diff --git a/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/searchcvs.php b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/searchcvs.php
new file mode 100644
index 0000000..a379b81
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_cvs/searchcvs/www/projectName/searchcvs.php
@@ -0,0 +1,267 @@
+<?php
+require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php"); require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/nav.class.php"); require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/menu.class.php"); $App = new App(); $Nav = new Nav(); $Menu = new Menu(); include($App->getProjectCommon());
+ob_start();
+
+include("includes/db.php");
+
+$pagesize = 25; //results per page
+$scroll = 5; //+- pages to show in nav
+$days = 7;
+$page = (preg_match("/^\d+$/", $_GET["p"]) ? $_GET["p"] : 1);
+$offset = ($page - 1) * $pagesize;
+
+$where = "WHERE `date` >= DATE_SUB(CURDATE(), INTERVAL $days DAY)";
+$order = "ORDER BY `date` DESC";
+
+$extraf = array(
+	array("regex" => "/author: ?(\S+)/", "sql" => "`author` LIKE '%%%s%%'", "sqlpart" => "where"),
+	array("regex" => "/file: ?(\S+)/", "sql" => "`cvsname` LIKE '%%%s%%'", "sqlpart" => "where"),
+	array("regex" => "/days: ?(\d+)/", "sql" => "`date` >= DATE_SUB(CURDATE(), INTERVAL %d DAY)", "sqlpart" => "where"),
+	array("regex" => "/(?:project|module): ?(\S+)/", "sql" => "`project` LIKE '%s'", "sqlpart" => "where"),
+	array("regex" => "/branch: ?(\S+)/", "sql" => "`branch` LIKE '%%%s%%'", "sqlpart" => "having") //is a calculated value, won't work in WHERE
+);
+
+$q = (get_magic_quotes_gpc() ? $_GET["q"] : addslashes($_GET["q"]));
+$extra = array("where" => array(), "having" => array());
+foreach ($extraf as $z)
+{
+	while (preg_match($z["regex"], $q, $regs))
+	{
+		array_push($extra[$z["sqlpart"]], sprintf($z["sql"], $regs[1]));
+		$q = preg_replace($z["regex"], "", $q);
+	}
+}
+
+$regs = array();
+/* this *could* be put into $extraf, but it would change the semantics slightly, in that any number searched for would be treated as a bug #, which i think is undesirable */
+if (preg_match("/^\s*\[?(\d+)\]?\s*$/", $_GET["q"], $regs))
+{
+	$_GET["q"] = $regs[1]; 
+	$where = "WHERE `bugid` = $regs[1]";
+	$et = "Bug #";
+}
+else if (preg_match("/(\S)/", $q, $regs) || sizeof($extra["where"]) + sizeof($extra["having"]) > 0)
+{
+	$match = "'1'";
+	if (sizeof($regs) > 0)
+	{
+		$match = "MATCH(`message`) AGAINST('$q'" . (preg_match("/\".+\"/", $q) ? " IN BOOLEAN MODE" : "") . ")";
+	}
+	$where = "WHERE " . ($match ? $match : "1");
+	$where .= (sizeof($extra["where"]) > 0 ? " AND " . join($extra["where"], " AND ") : "");
+	$having = (sizeof($extra["having"]) > 0 ? " HAVING " . join($extra["having"], " AND ") : "");
+	$ec = ", $match AS `relevance`";
+	$order = "ORDER BY `relevance` DESC, `date` DESC";
+}
+?>
+<div id="midcolumn">
+<div class="homeitem3col">
+	<h3>Search</h3>
+	<div id="searchdiv">
+		<form action="" method="get">
+			<input type="text" size="60" id="q" name="q"<?php print ($_GET["q"] ? " value=\"" . sanitize($_GET["q"], "text") . "\"" : ""); ?>/>
+			<input type="submit" value="Go!"/>
+		</form>
+	</div>
+</div>
+<?php
+
+/* 1.1.2.x <- 1.1.0.2 = branch tag, likewise, 1.1.4.x <- 1.1.0.4 = branch tag, so dynamically rewrite a.b.c.d to a.b.0.c to find the branch tag */
+$branch = "IF(`revision` LIKE '%.%.%.%', (SELECT `tagname` FROM `tags` NATURAL JOIN `filetags` WHERE `fid` = `ofid` AND `revision` = CONCAT(SUBSTRING_INDEX(`orev`, '.', 2), '.0.', SUBSTRING_INDEX(SUBSTRING_INDEX(`orev`, '.', -2), '.', 1))), 'HEAD')";
+$sql = "SELECT SQL_CALC_FOUND_ROWS `cvsname`, `revision`, `date`, `author`, `message`, `keyword_subs`, `bugid`, `revision` AS `orev`, `fid` AS `ofid`, $branch AS `branch`$ec FROM `cvsfiles` NATURAL JOIN `commits` NATURAL LEFT JOIN `bugs` $where GROUP BY `fid`, `revision`, `bugid` $having $order LIMIT $offset, $pagesize";
+$result = wmysql_query($sql);
+
+$count = wmysql_query("SELECT FOUND_ROWS()"); //mysql_num_rows() doesn't do what we want here
+$row = mysql_fetch_row($count);
+$rows = $row[0];
+
+$title = "<span>$rows results total</span>Showing results " . ($offset + 1) . "-" . ($offset + $pagesize > $rows ? $rows : $offset + $pagesize) . " for " . ($_GET["q"] == "" ? "last $days days of commits" : "$et" . sanitize($_GET["q"], "text"));
+$title = ($rows == 0 ? "No results found for " . sanitize($_GET["q"], "text") . "" : $title);
+
+print "<div class=\"homeitem3col\">\n";
+print "<h3>$title</h3>\n";
+
+dopager($rows, $page, $pagesize);
+
+print "<ul>\n";
+
+while ($row = mysql_fetch_assoc($result))
+{
+	$file = basename($row["cvsname"], ",v");
+	$row["cvsname"] = preg_replace("#^/cvsroot/[^\/]+/(.+),v$#", "$1", $row["cvsname"]);
+	print "<li>\n";
+	print "<div>{$row['date']}</div>";
+	print ($row["bugid"] ? "[<a href=\"https://bugs.eclipse.org/bugs/show_bug.cgi?id={$row['bugid']}\">{$row['bugid']}</a>] " : "");
+	print "<a href=\"" . cvsfile($row["cvsname"]) . "\"><abbr title=\"{$row['cvsname']}\">$file</abbr></a> ({$row['branch']} " . showrev($row['revision'], $row["cvsname"]) . ")";
+	print "<ul>\n";
+	print "<li><div>{$row['author']}</div>" . pretty_comment($row["message"], $q) . "</li>";
+	print "</ul>\n";
+	print "</li>\n";
+}
+print "</ul>\n";
+
+dopager($rows, $page, $pagesize);
+
+print "</div>\n";
+print "</div>\n";
+mysql_close($connect);
+?>
+<div id="rightcolumn">
+	<div class="sideitem">
+		<h6>Help</h6>
+		<p><a href="http://wiki.eclipse.org/index.php/Search_CVS">Consult the wiki</a>, or try these examples:</p>
+		<ul>
+			<li><a href="?q=%5B155286%5D">[155286]</a></li>
+			<li><a href="?q=98877+file%3A+ChangeAdapter">98877 file: ChangeAdapter</a></li>
+			<li><a href="?q=file%3A+org.eclipse.emf%2F+days%3A+7">file: org.eclipse.emf/ days: 7</a></li>
+			<li><a href="?q=days%3A200+author%3Amerks">days:200 author:merks</a></li>
+			<li><a href="?q=branch%3A+R2_1_+file%3A+.xml">branch: R2_1_ file: .xml</a></li>
+			<li><a href="?q=static+dynamic+project%3A+org.eclipse.emf">static dynamic project: org.eclipse.emf</a></li>
+			<li><a href="?q=%22package+protected%22">"package protected"</a></li>
+			<li><a href="?q=Neil+Skrypuch">Neil Skrypuch</a></li>
+		</ul>
+        <p>See also the complete <a href="http://wiki.eclipse.org/index.php/Search_CVS#Parameter_List">Parameter List</a>.</p> 
+	</div>
+</div>
+<?php
+$html = ob_get_contents();
+ob_end_clean();
+
+$pageTitle = "Eclipse Tools - Search CVS";
+$pageKeywords = ""; 
+$pageAuthor = "Neil Skrypuch";
+
+$App->AddExtraHtmlHeader('<link rel="stylesheet" type="text/css" href="/emf/includes/searchcvs.css"/>' . "\n");
+if (!isset($_GET["totalonly"]))
+{
+	ob_start();
+	$App->generatePage($theme, $Menu, $Nav, $pageAuthor, $pageKeywords, $pageTitle, $html);
+	$html = ob_get_contents();
+	ob_end_clean();
+	print preg_replace("/<body>/", "<body onload=\"document.getElementById('q').focus()\">", $html);
+}
+else
+{
+	header("Content-Type: text/plain");
+	print $rows;
+}
+
+function pretty_comment($str, $hl)
+{
+	$str = preg_replace("/\n/", "<br/>", $str);
+	$hl = words($hl);
+
+	for ($i = 0; $i < sizeof($hl); $i++)
+	{
+		$str = preg_replace("/\b(\Q$hl[$i]\E)\b([^=]|\Z)/i", "<span class=\"hl$i\">$1</span>$2", $str);
+	}
+
+	$str = preg_replace("/^(\Q*** empty log message ***\E)$/", "<span class=\"empty\">$1</span>", $str);
+
+	return $str;
+}
+
+function cvsminus($rev)
+{
+	if (preg_match("/^1\.1$/", $rev)) // "1.10" == "1.1" returns true, curiously enough
+	{
+		return $rev;
+	}
+	else
+	{
+		if (preg_match("/\.1$/", $rev))
+		{
+			return preg_replace("/^(\d+\.\d+)\..+$/", "$1", $rev);
+		}
+		else
+		{
+			return preg_replace("/^(.+\.)(\d+)$/e", "\"$1\" . ($2 - 1);", $rev);
+		}
+	}
+}
+
+function showrev($rev, $file)
+{
+	$link = "<a href=\"" . cvsfile($file) . "\">$rev</a>";
+	if (!preg_match("/^1\.1$/", $rev)) // "1.10" == "1.1" returns true, curiously enough
+	{
+		$oldrev = cvsminus($rev);
+		$link = "<a href=\"" . cvsfile($file, $rev, $oldrev) . "\">$rev &gt; $oldrev</a>";
+	}
+
+        return $link;
+}
+
+function cvsfile($file, $rev = "", $oldrev = "")
+{
+	if ($rev && $oldrev)
+	{
+		$ext = ".diff";
+		$params = "r1=$oldrev&amp;r2=$rev&amp;";
+	}
+	$params .= (preg_match("/\.php$/", $file) && $ext != ".diff" ? "content-type=text/plain&amp;" : "");
+
+	if (preg_match("/^www/", $file))
+	{
+		return "http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/$file$ext?${params}cvsroot=Eclipse_Website";
+	}
+	else
+	{
+		return "http://dev.eclipse.org/viewcvs/indextools.cgi/~checkout~/$file$ext?$params";
+	}
+}
+
+function sanitize($str, $type = "url")
+{
+	$tmp = urlencode(urldecode((get_magic_quotes_gpc() ? stripslashes($str) : $str)));
+	return ($type == "url" ? $tmp : htmlspecialchars(urldecode($tmp)));
+}
+
+function pagelink($page, $selected, $linktext = "")
+{
+	$innertext = ($linktext ? $linktext : $page);
+	$text = (!$selected ? "<a href=\"?q=" . sanitize($_GET["q"]) . "&amp;p=$page\">$innertext</a>" : $innertext);
+	return "<span" . ($selected ? " class=\"selected\"" : "") . ">$text</span>";
+}
+
+function dopager($rows, $page, $pagesize)
+{
+	$startpage = ($page - 5 < 1 ? 1 : $page - 5);
+	$endpage = ($page + 5 > $rows/$pagesize ? ceil($rows/$pagesize) : $page + 5);
+
+	if ($rows > 0)
+	{
+		print "<div class=\"pager\">\n";
+		print ($page > 1 ? pagelink($page - 1, false, "Previous") : "");
+		for ($i = $startpage; $i <= $endpage; $i++)
+		{
+			print pagelink($i, $i == $page);
+		}
+		print ($page < ceil($rows/$pagesize) ? pagelink($page + 1, false, "Next") : "");
+		print "</div>\n";
+	}
+}
+
+function words($str)
+{
+	$str = stripslashes($str);
+        $list = array();
+
+        preg_match_all("/\"([^\"]+)\"/", $str, $regs);
+        foreach ($regs[1] as $word)
+        {
+		$word = addslashes($word);
+                $list[] = $word;
+                $str = preg_replace("/\Q$word\E/", "", $str);
+        }
+
+        $regs = null;
+        preg_match_all("/(\w+)/", $str, $regs);
+        foreach ($regs[1] as $word)
+        {
+                $list[] = addslashes($word);
+        }
+
+        return $list;
+}
+?>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/_README.txt b/bundles/org.eclipse.build.tools/scripts_rss/_README.txt
new file mode 100644
index 0000000..10095c4
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/_README.txt
@@ -0,0 +1,39 @@
+These scripts are examples to get you started.
+
+== Assembly ==
+
+To build the feedTools.jar, use buildFeedToolsJar.sh (or .xml).
+To create a zip of all the RSS-related code, use buildFeedToolsZip.sh (or .xml).
+
+== Feed Manipulation ==
+
+To do feed manipulation, like creating a feed, adding a entry to a feed, querying 
+for attribute values in a feed, or changing attribute values in a feed, look at 
+feedManipulation.sh (and .xml) and create a copy similar to suit your needs.
+
+== Feed Publishing ==
+
+To publish a feed, use a script similar to feedPublish.sh (and .xml). 
+
+== Feed Validation ==
+
+To validate a feed against the latest schema, you can use one of two EMF-based ant tasks. 
+Examples of both are shown in feedValidate.xml, including the classpath required to run the task.
+In the Dynamic case, the schema is used to validate the feed xml; in the Generated case, the 
+schema is used to create an .ecore model, which is used to generate model implementation and
+validation code, which is then used to validate the feed xml. Because the two techniques yield 
+slightly different error messages when validating invalid feed data, they are both provided for
+comparison.
+
+To build the feedValidator.jar, use buildFeedValidatorJar.xml.
+
+== Feed Watching (And Response) ==
+
+To watch a feed for changes or the appearance of specific attribute values (like 
+test results), use a script similar to feedWatch.sh (and .xml), along with properties 
+like those in properties/feedWatch.*.properties
+
+The script sendEmailAlert.sh is provided as an example of what to in response to 
+a feed change. You can customize the response to suit your needs. 
+
+Additional documentation can be found in the *.xml Ant scripts and *.properties files.
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsJar.sh b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsJar.sh
new file mode 100644
index 0000000..9ac7c99
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsJar.sh
@@ -0,0 +1,15 @@
+#*******************************************************************************
+# 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
+#*******************************************************************************
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+$ANT_HOME/bin/ant -f buildFeedToolsJar.xml;
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsJar.xml b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsJar.xml
new file mode 100644
index 0000000..f077101
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsJar.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<project name="Build feedTools.jar" default="buildFeedToolsJar" basedir="../">
+	<target name="buildFeedToolsJar">
+	  <echo message="Compiling feedTools ..."/>
+	  <delete file="feedTools.jar"/>
+	  <delete dir="bin/"/>
+  	  <mkdir dir="bin"/>
+	  <javac 
+	  	source="1.5"
+	  	target="1.5"
+		srcdir="."
+	    destdir="bin/"
+	    classpath="/lib/ant.jar"
+		includes="src_rss/**/*"
+	  	excludes="src_rss/**/emf/*, src_rss/**/emf/**, src_rss/**/emf/**/*"/>
+	  <echo message="Jarring feedTools.jar ..."/>
+	  <jar 
+		destfile="feedTools.jar" 
+		includes="**/*.class" 
+		basedir="bin"
+	    update="true"/>
+	  <jar 
+		destfile="feedTools.jar" 
+		includes="**/messages.properties" 
+		excludes="**/emf/messages.properties" 
+		basedir="src_rss"
+	    update="true"/>
+	</target>
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsZip.sh b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsZip.sh
new file mode 100644
index 0000000..66df7aa
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsZip.sh
@@ -0,0 +1,15 @@
+#*******************************************************************************
+# 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
+#*******************************************************************************
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+$ANT_HOME/bin/ant -f buildFeedToolsZip.xml;
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsZip.xml b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsZip.xml
new file mode 100644
index 0000000..78db396
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedToolsZip.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<project name="Build feedTools.zip" default="buildFeedToolsZip" basedir="../">
+	<target name="buildFeedToolsZip">
+		<property name="zipfile" value="org.eclipse.build.tools_feedTools.zip"/>
+		<ant antfile="scripts_rss/buildFeedToolsJar.xml" target="buildFeedToolsJar" inheritall="true"/>
+		<echo message="Bundling ${zipfile} ..."/>
+		<delete file="${zipfile}"/>
+		<zip
+			destfile="${zipfile}"
+			includes=".classpath, .project, 
+				feedTools.jar, 
+				src_rss/**/*, 
+				scripts_rss/**/*,
+                schema/**/*,
+		    	data/**/*,
+                properties/feedPublish.*.properties, 
+                properties/feedWatch.*.properties, 
+                properties/_README.txt"
+			basedir="."
+			update="true"/>
+	</target>
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/buildFeedValidatorJar.xml b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedValidatorJar.xml
new file mode 100644
index 0000000..b3a1c03
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/buildFeedValidatorJar.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<project name="Build feedValidator.jar" default="buildFeedValidatorJar" basedir="../">
+	<target name="buildFeedValidatorJar">
+	  <!-- TODO: set correct path to EMF plugins -->
+   	  <property name="EMF_HOME" value="../../eclipse-plugins-emf22/eclipse/plugins"/>
+	  <echo message="Using EMF_HOME = ${EMF_HOME}"/>
+
+	  <echo message="Compiling feedValidator (requires EMF) ..."/>
+	  <delete file="feedValidator.jar"/>
+	  <delete dir="bin_emf/"/>
+  	  <mkdir dir="bin_emf"/>
+	  <javac 
+		srcdir="."
+	    destdir="bin_emf/"
+		includes="src_rss/**/emf/*, src_rss/**/emf/**, src_rss/**/emf/**/*">
+	  	<classpath>
+	  		<path path="/lib/ant.jar"></path>
+	  		<fileset dir="${EMF_HOME}">
+				<include name="org.eclipse.emf.common_*.jar"/>
+				<include name="org.eclipse.emf.ecore_*.jar"/>
+	  			<include name="org.eclipse.emf.ecore.xmi_*.jar"/>
+	  			<include name="org.eclipse.xsd_*.jar"/>
+	  		</fileset>
+	    </classpath>
+	  </javac>
+	  <echo message="Jarring feedValidator.jar ..."/>
+	  <jar 
+		destfile="feedValidator.jar" 
+		basedir="bin_emf"
+	    update="true"/>
+	  <jar 
+		destfile="feedValidator.jar" 
+		includes="**/emf/messages.properties" 
+		basedir="src_rss"
+	    update="true"/>
+	  <delete dir="bin_emf/"/>
+	</target>
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedManipulation.sh b/bundles/org.eclipse.build.tools/scripts_rss/feedManipulation.sh
new file mode 100644
index 0000000..3dd3409
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedManipulation.sh
@@ -0,0 +1,36 @@
+#*******************************************************************************
+# 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
+#*******************************************************************************
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+
+#ant standalone
+
+CLASSPATH="$JAVA_HOME/lib/rt.jar";
+# note that com.sun.org.apache.xerces (Sun JDK 1.5, rt.jar) != org.apache.xerces (Ant 1.6.5, xercesImpl.jar) so must remove from classpath
+#for f in `find $ANT_HOME/lib  -maxdepth 1 -name "*.jar" -type f -not -name "xercesImpl.jar"`; do CLASSPATH=$CLASSPATH":"$f; done
+CLASSPATH=$CLASSPATH":"$ANT_HOME/lib/ant.jar":"$ANT_HOME/lib/ant-launcher.jar;
+
+cmd="$JAVA_HOME/bin/java \
+  -Dant.home=$ANT_HOME \
+  -Dant.library.dir=$JAVA_HOME/lib \
+  -classpath $CLASSPATH \
+  org.apache.tools.ant.launch.Launcher \
+  -buildfile feedManipulation.xml";
+
+echo "Running ..."; echo "";
+
+#prettyprint the command
+echo $cmd | sed -e 's/ \-/\n  \-/g' -e 's/:/\n    :/g'; echo "";
+
+# run the command
+$cmd;
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedManipulation.xml b/bundles/org.eclipse.build.tools/scripts_rss/feedManipulation.xml
new file mode 100644
index 0000000..cf0862d
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedManipulation.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+
+<project name="Create Feed, Add Entry, Update Entry, Read Entry" default="doAll">
+
+	<taskdef name="CreateFeed" classname="org.eclipse.releng.generators.rss.RSSFeedCreateFeedTask" classpath="../feedTools.jar"/>
+	<taskdef name="AddEntry" classname="org.eclipse.releng.generators.rss.RSSFeedAddEntryTask" classpath="../feedTools.jar"/>
+	<taskdef name="GetProperty" classname="org.eclipse.releng.generators.rss.RSSFeedGetPropertyTask" classpath="../feedTools.jar"/>
+	<taskdef name="UpdateEntry" classname="org.eclipse.releng.generators.rss.RSSFeedUpdateEntryTask" classpath="../feedTools.jar"/>
+
+	<target name="doAll">
+		<antcall target="addEntry"/>
+		<antcall target="getProperties"/>
+		<antcall target="updateFeed"/>
+	</target>
+
+	<target name="init">
+		<!-- 1. Define property file -->
+
+		<property file="../properties/feedPublish.eclipse.properties"/>		
+		<!--
+			<property file="../properties/feedPublish.emf.properties"/>		
+			<property file="../properties/feedPublish.uml2.properties"/>		
+		-->
+	</target>
+	
+	<target name="create" depends="init">
+		<!-- 2. Create feed (overwrite existing) -->
+
+		<CreateFeed 
+			debug="${debug}" 
+			project="${project}" 
+			file="${file}" 
+			feedURL="${feedURL}"/>
+		
+	</target>
+		
+	<target name="addEntry" depends="init">
+		<!-- 3. Add an entry (and create the feed if it doesn't already exist) -->
+
+		<!-- note: if not providing a buildType, must specify buildID to generate value -->
+		<AddEntry 
+			debug="${debug}" 
+			file="${file}" 
+
+			project="${project}"
+			version="${version}"
+			branch="${branch}"
+			buildID="${buildID}"
+			buildType="${buildType}"
+			feedURL="${feedURL}"
+			buildURL="${buildURL}"
+
+			buildAlias="${buildAlias}"
+			
+			buildDetailURL="${buildDetailURL}"
+			buildConfigURL="${buildConfigURL}"
+			buildLogURL="${buildLogURL}"
+			buildMapURL="${buildMapURL}"
+			
+			dependencyURLs="${dependencyURLs}"
+			releases="${Releases}"
+			
+			releaseNotesURL="${releaseNotesURL}"
+			updateManagerURL="${updateManagerURL}"
+			downloadsURL="${downloadsURL}"
+			jarSigningStatus="${jarSigningStatus}"
+			coordinatedstatus="${coordinatedStatus}"
+			
+			JUnitTestURL="${JUnitTestURL}"
+			performanceTestURL="${performanceTestURL}"
+			APITestURL="${APITestURL}"
+			JUnitTestResults="${JUnitTestResults}"
+			performanceTestResults="${performanceTestResults}"
+			APITestResults="${APITestResults}"
+		/>
+		
+	</target>
+		
+	<target name="getProperties" depends="init">
+		<!-- 4. Display a text or attribute value (ie., get a property) -->
+
+		<!-- on the feed itself, display modified date -->
+		<GetProperty 
+			debug="${debug}" 
+			file="${file}"
+			xpath="/*[name() = 'feed']/*[name() = 'updated']/text()"
+		/>
+
+		<!-- on the first entry, display the modified date -->
+		<GetProperty 
+			debug="${debug}" 
+			file="${file}"
+			xpath="/*/*[name() = 'entry'][1]/*[name() = 'updated']/text()"
+		/>
+
+		<!-- on the first entry, display the build's folder, filename, and type -->
+		<GetProperty 
+			file="${file}"
+			xpath="/*/*[name() = 'entry'][1]/*/*[name() = 'build']/@href"
+		/>
+		<GetProperty 
+			file="${file}"
+			xpath="/*/*[name() = 'entry'][1]/*/*[name() = 'build']/*[name() = 'releases']/*[@os = 'linux'][@ws = 'gtk'][@type = 'SDK']/text()"
+		/>
+		<GetProperty 
+			file="${file}"
+			xpath="/*/*[name() = 'entry'][1]/*/*[name() = 'build']/@type"
+		/>
+
+	</target>
+		
+	<target name="updateFeed" depends="init">
+		<!-- 5. Update an existing entry by changing a property value to something else -->
+		
+		<!-- on the first entry, change the coordinated status to COMPLETE -->
+		<UpdateEntry 
+			file="${file}"
+			xpath="/*/*[name() = 'entry'][1]/*/*/*[name() = 'coordinated']/@status"
+			replacement="COMPLETE"
+		/>
+		<!-- on the first entry, change the performance results (os-ws = RHEL4-3GHz-2.5GB-gtk) to PENDING -->
+		<UpdateEntry 
+			file="${file}"
+			xpath="/*/*[name() = 'entry'][1]/*/*/*/*[@type = 'performance']/*[name() = 'result'][@id = 'RHEL4-3GHz-2.5GB']/text()"
+			replacement="PENDING"
+		/>
+
+	</target>
+	
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedManipulationEclipse.sh b/bundles/org.eclipse.build.tools/scripts_rss/feedManipulationEclipse.sh
new file mode 100644
index 0000000..7846332
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedManipulationEclipse.sh
@@ -0,0 +1,42 @@
+#*******************************************************************************
+# 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
+#*******************************************************************************
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+export ECLIPSE_HOME=/home/eclipse/eclipse;
+
+#ant with Eclipse - requires org.eclipse.ant.ui*.jar
+tmpfolder="/tmp/antrunner-"`date +%Y%m%d_%H%M%S`;
+for f in `find $ECLIPSE_HOME/plugins -maxdepth 1 -name "org.eclipse.ant.ui*.jar" -type f`; do unzip $f lib/*.jar -d $tmpfolder; done
+
+CLASSPATH="$JAVA_HOME/lib/rt.jar";
+for d in "$ANT_HOME/lib" "$tmpfolder/lib"; do
+  # note that com.sun.org.apache.xerces (Sun JDK 1.5, rt.jar) != org.apache.xerces (Ant 1.6.5, xercesImpl.jar) so must remove from classpath
+  for f in `find $d  -maxdepth 1 -name "*.jar" -type f -not -name "xercesImpl.jar"`; do CLASSPATH=$CLASSPATH":"$f; done
+done
+
+cmd="$JAVA_HOME/bin/java \
+  -Dant.home=$ANT_HOME \
+  -Dant.library.dir=$ANT_HOME/lib \
+  -classpath $CLASSPATH \
+  org.eclipse.ant.internal.ui.antsupport.InternalAntRunner \
+  -buildfile feedManipulation.xml";
+
+echo "Running ...";
+
+#prettyprint the command
+echo $cmd | sed -e 's/ \-/\n  \-/g' -e 's/:/\n    :/g'; echo "";
+
+# run the command
+$cmd;
+
+rm -fr $tmpfolder;
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedPublish.sh b/bundles/org.eclipse.build.tools/scripts_rss/feedPublish.sh
new file mode 100644
index 0000000..e0d74d2
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedPublish.sh
@@ -0,0 +1,40 @@
+#*******************************************************************************
+# 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
+#*******************************************************************************
+#!/bin/sh
+
+###
+### NOTE: this script is ONLY an example, and needs to be customized for your personal use. ###
+###
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+CLASSPATH="$JAVA_HOME/lib/rt.jar";
+CLASSPATH=$CLASSPATH":"$ANT_HOME/lib/ant.jar":"$ANT_HOME/lib/ant-launcher.jar;
+CLASSPATH=$CLASSPATH":"../feedTools.jar;
+
+projectName="emf";
+
+# define/override variables not set in properties file (can also be passed in querystring, etc.)
+# this allows a static set of properties + some dynamic ones to be mixed in together
+debug=2;
+branch="HEAD"; # optional
+version="2.2.1";
+buildID="S200609210005";
+buildAlias="2.2.1RC2";
+dependencyURLs="http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/M20060919-1045/eclipse-SDK-M20060919-1045-linux-gtk.tar.gz"; # comma-separated if more than one
+# ...
+
+cmd="$JAVA_HOME/bin/java -debug -Dant.home=$ANT_HOME -Dant.library.dir=$JAVA_HOME/lib -classpath $CLASSPATH org.apache.tools.ant.launch.Launcher"
+cmd=$cmd" -buildfile feedPublish.xml -propertyfile ../properties/feedPublish.$projectName.properties"
+cmd=$cmd" -Dbranch=$branch -Dversion=$version -DbuildID=$buildID -DbuildAlias=$buildAlias -DbuildType="${buildID:0:1};
+cmd=$cmd" -DdependencyURLs=$dependencyURLs -Ddebug=$debug";
+echo ""; echo $cmd | sed -e "s/ \-/#  \-/g" -e "s/.jar:/.jar#    :/g" | tr "#" "\n"; echo "";
+$cmd;
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedPublish.xml b/bundles/org.eclipse.build.tools/scripts_rss/feedPublish.xml
new file mode 100644
index 0000000..5204db3
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedPublish.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+
+<project name="Create/Update + Publish Feed" default="publishFeed" basedir="..">
+	<taskdef name="PublishFeed" classname="org.eclipse.releng.services.rss.RSSFeedPublisherTask"    classpath="./feedTools.jar"/>
+	<taskdef name="AddEntry"    classname="org.eclipse.releng.generators.rss.RSSFeedAddEntryTask"   classpath="./feedTools.jar"/>
+	<!-- to run from a shell, modify feedPublish.sh to suit your needs -->
+	
+	<target name="publishFeed">	
+		<!-- TODO: include properties file here if not running from a shell script -->
+		<property file="properties/feedPublish.emf.properties"/>
+		<antcall target="addEntry"/>
+		<antcall target="publishFeedToCVS"/>
+		<antcall target="publishFeedWithSCP"/>
+	</target>
+
+	<target name="addEntry">
+		<!-- 1. Add an entry (and create the feed if it doesn't already exist) -->
+		<AddEntry 
+			debug="${debug}" 
+			file="${file}" 
+
+			project="${project}"
+			version="${version}"
+			branch="${branch}"
+			buildID="${buildID}"
+			buildtype="${buildType}"
+			feedURL="${feedURL}"
+			buildURL="${buildURL}"
+
+			buildAlias="${buildAlias}"
+			
+			buildDetailURL="${buildDetailURL}"
+			buildConfigURL="${buildConfigURL}"
+			buildLogURL="${buildLogURL}"
+			buildMapURL="${buildMapURL}"
+			
+			dependencyURLs="${dependencyURLs}"
+			releases="${Releases}"
+			
+			releaseNotesURL="${releaseNotesURL}"
+			updateManagerURL="${updateManagerURL}"
+			downloadsURL="${downloadsURL}"
+			jarSigningStatus="${jarSigningStatus}"
+			coordinatedStatus="${coordinatedStatus}"
+			coordinatedDetails="${coordinatedDetails}"
+			
+			JUnitTestURL="${JUnitTestURL}"
+			performanceTestURL="${performanceTestURL}"
+			APITestURL="${APITestURL}"
+			JUnitTestResults="${JUnitTestResults}"
+			performanceTestResults="${performanceTestResults}"
+			APITestResults="${APITestResults}"
+		/>
+	</target>
+
+	<!-- 2. Publish feed: commit file into CVS - overwrite existing and add if necessary -->
+	<target name="publishFeedToCVS">
+		<PublishFeed 
+			debug="${debug}" 
+			file="${file}" 
+			
+			cvsExec="${cvsExec}"
+			cvsRoot="${cvsRoot}"
+			cvsPath="${cvsPath}"
+			cvsTemp="${cvsTemp}"
+		/>
+		<delete dir="${cvsTemp}"/>
+	</target>
+
+	<!-- 3. Publish feed: SCP file to remote server, overwriting existing if exists -->
+	<target name="publishFeedWithSCP">
+		<PublishFeed 
+			debug="${debug}" 
+			file="${file}" 
+			
+			scpExec="${scpExec}"
+			scpTarget="${scpTarget}"
+			sshExec="${sshExec}"
+		/>
+	</target>	
+
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedValidate.xml b/bundles/org.eclipse.build.tools/scripts_rss/feedValidate.xml
new file mode 100644
index 0000000..351889d
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedValidate.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+
+<project name="Validate a Feed" default="validateFeed" basedir="../">
+	<!-- TODO: set correct path to EMF plugins -->
+ 	<property name="EMF_HOME" value="../../eclipse-plugins-emf22/eclipse/plugins"/>
+	<echo message="Using EMF_HOME = ${EMF_HOME}"/>
+
+	<taskdef name="validateFeed" 
+	  classname="org.eclipse.releng.util.rss.emf.RSSFeedValidatorDynamicTask">
+		<classpath>
+			<pathelement path="feedValidator.jar"/>
+		     <fileset dir="${EMF_HOME}">
+		        <include name="**/org.eclipse.emf.ecore_*.jar"/>
+		        <include name="**/org.eclipse.emf.common_*.jar"/>
+	    		<include name="**/org.eclipse.emf.ecore.xmi_*.jar"/>
+		    	<include name="**/org.eclipse.xsd_*.jar"/>
+		      </fileset>
+		</classpath>
+	</taskdef>
+
+	<!-- debug: 0|1|2 -->
+	<target name="validateFeed" description="verify if the feed is valid with respect to the schema">
+		<validateFeed
+			debug="2" 
+			XSDFile="../schema/atom10_build.xsd" 
+			XMLFile="../data/builds-emf.xml" >
+		</validateFeed>
+	</target>
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedWatch.sh b/bundles/org.eclipse.build.tools/scripts_rss/feedWatch.sh
new file mode 100644
index 0000000..631539c
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedWatch.sh
@@ -0,0 +1,15 @@
+#*******************************************************************************
+# 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
+#*******************************************************************************
+#!/bin/sh
+
+export JAVA_HOME=/opt/sun-java2-5.0;
+export ANT_HOME=/opt/apache-ant-1.6;
+$ANT_HOME/bin/ant -f feedWatch.xml;
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/feedWatch.xml b/bundles/org.eclipse.build.tools/scripts_rss/feedWatch.xml
new file mode 100644
index 0000000..9494d05
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/feedWatch.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+
+<project name="Watch Feed" default="watchFeed">
+
+	<taskdef name="WatchFeed" 
+	  classname="org.eclipse.releng.services.rss.RSSFeedWatcherTask" 
+	  classpath="../feedTools.jar"/>
+
+	<!-- 1. Define property file -->
+	<target name="init">
+		<!-- <property file="../properties/feedWatch.emf.properties"/> -->
+		<property file="../properties/feedWatch.uml2.properties"/>
+	</target>
+
+	<!-- 2. Watch feed for certain change conditions and respond if necessary -->
+	<target name="watchFeed" depends="init" description="Watch feed for certain change conditions and respond if necessary">
+		<WatchFeed 
+			debug="${debug}" 
+			file="${file}" 
+			feedURL="${feedURL}"
+			feedWatchActions="${feedWatchActions}"
+		/>
+
+		<!--
+			Different ways to get at the results of the task include:
+			
+			feedWatchAction.Result.0       - The result returned from the <exec> task; if set, something bad happened
+			feedWatchAction.Error.0        - Details about the error that occurred, if set, something bad happened
+			feedWatchAction.Output.0       - Console output from your <exec>, if any; 
+				this will depend on what you run in response to a feed change, and if that process produces console output
+			feedWatchAction.OldValue.0 - The original value of the node you asked for, from the previous cached version of the feed
+			feedWatchAction.NewValue.0 - The changed value of the node you asked for, from the latest version of the feed; 
+				if you asked for changes to the test status for performance tests, this could be "PASS"; 
+				if you asked for any changes to the feed, this will be the XML datestamp of the last update, eg: 2006-05-04T12:14:33Z
+			feedWatchAction.TheValue.0 - Even if the original value is unchanged, this will contain the NewValue. 
+				This is useful to determine if the value changed (check if NewValue.0 is defined), but also to have the value, 
+				in case you need to pass it to a downstream script. For example, you can check //*[name()='entry'][1]/*/*[name()='build']/@type - which
+				if the feed only contains S builds, will never change, and NewValue.0 will be undefined; however, you need the "S" value, 
+				so use this property instead of NewValue.0 in order to pass it to the next script.
+				
+			If you are watching for more than one condition, you can check for feedWatchAction.NewValue.0, feedWatchAction.NewValue.1, feedWatchAction.NewValue.2, etc.
+		-->
+		
+		<!--
+		<echoproperties format="text">
+			<propertyset><propertyref regex="feedWatchAction\.(Result|Error|Output|NewValue|OldValue|TheValue)\.0"/></propertyset>		
+		</echoproperties>
+		-->
+		
+		<echo message="feedWatchAction.OldValue.0 = ${feedWatchAction.OldValue.0}"/>
+		<echo message="feedWatchAction.NewValue.0 = ${feedWatchAction.NewValue.0}"/>
+		<echo message="feedWatchAction.TheValue.0 = ${feedWatchAction.TheValue.0}"/>
+		<echo message="feedWatchAction.OldValue.1 = ${feedWatchAction.OldValue.1}"/>
+		<echo message="feedWatchAction.NewValue.1 = ${feedWatchAction.NewValue.1}"/>
+		<echo message="feedWatchAction.TheValue.1 = ${feedWatchAction.TheValue.1}"/>
+		<echo message="feedWatchAction.OldValue.2 = ${feedWatchAction.OldValue.2}"/>
+		<echo message="feedWatchAction.NewValue.2 = ${feedWatchAction.NewValue.2}"/>
+		<echo message="feedWatchAction.TheValue.2 = ${feedWatchAction.TheValue.2}"/>
+		
+		<!-- 3. now we can respond to a changed/unchanged condition if desired -->
+		<antcall target="respondToFeedUnchanged"/>
+		<antcall target="respondToFeedChanged"/>
+
+	</target>
+	
+	<!-- 3a. If not responding via shell <exec>, respond now by checking property(ies) for results -->
+	<target name="respondToFeedUnchanged" description="Respond to feed unchanged" unless="feedWatchAction.NewValue.0">
+		<echo message="Feed unchanged! Should we do something, like waiting for some period of time and trying again?"/>
+		<!-- add steps to do here, like sleeping & re-firing, sending a nag note to someone, etc.  -->
+	</target>
+	<!-- 3b. If not responding via shell <exec>, respond now by checking property(ies) for results -->
+	<target name="respondToFeedChanged" description="Respond to feed changed" if="feedWatchAction.NewValue.0">
+		<echo message="Feed changed! Time to do something... but what?"/>
+		<!-- add steps to do here, like firing an email, starting a build, etc. -->
+		
+		<!-- eg., wait 30 seconds and try again -->
+		<!-- <echo message="Sleeping..."/>
+		<exec executable="sleep" resolveexecutable="true"><arg line="10"/></exec>
+		<echo message="Spawning..."/>
+		<exec executable="feedWatch.sh" resolveexecutable="true" spawn="true"/> -->
+	</target>
+	
+</project>
diff --git a/bundles/org.eclipse.build.tools/scripts_rss/sendEmailAlert.sh b/bundles/org.eclipse.build.tools/scripts_rss/sendEmailAlert.sh
new file mode 100644
index 0000000..3906f9d
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/scripts_rss/sendEmailAlert.sh
@@ -0,0 +1,103 @@
+#*******************************************************************************
+# 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
+#*******************************************************************************
+#!/bin/sh
+
+# simple sample script to fire an email from the local machine to some user to 
+# notify them of a change to the watched feed
+
+# Requirements:
+# tested on Debian (Kubuntu), using
+# exim 3.36-16
+# mailx 1:8.1.2-0.20040524cvs-4
+
+debug=0;
+feedURL="";
+xpath="";
+newvalue="";
+oldvalue="";
+
+while [ "$#" -gt 0 ]; do
+	case $1 in
+		'-debug')
+			debug=$2;
+			shift 1
+			;;
+		'-feedURL')
+			feedURL=$2;
+			shift 1
+			;;
+		'-xpath')
+			xpath=$2;
+			shift 1
+			;;
+		'-oldvalue')
+			oldvalue=$2;
+			shift 1
+			;;
+		'-newvalue')
+			newvalue=$2;
+			shift 1
+			;;
+	esac
+	shift 1
+done
+
+if [ $debug -gt 0 ]; then
+  echo "[sendEmailAlert] Started `date +%H:%M:%S`. Executing with the following options:"
+  echo "-debug $debug";
+  echo "-feedURL $feedURL";
+  echo "-xpath $xpath";
+  echo "-oldvalue $oldvalue";
+  echo "-newvalue $newvalue";
+fi
+
+tmpfile="/tmp/sendEmailAlert.sh.tmp";
+echo "" > $tmpfile;
+
+# compose message
+echo "Eclipse RSS Feed has been updated." >> $tmpfile;
+echo "" >> $tmpfile;
+echo "Here's what happened:" >> $tmpfile;
+echo "" >> $tmpfile;
+
+if [ "x$xpath" != "x" ]; then    echo "Changed Node: "$xpath >> $tmpfile; fi
+if [ "x$oldvalue" != "x" ]; then echo "Old Value:    "$oldvalue >> $tmpfile; fi
+if [ "x$newvalue" != "x" ]; then echo "New Value:    "$newvalue >> $tmpfile; fi
+if [ "x$feedURL" != "x" ]; then  echo "Feed URL:     "$feedURL >> $tmpfile; fi
+
+echo "" >> $tmpfile;
+
+#assemble mail info
+toAddress="codeslave@ca.ibm.com";
+fromAddress="Eclipse Build Team <NOSUCHADDRESS@eclipse.org>";
+subject="Eclipse RSS Feed Updated!";
+MAIL="/usr/bin/mail";
+
+if [ $debug -gt 0 ]; then
+    echo "Sending the following email using "$MAIL":";
+    echo "--";
+    echo "Subject: "$subject;
+    echo "To: "$toAddress
+    echo "From: "$fromAddress
+    echo "--";
+    cat $tmpfile;
+    echo "--";
+fi
+
+# send message
+cat $tmpfile | $MAIL -s "$subject" -a "From: $fromAddress" $toAddress;
+
+# cleanup
+rm -fr $tmpfile;
+
+if [ $debug -gt 0 ]; then
+    echo "Done.";
+fi
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/BuildMachineManager.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/BuildMachineManager.java
new file mode 100644
index 0000000..8cb6400
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/BuildMachineManager.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+/*
+ * Created on 8-Jan-2004
+ *
+ * To change this generated comment go to 
+ * Window>Preferences>Java>Code Generation>Code Template
+ */
+package org.eclipse.releng;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.StringTokenizer;
+import java.util.Properties;
+import java.io.PrintWriter;
+
+/**
+ * @author SDimitrov
+ * 
+ * This class finds an available build machine by reading a registry of build
+ * machines and their status.
+ *  
+ */
+public class BuildMachineManager extends Thread {
+	// registry mapping of machines being used by a given build
+	//list is the path to the configuration of build machines available for a
+	// given build type
+	// waitInterval is the number of seconds to wait if no machines are
+	// available for a given build purpose
+	// findKey represents the key in cfg from which which to obtain the list of
+	// machines
+	// createKey is written to the registry with the machine name that is
+	// available
+
+	private String markerContainer;
+	private int waitInterval;
+	private String markerName;
+	private String markerKey="0";
+	private String cfgKey;
+	private String cfg;
+
+	public BuildMachineManager() {
+		super();
+	}
+	public BuildMachineManager(
+		String cfg,
+		String markerContainer,
+		int waitInterval,
+		String markerName,
+		String markerKey,
+		String cfgKey) {
+		this.waitInterval = waitInterval;
+		this.cfg = cfg;
+		this.markerContainer = markerContainer;
+		this.markerName = markerName;
+		this.markerKey = markerKey;
+		this.cfgKey = cfgKey;
+		this.run();
+	}
+
+	public void run() {
+		String[] machines = getNames();
+		
+		if (new File(markerContainer+"/"+markerName+".marker").exists()){
+			System.out.println("Marker already exists: "+markerName+".marker");
+			return;
+		}
+		
+		boolean machineFound = false;
+		try {
+			while (!machineFound) {
+				for (int i = 0; i < machines.length; i++) {
+					if (!inUse(machines[i])) {
+						if (createNewMarker(machines[i])) {
+							machineFound = true;
+							return;
+						}
+					}
+				}
+
+				//wait a given interval before re-checking for an available
+				// build machine.
+				sleep(1000 * waitInterval);
+			}
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private boolean createNewMarker(String machineName) {
+		//create a temporary lock on marker container
+		
+		File lock = new File(markerContainer + "/" + "lock");
+		if (lock.exists())
+			return false;
+
+		try {
+			File markerFile =  new File(markerContainer+"/"+markerName+".marker");
+			lock.createNewFile();
+			markerFile.createNewFile();
+			PrintWriter out = new PrintWriter(new FileWriter(markerFile));
+			out.println(markerKey+"=" + machineName);
+			out.flush();
+			out.close();
+			lock.delete();
+		} catch (IOException e) {
+			e.printStackTrace();
+			return false;
+		}
+		return true;
+	}
+
+	private boolean inUse(String machineName) {
+		
+		File container = new File(markerContainer);
+
+		if (container.exists() && container.isDirectory()) {
+			Properties properties = null;
+			File[] markerFiles = container.listFiles();
+			for (int i = 0; i < markerFiles.length; i++) {
+				File markerFile = markerFiles[i];
+				if (markerFile.getName().endsWith(".marker")) {
+					properties = new Properties();
+					try {
+						FileInputStream in = new FileInputStream(markerFiles[i]);
+						properties.load(in);
+						in.close();
+						if (properties.containsValue(machineName)){
+							return true;
+						}
+					} catch (FileNotFoundException e) {
+						e.printStackTrace();
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	private String[] getNames() {
+		StringTokenizer tokenizer = new StringTokenizer(getList(), ",");
+		String[] names = new String[tokenizer.countTokens()];
+		int i = 0;
+
+		while (tokenizer.hasMoreTokens()) {
+			names[i++] = tokenizer.nextToken();
+		}
+		return names;
+	}
+
+	private String getList() {
+		Properties cfgProperties = new Properties();
+		try {
+			FileInputStream in = new FileInputStream(new File(cfg));
+			cfgProperties.load(in);
+			in.close();
+
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return cfgProperties.getProperty(cfgKey);
+	}
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/BuildProperties.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/BuildProperties.java
new file mode 100644
index 0000000..927e461
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/BuildProperties.java
@@ -0,0 +1,334 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng;
+
+import java.io.*;
+import java.util.Properties;
+
+/**
+ * Class that stores build identification information taken from monitor.
+ * properties as String objects
+ */
+public class BuildProperties {
+	// recipients key value setting.  Comma separated list of email addresses of those who should
+	// receive build information
+	private String toRecipientList = "";
+
+	// text message notification list
+	private String textRecipientList = "";
+
+	// email address of the sender
+	private String sender = "";
+	// mail server name
+	private String host = "";
+
+	// default name of the build log file used with listener
+	private String logFile = "index.php";
+
+	// the prefix prepended to the subject of build related emails
+	private String buildSubjectPrefix="[build]";
+
+	// the build id,  typically <buildType><build date>	
+	private String buildid;
+	// the date and time of the build
+	private String timestamp;
+	// the name of the directory containing the builds, typically <buildType>-<buildType><build date>-<timestamp>
+	private String buildLabel;
+
+	// the http download URL
+	private String httpUrl;
+
+	// the ftp download URL
+//	private String ftpUrl;
+	
+	// the Object that holds the key value pairs in monitor.properties
+	private Properties buildProperties;
+
+	public BuildProperties (){
+		this("monitor.properties");
+	}
+	
+		
+	public BuildProperties(String monitorProperties) {
+		buildProperties = new Properties();
+		// retrieve information from monitor.properties file.
+		//  This file should reside in the same directory as the startup.jar at build time.
+		try {
+			buildProperties.load(
+				new FileInputStream(new File(monitorProperties)));
+
+			try {
+					buildSubjectPrefix = buildProperties.get("buildSubjectPrefix").toString();
+				} catch (NullPointerException e) {
+					System.out.println(
+						"Value for buildSubjectPrefix not found in monitor.properties");
+					System.out.println(
+							"Default value, buildSubjectPrefix=[build] will be used.");
+
+				}
+
+			try {
+					httpUrl = buildProperties.get("httpUrl").toString();
+				} catch (NullPointerException e) {
+					System.out.println(
+						"Value for httpUrl not found in monitor.properties");
+				}
+				
+			/*try {
+				ftpUrl = buildProperties.get("ftpUrl").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+				"Value for ftpUrl not found in monitor.properties");
+			}*/
+				
+			try {
+				buildid = buildProperties.get("buildId").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for buildId not found in monitor.properties");
+			}
+
+			try {
+				buildLabel = buildProperties.get("buildLabel").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for buildLabel not found in monitor.properties");
+			}
+			try {
+				timestamp = buildProperties.get("timestamp").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for timestamp not found in monitor.properties");
+			}
+
+			try {
+				toRecipientList = buildProperties.get("recipients").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for recipients not found in monitor.properties");
+
+			}
+
+			try {
+				textRecipientList = buildProperties.get("textRecipients").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for textRecipients not found in monitor.properties");
+
+			}
+
+			try {
+				sender = buildProperties.get("sender").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for sender not found in monitor.properties");
+			}
+
+			try {
+				host = buildProperties.get("host").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for host not found in monitor.properties");
+			}
+
+			try {
+				logFile = buildProperties.get("log").toString();
+			} catch (NullPointerException e) {
+				System.out.println(
+					"Value for log not found in monitor.properties");
+				System.out.println(
+					"Default value, log=index.php will be used.");
+				
+			}
+
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+
+	public static void main(String args[]) {
+		new BuildProperties();
+	}
+
+
+	/**
+	 * Returns the buildLabel.
+	 * @return String
+	 */
+	public String getBuildLabel() {
+		return buildLabel;
+	}
+
+	/**
+	 * Sets the buildLabel.
+	 * @param buildLabel The buildLabel to set
+	 */
+	public void setBuildLabel(String buildLabel) {
+		this.buildLabel = buildLabel;
+	}
+
+	/**
+	 * Returns the logFile.
+	 * @return String
+	 */
+	public String getLogFile() {
+		return logFile;
+	}
+
+	/**
+	 * Sets the logFile.
+	 * @param logFile The logFile to set
+	 */
+	public void setLogFile(String logFile) {
+		this.logFile = logFile;
+	}
+
+	/**
+	 * Returns the buildid.
+	 * @return String
+	 */
+	public String getBuildid() {
+		return buildid;
+	}
+
+	/**
+	 * Returns the timestamp.
+	 * @return String
+	 */
+	public String getTimestamp() {
+		return timestamp;
+	}
+
+	/**
+	 * Sets the buildid.
+	 * @param buildid The buildid to set
+	 */
+	public void setBuildid(String buildid) {
+		this.buildid = buildid;
+	}
+
+	/**
+	 * Sets the timestamp.
+	 * @param timestamp The timestamp to set
+	 */
+	public void setTimestamp(String timestamp) {
+		this.timestamp = timestamp;
+	}
+
+	/**
+	 * Returns the host.
+	 * @return String
+	 */
+	public String getHost() {
+		return host;
+	}
+
+	/**
+	 * Returns the recipientList.
+	 * @return String
+	 */
+	public String getToRecipientList() {
+		return toRecipientList;
+	}
+
+	/**
+	 * Returns the sender.
+	 * @return String
+	 */
+	public String getSender() {
+		return sender;
+	}
+
+	/**
+	 * Sets the host.
+	 * @param host The host to set
+	 */
+	public void setHost(String host) {
+		this.host = host;
+	}
+
+	/**
+	 * Sets the recipientList.
+	 * @param recipientList The recipientList to set
+	 */
+	public void setRecipientList(String recipientList) {
+		this.toRecipientList = recipientList;
+	}
+
+	/**
+	 * Sets the sender.
+	 * @param sender The sender to set
+	 */
+	public void setSender(String sender) {
+		this.sender = sender;
+	}
+
+	/**
+	 * Returns the buildSubjectPrefix.
+	 * @return String
+	 */
+	public String getBuildSubjectPrefix() {
+		return buildSubjectPrefix;
+	}
+
+	/**
+	 * Sets the buildSubjectPrefix.
+	 * @param buildSubjectPrefix The buildSubjectPrefix to set
+	 */
+	public void setBuildSubjectPrefix(String buildSubjectPrefix) {
+		this.buildSubjectPrefix = buildSubjectPrefix;
+	}
+
+	/**
+	 * Returns the httpUrl.
+	 * @return String
+	 */
+	public String getHttpUrl() {
+		return httpUrl;
+	}
+
+	/**
+	 * Sets the httpUrl.
+	 * @param httpUrl The httpUrl to set
+	 */
+	public void setHttpUrl(String downloadUrl) {
+		this.httpUrl = downloadUrl;
+	}
+	
+	/**
+	 * Returns the ftpUrl.
+	 * @return String
+	 *//*
+	public String getftpUrl() {
+		return ftpUrl;
+	}*/
+
+	/**
+	 * Sets the ftpUrl.
+	 * @param ftpUrl The httpUrl to set
+	 *//*
+	public void setftpUrl(String downloadUrl) {
+		this.ftpUrl = downloadUrl;
+	}*/
+
+
+	public String getTextRecipientList() {
+		return textRecipientList;
+	}
+
+
+	public void setTextRecipientList(String textRecipientList) {
+		this.textRecipientList = textRecipientList;
+	}
+	
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/CompileErrorCheck.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/CompileErrorCheck.java
new file mode 100644
index 0000000..187f3c5
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/CompileErrorCheck.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng;
+
+/**
+ * A custom Ant task that finds compile logs containing compile
+ * errors.  The compile logs with errors are sent as email attachments using
+ * information in monitor.properties.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class CompileErrorCheck extends Task {
+
+	private static final class CompilerErrorCheckerHandler extends DefaultHandler {
+		boolean hasErrors = false;
+		
+		public void startElement(String uri, String localName,
+				String name, Attributes attributes) throws SAXException {
+			if (this.hasErrors) return;
+			if ("problem_summary".equals(name)) {
+				// problem_summary name
+				String value = attributes.getValue("errors");
+				this.hasErrors = value != null && !value.equals("0");
+			}
+		}
+		public boolean hasErrors() {
+			return this.hasErrors;
+		}
+	}
+
+	//directory containing of build source, parent of features and plugins
+	private String install = "";
+
+	//keep track of compile logs containing errors
+	private Vector logsWithErrors;
+	
+	// keep track of the factory to use
+	private SAXParser parser;
+	
+	public CompileErrorCheck() {
+		this.logsWithErrors = new Vector();
+		SAXParserFactory factory = SAXParserFactory.newInstance();
+		this.parser = null;
+
+		try {
+			this.parser = factory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		} catch (SAXException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public void execute() throws BuildException {
+		if (parser == null) return;
+		findLogs(new File(install));
+		sendNotice();
+	}
+
+	// test
+	public static void main(String[] args) {
+		CompileErrorCheck checker = new CompileErrorCheck();
+		checker.install="d:/compilelogs";
+		checker.execute();
+	}
+
+	private void findLogs(File aFile) {
+		if (!aFile.exists()) return;
+		// basis case
+		if (aFile.isFile()) {
+			String absolutePath = aFile.getAbsolutePath();
+			if (absolutePath.endsWith(".xml")) {
+				parse(aFile);
+			} else if (absolutePath.endsWith(".jar.bin.log")||absolutePath.endsWith("dot.bin.log")){
+				read(aFile);
+			}
+		} else {
+			//recurse into directories looking for and reading compile logs
+			File files[] = aFile.listFiles();
+
+			if (files != null) {
+				for (int i = 0, max = files.length; i < max; i++) {
+					findLogs(files[i]);
+				}
+			}
+		}
+	}
+
+	private void read(File file) {
+		//read the contents of the log file, and return contents as a String
+		if (file.length()==0)
+			return;
+		
+		BufferedReader in = null;
+		String aLine;
+
+		try {
+			in = new BufferedReader(new FileReader(file));
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+
+		try {
+			while ((aLine = in.readLine()) != null) {
+				int statusSummaryIndex=aLine.indexOf("problem (");
+				if (statusSummaryIndex==-1)
+					statusSummaryIndex=aLine.indexOf("problems (");
+				
+				if (statusSummaryIndex!=-1&&(aLine.indexOf("error", statusSummaryIndex) != -1)){
+					logsWithErrors.add(file);
+					System.out.println(file.getName()+" has compile errors.");
+					return;
+				}
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			// make sure we don't leave any file handle open
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					// ignore
+				}
+			}
+		}
+	}
+
+	private void parse(File file) {
+		BufferedReader reader = null;
+		try {
+			reader = new BufferedReader(new FileReader(file));
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+
+		InputSource inputSource = new InputSource(reader);
+
+		CompilerErrorCheckerHandler compilerErrorCheckerHandler = new CompilerErrorCheckerHandler();
+		try {
+			parser.parse(inputSource, compilerErrorCheckerHandler);
+		} catch (SAXException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			// make sure we don't leave any file handle open
+			if (reader != null) {
+				try {
+					reader.close();
+				} catch (IOException e) {
+					// ignore
+				}
+			}
+		}
+		
+		if (compilerErrorCheckerHandler.hasErrors()) {
+			logsWithErrors.add(new File(file.getParentFile(),file.getName().replaceAll(".xml", ".html")));
+			System.out.println(file.getName()+" has compile errors.");
+		}
+	}
+	
+	private void sendNotice() {
+		//send email notification that there are compile errors in the build
+		//send the logs as attachments
+		Enumeration enumeration = logsWithErrors.elements();
+
+		if (logsWithErrors.size() > 0) {
+			try{
+
+				Mailer mailer = new Mailer();
+				String [] logFiles = new String [logsWithErrors.size()];
+
+				int i=0;
+
+				while (enumeration.hasMoreElements()) {
+					logFiles[i++]=((File) enumeration.nextElement()).getAbsolutePath();
+				}
+
+				mailer.sendMultiPartMessage("Compile errors in build", "Compile errors in build.  See attached compile logs.", logFiles);
+			} catch (NoClassDefFoundError e){
+				while (enumeration.hasMoreElements()) {
+					String path=((File) enumeration.nextElement()).getAbsolutePath();
+					String nameWithPlugin=path.substring(path.indexOf("plugins"),path.length());
+					System.out.println("Compile errors detected in "+nameWithPlugin);
+				}
+
+				System.out.println("Unable to send email notice of compile errors.");
+				System.out.println("The j2ee.jar may not be on the Ant classpath.");
+
+			}
+
+		}
+
+	}
+
+	/**
+	 * Gets the install.
+	 * @return Returns a String
+	 */
+	public String getInstall() {
+		return install;
+	}
+
+	/**
+	 * Sets the install.
+	 * @param install The install to set
+	 */
+	public void setInstall(String install) {
+		this.install = install;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/CvsDiffParser.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/CvsDiffParser.java
new file mode 100644
index 0000000..9fdc204
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/CvsDiffParser.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+/*
+ * Created on Dec 9, 2003
+ * 
+ */
+package org.eclipse.releng;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+
+import java.util.Vector;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * @author kmoir
+ * 
+ * To change the template for this generated type comment go to Window -
+ * Preferences - Java - Code Generation - Code and Comments
+ */
+public class CvsDiffParser extends Task {
+
+	private String mapDiffFile;
+	private Vector updatedMaps;
+	
+	/**
+	 *  
+	 */
+	public CvsDiffParser() {
+		super();
+		// TODO Auto-generated constructor stub
+	}
+
+	public static void main(String[] args) {
+
+		CvsDiffParser parser = new CvsDiffParser();
+		parser.setMapDiffFile("d:/junk/cvsDiff.txt");
+		parser.execute();		
+	}
+
+	public void execute() throws BuildException {
+		parseMapDiffFile();
+		sendNotice();
+	}
+
+	/**
+	 * @return Returns the mapDiffFile.
+	 */
+	public String getMapDiffFile() {
+		return mapDiffFile;
+	}
+
+	/**
+	 * @param mapDiffFile
+	 *            The mapDiffFile to set.
+	 */
+	public void setMapDiffFile(String mapDiffFile) {
+		this.mapDiffFile = mapDiffFile;
+	}
+
+	private void parseMapDiffFile() {
+		updatedMaps = new Vector();
+
+		//read the contents of the Diff file, and return contents as a String
+		if (mapDiffFile.length() == 0)
+			updatedMaps=null;
+
+		BufferedReader in = null;
+		String aLine;
+		
+		try {
+			in = new BufferedReader(new FileReader(mapDiffFile));
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+
+		try {
+			while ((aLine = in.readLine()) != null) {
+				if (aLine.startsWith("RCS file")) {
+					String mapPath =
+						(aLine
+							.substring(aLine.indexOf(":"), aLine.indexOf(",")))
+							.trim();
+					
+					//verification for actual changes in tags base.plugin
+					while ((aLine = in.readLine()) != null && !aLine.startsWith("===")){
+						if (aLine.startsWith("< plugin")||aLine.startsWith("< fragment")||aLine.startsWith("< feature")||aLine.startsWith("< base.plugin")){
+							updatedMaps.add(new File(mapPath).getName());
+							break;
+						}
+					}
+		
+				}
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private void sendNotice(){
+		
+		if (updatedMaps==null || updatedMaps.size()==0){
+			throw new BuildException("Build cancelled - map files unchanged.");
+		} 
+		
+		Mailer mailer = new Mailer();
+		
+		String subject="updated map file listing";
+		String message ="these map files have been updated for the build:\n\n";
+		
+		for (int i=0; i<updatedMaps.size();i++){
+			message=message.concat(updatedMaps.elementAt(i).toString()+"\n");
+		}
+		
+		try {
+			mailer.sendMessage(subject,message);
+		} catch (NoClassDefFoundError e){
+			System.out.println(message);
+		}		
+	}
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/ElementParser.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/ElementParser.java
new file mode 100644
index 0000000..01430e9
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/ElementParser.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+/**
+ * Parses feature.xml, plugin.xml, and fragment.xml files
+ *
+ */
+
+package org.eclipse.releng;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.SAXException;
+import java.io.IOException;
+import java.util.Vector;
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+
+
+public class ElementParser extends DefaultHandler {
+
+	private SAXParser parser;
+	private Vector plugins;	
+	private Vector features;
+
+	public Vector getPlugins(){return plugins;}
+	public Vector getFeatures(){return features;}
+
+    public ElementParser() {
+        //  Create a Xerces SAX Parser
+    	SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+    	
+    	try {
+			parser = saxParserFactory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		} catch (SAXException e) {
+			e.printStackTrace();
+		}
+    	
+        
+        
+        // instantiate vectors that will hold lists of plugins and features read from feature.xml
+        plugins = new Vector();
+        features = new Vector();
+    }
+    
+    public void parse(String xmlFile){
+			
+	    //  Parse the Document      
+        try {
+            parser.parse(xmlFile,this);
+        } catch (SAXException e) {
+            System.err.println (e);
+        } catch (IOException e) {
+            System.err.println (e);
+          
+        }
+    }
+
+	public void parse(String install, String type, String id){
+				
+		String xmlFile=null;		
+		
+		if (type.equals("feature"))
+			xmlFile=install+"/features/"+id+"/"+"feature.xml";
+		if (type.equals("plugin"))
+			xmlFile=install+"/plugins/"+id+"/"+"plugin.xml";
+		if (type.equals("fragment"))
+			xmlFile=install+"/plugins/"+"/"+id+"/"+"fragment.xml";
+
+		if (new File(xmlFile).exists())	
+			parse(xmlFile);
+		
+		else{
+			throw new BuildException("The following "+type+" "+id+" did not get fetched.");
+		}
+	
+	}
+    
+    //  Start Element Event Handler
+    public void startElement (String uri, String local,
+        String qName, Attributes atts)  {
+        if (local.equals("plugin")||local.equals("fragment"))
+    		add(atts.getValue("id"), plugins);
+    	if (local.equals("feature"))
+    		add(atts.getValue("id")+"-feature", features);
+    }
+    
+    public void add(String element, Vector v){
+    	if (!v.contains(element))
+    		v.add(element);
+    }
+    
+    // Test
+    public static void main (String[] args) {
+        ElementParser xmlParser = new ElementParser();
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.win32-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.linux.motif-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.linux.gtk-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.solaris.motif-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.aix.motif-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.qnx.photon-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.jdt-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.pde-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.sdk.examples-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.sdk.tests-feature");
+
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.source-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.win32.source-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.linux.motif.source-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.linux.gtk.source-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.solaris.motif.source-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.aix.motif.source-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.platform.qnx.photon.source-feature");
+        xmlParser.parse("l:/vabase/team/sonia", "feature", "org.eclipse.jdt.source-feature");
+
+        System.out.println(xmlParser.plugins);
+        System.out.println(xmlParser.features);
+        
+        System.out.println(xmlParser.plugins.size()+" plugins expected");
+        System.out.println(xmlParser.features.size()+" features expected");
+    }
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/FetchValidator.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/FetchValidator.java
new file mode 100644
index 0000000..e9a8383
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/FetchValidator.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.io.*;
+
+/**
+ * Uses an ElementParser to read a list of feature.xml files and to determine
+ * if all plugins defined in the features are present.
+ */
+
+public class FetchValidator extends Task {
+
+	//directory containing of build source, parent of features and plugins
+	private String install = "";
+
+	//Comma separated list of features to parse
+	private String list;
+
+	//Elements to check for post fetch (use name of project in dev.eclipse.org)
+	private Vector features;
+	private Vector plugins;
+
+	//keeps track of missing elements
+	private Vector missingPlugins;
+	private Vector missingFeatures;
+	
+	
+	//parser
+	ElementParser parser;
+
+	//test
+	public static void main(String args[]) {
+		FetchValidator validator = new FetchValidator();
+		validator.install = "l:/vabase/team/sonia";
+		validator.list =
+			"org.eclipse.platform-feature,org.eclipse.platform.win32-feature,org.eclipse.platform.linux.motif-feature";
+		validator.getListOfFeatures(validator.list);
+
+		validator.execute();
+	}
+
+	// entry point
+	public void execute() throws BuildException {
+		getListOfFeatures(list);
+
+		if (!allPresent()) {
+			String missingFeaturesDesc="";
+			String missingPluginsDesc="";
+
+			if (missingFeatures.size() > 0) {
+				for (int i = 0; i < missingFeatures.size(); i++) {
+					missingFeaturesDesc+="\n\r"+missingFeatures.get(i).toString();
+				}
+			}
+
+			if (missingPlugins.size() > 0) {
+				for (int i = 0; i < missingPlugins.size(); i++) {
+					missingPluginsDesc+="\n\t"+missingPlugins.get(i).toString();
+				}
+			}
+			throw new BuildException("The following projects did not get fetched: \n"+missingFeaturesDesc+missingPluginsDesc+"\n"
+			+"\n\nPossible causes of missing source files include an incorrect Tag entry in a .map file or problems with CVS repositories.");					
+		}
+
+		System.out.println("Fetch Complete.");
+	}
+
+	public FetchValidator() {
+		parser = new ElementParser();
+		missingPlugins = new Vector();
+		missingFeatures = new Vector();
+	}
+
+	private void getListOfFeatures(String list) {
+
+		StringTokenizer tokenizer = new StringTokenizer(list, ",");
+
+		while (tokenizer.hasMoreTokens()) {
+			parser.parse(
+				install,
+				"feature",
+				(String) tokenizer.nextToken().trim());
+		}
+
+		features = parser.getFeatures();
+		plugins = parser.getPlugins();
+	}
+
+	private boolean allPresent() {
+		// verify presence of all source projects for the build.  
+		// collect a list of missing plugins (or fragments), and features
+
+		boolean allPresent = true;
+		Enumeration enumeration = plugins.elements();
+
+		while (enumeration.hasMoreElements()) {
+			String plugin = (String) enumeration.nextElement();
+			if (new File(install + "/plugins/" + plugin).exists())
+				continue;
+			else {
+				missingPlugins.add(plugin);
+				allPresent = false;
+			}
+		}
+
+		enumeration = features.elements();
+
+		while (enumeration.hasMoreElements()) {
+			String feature = (String) enumeration.nextElement();
+			if (new File(install + "/features/" + feature).exists())
+				continue;
+			else {
+				missingFeatures.add(feature);
+				allPresent = false;
+			}
+		}
+
+		return allPresent;
+	}
+
+	/**
+	 * Gets the install.
+	 * @return Returns a String
+	 */
+	public String getInstall() {
+		return install;
+	}
+
+	/**
+	 * Gets the list.
+	 * @return Returns a String
+	 */
+	public String getList() {
+		return list;
+	}
+
+	/**
+	 * Sets the install.
+	 * @param install The install to set
+	 */
+	public void setInstall(String install) {
+		this.install = install;
+	}
+
+	/**
+	 * Sets the list.
+	 * @param list The list to set
+	 */
+	public void setList(String list) {
+		this.list = list;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/FindMachineTask.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/FindMachineTask.java
new file mode 100644
index 0000000..aff7723
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/FindMachineTask.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+/*
+ * Created on 8-Jan-2004
+ *
+ * To change this generated comment go to 
+ * Window>Preferences>Java>Code Generation>Code Template
+ */
+package org.eclipse.releng;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+/**
+ * @author SDimitrov
+ *
+ * To change this generated comment go to 
+ * Window>Preferences>Java>Code Generation>Code Template
+ */
+public class FindMachineTask extends Task {
+
+	// findKey represents the key in cfg from which which to obtain the list of machines
+	// createKey is written to the registry with the machine name that is available
+	//registry mapping of machines being used by a given build
+	private String markerContainer;
+	
+//time in seconds to wait before re-checking for an available machine
+	private int waitInterval;
+	private String markerName;
+	private String markerKey="0";
+//list is the path to the configuration of build machines available for a given build type
+	private String cfgKey;
+	private String cfg;	
+	
+	/**
+	 * 
+	 */
+	public static void main(String args[]){
+		FindMachineTask test = new FindMachineTask();
+		test.markerContainer = "D:\\junk\\markers";
+		test.cfg="D:\\workspaces\\current\\eclipseInternalBuildTools\\testConfig.properties";
+		test.cfgKey="windowstesting";
+		test.markerName = "200412091500";
+		test.waitInterval = 10;
+		test.execute();
+	}
+	public FindMachineTask() {
+		super();
+	}
+	public void execute() throws BuildException {
+		new BuildMachineManager(cfg,markerContainer,waitInterval,markerName,markerKey,cfgKey);
+	}
+	/**
+	 * @return Returns the cfg.
+	 */
+	public String getCfg() {
+		return cfg;
+	}
+
+	/**
+	 * @param cfg The cfg to set.
+	 */
+	public void setCfg(String cfg) {
+		this.cfg = cfg;
+	}
+
+	/**
+	 * @return Returns the createKey.
+	 */
+	public String getMarkerName() {
+		return markerName;
+	}
+
+	/**
+	 * @param createKey The createKey to set.
+	 */
+	public void setMarkerName(String markerName) {
+		this.markerName = markerName;
+	}
+
+/**
+ * @return Returns the findKey.
+ */
+public String getCfgKey() {
+	return cfgKey;
+}
+
+/**
+ * @param findKey The findKey to set.
+ */
+public void setCfgKey(String cfgKey) {
+	this.cfgKey = cfgKey;
+}
+
+	/**
+	 * @return Returns the registry.
+	 */
+	public String getMarkerContainer() {
+		return markerContainer;
+	}
+
+	/**
+	 * @param registry The registry to set.
+	 */
+	public void setMarkerContainer(String markerContainer) {
+		this.markerContainer = markerContainer;
+	}
+
+/**
+ * @return Returns the waitInterval.
+ */
+public int getWaitInterval() {
+	return waitInterval;
+}
+
+/**
+ * @param waitInterval The waitInterval to set.
+ */
+public void setWaitInterval(int waitInterval) {
+	this.waitInterval = waitInterval;
+}
+
+	/**
+	 * @return Returns the markerKey.
+	 */
+	public String getMarkerKey() {
+		return markerKey;
+	}
+
+	/**
+	 * @param markerKey The markerKey to set.
+	 */
+	public void setMarkerKey(String markerKey) {
+		this.markerKey = markerKey;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/GenerateExcludeListTask.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/GenerateExcludeListTask.java
new file mode 100644
index 0000000..fcc5f0f
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/GenerateExcludeListTask.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 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.releng;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class GenerateExcludeListTask extends Task {
+
+	private ArrayList a = new ArrayList();
+	private String mapFile;
+	private String outputFile;
+
+	public GenerateExcludeListTask() {
+		super();
+	}
+
+	public static void main(String[] args) {
+		GenerateExcludeListTask parser = new GenerateExcludeListTask();
+		parser.setMapFile("c:\\temp\\orbit.map");
+		parser.setOutputFile("c:\\temp\\orbit.map.new");
+		parser.execute();
+	}
+
+	public String getOutputFile() {
+		return outputFile;
+	}
+
+	public void setOutputFile(String outputFile) {
+		this.outputFile = outputFile;
+	}
+
+	public String getMapFile() {
+		return mapFile;
+	}
+
+	public void setMapFile(String mapFile) {
+		this.mapFile = mapFile;
+	}
+
+	public void execute() throws BuildException {
+		readMap();
+		writeProp();
+	}
+
+	// for old map file format //
+
+	/* private void readMap() {
+		try {
+			BufferedReader r = new BufferedReader(new FileReader(mapFile));
+			String line;
+			while ((line = r.readLine()) != null) {
+				int start = line.lastIndexOf('/');
+				int lastcomma = line.lastIndexOf(',');
+				int end = line.length();
+				if (lastcomma > start) {
+					end = lastcomma;
+				}
+				int lastzip = line.lastIndexOf(".zip");
+				if (lastzip > start) {
+					String rstring = line.substring(0, lastzip);
+					line = rstring + ".jar";
+				}
+				if ((start < end) && (start > 0)) {
+					String substr = line.substring(start + 1, end);
+					a.add(substr);
+				}
+			}
+			r.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	} */
+
+	private void readMap() {
+		try {
+			BufferedReader r = new BufferedReader(new FileReader(mapFile));
+			String line;
+			while ((line = r.readLine()) != null) {
+				int start = line.indexOf("plugin@") + 7;
+				int end = line.indexOf(",");
+				String plugin = "";		
+				if ((start > 0) && (end > 0)) {				
+					plugin = line.substring(start, end);
+				}				
+				String version = "";
+				int startv = line.indexOf("version=") + 8;
+				int endv = line.indexOf(",", startv);
+				if ((startv > 0) && (endv > 0)) {
+					version = line.substring(startv, endv);
+				}
+				if ((version != "") && (plugin != "")) {
+				String l = plugin + "_" + version + ".jar";				
+				a.add(l);			
+				}						
+			}
+			r.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private void writeProp() {
+
+		try {
+			BufferedWriter out = new BufferedWriter(new FileWriter(outputFile));
+			for (Iterator iterator = a.iterator(); iterator.hasNext();) {
+				String s = iterator.next().toString();
+				if (iterator.hasNext()) {
+					out.write("plugins/" + s + ",");
+				} else {
+					out.write("plugins/" + s);
+				}
+			}
+			out.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/Mailer.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/Mailer.java
new file mode 100644
index 0000000..ab83b38
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/Mailer.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng;
+
+/**
+ * A Class that sends build related email messages.  host, sender, recipient and
+ * build related information set in monitor.properties
+ */
+
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+import javax.mail.*;
+import javax.mail.internet.*;
+
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.Properties;
+
+public class Mailer {
+
+	// flag that determines whether or not to send mail
+	boolean sendMail = true;
+
+	// the Object that holds the key value pairs in monitor.properties
+	private BuildProperties buildProperties;
+
+	//convert the comma separated list of email addressed into an array of Address objects
+	private Address[] getAddresses(String recipientList) {
+		int i = 0;
+		StringTokenizer recipients = new StringTokenizer(recipientList, ",");
+		Address[] addresses = new Address[recipients.countTokens()];
+
+		while (recipients.hasMoreTokens()) {
+			try {
+				addresses[i++] = new InternetAddress(recipients.nextToken());
+			} catch (AddressException e) {
+				System.out.println("Unable to create address");
+			}
+		}
+		return addresses;
+	}
+
+	public Mailer(){
+		this("monitor.properties");
+	}
+	public Mailer(String buildPropertiesPath){
+		buildProperties = new BuildProperties(buildPropertiesPath);
+		if (buildProperties.getHost().equals("")||buildProperties.getSender().equals("")||buildProperties.getToRecipientList().equals("")){
+			sendMail=false;
+		}
+	}
+
+
+	public static void main(String args[]) {
+		Mailer mailer=new Mailer(args[0]);
+		mailer.sendTextMessage(args[1],args[2]);
+	}
+	
+	public void sendMessage(String aSubject, String aMessage) {
+		if (aSubject == null || aMessage == null || sendMail == false){
+			printEmailFailureNotice(aSubject,aMessage);
+		}
+
+		// Get system properties
+		Properties props = System.getProperties();
+
+		// Setup mail server
+		props.put("mail.smtp.host", buildProperties.getHost());
+
+		// Get session
+		Session session = Session.getDefaultInstance(props, null);
+
+		// Define message
+		MimeMessage message = new MimeMessage(session);
+
+		try {
+			
+			// Set the from address
+			message.setFrom(new InternetAddress(buildProperties.getSender()));
+
+			// Set the to address
+			message.addRecipients(Message.RecipientType.TO, getAddresses(buildProperties.getToRecipientList()));
+
+			// Set the subject
+			message.setSubject(buildProperties.getBuildSubjectPrefix()+
+						"Build "
+							+ buildProperties.getBuildid()
+							+ " (Timestamp:  "
+							+ buildProperties.getTimestamp()
+							+ "):"
+							+ aSubject);
+
+			// Set the content
+			message.setText(
+				"Build "
+					+ buildProperties.getBuildid()
+					+ " (Timestamp: "
+					+ buildProperties.getTimestamp()
+					+ "):  "
+					+ aMessage);
+
+			// Send message
+			Transport.send(message);
+
+		} catch (AddressException e) {
+			e.printStackTrace();
+		} catch (MessagingException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public void sendTextMessage(String aSubject, String aMessage) {
+		if (aSubject == null || aMessage == null || sendMail == false){
+			printEmailFailureNotice(aSubject,aMessage);
+		}
+
+		// Get system properties
+		Properties props = System.getProperties();
+
+		// Setup mail server
+		props.put("mail.smtp.host", buildProperties.getHost());
+
+		// Get session
+		Session session = Session.getDefaultInstance(props, null);
+
+		// Define message
+		MimeMessage message = new MimeMessage(session);
+
+		try {
+			
+			// Set the from address
+			message.setFrom(new InternetAddress(buildProperties.getSender()));
+
+			// Set the to address
+			message.addRecipients(Message.RecipientType.BCC, getAddresses(buildProperties.getTextRecipientList()));
+
+			// Set the subject
+			message.setSubject(buildProperties.getBuildSubjectPrefix()+
+						"Build "
+							+ buildProperties.getBuildid()
+							+ ":"
+							+ aSubject);
+
+			// Set the content
+			message.setText(aMessage);
+
+			// Send message
+			Transport.send(message);
+
+		} catch (AddressException e) {
+			e.printStackTrace();
+		} catch (MessagingException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public void sendMultiPartMessage(
+	// a method for sending mail with attachments
+	String aSubject, String aMessage, String[] attachments) {
+		if (aSubject == null || aMessage == null || sendMail == false){
+			printEmailFailureNotice(aSubject,aMessage);
+		}
+
+		// Get system properties
+		Properties props = System.getProperties();
+
+		// Setup mail server
+		props.put("mail.smtp.host", buildProperties.getHost());
+
+		// Get session
+		Session session = Session.getDefaultInstance(props, null);
+
+		// Define message
+		MimeMessage message = new MimeMessage(session);
+
+		Multipart mp = new MimeMultipart();
+
+		try {
+			// Set the from address
+			message.setFrom(new InternetAddress(buildProperties.getSender()));
+
+			// Set the to address
+			message.addRecipients(Message.RecipientType.TO, getAddresses(buildProperties.getToRecipientList()));
+
+			// Set the subject
+			message.setSubject(buildProperties.getBuildSubjectPrefix()+
+			"Build "
+				+ buildProperties.getBuildid()
+				+ " (Timestamp:  "
+				+ buildProperties.getTimestamp()
+				+ "):"
+				+ aSubject);
+
+			// create and fill the first message part 
+			MimeBodyPart part = new MimeBodyPart();
+			part.setText(
+				"Build "
+					+ buildProperties.getBuildid()
+					+ " (Timestamp: "
+					+ buildProperties.getTimestamp()
+					+ "):  "
+					+ aMessage);
+			mp.addBodyPart(part);
+
+			//for each attachment create new message part
+			for (int i = 0; i < attachments.length; i++) {
+				MimeBodyPart attachmentPart = new MimeBodyPart();
+				// attach the file to the message 
+				FileDataSource attachment = new FileDataSource(attachments[i]);
+				attachmentPart.setDataHandler(new DataHandler(attachment));
+				File attachmentFile=new File(attachments[i]);							
+				attachmentPart.setFileName(attachmentFile.getParent()+"-"+attachmentFile.getName());
+				mp.addBodyPart(attachmentPart);
+			}
+
+			// add the Multipart to the message 
+			message.setContent(mp);
+
+			Transport.send(message);
+
+		} catch (AddressException e) {
+		} catch (MessagingException e) {
+		}
+	}
+
+	private void printEmailFailureNotice(String aSubject, String aMessage){
+		System.out.println("Email failed.  Settings:");
+		System.out.println("\nhost="+buildProperties.getHost()+"\nsender="+buildProperties.getSender()+"\nrecipients="+buildProperties.getToRecipientList());
+		System.out.println("\nSubject="+aSubject+"\nMessage="+aMessage);
+		return;
+	}
+
+	/**
+	 * Returns the buildProperties.
+	 * @return BuildProperties
+	 */
+	public BuildProperties getBuildProperties() {
+		return buildProperties;
+	}
+
+	/**
+	 * Sets the buildProperties.
+	 * @param buildProperties The buildProperties to set
+	 */
+	public void setBuildProperties(BuildProperties buildProperties) {
+		this.buildProperties = buildProperties;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/SystemProperty.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/SystemProperty.java
new file mode 100644
index 0000000..c507ead
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/SystemProperty.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng;
+
+import org.apache.tools.ant.Task;
+
+public class SystemProperty extends Task{
+	//utility to allow modification of System properties from Ant script.
+	private String key;
+	private String value;
+	
+	public SystemProperty(){
+		super();
+	}
+	
+	public void execute(){
+		System.setProperty(key, value);	
+		if (System.getProperty(key).equals(value))
+			System.out.println("System property "+key+" set to "+System.getProperty(key));
+		else{
+			System.out.println("System property "+key+" could not be set. Currently set to "+System.getProperty(key));
+		}
+	}
+
+	public String getKey() {
+		return key;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/UnpackUpdateJars.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/UnpackUpdateJars.java
new file mode 100644
index 0000000..ec9d50d
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/UnpackUpdateJars.java
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.tools.ant.Task;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class UnpackUpdateJars extends Task {
+
+	/**
+	 * @param args
+	 */
+	//parent to plugins and features directory which contains update jars
+	private String site;
+	private String output;
+	ArrayList unpackedPlugins=new ArrayList();
+	
+	public static void main(String[] args) {
+		// TODO Auto-generated method stub
+		UnpackUpdateJars up=new UnpackUpdateJars();
+		up.site="C:\\updatejars\\eclipse";
+		up.output="C:\\updatejars\\newsite";
+		up.execute();
+	}
+
+	public UnpackUpdateJars(){
+		super();
+	}
+	
+	//unpack features, then unpack plug-ins which are not set to unpack="false"
+	public void execute(){
+		new File(output).mkdirs();
+		new File(output+"/features").mkdirs();
+		new File(output+"/plugins").mkdirs();
+
+		//extract features
+		File featureDir=new File(site,"features");
+		if (!featureDir.exists()){
+			return;
+		}
+		File[] features = featureDir.listFiles();
+		for (int i = 0; i < features.length; i++) {
+			File feature = features[i];
+			if (feature.getName().endsWith(".jar")) {
+				String fileName = feature.getName();
+				String unpackedFeatureName = fileName.substring(0, fileName.length() - 4);
+				File unPackedFeature=new File(output+"/features/"+ unpackedFeatureName);
+				unzip(feature, unPackedFeature);
+				getUnpackedPluginList(new File(unPackedFeature,"feature.xml"));
+			}
+		}
+		
+		//unpack plug-ins
+		for (int i=0;i<unpackedPlugins.size();i++){
+			File unpackedPluginDirName=new File(output+"/plugins/"+(String)unpackedPlugins.get(i));
+			File jardPlugin=new File(site,"plugins/"+(String)unpackedPlugins.get(i)+".jar");
+			if (jardPlugin.exists())
+				unzip (jardPlugin,unpackedPluginDirName);
+		}
+	}
+
+	public void unzip(File src, File dest) {
+		Runtime rt = Runtime.getRuntime();
+		String command = "unzip -qo " + src.getPath() + " -d " + dest.getPath();
+		System.out.println("[exec] "+command);
+		Process proc = null;
+		try {
+			proc = rt.exec(command);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		// pick up error messages
+		StreamHandler errorHandler = new StreamHandler(proc.getErrorStream(), "ERROR");
+
+		// pick up output
+		StreamHandler outputHandler = new StreamHandler(proc.getInputStream(), "OUTPUT");
+
+		// kick them off
+		errorHandler.start();
+		outputHandler.start();
+
+		// capture return code
+		int returnCode = 0;
+		try {
+			returnCode = proc.waitFor();
+		} catch (InterruptedException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		if (returnCode!=0)
+			System.out.println("returnCode: " + returnCode);
+
+	}
+
+	class StreamHandler extends Thread {
+		InputStream is;
+
+		String type;
+
+		StreamHandler(InputStream is, String type) {
+			this.is = is;
+			this.type = type;
+		}
+
+		public void run() {
+			try {
+				InputStreamReader isr = new InputStreamReader(is);
+				BufferedReader br = new BufferedReader(isr);
+				String line = null;
+				while ((line = br.readLine()) != null)
+					System.out.println(type + ">" + line);
+			} catch (IOException ioe) {
+				ioe.printStackTrace();
+			}
+		}
+	}
+
+	public String getSite() {
+		return site;
+	}
+
+	public void setSite(String site) {
+		this.site = site;
+	}
+
+	private void getUnpackedPluginList(File featureXml) {
+		Document aDocument=null;
+		BufferedReader reader = null;
+		try {
+			reader = new BufferedReader(new FileReader(featureXml));
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+
+		InputSource inputSource = new InputSource(reader);
+		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+		DocumentBuilder builder = null;
+
+		try {
+			builder = factory.newDocumentBuilder();
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		}
+
+		try {
+			aDocument = builder.parse(inputSource);
+		} catch (SAXException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		// Get feature attributes
+		NodeList nodeList=aDocument.getElementsByTagName("plugin");
+		if (nodeList==null)
+			return;
+		
+		for (int i = 0; i < nodeList.getLength(); i++) {
+			Node pluginNode = nodeList.item(i);
+			NamedNodeMap aNamedNodeMap = pluginNode.getAttributes();
+			Node idNode = aNamedNodeMap.getNamedItem("id");
+			Node versionNode = aNamedNodeMap.getNamedItem("version");
+			String pluginDirName = idNode.getNodeValue() + "_" + versionNode.getNodeValue();
+			Node unpackNode = aNamedNodeMap.getNamedItem("unpack");
+			if (unpackNode == null) {
+				if (!unpackedPlugins.contains(pluginDirName)) {
+					unpackedPlugins.add(pluginDirName);
+				}
+				continue;
+			}
+			
+			if (unpackNode.getNodeValue().toString().trim().toLowerCase().equals("true")) {
+				if (!unpackedPlugins.contains(pluginDirName)){
+					System.out.println(pluginDirName);
+					unpackedPlugins.add(pluginDirName);
+				}
+				continue;
+			}
+			//copy file to new location
+			File jardPlugin=new File(site,"plugins/"+pluginDirName+".jar");
+			if (jardPlugin.exists())
+				if (!jardPlugin.renameTo(new File(output,"plugins/"+pluginDirName+".jar")))
+					System.out.println("Failed to move "+jardPlugin.getAbsolutePath()+" to "+output+"plugins/"+pluginDirName+".jar");
+		}
+	}
+
+	public String getOutput() {
+		return output;
+	}
+
+	public void setOutput(String output) {
+		this.output = output;
+	}	
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/VersionNumberStripper.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/VersionNumberStripper.java
new file mode 100644
index 0000000..bea7263
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/VersionNumberStripper.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import java.io.File;
+
+/*
+ * A class that strips version numbers off built plugin directory names.  This
+ * is helpful when prebuilt plugins are used in generating javadoc (on the
+ * classpath).
+ */
+ 
+public class VersionNumberStripper extends Task {
+
+	//the directory containing the directories and files from which to remove version information
+	private String directory;
+	
+	public VersionNumberStripper() {
+		super();
+	}
+
+	public void setDirectory(String dir){directory=dir;}
+	
+	public String getDirectory(){return directory;}
+
+  	public void execute() throws BuildException {
+  		setDirectory(directory);
+  		stripVersions();
+  	}
+
+	public static void main(String[] args) {
+		new VersionNumberStripper().execute();
+	}
+
+	private void stripVersions(){
+		/* rename directories by removing anything from an underscore onward,
+		 * assuming that anything following the first
+		 * occurence of an underscore is a version number
+		 */
+		 
+		File file=new File(directory);
+		
+		File [] files = file.listFiles();
+		
+		for (int i=0; i<files.length; i++){
+			String absolutePath = files[i].getAbsolutePath();
+			String path = absolutePath.substring(0, absolutePath.length() 
+					- files[i].getName().length());
+			
+			int underScorePos = files[i].getName().indexOf("_");
+			int jarExtPos = files[i].getName().indexOf(".jar");
+			if (underScorePos != -1) {
+				String targetPath;
+				if (jarExtPos != -1) {
+					targetPath =path
+							+ files[i].getName().substring(0, underScorePos)
+							+ ".jar";
+				} else {
+					targetPath = path  
+							+ files[i].getName().substring(0, underScorePos);
+				}
+				files[i].renameTo(new File(targetPath));
+			}
+
+		}
+	}
+}
+
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/AbstractApplication.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/AbstractApplication.java
new file mode 100644
index 0000000..230ad24
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/AbstractApplication.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng.generators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Defines common behaviour for PDE Core applications.
+ */
+public abstract class AbstractApplication {
+
+/**
+ * Starting point for application logic.
+ */
+protected abstract void run() throws Exception;
+
+/*
+ * @see IPlatformRunnable#run(Object)
+ */
+public Object run(Object args) throws Exception {
+	processCommandLine(getArrayList((String[]) args));
+	try {
+		run();
+	} catch (Exception e) {
+		e.printStackTrace(System.out);
+	}
+	return null;
+}
+
+/**
+ * Helper method to ensure an array is converted into an ArrayList.
+ */
+public static ArrayList getArrayList(Object[] args) {
+	// We could be using Arrays.asList() here, but it does not specify
+	// what kind of list it will return. We do need a list that
+	// implements the method List.remove(int) and ArrayList does.
+	ArrayList result = new ArrayList(args.length);
+	for (int i = 0; i < args.length; i++)
+		result.add(args[i]);
+	return result;
+}
+
+
+
+
+/**
+ * Looks for interesting command line arguments.
+ */
+protected void processCommandLine(List commands) {
+}
+
+/**
+ * From a command line list, get the array of arguments of a given parameter.
+ * The parameter and its arguments are removed from the list.
+ * @return null if the parameter is not found or has no arguments
+ */
+protected String[] getArguments(List commands, String param) {
+	int index = commands.indexOf(param);
+	if (index == -1)
+		return null;
+	commands.remove(index);
+	if (index == commands.size()) // if this is the last command
+		return null;
+	List args = new ArrayList(commands.size());
+	while (index < commands.size()) { // while not the last command
+		String command = (String) commands.get(index);
+		if (command.startsWith("-")) // is it a new parameter?
+			break;
+		args.add(command);
+		commands.remove(index);
+	}
+	if (args.isEmpty())
+		return null;
+	return (String[]) args.toArray(new String[args.size()]);
+}
+
+
+
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/EclipseTestResultsGeneratorNoMail.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/EclipseTestResultsGeneratorNoMail.java
new file mode 100644
index 0000000..98be942
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/EclipseTestResultsGeneratorNoMail.java
@@ -0,0 +1,173 @@
+/*
+ * Created on Apr 8, 2003
+ *
+ * To change the template for this generated file go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+package org.eclipse.releng.generators;
+import java.io.File;
+
+
+
+/**
+ * @author SDimitrov
+ *
+ * To change the template for this generated type comment go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+public class EclipseTestResultsGeneratorNoMail extends TestResultsGenerator {
+ 
+	
+	// buildType used to determine if mail should be sent on
+	// successful build completion
+	private String buildType;
+	private boolean sendMail=true;
+	/**
+	 * @return
+	 */
+	public String getBuildType() {
+		return buildType;
+	}
+
+	/**
+	 * @param buildType
+	 */
+	public void setBuildType(String buildType) {
+		this.buildType = buildType;
+	}
+
+	public static void main(String[] args) {
+		String publishingContent="C:\\Documents and Settings\\IBMEmployee\\workspace\\org.eclipse.releng.eclipsebuilder\\eclipse\\publishingFiles";
+
+		EclipseTestResultsGeneratorNoMail test = new EclipseTestResultsGeneratorNoMail();
+		test.buildType="N";
+		test.setIsBuildTested(true);
+		test.setDropTokenList(
+		"%sdk%,%tests%,%example%,%rcpruntime%,%rcpsdk%,%deltapack%,%icubase%,%runtime%,%platformsdk%,%jdt%,%jdtsdk%,%jdtc%,%jarprocessor%,%pde%,%pdesdk%,%cvs%,%cvssdk%,%teamextras%,%swt%,%relengtools%");
+		test.getDropTokensFromList(test.getDropTokenList());
+		test.setXmlDirectoryName("C:\\junk\\testresults\\xml");
+		test.setHtmlDirectoryName("C:\\junk\\testresults\\html");
+		test.setDropDirectoryName("C:\\junk");
+		test.setTestResultsTemplateFileName(
+				publishingContent+"\\templateFiles\\testResults.php.template");
+		test.setDropTemplateFileName(
+				publishingContent+"\\templateFiles\\index.php.template");
+		test.setTestResultsHtmlFileName("testResults.php");
+		test.setDropHtmlFileName("index.php");
+		//test.setDropHtmlFileName("index.html");
+		test.setPlatformIdentifierToken("%platform%");
+	
+		test.setPlatformSpecificTemplateList("Windows,"+publishingContent+"/templateFiles/platform.php.template,winPlatform.php;Linux,"+publishingContent+"/templateFiles/platform.php.template,linPlatform.php;Solaris,"+publishingContent+"/templateFiles/platform.php.template,solPlatform.php;AIX,"+publishingContent+"/templateFiles/platform.php.template,aixPlatform.php;Macintosh,"+publishingContent+"/templateFiles/platform.php.template,macPlatform.php;Source Build,"+publishingContent+"/templateFiles/sourceBuilds.php.template,sourceBuilds.php");
+		/*<property name="platformIdentifierToken" value="%platform%" />
+			<property name="platformSpecificTemplateList" value="Windows,${publishingContent}/templateFiles/platform.php.template,winPlatform.php;Linux,${publishingContent}/templateFiles/platform.php.template,linPlatform.php;Solaris,${publishingContent}/templateFiles/platform.php.template,solPlatform.php;AIX,${publishingContent}/templateFiles/platform.php.template,aixPlatform.php;Macintosh,${publishingContent}/templateFiles/platform.php.template,macPlatform.php;Source Build,${publishingContent}/templateFiles/sourceBuilds.php.template,sourceBuilds.php" />
+			*/
+		
+		test.setHrefTestResultsTargetPath("testresults");
+		test.setCompileLogsDirectoryName(
+			"C:\\junk\\compilelogs");
+		test.setHrefCompileLogsTargetPath("compilelogs");
+		test.setTestManifestFileName("C:\\junk\\testManifest.xml");
+		test.execute();
+	}
+	
+	public void execute() {
+		super.execute();
+//		if (sendMail)
+//			mailResults();
+	}
+	
+	protected String processDropRow(PlatformStatus aPlatform) {
+		String imageName = "";
+
+		if (aPlatform.hasErrors()) {
+			imageName =
+				"<a href=\"" + getTestResultsHtmlFileName() + "\"><img src = \"FAIL.gif\" width=19 height=23></a>";
+			testResultsStatus = "failed";		
+			
+		} else {
+			if (testsRan()) {
+				imageName = "<img src = \"OK.gif\" width=19 height=23>";
+			} else {
+				if (isBuildTested()) {
+					imageName =
+						"<font size=\"-1\" color=\"#FF0000\">pending</font>";
+					testResultsStatus = "pending";
+				} else {
+					imageName = "<img src = \"OK.gif\" width=19 height=23>";
+				}
+			}
+		}
+
+		String result = "<tr>";
+
+		result = result + "<td><div align=left>" + imageName + "</div></td>\n";
+		result = result + "<td>" + aPlatform.getName() + "</td>";
+		
+		//generate http, md5 and sha1 links by calling php functions in the template		
+		result = result + "<td><?php genLinks($_SERVER[\"SERVER_NAME\"],\"@buildlabel@\",\"" + aPlatform.getFileName() +"\"); ?></td>\n";		
+		result = result + "</tr>\n";
+
+		return result;
+	}
+	
+//	private void mailResults() {
+//		//send a different message for the following cases:
+//		//build is not tested at all
+//		//build is tested, tests have not run
+//		//build is tested, tests have run with error and or failures
+//		//build is tested, tests have run with no errors or failures
+//		try {
+//			mailer = new Mailer();
+//		} catch (NoClassDefFoundError e) {
+//			return;
+//		}
+//		String buildLabel = mailer.getBuildProperties().getBuildLabel();
+//		String httpUrl = mailer.getBuildProperties().getHttpUrl()+"/"+buildLabel;
+////		String ftpUrl = mailer.getBuildProperties().getftpUrl()+"/"+buildLabel;
+//		
+//		String subject = "Build is complete.  ";
+//		
+//		String downloadLinks="\n\nHTTP Download:\n\n\t"+httpUrl+" \n\n";
+//	/*	downloadLinks=downloadLinks.concat("FTP Download:\n\n");
+//		downloadLinks=downloadLinks.concat("\tuser: anonymous\n\tpassword: (e-mail address or leave blank)\n\tserver:  download.eclipse.org\n\tcd to directory:  "+buildLabel);
+//		downloadLinks=downloadLinks.concat("\n\n\tor");
+//		downloadLinks=downloadLinks.concat("\n\n\t"+ftpUrl);*/
+//		
+//		//provide http links
+//		String message = "The build is complete."+downloadLinks;
+//
+//		if (testsRan()) {
+//			subject = "Automated JUnit testing complete.  ";
+//			message = "Automated JUnit testing is complete.  ";
+//			subject =
+//				subject.concat(
+//					(getTestResultsWithProblems().endsWith("\n"))
+//						? "All tests pass."
+//						: "Test failures/errors occurred.");
+//			message =
+//				message.concat(
+//					(getTestResultsWithProblems().endsWith("\n"))
+//						? "All tests pass."
+//						: "Test failures/errors occurred in the following:  "
+//							+ getTestResultsWithProblems())+downloadLinks;
+//		} else if (isBuildTested() && (!buildType.equals("N"))) {
+//			subject = subject.concat("Automated JUnit testing is starting.");
+//			message = "The " + subject+downloadLinks;
+//		}
+//
+//		if (subject.endsWith("Test failures/errors occurred."))
+//			mailer.sendMessage(subject, message);
+//		else if (!buildType.equals("N"))
+//			mailer.sendMessage(subject, message);
+//
+//	}
+
+	public boolean isSendMail() {
+		return sendMail;
+	}
+
+	public void setSendMail(boolean sendMail) {
+		this.sendMail = sendMail;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/ErrorTracker.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/ErrorTracker.java
new file mode 100644
index 0000000..e9c6ad0
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/ErrorTracker.java
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng.generators;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.io.File;
+
+import java.util.Vector;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * @version 	1.0
+ * @author
+ */
+public class ErrorTracker {
+
+	// List of test logs expected at end of build
+	private Vector testLogs = new Vector();
+
+
+	// Platforms keyed on 
+	private Hashtable platforms = new Hashtable();
+	private Hashtable logFiles = new Hashtable();
+	private Hashtable typesMap = new Hashtable();
+	private Vector typesList = new Vector();
+	
+	public static void main(String[] args) {
+		
+		// For testing only.  Should not be invoked
+		
+		ErrorTracker anInstance = new ErrorTracker();
+		anInstance.loadFile("C:\\junk\\testManifest.xml");
+		String[] theTypes = anInstance.getTypes();
+		for (int i=0; i < theTypes.length; i++) {
+			// System.out.println("Type: " + theTypes[i]);
+			PlatformStatus[] thePlatforms = anInstance.getPlatforms(theTypes[i]);
+			for (int j=0; j < thePlatforms.length; j++) {
+				// System.out.println("Out ID: " + thePlatforms[j].getId());
+			}
+		}
+	}
+	
+	public void loadFile(String fileName) {
+		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+		DocumentBuilder parser=null;
+		try {
+			parser = docBuilderFactory.newDocumentBuilder();
+		} catch (ParserConfigurationException e1) {
+			e1.printStackTrace();
+		}
+		try {
+			
+			Document document = parser.parse(fileName);
+			NodeList elements = document.getElementsByTagName("platform");
+			int elementCount = elements.getLength();
+			for (int i = 0; i < elementCount; i++) {
+				PlatformStatus aPlatform = new PlatformStatus((Element) elements.item(i));
+				//System.out.println("ID: " + aPlatform.getId());
+				platforms.put(aPlatform.getId(), aPlatform);
+				
+				Node zipType = elements.item(i).getParentNode();
+				String zipTypeName = (String) zipType.getAttributes().getNamedItem("name").getNodeValue();
+				
+				Vector aVector = (Vector) typesMap.get(zipTypeName);
+				if (aVector == null) {
+					typesList.add(zipTypeName);
+					aVector = new Vector();
+					typesMap.put(zipTypeName, aVector);
+				}
+				aVector.add(aPlatform.getId());
+				
+			}
+
+			NodeList effectedFiles = document.getElementsByTagName("effectedFile");
+			int effectedFilesCount = effectedFiles.getLength();
+			for (int i = 0; i < effectedFilesCount; i++) {
+				Node anEffectedFile = effectedFiles.item(i);
+				Node logFile = anEffectedFile.getParentNode();
+				String logFileName = (String) logFile.getAttributes().getNamedItem("name").getNodeValue();
+				logFileName=convertPathDelimiters(logFileName);
+				String effectedFileID = (String) anEffectedFile.getAttributes().getNamedItem("id").getNodeValue();				
+				//System.out.println(logFileName);
+				Vector aVector = (Vector) logFiles.get(logFileName);
+				if (aVector == null) {
+					aVector = new Vector();
+					logFiles.put(logFileName, aVector);
+					
+				}
+				PlatformStatus ps=(PlatformStatus) platforms.get(effectedFileID);
+				if (ps!=null)
+					aVector.addElement(ps);
+			}
+			
+			// store a list of the test logs expected after testing
+			NodeList testLogList = document.getElementsByTagName("logFile");
+				int testLogCount = testLogList.getLength();
+				for (int i = 0; i < testLogCount; i++) {
+								
+					Node testLog = testLogList.item(i);
+					String testLogName = (String) testLog.getAttributes().getNamedItem("name").getNodeValue();
+					Node typeNode=testLog.getAttributes().getNamedItem("type");
+					String type="test";
+					if (typeNode!=null){
+						type = typeNode.getNodeValue();
+					}
+					if (testLogName.endsWith(".xml")&&type.equals("test")){
+						testLogs.add(testLogName);
+						//System.out.println(testLogName);
+					}
+			
+			}
+
+
+//			// Test this mess.
+//			Object[] results = platforms.values().toArray();
+//			for (int i=0; i < results.length; i++) {
+//				PlatformStatus ap = (PlatformStatus) results[i];
+//				System.out.println("ID: " + ap.getId() + " passed: " + ap.getPassed());
+//			}
+//		
+//			Enumeration anEnumeration = logFiles.keys();
+//			while (anEnumeration.hasMoreElements()) {
+//				String aKey = (String) anEnumeration.nextElement();
+//				System.out.println("Whack a key: " + aKey);
+//				((PlatformStatus) logFiles.get(aKey)).setPassed(false);
+//			}
+//			
+//			results = platforms.values().toArray();
+//			for (int i=0; i < results.length; i++) {
+//				PlatformStatus ap = (PlatformStatus) results[i];
+//				System.out.println("ID: " + ap.getId() + " passed: " + ap.getPassed());
+//			}
+			
+			
+		
+			
+			
+		} catch (IOException e) {
+			System.out.println("IOException: " + fileName);
+			// e.printStackTrace();
+			
+		} catch (SAXException e) {
+			System.out.println("SAXException: " + fileName);
+			e.printStackTrace();
+			
+		}
+	}
+	
+	public void registerError(String fileName) {
+		// System.out.println("Found an error in: " + fileName);
+		if (logFiles.containsKey(fileName)) {
+			Vector aVector = (Vector) logFiles.get(fileName);
+			for (int i = 0; i < aVector.size(); i++) {
+				((PlatformStatus) aVector.elementAt(i)).registerError();
+			}
+		} else {
+			
+			// If a log file is not specified explicitly it effects
+			// all "platforms" except JDT
+			
+			Enumeration values = platforms.elements();
+			while (values.hasMoreElements()) {
+				PlatformStatus aValue = (PlatformStatus) values.nextElement();
+				if (!aValue.getId().equals("JA") && 
+					!aValue.getId().equals("EW") && 
+					!aValue.getId().equals("EA")) {
+						aValue.registerError();
+				}
+			}
+		}
+	}
+	
+	public boolean hasErrors(String id) {
+		return ((PlatformStatus) platforms.get(id)).hasErrors();
+	}
+	
+	// Answer a string array of the zip type names in the order they appear in
+	// the .xml file.
+	public String[] getTypes() {
+		return (String[]) typesList.toArray(new String[typesList.size()]);
+	}
+	
+	// Answer an array of PlatformStatus objects for a given type.
+
+	public PlatformStatus[] getPlatforms(String type) {
+		Vector platformIDs = (Vector) typesMap.get(type);
+		PlatformStatus[] result = new PlatformStatus[platformIDs.size()];
+		for (int i = 0; i < platformIDs.size(); i++) {
+			result[i] = (PlatformStatus) platforms.get((String) platformIDs.elementAt(i));
+		}
+		return  result;
+	}	
+
+	/**
+	 * Returns the testLogs.
+	 * @return Vector
+	 */
+	public Vector getTestLogs() {
+		return testLogs;
+	}
+
+	private String convertPathDelimiters(String path){
+		return new File(path).getPath();
+	}
+	
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/FetchBaseTask.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/FetchBaseTask.java
new file mode 100644
index 0000000..6bcf213
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/FetchBaseTask.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.generators;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.apache.tools.ant.Task;
+
+public class FetchBaseTask extends Task {
+
+	private String mapFile;
+	private String outputFile;
+	private Hashtable entries;
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		// TODO Auto-generated method stub
+		FetchBaseTask task=new FetchBaseTask();
+		task.mapFile="d:/workspace/org.eclipse.releng/maps/base.map";
+		task.outputFile="d:/workspace/org.eclipse.releng/fetch.xml";
+		task.execute();
+	}
+
+	public FetchBaseTask(){
+		entries=new Hashtable();
+	}
+	public void execute(){
+		readMap();
+		printScript(fetchScript());
+	}
+
+	private void readMap (){
+		File file=new File(mapFile);
+		Properties properties=new Properties();
+		try {
+			properties.load(new FileInputStream(file));
+			Enumeration keys=properties.keys();
+			while (keys.hasMoreElements()){
+				String key=keys.nextElement().toString();
+				String script=getScript(key,properties.get(key).toString());
+				entries.put("fetch."+key,script);
+			}
+			
+		} catch (FileNotFoundException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+	
+	private String getScript(String key,String entry){
+		String[] keyParts=key.split("@");
+		if (keyParts.length==0)
+			return null;
+		String [] cvsinfo=entry.split(",");
+		if (cvsinfo.length<3)
+			return null;
+
+		String[] typeParts=keyParts[0].split("base.");
+		String type=typeParts[1];
+		String id=keyParts[1];
+		
+		String fullName=entry.substring(entry.lastIndexOf("/")+1,entry.length());
+		if (fullName.endsWith(".jar"))
+			return fetchJarTarget(type, id, cvsinfo);
+		else
+			return fetchDirectoryTarget(type, id, fullName, cvsinfo);	
+	}
+	
+	public String getMapFile() {
+		return mapFile;
+	}
+
+	public void setMapFile(String mapFile) {
+		this.mapFile = mapFile;
+	}
+
+	public String getOutputFile() {
+		return outputFile;
+	}
+
+	public void setOutputFile(String outputFile) {
+		this.outputFile = outputFile;
+	}
+	
+	private String fetchJarTarget(String type, String id, String[] cvsinfo){
+		return "\t<target name=\"fetch.base."+type+"@"+id+"\">\n" +
+		"\t\t<mkdir dir=\"${baseLocation}/"+type+"s\" />\n" +
+		"\t\t<property name=\"cvsroot\" value=\""+cvsinfo[1]+"\" />\n" +
+		"\t\t<property name=\"fetchTag\" value=\""+cvsinfo[0]+"\" />\n" +
+		"\t\t<cvs command=\"export -d "+type+"s\"\n" +
+		"\t\t\tcvsRoot=\"${cvsroot}\"\n" +
+		"\t\t\tpackage=\""+cvsinfo[3]+"\"\n" +
+		"\t\t\ttag=\"${fetchTag}\"\n" +
+		"\t\t\tdest=\"${baseLocation}\"\n" +
+		"\t\t\tquiet=\"true\"/>\n" +
+		"\t\t<delete includeemptydirs=\"true\">\n"+
+		"\t\t\t<fileset dir=\"${baseLocation}/"+type+"s\" includes=\"**/CVS/**\" defaultexcludes=\"no\"/>\n"+
+		"\t\t</delete>\n"+
+		"\t</target>\n";
+	
+	}
+	
+	private String fetchDirectoryTarget(String type, String id, String fullName,String[] cvsinfo){
+		return "\t<target name=\"fetch.base."+type+"@"+id+"\">\n" +
+		"\t\t<mkdir dir=\"${baseLocation}/"+type+"s\" />\n" +
+		"\t\t<property name=\"cvsroot\" value=\""+cvsinfo[1]+"\" />\n" +
+		"\t\t<cvs command=\"export -d "+fullName+"\"\n" +
+		"\t\t\tcvsRoot=\"${cvsroot}\"\n" +
+		"\t\t\tpackage=\""+cvsinfo[3]+"\"\n" +
+		"\t\t\ttag=\""+cvsinfo[0]+"\"\n" +
+		"\t\t\tdest=\"${baseLocation}/"+type+"s\"\n" +
+		"\t\t\tquiet=\"true\"/>\n" +
+		"\t\t<delete includeemptydirs=\"true\">\n"+
+		"\t\t\t<fileset dir=\"${baseLocation}/"+type+"s\" includes=\"**/CVS/**\" defaultexcludes=\"no\"/>\n"+
+		"\t\t</delete>\n"+
+		"\t</target>\n";
+	
+	}
+	
+	private String fetchScript(){
+		String script="<project default=\"all.elements\">\n" +
+			"<!--Ant script which will fetch pre-built plug-ins and features to a location where they\n" +
+			"will be consumed by the build, i.e. ${baseLocation}.  Stored in this project to capture revisions/urls of\n" +
+			"binaries.-->\n" +
+			"\t<property name=\"baseLocation\" value=\"${basedir}/baseLocation\" />\n" +
+			"\t<target name=\"all.elements\">\n";
+		
+		Enumeration keys=entries.keys();
+		while (keys.hasMoreElements()){
+			script=script.concat("\t\t<antcall target=\""+keys.nextElement()+"\" />\n");
+		}
+		script=script.concat("\t</target>");
+		keys=entries.keys();
+		while (keys.hasMoreElements()){
+			script=script.concat("\n\n"+entries.get(keys.nextElement()));
+		}
+		script=script.concat("</project>");
+
+		return script;	
+	}
+	
+	private void printScript(String script){
+		try {
+			PrintWriter out = new PrintWriter(new FileWriter(new File(outputFile)));
+			out.print(script);
+			out.flush();
+			out.close();
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/FileCounter.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/FileCounter.java
new file mode 100644
index 0000000..9490c66
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/FileCounter.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+
+package org.eclipse.releng.generators;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * This task will count the number of fils in a given directory
+ * that match a given filter.  The number of fils will be output
+ * to a given output file.  The output file will be overwritten
+ * if it already exists.
+ * 
+ * Note: Filter comparison is NOT case sensitive.  Do not use wild cards.
+ * ie .zip counts all files with .zip anywere in the name.
+ */
+public class FileCounter extends Task {
+	
+	private String sourceDirectory = "";
+	private String filterString = ".zip";
+	private String outputFile = "";
+	
+	public static void main(String args[]) {
+		// For testing only.
+		FileCounter aFileCounter = new FileCounter();
+		aFileCounter.setSourceDirectory("c:\\RelEng\\dean");
+		aFileCounter.setOutputFile("c:\\RelEng\\dean\\files.count");
+		aFileCounter.setFilterString(".zip");
+		aFileCounter.execute();
+	}
+
+	public void execute() throws BuildException {
+		// Do the work.
+		
+		int count = 0;
+		
+		System.out.println("Source Directory: " + this.getSourceDirectory());
+		System.out.println("Output File: " + this.getOutputFile());
+		System.out.println("Filter String: " + this.getFilterString());
+		
+		File aDirectory = new File(this.getSourceDirectory());
+		if (aDirectory == null) {
+			throw new BuildException("Directory " + this.getSourceDirectory() + " not found.");
+		}
+		
+		String[] names = aDirectory.list();
+		if (names == null) {
+			throw new BuildException("Directory " + this.getSourceDirectory() + " not found.");
+		}			
+		
+		System.out.println("List size: " + names.length);
+		
+		for (int i = 0; i < names.length; i++) {
+			System.out.println("Name: " + names[i]);
+			
+			int index = -1;
+			StringTokenizer types = getFileTypes();
+			
+			while (types.hasMoreTokens()){
+				index = names[i].toLowerCase().indexOf(types.nextToken().toLowerCase());
+				if (index != -1) {
+					count++;
+				}
+			}
+
+		}
+		
+		try {
+			FileOutputStream anOutputStream = new FileOutputStream(this.getOutputFile());
+			anOutputStream.write(String.valueOf(count).getBytes());
+			anOutputStream.close();
+		} catch (FileNotFoundException e) {
+			throw new BuildException("Can not create file.count file");
+		} catch (IOException e) {
+			throw new BuildException("Can not create file.count file");
+		}
+		
+	}
+
+	private StringTokenizer getFileTypes(){
+		return new StringTokenizer(getFilterString(),",");
+	}
+
+	/**
+	 * Gets the sourceDirectory.
+	 * @return Returns a String
+	 */
+	public String getSourceDirectory() {
+		return sourceDirectory;
+	}
+
+	/**
+	 * Sets the sourceDirectory.
+	 * @param sourceDirectory The sourceDirectory to set
+	 */
+	public void setSourceDirectory(String sourceDirectory) {
+		this.sourceDirectory = sourceDirectory;
+	}
+
+	/**
+	 * Gets the filterString.
+	 * @return Returns a String
+	 */
+	public String getFilterString() {
+		return filterString;
+	}
+
+	/**
+	 * Sets the filterString.
+	 * @param filterString The filterString to set
+	 */
+	public void setFilterString(String filterString) {
+		this.filterString = filterString;
+	}
+
+	/**
+	 * Gets the outputFile.
+	 * @return Returns a String
+	 */
+	public String getOutputFile() {
+		return outputFile;
+	}
+
+	/**
+	 * Sets the outputFile.
+	 * @param outputFile The outputFile to set
+	 */
+	public void setOutputFile(String outputFile) {
+		this.outputFile = outputFile;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/PlatformStatus.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/PlatformStatus.java
new file mode 100644
index 0000000..b23a66a
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/PlatformStatus.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng.generators;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+
+/**
+ * @version 	1.0
+ * @author
+ */
+public class PlatformStatus {
+	
+	private String id;
+	private String name;
+	private String fileName;
+	private boolean hasErrors = false;
+	
+	PlatformStatus(Element anElement) {
+		super();
+		NamedNodeMap attributes = anElement.getAttributes();
+		this.id = (String) attributes.getNamedItem("id").getNodeValue();
+		this.name = (String) attributes.getNamedItem("name").getNodeValue();
+		this.fileName = (String) attributes.getNamedItem("fileName").getNodeValue();
+
+	}
+
+	/**
+	 * Gets the id.
+	 * @return Returns a String
+	 */
+	public String getId() {
+		return id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public String getFileName() {
+		return fileName;
+	}
+	
+	public void registerError() {
+		this.hasErrors = true;
+	}
+	
+	public boolean hasErrors() {
+		return this.hasErrors;
+	}
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/TestResultsGenerator.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/TestResultsGenerator.java
new file mode 100644
index 0000000..a0b261d
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/TestResultsGenerator.java
@@ -0,0 +1,1397 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng.generators;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.Task;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/**
+ * @version 	1.0
+ * @author Dean Roberts
+ */
+public class TestResultsGenerator extends Task {
+	private static final String WARNING_SEVERITY = "WARNING";
+	private static final String ERROR_SEVERITY = "ERROR";
+	private static final String ForbiddenReferenceID = "ForbiddenReference";
+	private static final String DiscouragedReferenceID = "DiscouragedReference";
+
+	private static final int DEFAULT_READING_SIZE = 8192;
+
+	static final String elementName = "testsuite";
+	static final String testResultsToken = "%testresults%";
+	static final String compileLogsToken = "%compilelogs%";
+	static final String accessesLogsToken = "%accesseslogs%";
+	public Vector dropTokens;
+	public Vector platformSpecs;
+	public Vector differentPlatforms;
+	public String testResultsWithProblems = "\n";
+	public String testResultsXmlUrls = "\n";
+
+	private DocumentBuilder parser =null;
+	public ErrorTracker anErrorTracker;
+	public String testResultsTemplateString = "";
+	public String dropTemplateString = "";
+
+	public Vector platformDescription;
+	public Vector platformTemplateString;
+	public Vector platformDropFileName;
+
+	//Status of tests results (pending, successful, failed), used to specify the color
+	//of the test Results link on the build pages (standard, green, red), once failures
+	//are encountered, this is set to failed
+	protected String testResultsStatus = "successful";
+	//assume tests ran.  If no html files are found, this is set to false
+	private boolean testsRan = true;
+
+	// Parameters
+	// build runs JUnit automated tests
+	private boolean isBuildTested;
+
+	// buildType, I, N
+	public String buildType;
+
+	// Comma separated list of drop tokens
+	public String dropTokenList;
+
+	// Token in platform.php.template to be replaced by the desired platform ID
+	public String platformIdentifierToken;
+
+	// Location of the xml files
+	public String xmlDirectoryName;
+
+	// Location of the html files
+	public String htmlDirectoryName;
+
+	// Location of the resulting index.php file.
+	public String dropDirectoryName;
+
+	// Location and name of the template index.php file.
+	public String testResultsTemplateFileName;
+
+	// Platform specific template and output list (colon separated) in the following format:
+	// <descriptor, ie. OS name>,path to template file, path to output file
+	public String platformSpecificTemplateList="";
+
+	// Location and name of the template drop index.php file.
+	public String dropTemplateFileName;
+
+	// Name of the generated index php file.
+	public String testResultsHtmlFileName;
+
+	// Name of the generated drop index php file;
+	public String dropHtmlFileName;
+
+	// Arbitrary path used in the index.php page to href the
+	// generated .html files.
+	public String hrefTestResultsTargetPath;
+
+	// Aritrary path used in the index.php page to reference the compileLogs
+	public String hrefCompileLogsTargetPath;
+
+	// Location of compile logs base directory
+	public String compileLogsDirectoryName;
+
+	// Location and name of test manifest file
+	public String testManifestFileName;
+
+	//Initialize the prefix to a default string
+	private String prefix = "default";
+	private String testShortName = "";
+	private int counter = 0;
+	//The four configurations, add new configurations to test results here + update
+	//testResults.php.template for changes
+	private String[] testsConfig = {"linux.gtk.x86.xml",
+			"linux.gtk.x86_6.0.xml",
+			"macosx.cocoa.x86_5.0.xml",
+			"win32.win32.x86.xml",
+			"win32.win32.x86_6.0.xml"};
+
+
+	public static void main(String[] args) {
+		TestResultsGenerator test = new TestResultsGenerator();
+		test.setDropTokenList(
+			"%sdk%,%tests%,%example%,%rcpruntime%,%rcpsdk%,%deltapack%,%icubase%,%runtime%,%platformsdk%,%jdt%,%jdtsdk%,%pde%,%pdesdk%,%cvs%,%cvssdk%,%teamextras%,%swt%,%relengtools%");
+		test.setPlatformIdentifierToken("%platform%");
+		test.getDropTokensFromList(test.dropTokenList);
+		test.setIsBuildTested(true);
+		test.setXmlDirectoryName("C:\\junk\\testresults\\xml");
+		test.setHtmlDirectoryName("C:\\junk\\testresults");
+		test.setDropDirectoryName("C:\\junk");
+		test.setTestResultsTemplateFileName(
+			"C:\\junk\\templateFiles\\testResults.php.template");
+		test.setPlatformSpecificTemplateList(
+				"Windows,C:\\junk\\templateFiles\\platform.php.template,winPlatform.php;Linux,C:\\junk\\templateFiles\\platform.php.template,linPlatform.php;Solaris,C:\\junk\\templateFiles\\platform.php.template,solPlatform.php;AIX,C:\\junk\\templateFiles\\platform.php.template,aixPlatform.php;Macintosh,C:\\junk\\templateFiles\\platform.php.template,macPlatform.php;Source Build,C:\\junk\\templateFiles\\sourceBuilds.php.template,sourceBuilds.php");
+		test.setDropTemplateFileName(
+			"C:\\junk\\templateFiles\\index.php.template");
+		test.setTestResultsHtmlFileName("testResults.php");
+		//test.setDropHtmlFileName("index.php");
+		test.setDropHtmlFileName("index.html");
+
+		test.setHrefTestResultsTargetPath("testresults");
+		test.setCompileLogsDirectoryName(
+			"C:\\junk\\compilelogs\\plugins");
+		test.setHrefCompileLogsTargetPath("compilelogs");
+		test.setTestManifestFileName("C:\\junk\\testManifest.xml");
+		test.execute();
+	}
+
+	public void execute() {
+
+		anErrorTracker = new ErrorTracker();
+		platformDescription = new Vector();
+		platformTemplateString = new Vector();
+		platformDropFileName = new Vector();
+		anErrorTracker.loadFile(testManifestFileName);
+		getDropTokensFromList(dropTokenList);
+		testResultsTemplateString = readFile(testResultsTemplateFileName);
+		dropTemplateString = readFile(dropTemplateFileName);
+
+		//Specific to the platform build-page
+		if(platformSpecificTemplateList!="") {
+			String description, platformTemplateFile, platformDropFile;
+			//Retrieve the different platforms and their info
+			getDifferentPlatformsFromList(platformSpecificTemplateList);
+			//Parses the platform info and retrieves the platform name,
+			//template file, and drop file
+			for(int i=0; i<differentPlatforms.size(); i++) {
+				getPlatformSpecsFromList(differentPlatforms.get(i).toString());
+				description = platformSpecs.get(0).toString();
+				platformTemplateFile = platformSpecs.get(1).toString();
+				platformDropFile = platformSpecs.get(2).toString();
+				platformDescription.add(description);
+				platformTemplateString.add(readFile(platformTemplateFile));
+				platformDropFileName.add(platformDropFile);
+
+			}
+
+		}
+
+		System.out.println("Begin: Generating test results index page");
+		System.out.println("Parsing XML files");
+		parseXml();
+		System.out.println("Parsing compile logs");
+		parseCompileLogs();
+		System.out.println("End: Generating test results index page");
+		writeTestResultsFile();
+		//For the platform build-page, write platform files, in addition to the index file
+		if(platformSpecificTemplateList!="") {
+			writeDropFiles();
+		}
+		else {
+			writeDropIndexFile();
+		}
+	}
+
+	public void parseCompileLogs() {
+
+		StringBuffer compilerString = new StringBuffer();
+		StringBuffer accessesString = new StringBuffer();
+		processCompileLogsDirectory(
+			compileLogsDirectoryName,
+			compilerString,
+			accessesString);
+		if (compilerString.length() == 0) {
+			compilerString.append("None");
+		}
+		if (accessesString.length() == 0) {
+			accessesString.append("None");
+		}
+		testResultsTemplateString =
+			replace(testResultsTemplateString, compileLogsToken, String.valueOf(compilerString));
+
+		testResultsTemplateString =
+			replace(testResultsTemplateString, accessesLogsToken, String.valueOf(accessesString));
+	}
+
+	private void processCompileLogsDirectory(String directoryName, StringBuffer compilerLog, StringBuffer accessesLog) {
+		File sourceDirectory = new File(directoryName);
+		if (sourceDirectory.isFile()) {
+			if (sourceDirectory.getName().endsWith(".log"))
+				readCompileLog(sourceDirectory.getAbsolutePath(), compilerLog, accessesLog);
+			if (sourceDirectory.getName().endsWith(".xml"))
+				parseCompileLog(sourceDirectory.getAbsolutePath(), compilerLog, accessesLog);
+		}
+		if (sourceDirectory.isDirectory()) {
+			File[] logFiles = sourceDirectory.listFiles();
+			Arrays.sort(logFiles);
+			for (int j = 0; j < logFiles.length; j++) {
+				processCompileLogsDirectory(logFiles[j].getAbsolutePath(), compilerLog, accessesLog);
+			}
+		}
+	}
+
+	private void readCompileLog(String log, StringBuffer compilerLog, StringBuffer accessesLog) {
+		String fileContents = readFile(log);
+
+		int errorCount = countCompileErrors(fileContents);
+		int warningCount = countCompileWarnings(fileContents);
+		int forbiddenWarningCount = countForbiddenWarnings(fileContents);
+		int discouragedWarningCount = countDiscouragedWarnings(fileContents);
+		if (errorCount != 0) {
+			//use wildcard in place of version number on directory names
+			String logName =
+				log.substring(getCompileLogsDirectoryName().length() + 1);
+			StringBuffer stringBuffer = new StringBuffer(logName);
+			stringBuffer.replace(
+				logName.indexOf("_") + 1,
+				logName.indexOf(File.separator, logName.indexOf("_") + 1),
+				"*");
+			logName = new String(stringBuffer);
+
+			anErrorTracker.registerError(logName);
+		}
+		formatCompileErrorRow(log, errorCount, warningCount, compilerLog);
+		formatAccessesErrorRow(log, forbiddenWarningCount, discouragedWarningCount, accessesLog);
+	}
+
+	private void parseCompileLog(String log, StringBuffer compilerLog, StringBuffer accessesLog) {
+		int errorCount = 0;
+		int warningCount = 0;
+		int forbiddenWarningCount = 0;
+		int discouragedWarningCount = 0;
+
+		File file=new File(log);
+		Document aDocument=null;
+		BufferedReader reader = null;
+		try {
+			reader = new BufferedReader(new FileReader(file));
+			InputSource inputSource = new InputSource(reader);
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			DocumentBuilder builder = factory.newDocumentBuilder();
+			aDocument = builder.parse(inputSource);
+		} catch (SAXException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		} finally {
+			if (reader != null) {
+				try {
+					reader.close();
+				} catch(IOException e) {
+					// ignore
+				}
+			}
+		}
+
+		if (aDocument == null) return;
+		// Get summary of problems
+		NodeList nodeList = aDocument.getElementsByTagName("problem");
+		if (nodeList == null ||nodeList.getLength()==0)
+			return;
+
+		int length = nodeList.getLength();
+		for (int i = 0; i < length; i++) {
+			Node problemNode = nodeList.item(i);
+			NamedNodeMap aNamedNodeMap = problemNode.getAttributes();
+			Node severityNode = aNamedNodeMap.getNamedItem("severity");
+			Node idNode = aNamedNodeMap.getNamedItem("id");
+			if (severityNode != null) {
+				String severityNodeValue = severityNode.getNodeValue();
+				if (WARNING_SEVERITY.equals(severityNodeValue)) {
+					// this is a warning
+					// need to check the id
+					String nodeValue = idNode.getNodeValue();
+					if (ForbiddenReferenceID.equals(nodeValue)) {
+						forbiddenWarningCount++;
+					} else if (DiscouragedReferenceID.equals(nodeValue)) {
+						discouragedWarningCount++;
+					} else {
+						warningCount++;
+					}
+				} else if (ERROR_SEVERITY.equals(severityNodeValue)) {
+					// this is an error
+					errorCount++;
+				}
+			}
+		}
+		if (errorCount != 0) {
+			//use wildcard in place of version number on directory names
+			//System.out.println(log + "/n");
+			String logName =
+				log.substring(getCompileLogsDirectoryName().length() + 1);
+			StringBuffer buffer = new StringBuffer(logName);
+			buffer.replace(
+				logName.indexOf("_") + 1,
+				logName.indexOf(File.separator, logName.indexOf("_") + 1),
+				"*");
+			logName = new String(buffer);
+
+			anErrorTracker.registerError(logName);
+		}
+		String logName = log.replaceAll(".xml", ".html");
+		formatCompileErrorRow(
+				logName,
+				errorCount,
+				warningCount,
+				compilerLog);
+		formatAccessesErrorRow(
+				logName,
+				forbiddenWarningCount,
+				discouragedWarningCount,
+				accessesLog);
+	}
+
+	public static byte[] getFileByteContent(String fileName) throws IOException {
+		InputStream stream = null;
+		try {
+			File file = new File(fileName);
+			stream = new FileInputStream(file);
+			return getInputStreamAsByteArray(stream, (int) file.length());
+		} finally {
+			if (stream != null) {
+				try {
+					stream.close();
+				} catch (IOException e) {
+					// ignore
+				}
+			}
+		}
+	}
+
+	/**
+	 * Returns the given input stream's contents as a byte array.
+	 * If a length is specified (ie. if length != -1), only length bytes
+	 * are returned. Otherwise all bytes in the stream are returned.
+	 * Note this doesn't close the stream.
+	 * @throws IOException if a problem occured reading the stream.
+	 */
+	public static byte[] getInputStreamAsByteArray(InputStream stream, int length)
+		throws IOException {
+		byte[] contents;
+		if (length == -1) {
+			contents = new byte[0];
+			int contentsLength = 0;
+			int amountRead = -1;
+			do {
+				int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE);  // read at least 8K
+
+				// resize contents if needed
+				if (contentsLength + amountRequested > contents.length) {
+					System.arraycopy(
+						contents,
+						0,
+						contents = new byte[contentsLength + amountRequested],
+						0,
+						contentsLength);
+				}
+
+				// read as many bytes as possible
+				amountRead = stream.read(contents, contentsLength, amountRequested);
+
+				if (amountRead > 0) {
+					// remember length of contents
+					contentsLength += amountRead;
+				}
+			} while (amountRead != -1);
+
+			// resize contents if necessary
+			if (contentsLength < contents.length) {
+				System.arraycopy(
+					contents,
+					0,
+					contents = new byte[contentsLength],
+					0,
+					contentsLength);
+			}
+		} else {
+			contents = new byte[length];
+			int len = 0;
+			int readSize = 0;
+			while ((readSize != -1) && (len != length)) {
+				// See PR 1FMS89U
+				// We record first the read size. In this case len is the actual read size.
+				len += readSize;
+				readSize = stream.read(contents, len, length - len);
+			}
+		}
+
+		return contents;
+	}
+
+	public String readFile(String fileName) {
+		byte[] aByteArray = null;
+		try {
+			aByteArray = getFileByteContent(fileName);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		if (aByteArray == null) {
+			return "";
+		}
+		return new String(aByteArray);
+	}
+
+	private int countCompileErrors(String aString) {
+		return extractNumber(aString, "error");
+	}
+
+	private int countCompileWarnings(String aString) {
+		return extractNumber(aString, "warning");
+	}
+
+	private int countForbiddenWarnings(String aString) {
+		return extractNumber(aString, "Access restriction:");
+	}
+
+	private int countDiscouragedWarnings(String aString) {
+		return extractNumber(aString, "Discouraged access:");
+	}
+
+	private int extractNumber(String aString, String endToken) {
+		int endIndex = aString.lastIndexOf(endToken);
+		if (endIndex == -1) {
+			return 0;
+		}
+
+		int startIndex = endIndex;
+		while (startIndex >= 0
+			&& aString.charAt(startIndex) != '('
+			&& aString.charAt(startIndex) != ',') {
+			startIndex--;
+		}
+
+		String count = aString.substring(startIndex + 1, endIndex).trim();
+		try {
+			return Integer.parseInt(count);
+		} catch (NumberFormatException e) {
+			return 0;
+		}
+
+	}
+
+	private int missingCount = 0;
+	private String verifyAllTestsRan(String directory) {
+		Enumeration enumeration = (anErrorTracker.getTestLogs()).elements();
+
+		String replaceString="";
+		while (enumeration.hasMoreElements()) {
+			String testLogName = enumeration.nextElement().toString();
+
+			if (new File(directory + File.separator + testLogName)
+				.exists())
+				continue;
+
+			anErrorTracker.registerError(testLogName);
+			String tmp=((platformSpecificTemplateList.equals(""))?formatRow(testLogName, -1, false):formatRowReleng(testLogName, -1, false));
+			if(missingCount==0) {
+				replaceString=replaceString+"</table></br>"+"\n"+
+				"<table width=\"65%\" border=\"1\" bgcolor=\"#EEEEEE\" rules=\"groups\" align=\"center\">"+
+				"<tr bgcolor=\"#9999CC\"> <th width=\"80%\" align=\"center\"> Missing Files </th><th  align=\"center\"> Status </th></tr>";
+			}
+			replaceString=replaceString+tmp;
+			testResultsWithProblems=testResultsWithProblems.concat("\n" + testLogName.substring(0,testLogName.length()-4) +" (file missing)");
+			missingCount++;
+		}
+		return replaceString;
+	}
+
+	public void parseXml() {
+
+		File sourceDirectory = new File(xmlDirectoryName);
+
+		if (sourceDirectory.exists()) {
+
+			String replaceString = "";
+
+			File[] xmlFileNames = sourceDirectory.listFiles();
+			Arrays.sort(xmlFileNames)	;
+
+			File sourceDirectoryParent = sourceDirectory.getParentFile();
+			if (sourceDirectoryParent != null) {
+				sourceDirectoryParent = sourceDirectoryParent.getParentFile();
+			}
+			String sourceDirectoryCanonicalPath = null;
+			try {
+				sourceDirectoryCanonicalPath = sourceDirectoryParent.getCanonicalPath();
+			} catch (IOException e) {
+				// ignore
+			}
+			for (int i = 0; i < xmlFileNames.length; i++) {
+				if (xmlFileNames[i].getPath().endsWith(".xml")) {
+					String fullName = xmlFileNames[i].getPath();
+					int errorCount = countErrors(fullName);
+					if (errorCount != 0) {
+						String testName =
+							xmlFileNames[i].getName().substring(
+								0,
+								xmlFileNames[i].getName().length() - 4);
+						testResultsWithProblems =
+							testResultsWithProblems.concat("\n" + testName);
+						testResultsXmlUrls =
+							testResultsXmlUrls.concat("\n" + extractXmlRelativeFileName(sourceDirectoryCanonicalPath, xmlFileNames[i]));
+						anErrorTracker.registerError(
+							fullName.substring(
+								getXmlDirectoryName().length() + 1));
+					}
+
+
+					String tmp=((platformSpecificTemplateList.equals(""))?formatRow(xmlFileNames[i].getPath(), errorCount,true):formatRowReleng(xmlFileNames[i].getPath(), errorCount,true));
+					replaceString=replaceString+tmp;
+
+
+				}
+			}
+			//check for missing test logs
+			replaceString=replaceString+verifyAllTestsRan(xmlDirectoryName);
+
+			testResultsTemplateString =
+				replace(
+					testResultsTemplateString,
+					testResultsToken,
+					replaceString);
+			testsRan = true;
+
+		} else {
+			testsRan = false;
+			System.out.println(
+				"Test results not found in "
+					+ sourceDirectory.getAbsolutePath());
+		}
+
+	}
+	private static String extractXmlRelativeFileName(String rootCanonicalPath, File xmlFile) {
+		if (rootCanonicalPath != null) {
+			String xmlFileCanonicalPath = null;
+			try {
+				xmlFileCanonicalPath = xmlFile.getCanonicalPath();		
+			} catch (IOException e) {
+				// ignore
+			}
+			if (xmlFileCanonicalPath != null) {
+				// + 1 to remove the '\'
+				return xmlFileCanonicalPath.substring(rootCanonicalPath.length() + 1).replace('\\', '/');
+			}
+		}
+		return "";
+	}
+	private String replace(
+		String source,
+		String original,
+		String replacement) {
+
+		int replaceIndex = source.indexOf(original);
+		if (replaceIndex > -1) {
+			String resultString = source.substring(0, replaceIndex);
+			resultString = resultString + replacement;
+			resultString =
+				resultString
+					+ source.substring(replaceIndex + original.length());
+			return resultString;
+		} else {
+			System.out.println("Could not find token: " + original);
+			return source;
+		}
+
+	}
+
+	protected void writeDropFiles() {
+		writeDropIndexFile();
+		//Write all the platform files
+		for(int i=0; i<platformDescription.size(); i++) {
+			writePlatformFile(platformDescription.get(i).toString(), platformTemplateString.get(i).toString(), platformDropFileName.get(i).toString());
+		}
+	}
+
+	protected void writeDropIndexFile() {
+
+		String[] types = anErrorTracker.getTypes();
+		for (int i = 0; i < types.length; i++) {
+			PlatformStatus[] platforms = anErrorTracker.getPlatforms(types[i]);
+			String replaceString = processDropRows(platforms);
+			dropTemplateString =
+				replace(
+					dropTemplateString,
+					dropTokens.get(i).toString(),
+					replaceString);
+			}
+		//Replace the token %testsStatus% with the status of the test results
+		dropTemplateString = replace(dropTemplateString,"%testsStatus%",testResultsStatus);
+		String outputFileName =
+			dropDirectoryName + File.separator + dropHtmlFileName;
+		writeFile(outputFileName, dropTemplateString);
+	}
+
+	//Writes the platform file (dropFileName) specific to "desiredPlatform"
+	protected void writePlatformFile(String desiredPlatform, String templateString, String dropFileName) {
+
+		String[] types = anErrorTracker.getTypes();
+		for (int i = 0; i < types.length; i++) {
+			PlatformStatus[] platforms = anErrorTracker.getPlatforms(types[i]);
+			//Call processPlatformDropRows passing the platform's name
+			String replaceString = processPlatformDropRows(platforms, desiredPlatform);
+			templateString =
+				replace(
+					templateString,
+					dropTokens.get(i).toString(),
+					replaceString);
+		}
+		//Replace the platformIdentifierToken with the platform's name and the testsStatus
+		//token with the status of the test results
+		templateString = replace(templateString, platformIdentifierToken, desiredPlatform);
+		templateString = replace(templateString,"%testsStatus%",testResultsStatus);
+		String outputFileName =
+			dropDirectoryName + File.separator + dropFileName;
+		writeFile(outputFileName, templateString);
+	}
+
+	//Process drop rows specific to each of the platforms
+	protected String processPlatformDropRows(PlatformStatus[] platforms, String name) {
+
+		String result = "";
+		boolean found = false;
+		for (int i = 0; i < platforms.length; i++) {
+			//If the platform description indicates the platform's name, or "All",
+			//call processDropRow
+			if(platforms[i].getName().startsWith(name.substring(0, 3)) || platforms[i].getName().equals("All")) {
+				result = result + processDropRow(platforms[i]);
+				found = true;
+			}
+			//If the platform description indicates "All Other Platforms", process
+			//the row locally
+			else if(platforms[i].getName().equals("All Other Platforms") && !found)
+			{
+				String imageName = "";
+
+				if (platforms[i].hasErrors()) {
+					imageName =
+						"<a href=\"" + getTestResultsHtmlFileName() + "\"><img src = \"FAIL.gif\" width=19 height=23></a>";
+				} else {
+					if (testsRan) {
+						imageName = "<img src = \"OK.gif\" width=19 height=23>";
+					} else {
+						if (isBuildTested) {
+							imageName =
+								"<font size=\"-1\" color=\"#FF0000\">pending</font>";
+						} else {
+							imageName = "<img src = \"OK.gif\" width=19 height=23>";
+						}
+					}
+				}
+
+				result = result + "<tr>";
+				result = result + "<td><div align=left>" + imageName + "</div></td>\n";
+				result = result + "<td>All " + name + "</td>";
+				//generate http, md5 and sha1 links by calling php functions in the template
+				result = result + "<td><?php genLinks($_SERVER[\"SERVER_NAME\"],\"@buildlabel@\",\"" + platforms[i].getFileName() +"\"); ?></td>\n";
+				result = result + "</tr>\n";
+			}
+		}
+
+		return result;
+	}
+
+	protected String processDropRows(PlatformStatus[] platforms) {
+
+		String result = "";
+		for (int i = 0; i < platforms.length; i++) {
+			result = result + processDropRow(platforms[i]);
+		}
+
+		return result;
+	}
+
+	protected String processDropRow(PlatformStatus aPlatform) {
+
+		String imageName = "";
+
+		if (aPlatform.hasErrors()) {
+			imageName =
+				"<a href=\"" + getTestResultsHtmlFileName()+ "\"><img src = \"FAIL.gif\" width=19 height=23></a>";
+			//Failure in tests
+			testResultsStatus = "failed";
+		} else {
+			if (testsRan) {
+				imageName = "<img src = \"OK.gif\" width=19 height=23>";
+			} else {
+				if (isBuildTested) {
+					imageName =
+						"<font size=\"-1\" color=\"#FF0000\">pending</font>";
+					//Tests are pending
+					testResultsStatus = "pending";
+				} else {
+					imageName = "<img src = \"OK.gif\" width=19 height=23>";
+				}
+			}
+		}
+
+		String result = "<tr>";
+
+		result = result + "<td><div align=left>" + imageName + "</div></td>\n";
+		result = result + "<td>" + aPlatform.getName() + "</td>";
+		result = result + "<td>" + aPlatform.getFileName() + "</td>\n";
+		result = result + "</tr>\n";
+
+		return result;
+	}
+
+	public void writeTestResultsFile() {
+
+		String outputFileName =
+			dropDirectoryName + File.separator + testResultsHtmlFileName;
+		writeFile(outputFileName, testResultsTemplateString);
+	}
+
+	private void writeFile(String outputFileName, String contents) {
+		FileOutputStream outputStream = null;
+		try {
+			outputStream = new FileOutputStream(outputFileName);
+			outputStream.write(contents.getBytes());
+		} catch (FileNotFoundException e) {
+			System.out.println(
+				"File not found exception writing: " + outputFileName);
+		} catch (IOException e) {
+			System.out.println("IOException writing: " + outputFileName);
+		} finally {
+			if (outputStream != null) {
+				try {
+					outputStream.close();
+				} catch(IOException e) {
+					// ignore
+				}
+			}
+		}
+	}
+
+	public void setTestResultsHtmlFileName(String aString) {
+		testResultsHtmlFileName = aString;
+	}
+
+	public String getTestResultsHtmlFileName() {
+		return testResultsHtmlFileName;
+	}
+
+	public void setTestResultsTemplateFileName(String aString) {
+		testResultsTemplateFileName = aString;
+	}
+
+	public String getTestResultsTemplateFileName() {
+		return testResultsTemplateFileName;
+	}
+
+	public void setXmlDirectoryName(String aString) {
+		xmlDirectoryName = aString;
+	}
+
+	public String getXmlDirectoryName() {
+		return xmlDirectoryName;
+	}
+
+	public void setHtmlDirectoryName(String aString) {
+		htmlDirectoryName = aString;
+	}
+
+	public String getHtmlDirectoryName() {
+		return htmlDirectoryName;
+	}
+
+	public void setDropDirectoryName(String aString) {
+		dropDirectoryName = aString;
+	}
+
+	public String getDropDirectoryName() {
+		return dropDirectoryName;
+	}
+
+	private void formatCompileErrorRow(
+		String fileName,
+		int errorCount,
+		int warningCount,
+		StringBuffer buffer) {
+
+		if (errorCount == 0 && warningCount == 0) {
+			return;
+		}
+
+		String hrefCompileLogsTargetPath2 = getHrefCompileLogsTargetPath();
+		int i = fileName.indexOf(hrefCompileLogsTargetPath2);
+
+		String shortName =
+			fileName.substring(i + hrefCompileLogsTargetPath2.length());
+
+		buffer
+			.append("<tr>\n<td>\n")
+			.append("<a href=")
+			.append("\"")
+			.append(hrefCompileLogsTargetPath2)
+			.append(shortName)
+			.append("\">")
+			.append(shortName)
+			.append("</a>")
+			.append("</td>\n")
+			.append("<td align=\"center\">")
+			.append("<a href=")
+			.append("\"")
+			.append(hrefCompileLogsTargetPath2)
+			.append(shortName)
+			.append("#ERRORS")
+			.append("\">")
+			.append(errorCount)
+			.append("</a>")
+			.append("</td>\n")
+			.append("<td align=\"center\">")
+			.append("<a href=")
+			.append("\"")
+			.append(hrefCompileLogsTargetPath2)
+			.append(shortName)
+			.append("#OTHER_WARNINGS")
+			.append("\">")
+			.append(warningCount)
+			.append("</a>")
+			.append("</td>\n")
+			.append("</tr>\n");
+	}
+
+	private void formatAccessesErrorRow(
+			String fileName,
+			int forbiddenAccessesWarningsCount,
+			int discouragedAccessesWarningsCount,
+			StringBuffer buffer) {
+
+		if (forbiddenAccessesWarningsCount == 0 && discouragedAccessesWarningsCount == 0) {
+			return;
+		}
+
+		String hrefCompileLogsTargetPath2 = getHrefCompileLogsTargetPath();
+		int i = fileName.indexOf(hrefCompileLogsTargetPath2);
+
+		String shortName =
+			fileName.substring(i + hrefCompileLogsTargetPath2.length());
+
+		buffer
+			.append("<tr>\n<td>\n")
+			.append("<a href=")
+			.append("\"")
+			.append(hrefCompileLogsTargetPath2)
+			.append(shortName)
+			.append("\">")
+			.append(shortName)
+			.append("</a>")
+			.append("</td>\n")
+			.append("<td align=\"center\">")
+			.append("<a href=")
+			.append("\"")
+			.append(hrefCompileLogsTargetPath2)
+			.append(shortName)
+			.append("#FORBIDDEN_WARNINGS")
+			.append("\">")
+			.append(forbiddenAccessesWarningsCount)
+			.append("</a>")
+			.append("</td>\n")
+			.append("<td align=\"center\">")
+			.append("<a href=")
+			.append("\"")
+			.append(hrefCompileLogsTargetPath2)
+			.append(shortName)
+			.append("#DISCOURAGED_WARNINGS")
+			.append("\">")
+			.append(discouragedAccessesWarningsCount)
+			.append("</a>")
+			.append("</td>\n")
+			.append("</tr>\n");
+		}
+
+	private String formatRow(String fileName, int errorCount, boolean link) {
+
+		// replace .xml with .html
+
+		String aString = "";
+		if (!link) {
+			return "<tr><td>" + fileName + " (missing)" + "</td><td>" + "DNF";
+		}
+
+		if (fileName.endsWith(".xml")) {
+
+			int begin = fileName.lastIndexOf(File.separatorChar);
+			int end = fileName.lastIndexOf(".xml");
+
+			String shortName = fileName.substring(begin + 1, end);
+			String displayName = shortName;
+			if (errorCount != 0)
+			   aString = aString + "<tr><td><b>";
+			else
+				aString = aString + "<tr><td>";
+
+
+			if (errorCount!=0){
+				displayName="<font color=\"#ff0000\">"+displayName+"</font>";
+			}
+			if (errorCount==-1){
+				aString=aString.concat(displayName);
+			}else {
+				aString=aString
+					+ "<a href="
+					+ "\""
+					+ hrefTestResultsTargetPath
+					+ "/"
+					+ shortName
+					+ ".html"
+					+ "\">"
+					+ displayName
+					+ "</a>";
+			}
+			if (errorCount > 0)
+				   aString = aString + "</td><td><b>";
+			else
+				aString = aString + "</td><td>";
+
+			if (errorCount == -1)
+				aString = aString + "<font color=\"#ff0000\">DNF";
+
+			else if (errorCount >0)
+				aString = aString + "<font color=\"#ff0000\">"+String.valueOf(errorCount);
+			else
+				aString = aString +String.valueOf(errorCount);
+
+			if (errorCount != 0)
+				aString = aString + "</font></b></td></tr>";
+			else
+				aString = aString + "</td></tr>";
+		}
+
+		return aString;
+
+	}
+
+	//Specific to the RelEng test results page
+	private String formatRowReleng(String fileName, int errorCount, boolean link) {
+
+		//If the file name doesn't end with any of the set test configurations, do nothing
+		boolean endsWithConfig = false;
+		int card = testsConfig.length;
+		for(int i=0; i<card; i++) {
+			if(fileName.endsWith(testsConfig[i]))
+				endsWithConfig = true;
+		}
+		if(!endsWithConfig)
+			return "";
+
+		String aString = "";
+		if (!link) {
+			return "<tr><td>" + fileName + "</td><td align=\"center\">" + "DNF </tr>";
+		}
+
+		if (fileName.endsWith(".xml")) {
+
+			int begin = fileName.lastIndexOf(File.separatorChar);
+
+			//Get org.eclipse. out of the component name
+			String shortName = fileName.substring(begin + 13, fileName.indexOf('_'));
+			String displayName = shortName;
+
+			//If the short name does not start with this prefix
+			if(!shortName.startsWith(prefix)) {
+				//If the prefix is not yet set
+				if(prefix=="default"){
+					//Set the testShortName variable to the current short name
+					testShortName = shortName;
+					counter=0;
+					//Set new prefix
+					prefix = shortName.substring(0, shortName.indexOf(".tests") + 6);
+					aString = aString + "<tbody><tr><td><b>" + prefix + ".*" + "</b><td><td><td><td>";
+					aString = aString + "<tr><td><P>" + shortName;
+
+					//Loop until the matching string postfix(test config.) is found
+					while(counter<card && !fileName.endsWith(testsConfig[counter])) {
+						aString = aString + "<td align=\"center\">-</td>";
+						counter++;
+					}
+				}
+				else {
+					//Set new prefix
+					prefix = shortName.substring(0, shortName.indexOf(".tests") + 6);
+
+					//Loop until the matching string postfix(test config.) is found
+					while(counter<card && !fileName.endsWith(testsConfig[counter])) {
+						aString = aString + "<td align=\"center\">-</td>";
+						counter++;
+					}
+
+					//In this case, the new prefix should be set with the short name under it,
+					//since this would mean that the team has more than one component test
+					if(!shortName.endsWith("tests")) {
+						aString = aString + "<tbody><tr><td><b>" + prefix + ".*" + "</b><td><td><td><td>";
+						aString = aString + "<tr><td><P>" + shortName;
+					}
+					//The team has only one component test
+					else
+						aString = aString + "<tbody><tr><td><b>" + shortName;
+					testShortName = shortName;
+
+					counter = 0;
+				}
+			}
+			//If the file's short name starts with the current prefix
+			if(shortName.startsWith(prefix)) {
+				//If the new file has a different short name than the current one
+				if(!shortName.equals(testShortName)){
+					//Fill the remaining cells with '-'. These files will later be listed as
+					//missing
+					while(counter<card) {
+						aString = aString + "<td align=\"center\">-</td>";
+						counter++;
+					}
+					counter = 0;
+					//Print the component name
+					aString = aString + "<tr><td><P>" + shortName;
+					//Loop until the matching string postfix(test config.) is found
+					while(counter<card && !fileName.endsWith(testsConfig[counter])) {
+						aString = aString + "<td align=\"center\">-</td>";
+						counter++;
+					}
+				}
+				else {
+					//Loop until the matching string postfix(test config.) is found
+					while(counter<card && !fileName.endsWith(testsConfig[counter])) {
+						aString = aString + "<td align=\"center\">-</td>";
+						counter++;
+					}
+					//If the previous component has no more test files left
+					if(counter==card) {
+						counter = 0;
+						//Print the new component name
+						aString = aString + "<tr><td><P>" + shortName;
+						//Loop until the matching string postfix(test config.) is found
+						while(counter<card && !fileName.endsWith(testsConfig[counter])) {
+							aString = aString + "<td align=\"center\">-</td>";
+							counter++;
+						}
+					}
+				}
+
+				testShortName = shortName;
+
+				if (errorCount != 0)
+					aString = aString + "<td align=\"center\"><b>";
+				else
+					aString = aString + "<td align=\"center\">";
+
+				//Print number of errors
+				if (errorCount!=0){
+					displayName="<font color=\"#ff0000\">"+ "(" + String.valueOf(errorCount) + ")" +"</font>";
+				}
+				else {
+					displayName="(0)";
+				}
+
+				//Reference
+				if (errorCount==-1){
+					aString=aString.concat(displayName);
+				}else {
+					aString=aString
+						+ "<a href="
+						+ "\""
+						+ hrefTestResultsTargetPath
+						+ "/"
+						+ fileName.substring(begin+1, fileName.length()-4)
+						+ ".html"
+						+ "\">"
+						+ displayName
+						+ "</a>";
+				}
+
+				if (errorCount == -1)
+					aString = aString + "<font color=\"#ff0000\">DNF";
+
+				if (errorCount != 0)
+					aString = aString + "</font></b></td>";
+				else
+					aString = aString + "</td>";
+				counter++;
+			}
+		}
+
+		return aString;
+	}
+
+	private int countErrors(String fileName) {
+		int errorCount = 0;
+
+		if (new File(fileName).length()==0)
+			return -1;
+
+		try {
+			DocumentBuilderFactory docBuilderFactory=DocumentBuilderFactory.newInstance();
+			parser=docBuilderFactory.newDocumentBuilder();
+
+			Document document = parser.parse(fileName);
+			NodeList elements = document.getElementsByTagName(elementName);
+
+			int elementCount = elements.getLength();
+			if (elementCount == 0)
+				return -1;
+			for (int i = 0; i < elementCount; i++) {
+				Element element = (Element) elements.item(i);
+				NamedNodeMap attributes = element.getAttributes();
+				Node aNode = attributes.getNamedItem("errors");
+				errorCount =
+					errorCount + Integer.parseInt(aNode.getNodeValue());
+				aNode = attributes.getNamedItem("failures");
+				errorCount =
+					errorCount + Integer.parseInt(aNode.getNodeValue());
+
+			}
+
+		} catch (IOException e) {
+			System.out.println("IOException: " + fileName);
+			// e.printStackTrace();
+			return 0;
+		} catch (SAXException e) {
+			System.out.println("SAXException: " + fileName);
+			// e.printStackTrace();
+			return 0;
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		}
+		return errorCount;
+	}
+
+
+
+	/**
+	 * Gets the hrefTestResultsTargetPath.
+	 * @return Returns a String
+	 */
+	public String getHrefTestResultsTargetPath() {
+		return hrefTestResultsTargetPath;
+	}
+
+	/**
+	 * Sets the hrefTestResultsTargetPath.
+	 * @param hrefTestResultsTargetPath The hrefTestResultsTargetPath to set
+	 */
+	public void setHrefTestResultsTargetPath(String htmlTargetPath) {
+		this.hrefTestResultsTargetPath = htmlTargetPath;
+	}
+
+	/**
+	 * Gets the compileLogsDirectoryName.
+	 * @return Returns a String
+	 */
+	public String getCompileLogsDirectoryName() {
+		return compileLogsDirectoryName;
+	}
+
+	/**
+	 * Sets the compileLogsDirectoryName.
+	 * @param compileLogsDirectoryName The compileLogsDirectoryName to set
+	 */
+	public void setCompileLogsDirectoryName(String compileLogsDirectoryName) {
+		this.compileLogsDirectoryName = compileLogsDirectoryName;
+	}
+
+	/**
+	 * Gets the hrefCompileLogsTargetPath.
+	 * @return Returns a String
+	 */
+	public String getHrefCompileLogsTargetPath() {
+		return hrefCompileLogsTargetPath;
+	}
+
+	/**
+	 * Sets the hrefCompileLogsTargetPath.
+	 * @param hrefCompileLogsTargetPath The hrefCompileLogsTargetPath to set
+	 */
+	public void setHrefCompileLogsTargetPath(String hrefCompileLogsTargetPath) {
+		this.hrefCompileLogsTargetPath = hrefCompileLogsTargetPath;
+	}
+
+	/**
+	 * Gets the testManifestFileName.
+	 * @return Returns a String
+	 */
+	public String getTestManifestFileName() {
+		return testManifestFileName;
+	}
+
+	/**
+	 * Sets the testManifestFileName.
+	 * @param testManifestFileName The testManifestFileName to set
+	 */
+	public void setTestManifestFileName(String testManifestFileName) {
+		this.testManifestFileName = testManifestFileName;
+	}
+
+	/**
+	 * Gets the dropHtmlFileName.
+	 * @return Returns a String
+	 */
+	public String getDropHtmlFileName() {
+		return dropHtmlFileName;
+	}
+
+	/**
+	 * Sets the dropHtmlFileName.
+	 * @param dropHtmlFileName The dropHtmlFileName to set
+	 */
+	public void setDropHtmlFileName(String dropHtmlFileName) {
+		this.dropHtmlFileName = dropHtmlFileName;
+	}
+
+	/**
+	 * Gets the dropTemplateFileName.
+	 * @return Returns a String
+	 */
+	public String getDropTemplateFileName() {
+		return dropTemplateFileName;
+	}
+
+	/**
+	 * Sets the dropTemplateFileName.
+	 * @param dropTemplateFileName The dropTemplateFileName to set
+	 */
+	public void setDropTemplateFileName(String dropTemplateFileName) {
+		this.dropTemplateFileName = dropTemplateFileName;
+	}
+
+	protected void getDropTokensFromList(String list) {
+		StringTokenizer tokenizer = new StringTokenizer(list, ",");
+		dropTokens = new Vector();
+
+		while (tokenizer.hasMoreTokens()) {
+			dropTokens.add(tokenizer.nextToken());
+		}
+	}
+
+	protected void getDifferentPlatformsFromList(String list) {
+		StringTokenizer tokenizer = new StringTokenizer(list, ";");
+		differentPlatforms = new Vector();
+
+		while (tokenizer.hasMoreTokens()) {
+			differentPlatforms.add(tokenizer.nextToken());
+		}
+	}
+
+	protected void getPlatformSpecsFromList(String list) {
+		StringTokenizer tokenizer = new StringTokenizer(list, ",");
+		platformSpecs = new Vector();
+
+		while (tokenizer.hasMoreTokens()) {
+			platformSpecs.add(tokenizer.nextToken());
+		}
+	}
+
+	public String getDropTokenList() {
+		return dropTokenList;
+	}
+
+	public void setDropTokenList(String dropTokenList) {
+		this.dropTokenList = dropTokenList;
+	}
+
+	public boolean isBuildTested() {
+		return isBuildTested;
+	}
+
+	public void setIsBuildTested(boolean isBuildTested) {
+		this.isBuildTested = isBuildTested;
+	}
+
+
+	/**
+	 * @return
+	 */
+	public boolean testsRan() {
+		return testsRan;
+	}
+
+	/**
+	 * @param b
+	 */
+	public void setTestsRan(boolean b) {
+		testsRan = b;
+	}
+
+	/**
+	 * @return
+	 */
+	public Vector getDropTokens() {
+		return dropTokens;
+	}
+
+	/**
+	 * @param vector
+	 */
+	public void setDropTokens(Vector vector) {
+		dropTokens = vector;
+	}
+
+	/**
+	 * @return
+	 */
+	public String getTestResultsWithProblems() {
+		return testResultsWithProblems;
+	}
+
+	/**
+	 * @return
+	 */
+	public String getTestResultsXmlUrls() {
+		return testResultsXmlUrls;
+	}
+
+	/**
+	 * @param string
+	 */
+	public void setTestResultsWithProblems(String string) {
+		testResultsWithProblems = string;
+	}
+
+	public String getBuildType() {
+		return buildType;
+	}
+
+	public void setBuildType(String buildType) {
+		this.buildType = buildType;
+	}
+
+	public String getPlatformSpecificTemplateList() {
+		return platformSpecificTemplateList;
+	}
+
+	public void setPlatformSpecificTemplateList(String platformSpecificTemplateList) {
+		this.platformSpecificTemplateList = platformSpecificTemplateList;
+	}
+
+	public void setPlatformIdentifierToken(String platformIdentifierToken) {
+		this.platformIdentifierToken = platformIdentifierToken;
+	}
+
+	public String getPlatformIdentifierToken() {
+		return platformIdentifierToken;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/TestVersionTracker.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/TestVersionTracker.java
new file mode 100644
index 0000000..7ed4ac3
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/TestVersionTracker.java
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.releng.generators;
+/**
+ * This class finds the version of a plug-in, or fragment listed in a feature
+ * and writes <element>=<element>_<version> for each in a properties file.
+ * The file produced from this task can be loaded by an Ant script to find files in the
+ * binary versions of plugins and fragments.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.tools.ant.Task;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class TestVersionTracker extends Task{
+
+	private Hashtable elements;
+	
+	//fields to hold temporary values for parsing
+	private SAXParser parser;
+	
+	//the feature to from which to collect version information
+	private String featureId;
+	
+	//buildDirectory
+	private String buildDirectory;
+	
+	//the path to the file in which to write the results
+	private String outputFile;
+		
+	public static void main(String[] args) {
+		TestVersionTracker tracker =
+		new TestVersionTracker();
+		tracker.buildDirectory="D:/src";
+		tracker.featureId="org.eclipse.sdk.tests";
+		tracker.outputFile="d:/eclipse-testing/test.properties";
+		tracker.execute();
+		
+	}
+
+	public TestVersionTracker(){
+		super();
+	}
+	
+
+	public void execute() {
+		elements = new Hashtable();
+				
+		SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+		try {
+			parser = saxParserFactory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+		  	e.printStackTrace();
+		} catch (SAXException e) {
+			e.printStackTrace();
+		}
+        
+		parse(buildDirectory+"/features/"+featureId+"/build.xml",new FeatureHandler());
+		getTestPluginProperties();
+		writeProperties(outputFile, true);
+	}
+
+    public void parse(String xmlFile,DefaultHandler handler){
+         try {
+          parser.parse(xmlFile,handler);
+        } catch (SAXException e) {
+            System.err.println (e);
+        } catch (IOException e) {
+            System.err.println (e);    
+        } 
+    }
+
+    private void getTestPluginProperties(){
+    	//set prerequisites list for each test plugin
+    	Enumeration keys=elements.keys();
+    	while (keys.hasMoreElements()){
+    		String id=keys.nextElement().toString();
+    		TestPlugin testPlugin=(TestPlugin)elements.get(id);
+    		testPlugin.getPrerequisitePlugins(id);
+    		testPlugin.setHasPerformanceTarget();
+    	}
+    }
+    
+    private class FeatureHandler extends DefaultHandler{
+    	//  Start Element Event Handler
+    	public void startElement(
+		 String uri,
+		 String local,
+			String qName,
+			Attributes atts) {
+
+    		if (qName.equals("eclipse.idReplacer")) {
+    			try{
+    				String pluginIds = atts.getValue("pluginIds");
+    				
+    				//get pluginIDs and versions from generated build.xml.  Create TestPlugin objects
+    				StringTokenizer tokenizer= new StringTokenizer(pluginIds,",");
+    				while (tokenizer.hasMoreTokens()){
+    					String idtmp=tokenizer.nextToken();
+    					String id=idtmp.substring(0, idtmp.indexOf(":"));
+    					String version=tokenizer.nextToken();
+    					TestPlugin testPlugin=new TestPlugin(id,version);
+    					elements.put(id,testPlugin);	
+    				} 								
+    			} catch (Exception e){
+    				e.printStackTrace();
+       			}
+ 			} 
+    	}
+    }
+ 
+    private class TestPlugin {
+    	String id;
+    	String version;
+    	boolean hasPerformanceTarget=false;
+    	ArrayList prerequisitePlugins=new ArrayList();
+    	File testXml;
+    	
+    	TestPlugin(String id, String version){
+    		this.id=id;
+    		this.version=version;
+    		this.testXml=new File(buildDirectory,"plugins/"+id+"/test.xml");
+    	}
+    	
+    	private void getPrerequisitePlugins(String id) {
+			Headers headers = null;
+			String value = null;
+			File manifest = new File(buildDirectory, "plugins/" + id + "/META-INF/MANIFEST.MF");
+			ManifestElement[] manifestElements=null;
+			if (manifest.exists()) {
+				try {
+					headers = Headers.parseManifest(new FileInputStream(manifest));
+					if (headers.get(Constants.REQUIRE_BUNDLE)==null)
+						return;
+					value = headers.get(Constants.REQUIRE_BUNDLE).toString();
+					manifestElements = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, value);
+
+				} catch (BundleException e) {
+					e.printStackTrace();
+				} catch (FileNotFoundException e) {
+					e.printStackTrace();
+				}
+
+				for (int i = 0; i < manifestElements.length; i++) {
+					String name = manifestElements[i].getValue();
+					if (elements.containsKey(name)){
+						if (!prerequisitePlugins.contains(name)){
+							boolean prereqAdded=prerequisitePlugins.add(name);
+							if (prereqAdded){
+								getPrerequisitePlugins(name);
+							}
+						}
+					}
+				}
+			}
+			getPrerequisitePluginsFromPluginXml(id);
+		}
+    	
+    	 /**
+		 * Returns the required list of plug-ins from plugin.xml
+		 */
+		private void getPrerequisitePluginsFromPluginXml(String id) {
+			File pluginXml = new File(buildDirectory, "/plugins/" + id + "/plugin.xml");
+			if (!pluginXml.exists())
+				return;
+
+			InputStream is = null;
+			Document doc = null;
+			try {
+				is = new BufferedInputStream(new FileInputStream(pluginXml));
+				InputSource inputSource = new InputSource(is);
+
+				DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+				DocumentBuilder builder = null;
+
+				try {
+					builder = factory.newDocumentBuilder();
+				} catch (ParserConfigurationException e1) {
+					e1.printStackTrace();
+				}
+
+				try {
+					doc = builder.parse(inputSource);
+				} catch (SAXParseException e) {
+					e.printStackTrace();
+				}
+
+			} catch (IOException e) {
+				e.printStackTrace();
+			} catch (SAXException e) {
+				e.printStackTrace();
+			} finally {
+				try {
+					is.close();
+				} catch (IOException e) {
+				}
+			}
+			// Find the fragment's plugins's name, id and version
+			NodeList nodeList = doc.getElementsByTagName("import");
+			if (nodeList == null || nodeList.getLength() == 0) {
+				return;
+			}
+			for (int i = 0; i < nodeList.getLength(); i++) {
+				Node node = nodeList.item(i);
+				NamedNodeMap map = node.getAttributes();
+				Node namedItem = map.getNamedItem("plugin");
+				String name = namedItem.getNodeValue();
+				if (namedItem == null) {
+					continue;
+				} else {
+					if (elements.containsKey(name)) {
+						if (!prerequisitePlugins.contains(name)) {
+							boolean prereqAdded = prerequisitePlugins.add(name);
+							if (prereqAdded) {
+								getPrerequisitePlugins(name);
+							}
+						}
+					}
+				}
+			}
+		}
+    	
+   	 /**
+		 * Returns the required list of plug-ins from plugin.xml
+		 */
+		private void setHasPerformanceTarget() {
+			File testXml = new File(buildDirectory, "/plugins/" + id + "/test.xml");
+			if (!testXml.exists())
+				return;
+
+			InputStream is = null;
+			Document doc = null;
+			try {
+				is = new BufferedInputStream(new FileInputStream(testXml));
+				InputSource inputSource = new InputSource(is);
+
+				DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+				DocumentBuilder builder = null;
+
+				try {
+					builder = factory.newDocumentBuilder();
+				} catch (ParserConfigurationException e1) {
+					e1.printStackTrace();
+				}
+
+				try {
+					doc = builder.parse(inputSource);
+				} catch (SAXParseException e) {
+					e.printStackTrace();
+				}
+
+			} catch (IOException e) {
+				e.printStackTrace();
+			} catch (SAXException e) {
+				e.printStackTrace();
+			} finally {
+				try {
+					is.close();
+				} catch (IOException e) {
+				}
+			}
+			// Find a target named "performance"
+			NodeList nodeList = doc.getElementsByTagName("target");
+			if (nodeList == null || nodeList.getLength() == 0) {
+				return;
+			}
+			for (int i = 0; i < nodeList.getLength(); i++) {
+				Node node = nodeList.item(i);
+				NamedNodeMap map = node.getAttributes();
+				Node namedItem = map.getNamedItem("name");
+				String name = namedItem.getNodeValue();
+				if (namedItem == null) {
+					continue;
+				} else {
+					if (name.equals("performance")){
+						hasPerformanceTarget=true;
+						return;
+					}
+				}
+			}
+		}
+    	
+    	public String toString(){
+    		String keyPrefix=id+"_"+version;
+    		String performanceProperty=hasPerformanceTarget?id+".has.performance.target="+hasPerformanceTarget+"\n":"";
+    		return id+"="+keyPrefix+"\n"+
+    			performanceProperty+
+    			id+".prerequisite.testplugins="+getPrerequisiteList()+"\n";
+    	}
+    	
+    	private String getPrerequisiteList(){
+    		String prerequisites="";
+    		for (int i=0;i<prerequisitePlugins.size();i++){
+    			prerequisites=prerequisites.concat("**/${"+prerequisitePlugins.get(i)+"}** ");
+    		}
+    		return prerequisites;
+    	}
+    }
+      
+	public void writeProperties(String propertiesFile,boolean append){
+		
+		try{
+			
+		PrintWriter writer = new PrintWriter(new FileWriter(propertiesFile,append));
+				
+			Object[] keys = elements.keySet().toArray();
+			Arrays.sort(keys);
+			for (int i=0;i<keys.length;i++){
+				Object key = keys[i];
+				writer.println(((TestPlugin)elements.get(key)).toString());
+				writer.flush();
+			}
+			writer.close();
+		
+		} catch (IOException e){
+			System.out.println("Unable to write to file "+propertiesFile);
+		}
+		
+		
+	}
+
+	
+	/**
+	 * @return Returns the outputFilePath.
+	 */
+	public String getOutputFilePath() {
+		return outputFile;
+	}
+
+	/**
+	 * @param outputFilePath The outputFilePath to set.
+	 */
+	public void setOutputFilePath(String outputFilePath) {
+		this.outputFile = outputFilePath;
+	}
+
+	public String getBuildDirectory() {
+		return buildDirectory;
+	}
+
+	public void setBuildDirectory(String buildDirectory) {
+		this.buildDirectory = buildDirectory;
+	}
+
+	public String getFeatureId() {
+		return featureId;
+	}
+
+	public void setFeatureId(String featureId) {
+		this.featureId = featureId;
+	}
+
+	public String getOutputFile() {
+		return outputFile;
+	}
+
+	public void setOutputFile(String outputFile) {
+		this.outputFile = outputFile;
+	}
+
+
+
+
+}
diff --git a/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/VersionTrackerTask.java b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/VersionTrackerTask.java
new file mode 100644
index 0000000..68c1631
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src/org/eclipse/releng/generators/VersionTrackerTask.java
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+/**
+ * This class finds the version of a plug-in, or fragment listed in a feature
+ * and writes <element>=<element>_<version> for each in a properties file.
+ * The file produced from this task can be loaded by an Ant script to find files in the
+ * binary versions of plugins and fragments.
+ */
+package org.eclipse.releng.generators;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.SAXException;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import org.apache.tools.ant.Task;
+import java.util.Vector;
+
+public class VersionTrackerTask extends Task {
+
+	private String buildDirectory;
+	private Hashtable elements;
+	private SAXParser parser;
+	private Vector allElements;
+	
+	//the feature to from which to collect version information
+	private String featurePath;
+	
+	//the path to the file in which to write the results
+	private String outputFilePath;
+	
+	public void execute(){
+		VersionTrackerTask tracker =
+			new VersionTrackerTask(getBuildDirectory());
+		tracker.parse(getFeaturePath(),new FeatureHandler());
+		tracker.parse(new PluginHandler());
+		tracker.writeProperties(getOutputFilePath(), true);
+	}
+	
+	//test
+	public static void main(String[] args) {
+		VersionTrackerTask Tracker =
+			new VersionTrackerTask(args[1]);
+		Tracker.parse(args[0],Tracker.new FeatureHandler());
+		Tracker.parse(Tracker.new PluginHandler());
+			Tracker.writeProperties(args[2], true);
+	}
+
+	public VersionTrackerTask(){
+	}
+	
+	public VersionTrackerTask(String install) {
+		elements = new Hashtable();
+		allElements=new Vector();
+		
+		SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+		try {
+			parser = saxParserFactory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+		  	e.printStackTrace();
+		} catch (SAXException e) {
+			e.printStackTrace();
+		}
+        
+       	// directory containing the source for a given build
+		buildDirectory = install;
+	}
+
+	private void parse (DefaultHandler handler){
+		for (int i=0; i<allElements.size();i++){
+			parse(allElements.elementAt(i).toString(), handler);
+		}
+	}
+	
+    public void parse(String xmlFile,DefaultHandler handler){
+         try {
+          parser.parse(xmlFile,handler);
+        } catch (SAXException e) {
+            System.err.println (e);
+        } catch (IOException e) {
+            System.err.println (e);    
+        } 
+    }
+
+    private class FeatureHandler extends DefaultHandler{
+    	//  Start Element Event Handler
+    	public void startElement(
+		 String uri,
+		 String local,
+			String qName,
+			Attributes atts) {
+
+    		String element = atts.getValue("id");
+    		//need to parse the plugin.xml or fragment.xml for the correct version value since the 3.0 features may list these as "0.0.0"
+    		if (qName.equals("plugin")) {
+    			try{
+     			allElements.add(getBuildDirectory()+File.separator+"plugins"+File.separator+element+File.separator+"plugin.xml");
+    			} catch (Exception e){
+    				e.printStackTrace();
+    		
+    			}
+ 			} else if (qName.equals("fragment")){
+				allElements.add(getBuildDirectory()+File.separator+"plugins"+File.separator+element+File.separator+"fragment.xml");
+			}
+    	}
+    }
+ 
+    private class PluginHandler extends DefaultHandler{
+    	//  Start Element Event Handler
+    	public void startElement(
+								 String uri,
+								 String local,
+								 String qName,
+								 Attributes atts) {
+
+    		
+    		String element = atts.getValue("id");
+    		String version = atts.getValue("version");
+    		System.out.println("Examining "+element);
+    		
+    		if (qName.equals("plugin") || qName.equals("fragment")){
+    			System.out.println("Found plugin "+element);
+    			elements.put(element,element+"_"+version);
+    		}
+    	}
+    }
+	
+	public void writeProperties(String propertiesFile,boolean append){
+		try{
+			
+		PrintWriter writer = new PrintWriter(new FileWriter(propertiesFile,append));
+				
+			Enumeration keys = elements.keys();
+
+			while (keys.hasMoreElements()){
+				Object key = keys.nextElement();
+				writer.println(key.toString()+"="+elements.get(key).toString());
+				writer.flush();
+			}
+			writer.close();
+		
+		} catch (IOException e){
+			System.out.println("Unable to write to file "+propertiesFile);
+		}
+		
+		
+	}
+
+	/**
+	 * @return Returns the featurePath.
+	 */
+	public String getFeaturePath() {
+		return featurePath;
+	}
+
+	/**
+	 * @param featurePath The featurePath to set.
+	 */
+	public void setFeaturePath(String featurePath) {
+		this.featurePath = featurePath;
+	}
+
+	/**
+	 * @return Returns the installDirectory.
+	 */
+	public String getBuildDirectory() {
+		return buildDirectory;
+	}
+
+	/**
+	 * @param installDirectory The installDirectory to set.
+	 */
+	public void setBuildDirectory(String buildDirectory) {
+		this.buildDirectory = buildDirectory;
+	}
+
+	/**
+	 * @return Returns the outputFilePath.
+	 */
+	public String getOutputFilePath() {
+		return outputFilePath;
+	}
+
+	/**
+	 * @param outputFilePath The outputFilePath to set.
+	 */
+	public void setOutputFilePath(String outputFilePath) {
+		this.outputFilePath = outputFilePath;
+	}
+
+}
diff --git a/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/services/bugzilla/UpdateBugStateTask.java b/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/services/bugzilla/UpdateBugStateTask.java
new file mode 100644
index 0000000..5e120cb
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/services/bugzilla/UpdateBugStateTask.java
@@ -0,0 +1,706 @@
+/**
+ * Copyright (c) 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
+ */
+
+package org.eclipse.releng.services.bugzilla;
+
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+import org.eclipse.releng.util.bugzilla.Messages;
+
+
+public class UpdateBugStateTask extends Task
+{
+  private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
+
+  private static final String GET = "GET"; //$NON-NLS-1$
+
+  private static final String CTYPE_RDF = "&ctype=rdf"; //$NON-NLS-1$
+
+  private static final String URL_TARGET_MILESTONE = "&target_milestone="; //$NON-NLS-1$
+
+  private static final String URL_CHFIELDTO = "&chfieldto="; //$NON-NLS-1$
+
+  private static final String URL_BUG_STATUS = "&bug_status="; //$NON-NLS-1$
+
+  private static final String HTTPS_BUGS_ECLIPSE_ORG_BUGS_BUGLIST_CGI_PRODUCT = "https://bugs.eclipse.org/bugs/buglist.cgi?product="; //$NON-NLS-1$
+
+  private static final String COOKIE = "Cookie"; //$NON-NLS-1$
+
+  private static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"; //$NON-NLS-1$
+
+  private static final String CONTENT_TYPE = "Content-type"; //$NON-NLS-1$
+
+  private static final String POST = "POST"; //$NON-NLS-1$
+
+  private static final String HTTPS_BUGS_ECLIPSE_ORG_BUGS_PROCESS_BUG_CGI = "https://bugs.eclipse.org/bugs/process_bug.cgi"; //$NON-NLS-1$
+
+  private static final String BUG_STATUS = "bug_status"; //$NON-NLS-1$
+
+  private static final String AMP = "&"; //$NON-NLS-1$
+
+  private static final String EQ = "="; //$NON-NLS-1$
+
+  private static final String HTTPS_BUGS_ECLIPSE_ORG_BUGS_SHOW_BUG_CGI_ID = "https://bugs.eclipse.org/bugs/show_bug.cgi?id="; //$NON-NLS-1$
+
+  private static final String CTYPE_XML = "&ctype=xml"; //$NON-NLS-1$
+
+  private static final String RESOLVE = "resolve"; //$NON-NLS-1$
+
+  private static final String RESOLUTION = "resolution"; //$NON-NLS-1$
+
+  private static final String KNOB = "knob"; //$NON-NLS-1$
+
+  private static final String LONGDESCLENGTH = "longdesclength"; //$NON-NLS-1$
+
+  private static final String SHORT_DESC = "short_desc"; //$NON-NLS-1$
+
+  private static final String BUG_FILE_LOC = "bug_file_loc"; //$NON-NLS-1$
+
+  private static final String BUG_SEVERITY = "bug_severity"; //$NON-NLS-1$
+
+  private static final String PRIORITY = "priority"; //$NON-NLS-1$
+
+  private static final String OP_SYS = "op_sys"; //$NON-NLS-1$
+
+  private static final String REP_PLATFORM = "rep_platform"; //$NON-NLS-1$
+
+  private static final String TARGET_MILESTONE = "target_milestone"; //$NON-NLS-1$
+
+  private static final String COMPONENT = "component"; //$NON-NLS-1$
+
+  private static final String VERSION = "version"; //$NON-NLS-1$
+
+  private static final String PRODUCT = "product"; //$NON-NLS-1$
+
+  private static final String ID = "id"; //$NON-NLS-1$
+
+  private static final String COMMENT = "comment"; //$NON-NLS-1$
+
+  private static final String PROCESS_BUG = "process_bug"; //$NON-NLS-1$
+
+  private static final String FORM_NAME = "form_name"; //$NON-NLS-1$
+
+  private static final String BUGZILLA_LOGINCOOKIE = "; Bugzilla_logincookie="; //$NON-NLS-1$
+
+  private static final String BUGZILLA_LOGIN = "Bugzilla_login="; //$NON-NLS-1$
+
+  private static final String DIGITS_REGEX = "(\\d+)"; //$NON-NLS-1$
+
+  private static final String COLON = ":"; //$NON-NLS-1$
+
+  private static final String DASH = "-"; //$NON-NLS-1$
+
+  private static final String BUGID_REGEX = "<bz:id(?: nc:parseType=\"Integer\")>(\\d+)</bz:id>"; //$NON-NLS-1$
+
+  private static final String BUILDID_REGEX = "([IMNRS]?-?)(\\d{4})(\\d{2})(\\d{2})-?(\\d{2})(\\d{2})"; //$NON-NLS-1$
+
+  private static final String TIMESTAMP_REGEX = "(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})"; //$NON-NLS-1$
+
+  private static final String JS = "Java said:"; //$NON-NLS-1$
+
+  private static final String SP = " "; //$NON-NLS-1$
+
+  private static final String XML_REGEX = "<(\\S+)>([^<]+)</\\1>"; //$NON-NLS-1$
+
+  private static final String NL = "\n"; //$NON-NLS-1$
+
+  private static final String CSO = ", or "; //$NON-NLS-1$
+
+  private static final String CS = ", "; //$NON-NLS-1$
+
+  private static final String BZ_IV = "INVALID"; //$NON-NLS-1$
+
+  private static final String BZ_WF = "WONTFIX"; //$NON-NLS-1$
+
+  private static final String BZ_LT = "LATER"; //$NON-NLS-1$
+
+  private static final String BZ_RM = "REMIND"; //$NON-NLS-1$
+
+  private static final String BZ_WK = "WORKSFORME"; //$NON-NLS-1$
+
+  private static final String BZ_FX = "FIXED"; //$NON-NLS-1$
+
+  private static final String BZ_RE = "REOPENED"; //$NON-NLS-1$
+
+  private static final String BZ_AS = "ASSIGNED"; //$NON-NLS-1$
+
+  private static final String BZ_NEW = "NEW"; //$NON-NLS-1$
+
+  private static final String BZ_UC = "UNCONFIRMED"; //$NON-NLS-1$
+
+  private static final String EMPTY = ""; //$NON-NLS-1$
+
+  private static final String LT = "<"; //$NON-NLS-1$
+
+  private static final String GT = ">"; //$NON-NLS-1$
+
+  private static final String QUOT = "\""; //$NON-NLS-1$
+
+  private static final String APOS = "'"; //$NON-NLS-1$
+
+  private static final String HTML_APOS = "&apos;"; //$NON-NLS-1$
+
+  private static final String HTML_QUOT = "&quot;"; //$NON-NLS-1$
+
+  private static final String HTML_LT = "&lt;"; //$NON-NLS-1$
+
+  private static final String HTML_GT = "&gt;"; //$NON-NLS-1$
+
+  private static final String HTML_NBSP = "&nbsp;"; //$NON-NLS-1$
+
+  private static final String HTML_AMP = "&amp;"; //$NON-NLS-1$
+
+  private int debug;
+
+  private int login;
+
+  private int loginCookie;
+
+  private String product;
+
+  private String status;
+
+  private String buildID;
+
+  private String buildAlias;
+
+  private String milestone;
+
+  private String bugList;
+
+  private String resolution;
+
+  private String endDate;
+
+  private LinkedHashMap trans;
+
+  public UpdateBugStateTask()
+  {
+    debug = 1;
+
+    login = 0;
+    loginCookie = 0;
+    product = EMPTY;
+    status = EMPTY;
+    buildID = EMPTY;
+    buildAlias = EMPTY;
+    endDate = EMPTY;
+    milestone = EMPTY;
+    bugList = EMPTY;
+    resolution = BZ_FX;
+
+    trans = new LinkedHashMap(8, 0.75f, false);
+    trans.put(HTML_APOS, APOS);
+    trans.put(HTML_QUOT, QUOT);
+    trans.put(HTML_LT, LT);
+    trans.put(HTML_GT, GT);
+    trans.put(HTML_NBSP, SP);
+    trans.put(HTML_AMP, AMP);
+  }
+
+  public void setDebug(int d)
+  {
+    debug = d;
+  }
+
+  public void setBugList(String b)
+  {
+    bugList = b;
+  }
+
+  public void setProduct(String p)
+  {
+    product = p;
+  }
+
+  public void setStatus(String s)
+  {
+    if (s.equals(BZ_UC) || s.equals(BZ_NEW) || s.equals(BZ_AS) || s.equals(BZ_RE))
+    {
+      status = s;
+    }
+    else
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.invalidStatus") + "!" + SP + //$NON-NLS-1$ //$NON-NLS-2$
+        Messages.getString("UpdateBugStateTask.expectedOne") + SP + BZ_UC + CS + BZ_NEW + CS + BZ_AS + CSO + BZ_RE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+  }
+
+  public void setLogin(int l)
+  {
+    login = l;
+  }
+
+  public void setLoginCookie(int lc)
+  {
+    loginCookie = lc;
+  }
+
+  public void setResolution(String r)
+  {
+    if (r.equals(BZ_FX) || r.equals(BZ_IV) || r.equals(BZ_WF) || r.equals(BZ_LT) || r.equals(BZ_RM) || r.equals(BZ_WK))
+    {
+      resolution = r;
+    }
+    else
+    {
+      System.err.println(Messages.getString("UpdateBugStateTask.invalidResolution") + "!" + SP + //$NON-NLS-1$ //$NON-NLS-2$
+        Messages.getString("UpdateBugStateTask.expected") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        + SP + BZ_FX + CS + BZ_IV + CS + BZ_WF + CS + BZ_LT + CS + BZ_RM + CSO + BZ_WK + SP
+        + "(" + Messages.getString("UpdateBugStateTask.default") + COLON + SP + BZ_FX + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+  }
+
+  public void setEndDate(String t)
+  {
+    Pattern p = Pattern.compile(TIMESTAMP_REGEX);
+    Matcher m = p.matcher(t);
+    if (m.matches())
+    {
+      endDate = m.group(1) + DASH + m.group(2) + DASH + m.group(3) + SP + m.group(4) + COLON + m.group(5);
+    }
+    else
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.invalidTimestamp") + COLON + SP + t + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+  }
+
+  public void setBuildID(String t)
+  {
+    Pattern p = Pattern.compile(BUILDID_REGEX);
+    Matcher m = p.matcher(t);
+    if (m.matches())
+    {
+      buildID = m.group(1) + m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6);
+    }
+    else
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.invalidBuildID") + COLON + SP + t + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+  }
+
+  public void setBuildAlias(String b)
+  {
+    buildAlias = b;
+  }
+
+  public void setMilestone(String m)
+  {
+    milestone = m;
+  }
+
+public void execute() throws BuildException
+  {
+    if (login == 0)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.expectingLogin") + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+    if (loginCookie == 0)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.expectingLogincookie") + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+    if (status.equals(EMPTY))
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.expectingStatus") + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    /* we take an explicit list OR do a query, not both */
+    if (!bugList.equals(EMPTY) && endDate.equals(EMPTY) && milestone.equals(EMPTY) && product.equals(EMPTY))
+    {
+      if (debug > 1)
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.usingBugList")); //$NON-NLS-1$
+      }
+      Pattern p = Pattern.compile(DIGITS_REGEX);
+      Matcher m = p.matcher(bugList);
+      while (m.find())
+      {
+        int bugID = Integer.parseInt(m.group(1));
+        if (debug > 1)
+        {
+          System.err.println(Messages.getString("UpdateBugStateTask.found") + SP + bugID); //$NON-NLS-1$
+        }
+        doBug(bugID);
+      }
+    }
+    else if (bugList.equals(EMPTY))
+    {
+      if (product.equals(EMPTY))
+      {
+        throw new BuildException(Messages.getString("UpdateBugStateTask.expectingProduct") + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+      }
+
+      if (debug > 1)
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.queryingFor") + SP + //$NON-NLS-1$ 
+          (!status.equals(EMPTY) ? status + SP : EMPTY) + 
+          (!product.equals(EMPTY) ? product + SP : EMPTY) + 
+          (!milestone.equals(EMPTY) ? milestone + SP : EMPTY) + 
+          Messages.getString("UpdateBugStateTask.bugs")); //$NON-NLS-1$ //$NON-NLS-2$
+      }
+
+      /* the Bugzilla search form generates a massive URL, but thankfully doesn't
+       * demand all sorts of superfluous fields like when updating a bug */
+      String url = HTTPS_BUGS_ECLIPSE_ORG_BUGS_BUGLIST_CGI_PRODUCT + urlEncode(product) + URL_BUG_STATUS + urlEncode(status)
+        + URL_CHFIELDTO + urlEncode(endDate) + URL_TARGET_MILESTONE + urlEncode(milestone) + CTYPE_RDF;
+      if (debug > 1)
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.connectingTo") + SP + //$NON-NLS-1$
+          url + SP + "..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+      }
+      HttpsURLConnection bugsconn = getConn(url, GET, true, false, EMPTY);
+      String bugs = slurpStream(bugsconn);
+      if (debug > 1)
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.gotBugList") + COLON); //$NON-NLS-1$
+        System.err.println(bugs);
+      }
+
+      Pattern p = Pattern.compile(BUGID_REGEX);
+      Matcher m = p.matcher(bugs);
+      if (m.find()) {
+        while (m.find())
+        {
+          int bugID = Integer.parseInt(m.group(1));
+          if (debug > 1)
+          {
+            System.out.println(Messages.getString("UpdateBugStateTask.found") + SP + bugID); //$NON-NLS-1$
+          }
+          doBug(bugID);
+        }
+      }
+      else
+      {
+        System.out.println("No bugs found matching specified state" + SP + "(" + status + "). Nothing to do!");
+      }
+    }
+    else
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.ambiguousRequest") + CS + //$NON-NLS-1$
+        Messages.getString("UpdateBugStateTask.mutuallyExclusive") + "!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+  }  private void doBug(int bugID) throws BuildException
+  {
+    if (bugID == 0)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.invalidBugID") + SP + bugID + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    String bugcookie = BUGZILLA_LOGIN + login + BUGZILLA_LOGINCOOKIE + loginCookie;
+    String buildstring = EMPTY;
+    if (buildAlias.equals(EMPTY) && buildID.equals(EMPTY))
+    {
+      buildstring = Messages.getString("UpdateBugStateTask.latestBuild"); //$NON-NLS-1$
+    }
+    else if (!buildAlias.equals(EMPTY) && !buildID.equals(EMPTY))
+    {
+      buildstring = buildAlias + SP + "(" + buildID + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+    }
+    else
+    {
+      buildstring = (!buildAlias.equals(EMPTY) ? buildAlias : buildID);
+    }
+
+    Hashtable args = new Hashtable();
+    args.put(FORM_NAME, PROCESS_BUG);
+    args.put(COMMENT, Messages.getString("UpdateBugStateTask.fixedIn") + SP + buildstring + "."); //$NON-NLS-1$ //$NON-NLS-2$
+    args.put(ID, new Integer(bugID));
+    args.put(PRODUCT, EMPTY);
+    args.put(VERSION, EMPTY);
+    args.put(COMPONENT, EMPTY);
+    args.put(TARGET_MILESTONE, EMPTY);
+    args.put(REP_PLATFORM, EMPTY);
+    args.put(OP_SYS, EMPTY);
+    args.put(PRIORITY, EMPTY);
+    args.put(BUG_SEVERITY, EMPTY);
+    args.put(BUG_FILE_LOC, EMPTY);
+    args.put(SHORT_DESC, EMPTY);
+    args.put(LONGDESCLENGTH, new Integer(1)); //Bugzilla doesn't seem to use this, but demands it anyways
+    args.put(KNOB, RESOLVE);
+    args.put(RESOLUTION, resolution);
+
+    if (debug > 1)
+    {
+      System.err.println(Messages.getString("UpdateBugStateTask.usingCookie") + COLON + SP + bugcookie); //$NON-NLS-1$
+      System.err.println(Messages.getString("UpdateBugStateTask.usingComment") + COLON + SP + args.get(COMMENT).toString()); //$NON-NLS-1$
+    }
+
+    /* slurp xml for bugID */
+    String url = HTTPS_BUGS_ECLIPSE_ORG_BUGS_SHOW_BUG_CGI_ID + urlEncode(args.get(ID).toString()) + CTYPE_XML;
+    if (debug > 1)
+    {
+      System.err.println(Messages.getString("UpdateBugStateTask.connectingTo") + SP + //$NON-NLS-1$
+        url + SP + "..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+    HttpsURLConnection xmlconn = getConn(url, GET, true, false, EMPTY);
+    String xml = slurpStream(xmlconn);
+    if (debug > 1)
+    {
+      System.err.println(Messages.getString("UpdateBugStateTask.gotXML") + COLON); //$NON-NLS-1$
+      System.err.println(xml);
+    }
+    xmlconn.disconnect();
+
+    /* parse xml, build post string */
+    String req = EMPTY;
+    Hashtable pxml = parseXML(xml);
+    for (Enumeration e = args.keys(); e.hasMoreElements();)
+    {
+      String elem = e.nextElement().toString();
+      /* sometimes Bugzilla omits bug_file_loc if it's blank... */
+      if (args.get(elem).equals(EMPTY) && pxml.get(elem) != null)
+      {
+        args.put(elem, pxml.get(elem));
+      }
+
+      req += urlEncode(elem) + EQ + urlEncode(args.get(elem).toString()) + AMP;
+    }
+
+    req = req.substring(0, req.length() - 1);
+
+    /* update bug, if applicable */
+    if (pxml.get(BUG_STATUS) == null)
+    {
+      if (debug > 0)
+      {
+        System.out.println(Messages.getString("UpdateBugStateTask.noBugStatus") + SP + //$NON-NLS-1$
+          bugID + CS + Messages.getString("UpdateBugStateTask.missingBug")); //$NON-NLS-1$ //$NON-NLS-2$
+      }
+    }
+    else if (pxml.get(BUG_STATUS).equals(status))
+    {
+      String bugurl = HTTPS_BUGS_ECLIPSE_ORG_BUGS_PROCESS_BUG_CGI;
+      if (debug > 1)
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.connectingTo") + SP + //$NON-NLS-1$
+          bugurl + SP + "..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+      }
+      HttpsURLConnection bugconn = getConn(bugurl, POST, true, true, bugcookie);
+
+      if (debug > 1)
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.postingData") + COLON); //$NON-NLS-1$
+        System.err.println(req);
+      }
+      sendStream(bugconn, req);
+      String response = slurpStream(bugconn);
+
+      // trap for invalid login cookie 
+      if (response.indexOf(Messages.getString("UpdateBugStateTask.legitimateLoginAndPassword")) > 0) //$NON-NLS-1$
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.couldNotLogIn")); //$NON-NLS-1$
+        System.err.println(Messages.getString("UpdateBugStateTask.BugzillaReplied") + COLON + SP + //$NON-NLS-1$
+          "\"" + Messages.getString("UpdateBugStateTask.legitimateLoginAndPassword") + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 
+        if (debug > 1)
+        {
+          System.err.println(Messages.getString("UpdateBugStateTask.gotResponse") + COLON); //$NON-NLS-1$
+          System.err.println(response);
+        }
+        bugconn.disconnect();
+        System.err.println(Messages.getString("UpdateBugStateTask.setBugFailed") + SP + bugID + SP + //$NON-NLS-1$
+          Messages.getString("UpdateBugStateTask.to") + SP + resolution + SP + //$NON-NLS-1$
+          "(" + Messages.getString("UpdateBugStateTask.was") + SP + pxml.get(BUG_STATUS) + ")" + COLON + SP + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+          "\"" + Messages.getString("UpdateBugStateTask.fixedIn") + SP + buildstring + ".\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+      }
+      else
+      {
+        if (debug > 1)
+        {
+          System.err.println(Messages.getString("UpdateBugStateTask.gotResponse") + COLON); //$NON-NLS-1$
+          System.err.println(response);
+        }
+        bugconn.disconnect();
+        if (debug > 0)
+        {
+          System.out.println(Messages.getString("UpdateBugStateTask.setBug") + SP + bugID + SP + //$NON-NLS-1$
+            Messages.getString("UpdateBugStateTask.to") + SP + resolution + SP + //$NON-NLS-1$
+            "(" + Messages.getString("UpdateBugStateTask.was") + SP + pxml.get(BUG_STATUS) + ")" + COLON + SP + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            "\"" + Messages.getString("UpdateBugStateTask.fixedIn") + SP + buildstring + ".\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        }
+      }
+    }
+    else
+    {
+      if (debug > 0)
+      {
+        System.out.println(Messages.getString("UpdateBugStateTask.ignoreBug") + SP + args.get(ID).toString() + SP + //$NON-NLS-1$
+          "(" + Messages.getString("UpdateBugStateTask.notInExpectedState") + SP + status + ")" + COLON + SP + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+          Messages.getString("UpdateBugStateTask.was") + SP + pxml.get(BUG_STATUS).toString() + "."); //$NON-NLS-1$ //$NON-NLS-2$
+      }
+    }
+  }
+
+  private String urlEncode(String elem)
+  {
+    elem = htmlDecode(elem);
+
+    try
+    {
+      elem = URLEncoder.encode(elem, UTF_8);
+    }
+    catch (java.io.UnsupportedEncodingException e)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.couldntEncode") + SP + //$NON-NLS-1$
+        "'" + elem + "'" + "!" + SP + JS + SP + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+
+    return elem;
+  }
+
+  private HttpsURLConnection getConn(String url, String method, boolean in, boolean out, String cookie)
+  {
+    URL u = null;
+    try
+    {
+      u = new URL(url);
+    }
+    catch (java.net.MalformedURLException e)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.badURL") + CS + //$NON-NLS-1$
+        url + "!" + SP + JS + SP + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+
+    URLConnection conn = null;
+    try
+    {
+      conn = u.openConnection();
+    }
+    catch (java.io.IOException e)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.failedConnection") + "!" + SP + JS + SP + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+    HttpsURLConnection sconn = (HttpsURLConnection)conn;
+
+    try
+    {
+      sconn.setRequestMethod(method);
+    }
+    catch (java.net.ProtocolException e)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.badHTTPMethod") + "!" + SP + JS + SP + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    if (method.equals(POST))
+    {
+      sconn.setRequestProperty(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
+    }
+
+    if (!cookie.equals(EMPTY))
+    {
+      sconn.setRequestProperty(COOKIE, cookie);
+    }
+
+    sconn.setDoInput(in);
+    sconn.setDoOutput(out);
+
+    try
+    {
+      sconn.connect();
+    }
+    catch (java.io.IOException e)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.connectError") + SP + //$NON-NLS-1$
+        url + "!" + SP + JS + SP + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+
+    return sconn;
+  }
+
+  private void sendStream(HttpsURLConnection conn, String req)
+  {
+    try
+    {
+      PrintWriter out = new PrintWriter(conn.getOutputStream());
+      out.print(req);
+      out.flush();
+      out.close();
+    }
+    catch (java.io.IOException e)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.streamWriteError") + "!" + SP + JS + SP + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+  }
+
+  private String slurpStream(HttpsURLConnection conn)
+  {
+    String ret = EMPTY;
+    try
+    {
+      BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+      String tmp;
+      while ((tmp = in.readLine()) != EMPTY && tmp != null)
+      {
+        ret += tmp + NL;
+      }
+
+      in.close();
+    }
+    catch (java.io.IOException e)
+    {
+      throw new BuildException(Messages.getString("UpdateBugStateTask.streamReadError") + "!" + SP + JS + SP + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    return ret;
+  }
+
+  /* this will only keep the last comment, but we don't use the comments anyways */
+  private Hashtable parseXML(String xml)
+  {
+    if (debug > 1)
+    {
+      System.err.println(Messages.getString("UpdateBugStateTask.parsingXML") + "..."); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+    Hashtable pxml = new Hashtable();
+    Pattern p = Pattern.compile(XML_REGEX);
+    Matcher m = p.matcher(xml);
+    while (m.find())
+    {
+      if (debug > 1)
+      {
+        System.err.println(Messages.getString("UpdateBugStateTask.found") + SP + m.group(1) + SP + EQ + SP + m.group(2)); //$NON-NLS-1$
+      }
+      pxml.put(m.group(1), m.group(2));
+    }
+
+    return pxml;
+  }
+
+  private String htmlDecode(String str)
+  {
+    for (Iterator i = trans.keySet().iterator(); i.hasNext();)
+    {
+      String elem = i.next().toString();
+
+      str = Pattern.compile(elem).matcher(str).replaceAll(trans.get(elem).toString());
+    }
+
+    return str;
+  }
+
+  public static void main(String args[])
+  {
+    new UpdateBugStateTask();
+  }
+}
diff --git a/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/util/bugzilla/Messages.java b/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/util/bugzilla/Messages.java
new file mode 100644
index 0000000..d36f0f1
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/util/bugzilla/Messages.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+package org.eclipse.releng.util.bugzilla;
+
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+
+public class Messages
+{
+  private static final String BUNDLE_NAME = "org.eclipse.releng.util.bugzilla.messages"; //$NON-NLS-1$
+
+  private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+
+  private Messages()
+  {
+  }
+
+  public static String getString(String key)
+  {
+    try
+    {
+      return RESOURCE_BUNDLE.getString(key);
+    }
+    catch (MissingResourceException e)
+    {
+      return '!' + key + '!';
+    }
+  }
+}
diff --git a/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/util/bugzilla/messages.properties b/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/util/bugzilla/messages.properties
new file mode 100644
index 0000000..415e9c1
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_bugzilla/org/eclipse/releng/util/bugzilla/messages.properties
@@ -0,0 +1,46 @@
+UpdateBugStateTask.invalidStatus=Invalid initial status
+UpdateBugStateTask.expectedOne=Expected one of
+UpdateBugStateTask.invalidResolution=Invalid resolution
+UpdateBugStateTask.expected=Expected
+UpdateBugStateTask.default=default
+UpdateBugStateTask.invalidBuildID=Invalid buildID. Entered
+UpdateBugStateTask.invalidTimestamp=Invalid timestamp (must be yyyymmddHHMM or yyyymmdd0000). Entered
+UpdateBugStateTask.expectingLogin=Was expecting a login
+UpdateBugStateTask.expectingLogincookie=Was expecting a logincookie
+UpdateBugStateTask.legitimateLoginAndPassword=I need a legitimate login and password to continue.
+UpdateBugStateTask.expectingStatus=Was expecting an initial status
+UpdateBugStateTask.usingBugList=Using explicit bug list
+UpdateBugStateTask.found=Found
+UpdateBugStateTask.expectingProduct=Was expecting a product
+UpdateBugStateTask.queryingFor=Querying for
+UpdateBugStateTask.bugs=bugs
+UpdateBugStateTask.connectingTo=Connecting to
+UpdateBugStateTask.gotBugList=Got bug list
+UpdateBugStateTask.ambiguousRequest=Ambiguous request
+UpdateBugStateTask.mutuallyExclusive=bugList and (endDate, milestone, product) are mutually exclusive
+UpdateBugStateTask.invalidBugID=Invalid bugID
+UpdateBugStateTask.latestBuild=latest build
+UpdateBugStateTask.fixedIn=Fixed in
+UpdateBugStateTask.usingCookie=Using cookie information
+UpdateBugStateTask.usingComment=Using comment
+UpdateBugStateTask.gotXML=Got xml
+UpdateBugStateTask.noBugStatus=Couldn't find bug_status for bug
+UpdateBugStateTask.missingBug=this means that the bug probably doesn't exist, ignoring.
+UpdateBugStateTask.postingData=Posting data
+UpdateBugStateTask.gotResponse=Got response
+UpdateBugStateTask.setBug=Set bug
+UpdateBugStateTask.setBugFailed=Failed to set bug
+UpdateBugStateTask.to=to
+UpdateBugStateTask.was=was
+UpdateBugStateTask.ignoreBug=Ignoring bug
+UpdateBugStateTask.notInExpectedState=as not in expected state
+UpdateBugStateTask.couldntEncode=Couldn't encode
+UpdateBugStateTask.couldNotLogIn=Could not use your login/logincookie credentials to log in.
+UpdateBugStateTask.badURL=Bad URL
+UpdateBugStateTask.failedConnection=Couldn't open connection
+UpdateBugStateTask.badHTTPMethod=Bad HTTP method
+UpdateBugStateTask.connectError=Error connecting to
+UpdateBugStateTask.streamWriteError=Error writing to stream
+UpdateBugStateTask.streamReadError=Error in reading from stream
+UpdateBugStateTask.BugzillaReplied=Bugzilla replied
+UpdateBugStateTask.parsingXML=Parsing xml
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedAddEntryTask.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedAddEntryTask.java
new file mode 100644
index 0000000..d0a6d96
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedAddEntryTask.java
@@ -0,0 +1,584 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.generators.rss;
+
+//TODO: bug - can't run CreateFeed and AddEntry together when debug=2 - file locking problem?
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Date;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.DateUtils;
+
+import org.eclipse.releng.util.rss.Messages;
+import org.eclipse.releng.util.rss.RSSFeedUtil;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+/**
+ * Parameters: 
+ *   debug      - more output to console - eg., 0|1|2
+ *   
+ *   file       - path to the XML file that will be created - eg., /path/to/file.to.create.xml
+ *   project    - project's name, used to label the feed - eg., Eclipse, EMF, UML2
+ *   branch     - build's branch, eg., 2.2.0
+ *   buildID    - build's ID, eg., S200605051234
+ *   feedURL    - URL of the feed where it will be published - eg., http://servername/path/to/feed.xml
+ *      note that feedURL is not required if the feed already exists, only if a new feed file must be created
+ *   buildURL   - URL of the build being added to the feed - eg., http://servername/path/to/project/branch/buildID/
+ *
+ *   buildAlias - build's alias, eg., 2.2.0RC2
+ *   
+ *   dependencyURLs   - upstream dependencies, eg., UML2 depends on emf and eclipse, so specify TWO URLs in properties file or ant task
+ *   
+ *   releaseNotesURL  - URL of the build's release notes page - eg., http://www.eclipse.org/project/news/release-notes.php 
+ *   updateManagerURL - URL of the build's Update Manager site - eg., http://servername/path/to/project/updates/
+ *   downloadsURL     - URL of the build's downloads - eg., http://servername/path/to/project/downloads/
+ *   
+ *   jarSigningStatus - code to define jar signing status - eg., one of:
+ *      NONE (or '')  - no status available or not participating 
+ *      UNSIGNED      - no jar signage available or done yet
+ *      SIGNREADY     - jars promoted to eclipse.org, ready for signing
+ *      BUILDREADY    - signed on eclipse.org, ready to be collected and bundled as zips and copied to UM site
+ *      SIGNED        - signed & bundled on download page and on UM site
+ *
+ *   callistoStatus   - code to define Callisto status, eg., one of:
+ *      NONE (or '')         - not part of Callisto or unknown status
+ *      BUILDCOMPLETE        - Have you finished your RC1 bits?
+ *      2006-05-02T20:50:00Z - When do you expect to finish them?
+ *      TPTP                 - If you're waiting for another project, which one(s)? (TPTP is just an example)
+ *      UMSITEREADY          - Have you placed those bits in your update site?
+ *      CALLISTOSITEREADY    - Have you updated the features.xml file in the Callisto CVS directory?
+ *      COMPLETE             - Are you ready for RC1 to be declared?
+ *   
+ *   buildType - code to define type of build, eg., one of: 
+ *      N      - Nightly
+ *      I      - Integration
+ *      M      - Maintenance
+ *      S      - Stable (Milestone or Release Candidate)
+ *      R      - Release
+ *      MC     - Maintenance-Callisto
+ *      SC     - Stable-Callisto
+ *      RC     - Release-Callisto
+ *   
+ *   Releases           - comma or space-separated list of releases in quints of os,ws,arch,type/name,filename,... 
+ *                      - eg., win32,win,x86,SDK,eclipse-SDK-3.2RC5-win32.zip,linux,gtk,x86_64,SDK,eclipse-SDK-3.2RC5-linux-gtk.tar.gz   
+ *                      - (for examples and definitions of ws, os + arch, see below) 
+ *
+ *   JUnitTestURL       - URL of the build's JUnit test results - eg., http://servername/path/to/project/branch/buildID/testResults.php
+ *   performanceTestURL - URL of the build's performance tests - eg., http://servername/path/to/project/branch/buildID/performance/performance.php
+ *   APITestURL         - URL of the build's API test results - eg., http://servername/path/to/project/branch/buildID/testResults.php
+ *   
+ *   JUnitTestResults       - comma or space-separated list of test results in quads of os,ws,arch,status,os,ws,status,arch,... - eg., win32,win,x86,PASS,linux,gtk,x86,PASS
+ *   performanceTestResults - comma or space-separated list of test results in quads of os,ws,arch,status,os,ws,status,arch,... - eg., win32,win,x86_64,PASS,linux,gtk,x86_64,PASS
+ *   APITestResults         - comma or space-separated list of test results in quads of os,ws,arch,status,os,ws,status,arch,... - eg., win32,win,ppc,PASS,linux,gtk,ppc,PASS
+ *      ws     - window system - eg., ALL, win32, win64, linux, macos...
+ *      os     - operating system - eg., ALL, win, gtk, motif, carbon, ...
+ *      arch   - architecture, eg., ALL, x86, x86_64, ppc, ...
+ *      status - status code for test results - eg., one of: PASS, PENDING, FAIL, UNKNOWN, SKIPPED
+ *      
+ * @author nickb
+ *
+ */
+public class RSSFeedAddEntryTask extends Task {
+
+  private int debug = 0;
+
+  private static final String now = getTimestamp();
+
+  //$ANALYSIS-IGNORE codereview.java.rules.portability.RulePortabilityLineSeparators
+  private static final String NL="\n"; //$NON-NLS-1$
+  private static final String NS = ""; //$NON-NLS-1$
+  private static final String SEP = "----"; //$NON-NLS-1$ 
+  private static final String SP = " "; //$NON-NLS-1$
+
+  private static final String splitter = "[,\t " + NL + "]+"; //$NON-NLS-1$ //$NON-NLS-2$
+
+  //required fields
+  private File file;
+  private String project;
+  private String branch;
+  private String buildID;
+  private String feedURL;
+  private String buildURL;
+
+  //optional
+  private String buildAlias;
+
+  //optional
+  private String[] dependencyURLs = new String[] {};
+
+  //optional
+  private String releaseNotesURL;
+  private String updateManagerURL;
+  private String downloadsURL;
+  private String jarSigningStatus;
+  private String callistoStatus;
+  private String buildType;
+
+  //optional
+  private String[] releases = new String[] {};
+
+  //optional
+  private String JUnitTestURL;     
+  private String performanceTestURL;
+  private String APITestURL;
+  private String[] JUnitTestResults;
+  private String[] performanceTestResults;
+  private String[] APITestResults;
+
+  //optional
+  public void setDebug(int debug) { this.debug = debug; }
+
+  //required fields
+  public void setFile(String file) { 
+    if (isNullString(file))
+    { System.err.println(Messages.getString("RSSFeedCommon.FileError")); }  //$NON-NLS-1$
+    else
+    { this.file = new File(file); }
+  }
+  public void setProject(String project) { 
+    if (isNullString(project))
+    { System.err.println(Messages.getString("RSSFeedCommon.ProjectError")); }  //$NON-NLS-1$
+    else
+    { this.project = project; }
+  }
+  public void setBranch(String branch) { 
+    if (isNullString(branch))
+    { System.err.println(Messages.getString("RSSFeedAddEntryTask.BranchError")); }  //$NON-NLS-1$
+    else
+    { this.branch = branch; }
+  }
+  public void setBuildID(String buildID) { 
+    if (isNullString(buildID))
+    { System.err.println(Messages.getString("RSSFeedAddEntryTask.BuildIDError")); }  //$NON-NLS-1$
+    else
+    { this.buildID = buildID; }
+  }
+  public void setFeedURL(String feedURL) { 
+    if (isNullString(feedURL))
+    { System.err.println(Messages.getString("RSSFeedCommon.FeedURLError")); }  //$NON-NLS-1$
+    else
+    { this.feedURL = feedURL; }
+  }
+  public void setBuildURL(String buildURL) { 
+    if (isNullString(buildURL))
+    { System.err.println(Messages.getString("RSSFeedAddEntryTask.BuildURLError")); }  //$NON-NLS-1$
+    else
+    { this.buildURL = buildURL; }
+  }
+
+  //optional: alias is usually something like "3.2.0M6"
+  public void setBuildAlias(String buildAlias) { this.buildAlias = buildAlias; }
+
+  //optional: upstream dependencies, eg., UML2 depends on emf and eclipse, so specify TWO URLs in properties file or ant task
+  public void setDependencyURLs(String dependencyURLs) { if (!isNullString(dependencyURLs)) { this.dependencyURLs = dependencyURLs.split(splitter); } }
+
+  //optional: define releases available in this build for a series of operating systems, windowing systems, and type
+  public void setReleases(String releases) { if (!isNullString(releases)) { this.releases = releases.split(splitter); } }
+
+  //optional: informational links to release notes, downloads, update manager
+  public void setReleaseNotesURL(String releaseNotesURL) { this.releaseNotesURL = releaseNotesURL; }
+  public void setUpdateManagerURL(String updateManagerURL) { this.updateManagerURL = updateManagerURL; }
+  public void setDownloadsURL(String downloadsURL) { this.downloadsURL = downloadsURL; }
+  public void setJarSigningStatus(String jarSigningStatus) { this.jarSigningStatus = jarSigningStatus; }
+  public void setCallistoStatus(String callistoStatus) { this.callistoStatus = callistoStatus; }
+  public void setBuildType(String buildType) {
+    if (!isNullString(buildType)) 
+    {
+      this.buildType = buildType;
+    }
+    else
+    {
+      this.buildType = buildID.replaceAll("[^NIMSR]", NS); //$NON-NLS-1$
+      if (this.buildType.length()>1) 
+      {
+        this.buildType=this.buildType.substring(0, 1);
+      }
+    }
+
+  }
+
+  //optional: test URLs and results
+  public void setJUnitTestURL(String JUnitTestURL) { this.JUnitTestURL = JUnitTestURL; }
+  public void setPerformanceTestURL(String performanceTestURL) { this.performanceTestURL = performanceTestURL; }
+  public void setAPITestURL(String APITestURL) { this.APITestURL = APITestURL; }
+  public void setJUnitTestResults(String JUnitTestResults) { if (!isNullString(JUnitTestResults)) { this.JUnitTestResults = JUnitTestResults.split(splitter); } }
+  public void setPerformanceTestResults(String performanceTestResults) { if (!isNullString(performanceTestResults)) { this.performanceTestResults = performanceTestResults.split(splitter); } }
+  public void setAPITestResults(String APITestResults) { if (!isNullString(APITestResults)) { this.APITestResults = APITestResults.split(splitter); } }
+
+  // The method executing the task
+  public void execute() throws BuildException {
+    if (debug>0) { 
+      System.out.println(Messages.getString("RSSFeedAddEntryTask.AddingEntryTo") + project + SP + Messages.getString("RSSFeedCommon.RSSFeedFile") + SP + file.toString() + ", " + Messages.getString("RSSFeedCommon.ToBePublishedAt") + feedURL); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+    }
+    updateFeedXML(file); // load previous
+  }
+
+  //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
+  private void updateFeedXML(File file){
+    if (!file.exists()) {
+      System.out.println(Messages.getString("RSSFeedCommon.RSSFeedFile") + SP + file.toString() + SP + Messages.getString("RSSFeedAddEntryTask.DoesNotExist")); //$NON-NLS-1$ //$NON-NLS-2$
+      RSSFeedCreateFeedTask creator=new RSSFeedCreateFeedTask();
+      creator.setFile(file.toString());
+      creator.setFeedURL(feedURL);
+      creator.setProject(project);
+      creator.setDebug(debug);
+      creator.execute();
+    }
+    DocumentBuilderFactory documentBuilderFactory=DocumentBuilderFactory.newInstance();
+    documentBuilderFactory.setNamespaceAware(true);
+    DocumentBuilder documentBuilder=null;
+    try {
+      documentBuilder=documentBuilderFactory.newDocumentBuilder();
+    }
+    catch (ParserConfigurationException e) {
+      e.printStackTrace();
+    }
+    Document document=null;
+    try {
+      document=documentBuilder.parse(file);
+    }
+    catch (SAXException e) {
+      e.printStackTrace();
+    }
+    catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    Transformer transformer = null;
+    try {
+      transformer = createTransformer("UTF-8"); //$NON-NLS-1$
+    } catch (TransformerException e) {
+      e.printStackTrace();
+    }
+
+    Element element=document.getDocumentElement();
+    for (Node child=element.getFirstChild(); child != null; child=child.getNextSibling()) {
+      if ("updated".equals(child.getLocalName())) { //$NON-NLS-1$
+        if (debug > 0) {
+          System.out.println(Messages.getString("RSSFeedCommon.Set") + " <" + child.getLocalName()+ ">"+ now+ "</"+ child.getLocalName()+ ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+        }
+        ((Element)child).setTextContent(now);
+      }
+      else if ("id".equals(child.getLocalName())) { //$NON-NLS-1$
+        Node newNode=createEntry(document);
+        if (debug > 0) {
+          System.out.println(Messages.getString("RSSFeedAddEntryTask.AttachNew") + " <entry/>"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        try {
+          if (debug > 0) {
+            System.out.println(SEP); //$NON-NLS-1$
+            transformer.transform(new DOMSource(newNode),new StreamResult(System.out));
+            System.out.println(SEP); //$NON-NLS-1$
+          }
+        }
+        catch (TransformerException e) {
+          e.printStackTrace();
+        }
+        Node refNode=child.getNextSibling();
+        element.insertBefore(document.createTextNode(NL + "  "),refNode); //$NON-NLS-1$
+        element.insertBefore(newNode,refNode);
+        break;
+      }
+    }
+    try {
+      transformer.transform(new DOMSource(document),new StreamResult(new OutputStreamWriter(new FileOutputStream(file))));
+      if (debug > 1) {
+        System.out.println(SEP); //$NON-NLS-1$
+        transformer.transform(new DOMSource(document),new StreamResult(System.out));
+        System.out.println(SEP); //$NON-NLS-1$
+      }
+    }
+    catch (FileNotFoundException e) {
+      e.printStackTrace();
+    }
+    catch (TransformerException e) {
+      e.printStackTrace();
+    }
+  }
+
+
+  private Element createEntry(Document document) {
+
+//  <entry>
+    Element entry =  document.createElement("entry"); //$NON-NLS-1$
+
+    String[] txt = { NL + "  ", NL + "    ", NL + "      ", NL + "        ", NL + "          " , NL + "            " }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+    Element elem = null;
+
+    String projectVersionString = project + SP + (!isNullString(buildAlias)?  //$NON-NLS-1$
+      (buildAlias.startsWith(branch) ? 
+        buildAlias + " (" + buildID + ")" :                   // 2.2.0RC2 (S200605051234) //$NON-NLS-1$ //$NON-NLS-2$
+          buildAlias + " (" + branch + "." + buildID + ")") : // Foobar (2.2.0.S200605051234)  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            branch + SP + buildID);                                // 2.2.0.S200605051234 //$NON-NLS-1$
+
+    doVarSubs();
+
+//  <title>[announce] " + project + SP + branch + SP + buildID + " is available</title>
+    elem = document.createElement("title"); //$NON-NLS-1$
+    elem.setTextContent(Messages.getString("RSSFeedAddEntryTask.AnnouncePrefix") + projectVersionString + SP + Messages.getString("RSSFeedAddEntryTask.IsAvailable")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    attachNode(document, entry, elem, txt[1]);
+
+//  <link href=\"" + buildURL + "\"/>
+    elem = document.createElement("link"); //$NON-NLS-1$
+    elem.setAttribute("href", !isNullString(buildURL) ? buildURL : projectVersionString); //$NON-NLS-1$
+    attachNode(document, entry, elem, txt[1]);
+
+//  <id>" + buildURL + "</id>
+    elem = document.createElement("id"); //$NON-NLS-1$
+    elem.setTextContent(!isNullString(buildURL) ? buildURL : projectVersionString);
+    attachNode(document, entry, elem, txt[1]);
+
+//  <updated>" + getTimestamp() + "</updated>
+    elem = document.createElement("updated"); //$NON-NLS-1$
+    elem.setTextContent(now);
+    attachNode(document, entry, elem, txt[1]);
+
+//  <summary>
+    Element summary = document.createElement("summary"); //$NON-NLS-1$
+    attachNode(document, entry, summary, txt[1]);
+
+//  <build callisto="" jars="" type="" href="" xmlns="http://www.eclipse.org/2006/BuildFeed">
+    Element build = document.createElement("build"); //$NON-NLS-1$
+    build.setAttribute("jars", jarSigningStatus); //$NON-NLS-1$
+    build.setAttribute("callisto", callistoStatus); //$NON-NLS-1$
+    build.setAttribute("type", buildType); //$NON-NLS-1$
+    build.setAttribute("xmlns", "http://www.eclipse.org/2006/BuildFeed"); //$NON-NLS-1$ //$NON-NLS-2$
+    if (!isNullString(buildURL)) {
+      build.setAttribute("href",buildURL); //$NON-NLS-1$
+    }
+    attachNode(document, summary, build, txt[2]);
+
+//  <update>" + usiteURL + "</update>
+    if (!isNullString(updateManagerURL)) {
+      elem = document.createElement("update"); //$NON-NLS-1$
+      elem.setTextContent(updateManagerURL);
+      attachNode(document, build, elem, txt[3]);
+    }
+
+//  <downloads>" + dropsURL + "</downloads>
+    if (!isNullString(downloadsURL)) {
+      elem = document.createElement("downloads"); //$NON-NLS-1$
+      elem.setTextContent(downloadsURL);
+      attachNode(document, build, elem, txt[3]);
+    }
+
+//  <releasenotes>" + releaseNotesURL + "</releasenotes>
+    if (!isNullString(releaseNotesURL)) {
+      elem = document.createElement("releasenotes"); //$NON-NLS-1$
+      elem.setTextContent(releaseNotesURL);
+      attachNode(document, build, elem, txt[3]);
+    }
+
+//  <releases>
+//    <release os="" ws="" type=""> + filename + </release>
+    if (releases!=null && releases.length>0) {
+      if (releases.length % 5 != 0) { 
+        System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf5") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "releases"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+      } 
+      Element releasesElem = document.createElement("releases"); //$NON-NLS-1$
+      for (int i = 0; i < releases.length; i+=5)
+      {
+        Element release = document.createElement("release"); //$NON-NLS-1$
+        release.setAttribute("os", releases[i]); //$NON-NLS-1$
+        release.setAttribute("ws", releases[i+1]); //$NON-NLS-1$
+        release.setAttribute("arch", releases[i+2]); //$NON-NLS-1$
+        release.setAttribute("type", releases[i+3]); //$NON-NLS-1$
+        release.setTextContent(varSub(releases[i+4]));
+        attachNode(document, releasesElem, release, txt[4]);          
+      }
+      attachNode(document, build, releasesElem, txt[3]);
+    }
+
+//  <tests>
+    Element tests = document.createElement("tests"); //$NON-NLS-1$
+
+//    <test type=\"junit\" href=\"" + JUnitTestURL + "\"/>
+    if (!isNullString(JUnitTestURL)) {
+      Element test = document.createElement("test"); //$NON-NLS-1$
+      test.setAttribute("type", "junit"); //$NON-NLS-1$ //$NON-NLS-2$
+      test.setAttribute("href", JUnitTestURL); //$NON-NLS-1$
+      if (JUnitTestResults!=null && JUnitTestResults.length>0) {
+        if (JUnitTestResults.length % 4 != 0) { 
+          System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf4") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "JUnitTestResults"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+        }
+        for (int i = 0; i < JUnitTestResults.length; i+=4)
+        {
+          Element result = document.createElement("result"); //$NON-NLS-1$
+          result.setAttribute("os", JUnitTestResults[i]); //$NON-NLS-1$
+          result.setAttribute("ws", JUnitTestResults[i+1]); //$NON-NLS-1$
+          result.setAttribute("arch", JUnitTestResults[i+2]); //$NON-NLS-1$
+          result.setTextContent(JUnitTestResults[i+3]);
+          attachNode(document, test, result, txt[5]);          
+        }
+        // extra space to close containing tag
+        elem.appendChild(document.createTextNode(txt[4]));
+      }
+      attachNode(document, tests, test, txt[4]);
+    }
+
+//    <test type=\"performance\" href=\"" + performanceTestURL + "\"/>
+    if (!isNullString(performanceTestURL)) {
+      Element test = document.createElement("test"); //$NON-NLS-1$
+      test.setAttribute("type", "performance"); //$NON-NLS-1$ //$NON-NLS-2$
+      test.setAttribute("href", performanceTestURL); //$NON-NLS-1$
+      if (performanceTestResults!=null && performanceTestResults.length>0) {
+        if (performanceTestResults.length % 4 != 0) { 
+          System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf4") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "performanceTestResults"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+        }
+        for (int i = 0; i < performanceTestResults.length; i+=4)
+        {
+          Element result = document.createElement("result"); //$NON-NLS-1$
+          result.setAttribute("os", performanceTestResults[i]); //$NON-NLS-1$
+          result.setAttribute("ws", performanceTestResults[i+1]); //$NON-NLS-1$
+          result.setAttribute("arch", performanceTestResults[i+2]); //$NON-NLS-1$
+          result.setTextContent(performanceTestResults[i+3]);
+          attachNode(document, test, result, txt[5]);          
+        }
+        // extra space to close containing tag
+        test.appendChild(document.createTextNode(txt[4]));
+      }
+      attachNode(document, tests, test, txt[4]);
+    }
+
+//    <test type=\"performance\" href=\"" + performanceTestURL + "\"/>
+    if (!isNullString(APITestURL)) {
+      Element test = document.createElement("test"); //$NON-NLS-1$
+      test.setAttribute("type", "api"); //$NON-NLS-1$ //$NON-NLS-2$
+      test.setAttribute("href", APITestURL); //$NON-NLS-1$
+      if (APITestResults!=null && APITestResults.length>0) {
+        if (APITestResults.length % 4 != 0) { 
+          System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf4") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "APITestResults"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+        }
+        for (int i = 0; i < APITestResults.length; i+=4)
+        {
+          Element result = document.createElement("result"); //$NON-NLS-1$
+          result.setAttribute("os", APITestResults[i]); //$NON-NLS-1$
+          result.setAttribute("ws", APITestResults[i+1]); //$NON-NLS-1$
+          result.setAttribute("arch", APITestResults[i+2]); //$NON-NLS-1$
+          result.setTextContent(APITestResults[i+3]);
+          attachNode(document, tests, result, txt[5]);          
+        }
+        // extra space to close containing tag
+        test.appendChild(document.createTextNode(txt[4]));
+      }
+      attachNode(document, tests, test, txt[4]);
+    }
+
+    attachNode(document, build, tests, txt[3]);
+
+    if (dependencyURLs!=null && dependencyURLs.length>0) {
+  //  <dependencies>
+  //    <dependency>" + dependencyURL + "</dependency>
+      Element dependencies = document.createElement("dependencies"); //$NON-NLS-1$
+      for (int i = 0; i < dependencyURLs.length; i++)
+      {
+        elem = document.createElement("dependency"); //$NON-NLS-1$
+        elem.setTextContent(dependencyURLs[i]);
+        attachNode(document, dependencies, elem, txt[4]);
+      }
+      attachNode(document, build, dependencies, txt[3]);
+    }
+    
+    return entry;
+  }
+
+  //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
+  private void attachNode(Document document,Element entry,Element elem,String txt){
+    entry.appendChild(document.createTextNode(txt));
+    entry.appendChild(elem);
+  }
+
+  private static String getTimestamp() { // eg., 2006-04-10T20:40:08Z
+    return DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN) + "Z";  //$NON-NLS-1$
+  }
+
+  private void doVarSubs()
+  {
+    feedURL = varSub(feedURL);
+    buildURL = varSub(buildURL);
+
+    releaseNotesURL = varSub(releaseNotesURL);
+    updateManagerURL = varSub(updateManagerURL);
+    downloadsURL = varSub(downloadsURL);
+
+    JUnitTestURL = varSub(JUnitTestURL);          
+    performanceTestURL = varSub(performanceTestURL);
+    APITestURL = varSub(APITestURL);
+  }
+
+  public static Transformer createTransformer(String encoding) throws TransformerException
+  {
+    TransformerFactory transformerFactory = TransformerFactory.newInstance();
+
+    try
+    {
+      transformerFactory.setAttribute("indent-number", new Integer(2)); //$NON-NLS-1$
+    }
+    catch (IllegalArgumentException exception)
+    {
+    }
+
+    Transformer transformer = transformerFactory.newTransformer();
+
+    transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
+    transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
+
+    // Unless a width is set, there will be only line breaks but no indentation.
+    // The IBM JDK and the Sun JDK don't agree on the property name,
+    // so we set them both.
+    //
+    transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
+    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
+    if (encoding != null)
+    {
+      transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
+    }
+    return transformer;
+  }
+
+  /*
+   * variable substitution in URLs - eg., replace %%branch%% and %%buildID%% in buildURL
+   */
+  private String varSub(String urlstring)
+  {
+    if (!isNullString(urlstring) && urlstring.indexOf("%%")>=0) //$NON-NLS-1$
+    {
+      return urlstring.replaceAll(Messages.getString("RSSFeedAddEntryTask.BranchKeyword"), branch).replaceAll(Messages.getString("RSSFeedAddEntryTask.BuildIDKeyword"), buildID).replaceAll(Messages.getString("RSSFeedAddEntryTask.BuildAliasKeyword"), buildAlias); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+    return urlstring;
+  }
+
+  private static boolean isNullString(String str)
+  {
+    return RSSFeedUtil.isNullString(str);
+  }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedCreateFeedTask.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedCreateFeedTask.java
new file mode 100644
index 0000000..042c0cf
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedCreateFeedTask.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.generators.rss;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Date;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.DateUtils;
+
+import org.eclipse.releng.util.rss.Messages;
+import org.eclipse.releng.util.rss.RSSFeedUtil;
+
+//TODO: bug - can't run CreateFeed and AddEntry back when debug=2
+
+/**
+ * Parameters: 
+ *   debug - more output to console - eg., 0|1|2
+ *   
+ *   file - path to the XML file that will be created - eg., /path/to/file.to.create.xml
+ *   project - project's name, used to label the feed - eg., Eclipse, EMF, UML2
+ *   feedURL - URL of the feed where it will be published - eg., http://servername/path/to/feed.xml
+ * @author nickb
+ *
+ */
+public class RSSFeedCreateFeedTask extends Task {
+
+  private int debug = 0;
+
+  //$ANALYSIS-IGNORE codereview.java.rules.portability.RulePortabilityLineSeparators
+  private static final String NL="\n"; //$NON-NLS-1$
+  private static final String NS = ""; //$NON-NLS-1$
+  private static final String SP = " "; //$NON-NLS-1$
+  
+  //required fields
+  private File file;
+  private String project;
+  private String feedURL;
+
+  //optional
+  public void setDebug(int debug) { this.debug = debug; }
+
+  //required fields
+  public void setFile(String file) { 
+    if (isNullString(file))
+    { System.err.println(Messages.getString("RSSFeedCommon.FileError")); }  //$NON-NLS-1$
+    else
+    { this.file = new File(file); }
+  }
+  public void setProject(String project) { 
+    if (isNullString(project))
+    { System.err.println(Messages.getString("RSSFeedCommon.ProjectError")); }  //$NON-NLS-1$
+    else
+    { this.project = project; }
+  }
+  public void setFeedURL(String feedURL) { 
+    if (isNullString(feedURL))
+    { System.err.println(Messages.getString("RSSFeedCommon.FeedURLError")); }  //$NON-NLS-1$
+    else
+    { this.feedURL = feedURL; }
+  }
+
+  // The method executing the task
+  public void execute() throws BuildException {
+    if (debug>0) { 
+      System.out.println(Messages.getString("RSSFeedCreateFeedTask.Creating") + project + SP + Messages.getString("RSSFeedCommon.RSSFeedFile") + SP + file.toString() + ", " + Messages.getString("RSSFeedCommon.ToBePublishedAt") + feedURL); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+    }
+    writeFeedXML(createFeedXML(),file);
+    if (debug>1) { 
+      writeFeedXML(createFeedXML(),System.out);
+    }
+  }
+
+  private String createFeedXML() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("<?xml-stylesheet href=\"http://www.blogger.com/styles/atom.css\" type=\"text/css\"?>" + NL); //$NON-NLS-1$
+    sb.append("<feed xmlns=\"http://www.w3.org/2005/Atom\">" + NL); //$NON-NLS-1$
+    sb.append("  <title>" + project + SP + Messages.getString("RSSFeedCreateFeedTask.Builds") + "</title>" + NL); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    sb.append("  <link rel=\"self\" type=\"application/atom+xml\" href=\"" + (!isNullString(feedURL)?feedURL:NS) + "\"/>" + NL); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    sb.append("  <updated>" + getTimestamp() + "</updated>" + NL); //$NON-NLS-1$ //$NON-NLS-2$
+    sb.append("  <author>" + NL); //$NON-NLS-1$
+    sb.append("    <name>" + (!isNullString(project)?project + SP : NS) + Messages.getString("RSSFeedCreateFeedTask.BuildTeam") + "</name>" + NL); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+    sb.append("  </author>" + NL); //$NON-NLS-1$
+    sb.append("  <id>" + (!isNullString(feedURL)?feedURL:NS) + "</id>" + NL); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    sb.append("</feed>" + NL + NL); //$NON-NLS-1$
+    return sb.toString();
+  }
+
+  private void writeFeedXML(String feedXML,File file) {
+    try{
+      PrintWriter writer = new PrintWriter(new FileWriter(file));
+      writer.println(feedXML);
+      writer.flush();
+      writer.close();
+    } catch (IOException e){
+      System.out.println(Messages.getString("RSSFeedCreateFeedTask.UnableToWriteToFile")+file); //$NON-NLS-1$
+    }
+
+  }
+
+  private void writeFeedXML(String feedXML, PrintStream ps) {
+    PrintWriter writer = new PrintWriter(ps);
+    writer.println(feedXML);
+    writer.flush();
+    writer.close();
+  }
+
+  private String getTimestamp() { // eg., 2006-04-10T20:40:08Z
+    return DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN) + "Z";  //$NON-NLS-1$
+  }
+
+  private static boolean isNullString(String str)
+  {
+    return RSSFeedUtil.isNullString(str);
+  }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedGetPropertyTask.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedGetPropertyTask.java
new file mode 100644
index 0000000..b8f4855
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedGetPropertyTask.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.generators.rss;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+import org.eclipse.releng.util.rss.Messages;
+import org.eclipse.releng.util.rss.RSSFeedUtil;
+
+/**
+ * Parameters: 
+ *   debug - more output to console - eg., 0|1|2
+ *   
+ *   file - path to the XML file that will be read - eg., /path/to/file.to.read.xml
+ *   xpath - xpath string representing the object to read
+ * 
+ * @author nickb
+ *
+ */
+public class RSSFeedGetPropertyTask extends Task {
+
+  private int debug = 0;
+
+  //required fields
+  private File file;
+
+  private String xpath;
+
+  //optional
+  public void setDebug(int debug) { this.debug = debug; }
+
+  //required fields
+  public void setFile(String file) { 
+    if (isNullString(file))
+    { System.err.println(Messages.getString("RSSFeedCommon.FileError")); } //$NON-NLS-1$
+    else
+    { this.file = new File(file); } 
+  }
+  public void setXpath(String xpath) { 
+    if (isNullString(xpath))
+    { System.err.println(Messages.getString("RSSFeedCommon.XpathError")); } //$NON-NLS-1$
+    else
+    { this.xpath = xpath; } 
+  }
+
+  // The method executing the task
+  public void execute() throws BuildException {
+    RSSFeedUpdateEntryTask updater = new RSSFeedUpdateEntryTask();
+    updater.setFile(file.toString());
+    updater.setXpath(xpath);
+    updater.setDebug(debug);
+    updater.execute();
+  }
+
+  private static boolean isNullString(String str)
+  {
+    return RSSFeedUtil.isNullString(str);
+  }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedUpdateEntryTask.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedUpdateEntryTask.java
new file mode 100644
index 0000000..417b8bb
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/generators/rss/RSSFeedUpdateEntryTask.java
@@ -0,0 +1,338 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.generators.rss;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Date;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.DateUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.xml.sax.SAXException;
+
+import org.eclipse.releng.util.rss.Messages;
+import org.eclipse.releng.util.rss.RSSFeedUtil;
+
+/**
+ * Parameters: 
+ *   debug - more output to console - eg., 0|1|2
+ *   
+ *   file - path to the XML file that will be read - eg., /path/to/file.to.read.xml
+ *   xpath - xpath string representing the object to modify
+ *   replacement - string to use as replacement
+ * 
+ * @author nickb
+ *
+ */
+public class RSSFeedUpdateEntryTask extends Task {
+
+  private int debug = 0;
+
+  private static final String now = getTimestamp();
+
+  private static final XPath xp = XPathFactory.newInstance().newXPath();
+
+  private static final String NS = ""; //$NON-NLS-1$
+  private static final String SEP = "----"; //$NON-NLS-1$ 
+  private static final String SP = " "; //$NON-NLS-1$
+
+  //required fields
+  private File file;
+
+  private String xpath;
+  private String replacement;
+
+  private Transformer transformer = null;
+
+  private boolean isNodeFound = false;
+  private boolean isNodeChanged = false;
+  private Node foundNode = null;
+  
+  //optional
+  public void setDebug(int debug) { this.debug = debug; }
+
+  //required fields
+  public void setFile(String file) { 
+    if (isNullString(file))
+    { System.err.println(Messages.getString("RSSFeedCommon.FileError")); } //$NON-NLS-1$
+    else
+    { this.file = new File(file); } 
+  }
+  public void setXpath(String xpath) { 
+    if (isNullString(xpath))
+    { System.err.println(Messages.getString("RSSFeedCommon.XpathError")); } //$NON-NLS-1$
+    else
+    { this.xpath = xpath; } 
+  }
+
+  //optional - if null, display value found instead of changing it - see RSSFeedGetPropertyTask
+  public void setReplacement(String replacement) { this.replacement = replacement; } 
+
+  // The method executing the task
+  public void execute() throws BuildException {
+    if (debug>0) { 
+      System.out.println(Messages.getString("RSSFeedUpdateEntryTask.SearchingFor") + SP + xpath + (!isNullString(replacement)?", " + Messages.getString("RSSFeedUpdateEntryTask.ReplacingWith") + " '" + replacement + "'":NS)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+    }
+    updateFeedXML(file); // load previous
+  }
+
+  //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
+  private void updateFeedXML(File file){
+    if (file.exists()) {
+      DocumentBuilderFactory documentBuilderFactory=DocumentBuilderFactory.newInstance();
+      documentBuilderFactory.setNamespaceAware(true);
+      DocumentBuilder documentBuilder=null;
+      try {
+        documentBuilder=documentBuilderFactory.newDocumentBuilder();
+      }
+      catch (ParserConfigurationException e) {
+        e.printStackTrace();
+      }
+      Document document=null;
+      try {
+        document=documentBuilder.parse(file);
+      }
+      catch (SAXException e) {
+        e.printStackTrace();
+      }
+      catch (IOException e) {
+        e.printStackTrace();
+      }
+
+      try {
+        transformer = RSSFeedAddEntryTask.createTransformer("UTF-8"); //$NON-NLS-1$
+      } catch (TransformerException e) {
+        e.printStackTrace();
+      }
+
+      if (!isNullString(replacement)) {
+        setEntryNodeUpdate(document.getDocumentElement());
+      }
+      Node newNode=findAndReplace(document);
+      if (debug > 1 && newNode != null) {
+        try {
+          System.out.println(SEP);
+          transformer.transform(new DOMSource(newNode),new StreamResult(System.out));
+          System.out.println(SEP);
+        }
+        catch (TransformerException e) {
+          e.printStackTrace();
+        }
+      }
+      if (!isNullString(replacement) && newNode != null) {
+        try {
+          transformer.transform(new DOMSource(document),new StreamResult(new PrintStream(file)));
+        }
+        catch (FileNotFoundException e) {
+          e.printStackTrace();
+        }
+        catch (TransformerException e) {
+          e.printStackTrace();
+        }
+      }
+    }
+    else {
+      System.out.println(Messages.getString("RSSFeedCommon.RSSFeedFile") + SP + file.toString()+ " "+ Messages.getString("RSSFeedUpdateEntryTask.DoesNotExist")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+  }
+
+  // get/set the desired node
+  public Node getFoundNode()
+  {
+    return this.foundNode;
+  }
+  private void setFoundNode(Node foundNode)
+  {
+    this.foundNode = foundNode;
+  }
+
+  // has the desired node been found?
+  public boolean getNodeFound()
+  {
+    return this.isNodeFound;
+  }
+  private void setNodeFound(boolean isNodeFound)
+  {
+    this.isNodeFound = isNodeFound;
+  }
+
+  // has the desired node been changed?
+  public boolean getNodeChanged()
+  {
+    return this.isNodeChanged;
+  }
+  private void setNodeChanged(boolean isNodeChanged)
+  {
+    this.isNodeChanged = isNodeChanged;
+  }
+
+  /**
+   * Modify an entry:
+   * 
+   *   <entry>
+   *      <title/>
+   *      <link href=""/>
+   *      <id/>
+   *      <updated/>
+   *      <summary>
+   *       ...
+   *     </summary>
+   *   </entry>
+   */
+  private Node findAndReplace(Document document) {
+    Node parentEntryNode = null;
+    Node aNode = null;
+    if (debug==0) { System.out.print(xpath + (isNullString(replacement)?" = ":" :: ")); } //$NON-NLS-1$ //$NON-NLS-2$
+    NodeList nodelist = getNodeList(document, xpath);
+    // Process the elements in the nodelist
+    if (nodelist != null && nodelist.getLength()>0) {
+      for (int i=0; i<nodelist.getLength(); i++) {
+        Node node = (Node)nodelist.item(i);
+        switch (node.getNodeType())
+        {
+          case Node.ATTRIBUTE_NODE :
+            aNode = (Attr)nodelist.item(i);
+            if (debug>0) { System.out.print(Messages.getString("RSSFeedUpdateEntryTask.DebugFoundAttribute")); }  //$NON-NLS-1$
+            break;
+
+          case Node.ELEMENT_NODE :
+            aNode = (Element)nodelist.item(i);
+            if (debug>0) { System.out.print(Messages.getString("RSSFeedUpdateEntryTask.DebugFoundElement")); } //$NON-NLS-1$
+            break;
+
+          case Node.TEXT_NODE :
+            aNode = (Text)nodelist.item(i);
+            if (debug>0) { System.out.print(Messages.getString("RSSFeedUpdateEntryTask.DebugFoundText")); } //$NON-NLS-1$
+            break;
+
+          default:
+            aNode = null;
+          break;
+        }
+        if (aNode != null) {
+          setFoundNode(aNode);
+          setNodeFound(true);
+          System.out.print((debug>0?aNode.getNodeName() + " = ":NS) + aNode.getNodeValue()); //$NON-NLS-1$ //$NON-NLS-2$
+          if (!isNullString(replacement)) { aNode.setTextContent(replacement); }
+          System.out.println(isNullString(replacement)?NS:" => " + replacement); //$NON-NLS-1$ //$NON-NLS-2$
+          if (debug>0) { 
+            try
+            {
+              // write to console
+              System.out.println(SEP); //$NON-NLS-1$
+              transformer.transform(new DOMSource(getParentNode(document,aNode,null,NS)), new StreamResult(System.out));  //$NON-NLS-1$
+              System.out.println(SEP); //$NON-NLS-1$
+            }
+            catch (TransformerException e)
+            {
+              e.printStackTrace();
+            }
+          }
+          if (!isNullString(replacement)) { 
+            parentEntryNode = getParentNode(document, aNode, "entry", NS); //$NON-NLS-1$ //$NON-NLS-2$
+            setEntryNodeUpdate(parentEntryNode); 
+          }
+        }
+      }
+    } else {
+      System.out.println(Messages.getString("RSSFeedUpdateEntryTask.XpathNodeNotFound")); //$NON-NLS-1$
+    }
+    return parentEntryNode;
+  }
+
+  private Node getParentNode(Document document, Node nodeIn, String target, String indent)
+  {
+    Node node = nodeIn;
+    if (node.getNodeType() != Node.ELEMENT_NODE) {
+      if (debug>1) { System.out.println(indent + Messages.getString("RSSFeedUpdateEntryTask.DebugGotATNode") + node.getNodeName()); } //$NON-NLS-1$
+      // get the element for the attrib/text node
+      NodeList nodelist = getNodeList(document, xpath.substring(0, xpath.lastIndexOf("/")));
+      if (nodelist !=null && nodelist.getLength()>0)
+      {
+        for (int i=0; i<nodelist.getLength(); i++) {
+          node = (Node)nodelist.item(i);
+          break;
+        }
+      }
+    }
+    if (debug>1) { System.out.println(indent + Messages.getString("RSSFeedUpdateEntryTask.DebugGotENode") + node.getNodeName() + " (" + node.getNodeType() + ")"); } //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    if (!isNullString(target) && !node.getNodeName().equals(target)) 
+    {
+      node = getParentNode(document, node.getParentNode(), target, indent + "_ "); //$NON-NLS-1$
+    }
+    return node;
+  }
+
+  private NodeList getNodeList(Document document, String xpath)
+  {
+    NodeList nodelist = null;
+    try
+    {
+      xp.reset();
+      Object o = xp.evaluate(xpath, document, XPathConstants.NODESET);
+      if (o instanceof NodeList)
+      {
+        nodelist = (NodeList)o;
+      }
+    }
+    catch (XPathExpressionException e)
+    {
+      e.printStackTrace();
+    }
+    return nodelist;
+  }
+
+  //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
+  private void setEntryNodeUpdate(Node parentEntryNode){
+    for (Node child=parentEntryNode.getFirstChild(); child != null; child=child.getNextSibling()) {
+      if ("updated".equals(child.getLocalName())) { //$NON-NLS-1$
+        if (debug > 0) {
+          System.out.println(Messages.getString("RSSFeedCommon.Set") + " <" + child.getLocalName()+ ">"+ now+ "</"+ child.getLocalName()+ ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+        }
+        ((Element)child).setTextContent(now);
+        setNodeChanged(true);
+        break;
+      }
+    }
+  }
+
+
+  private static String getTimestamp() { // eg., 2006-04-10T20:40:08Z
+    return DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN) + "Z";  //$NON-NLS-1$
+  }
+
+  private static boolean isNullString(String str)
+  {
+    return RSSFeedUtil.isNullString(str);
+  }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/services/rss/RSSFeedPublisherTask.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/services/rss/RSSFeedPublisherTask.java
new file mode 100644
index 0000000..b26e7f9
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/services/rss/RSSFeedPublisherTask.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.services.rss;
+
+//TODO: enable support for running task on Windows (problems with ssh, scp, cvs)
+//TODO: enable support for connecting to Windows server? (`mkdir -p` not supported)
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+import org.eclipse.releng.util.rss.Messages;
+import org.eclipse.releng.util.rss.RSSFeedUtil;
+
+/**
+ * Parameters: 
+ *   debug - more output to console - eg., 0|1|2
+ *   
+ *   file - path to the XML file that will be published - eg., /path/to/file.to.publish.xml
+ *   
+ *   cvsExec - path to the executable for cvs, eg., /usr/bin/cvs
+ *   cvsRoot - cvs root used to commit the file - eg., username@cvsserver:/cvsroot/path 
+ *   cvsPath - cvs module to update - eg., project/news/ (into which builds.xml would go)
+ *   cvsTemp - path to the temp folder to use for cvs checkout
+ * 
+ *   scpExec - path to the executable for scp, eg., /usr/bin/scp
+ *   scpTarget - scp target path for publishing the file - eg., username@server:/path/to/target/file.xml
+ *   
+ * Optionally, if the target folder might not exist, you can use ssh to create it before scp'ing
+ *   sshExec - path to the executable for ssh, eg., /usr/bin/ssh
+ *    
+ * @author nickb
+ *
+ */
+public class RSSFeedPublisherTask extends Task {
+
+  private int debug = 0;
+
+  private static final String CL = ":"; //$NON-NLS-1$
+  private static final String FS = File.separator;
+  private static final String SP = " "; //$NON-NLS-1$
+
+  // default values for optional fields 
+  private static final String DEFAULT_CVSTemp = "/tmp/tmp-RSSFeedPublisherTask"; //$NON-NLS-1$
+  private static final String DEFAULT_CVSExec = "cvs"; //$NON-NLS-1$
+  private static final String DEFAULT_SCPExec = "scp"; //$NON-NLS-1$
+
+  private static final RSSFeedUtil util = new RSSFeedUtil();
+
+  //required fields
+  private File file;
+
+  // required if doing CVS
+  private String CVSExec;
+  private String CVSRoot;
+  private String CVSPath;
+  private String CVSTemp;
+
+  // required if doing SCP
+  private String SCPExec;
+  private String SCPTarget;
+
+  // required if doing SCP and target dir may not already exist
+  private String SSHExec;
+
+  //optional
+  public void setDebug(int debug) { this.debug = debug; }
+
+  //required
+  public void setFile(String file) { 
+    if (!isNullString(file)) { this.file = new File(file); }     
+  }
+
+  //required for CVS commit (with default)
+  public void setCVSExec(String CVSExec) { 
+    if (!isNullString(CVSExec)) { 
+      this.CVSExec = CVSExec; 
+    } else { 
+      this.CVSExec = DEFAULT_CVSExec; 
+    }
+  }
+
+  //required for CVS commit
+  public void setCVSRoot(String CVSRoot) { this.CVSRoot = CVSRoot; }
+  public void setCVSPath(String CVSPath) { this.CVSPath = CVSPath; }
+
+  //required for CVS commit (with default)
+  public void setCVSTemp(String CVSTemp) { 
+    if (!isNullString(CVSTemp)) { 
+      this.CVSTemp = CVSTemp; 
+    } else { 
+      this.CVSTemp = DEFAULT_CVSTemp; 
+    }
+  }
+
+  //required for CVS commit (with default)
+  public void setSCPExec(String SCPExec) {
+    if (!isNullString(SCPExec)) { 
+      this.SCPExec = SCPExec; 
+    } else { 
+      this.SCPExec = DEFAULT_SCPExec; 
+    }
+  }
+  public void setSCPTarget(String SCPTarget) { this.SCPTarget = SCPTarget; }
+
+  // required if doing SCP and target dir may not already exist (with default, not assigned)
+  public void setSSHExec(String SSHExec) { 
+      this.SSHExec = SSHExec; 
+  }
+
+  // The method executing the task
+  public void execute() throws BuildException {
+    
+    if (file==null || !file.exists() || !file.isFile()) {
+      System.err.println(Messages.getString("RSSFeedPublisherTask.ErrorInvalidFile") + CL + SP + file + "!"); //$NON-NLS-1$ //$NON-NLS-2$
+    } else {
+      if (debug>0) { System.out.println(Messages.getString("RSSFeedPublisherTask.Publish") + SP + file); } //$NON-NLS-1$
+      if ((!isNullString(CVSRoot) && !isNullString(CVSPath)) || !isNullString(SCPTarget)) {
+        if ((!isNullString(CVSRoot) && !isNullString(CVSPath))) {
+          commitFeedToCVS();
+        }
+        if (!isNullString(SCPTarget)) {
+          publishFeedWithSCP();
+        }
+      } else {
+        System.err.println(Messages.getString("RSSFeedPublisherTask.ErrorNothingToDo")); //$NON-NLS-1$
+      }
+    }
+  }
+
+  private void commitFeedToCVS()
+  {
+    if (debug>1) {
+      System.out.println(Messages.getString("RSSFeedPublisherTask.UsingCVSRoot") + SP + CVSRoot); //$NON-NLS-1$
+      System.out.println(Messages.getString("RSSFeedPublisherTask.UsingCVSPath") + SP + CVSPath); //$NON-NLS-1$
+    }
+//  <!-- 3. get filename (eg., builds.xml) from file (which could include a path, eg. ./data/news/builds.xml) -->
+//  <pathconvert property="filename"><path path="${file}"/><mapper type="flatten"/></pathconvert>
+    String filename = file.getName();
+
+//  <!-- 4. create target temp folder & check out existing version from CVS -->
+//  <mkdir dir="${cvsTemp}"/>
+    File CVSTempDir = new File(CVSTemp);
+    if (CVSTempDir.isFile()) { // if dir exists as a file, we need a new tmp folder name 
+      CVSTemp += ".tmp";  //$NON-NLS-1$
+      CVSTempDir = new File(CVSTemp);
+    } 
+    if (CVSTempDir.isDirectory()) {
+      if (!CVSTempDir.delete()) {
+        System.err.println(Messages.getString("RSSFeedPublisherTask.ErrorCouldNotDeleteTempFolder") + SP + CVSTempDir); //$NON-NLS-1$
+      }
+    }
+    CVSTempDir.mkdir();
+    if (debug>1) {
+      System.out.println(Messages.getString("RSSFeedPublisherTask.UsingCVSTemp") + SP + CVSTempDir); //$NON-NLS-1$
+    }
+
+//  <exec executable="${cvsExec}" dir="${cvsTemp}"><arg line="-d ${cvsRoot} co -d checkoutDir ${cvsPath}"/></exec>
+    runCVSExecTask("co -d checkoutDir" + SP + CVSPath, CVSTemp); //$NON-NLS-1$
+
+//  <!-- 5. check if the file already exists in CVS to see if we need to add it -->
+//  <available file="${cvsTemp}/checkoutDir/${filename}" type="file" property="fileInCVS"/>
+    File destFile = new File(CVSTemp + FS + "checkoutDir" + FS + filename); //$NON-NLS-1$
+    boolean fileInCVS = destFile.isFile();
+
+//  <!-- 6. overwrite CVS copy with new version; or if new, copy file to destination for add then check-in -->
+//  <copy file="../${file}" overwrite="true" todir="${cvsTemp}/checkoutDir"/>
+    try
+    {
+      RSSFeedUtil.transferData(new FileInputStream(file),new FileOutputStream(destFile));
+    }
+    catch (FileNotFoundException e)
+    {
+      e.printStackTrace();
+    }
+    catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+
+//  <!-- 7. add to CVS (if new) -->
+//  <antcall target="addFileToCVS"/>
+//  <!-- 7. Add file to CVS (if file is new) -->
+//  <target name="addFileToCVS" depends="init" unless="fileInCVS" description="Add file to CVS (if file is new)">
+//  <exec executable="${cvsExec}" dir="${cvsTemp}/checkoutDir"><arg line="-d ${cvsRoot} add ${filename}"/></exec>
+//  </target>
+    if (!fileInCVS) { 
+      runCVSExecTask("add " + filename, CVSTemp + FS + "checkoutDir");  //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+//  <!-- 8. check in file -->
+//  <exec executable="${cvsExec}" dir="${cvsTemp}/checkoutDir"><arg line="-d ${cvsRoot} ci -m '' ${filename}"/></exec>
+    runCVSExecTask("ci -m '' " + filename, CVSTemp + FS + "checkoutDir"); //$NON-NLS-1$ //$NON-NLS-2$
+  }
+
+  private void publishFeedWithSCP()
+  {
+    if (debug>1) {
+      System.out.println(Messages.getString("RSSFeedPublisherTask.PublishToSCPTarget") + SP + SCPTarget); //$NON-NLS-1$
+    }
+
+//    <exec executable="${sshExec}"><arg line="${sshMakeDirCommand}"/></exec>
+    if (!isNullString(SSHExec) && SCPTarget.indexOf(CL)>0) {
+      String userAtHost = SCPTarget.substring(0, SCPTarget.indexOf(CL));
+      String targetPath = SCPTarget.substring(SCPTarget.indexOf(CL)+1,SCPTarget.lastIndexOf(FS));
+      util.runExecTask(SSHExec, userAtHost + " \"mkdir -p" + SP + targetPath + "\"", null); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+    
+//    <exec executable="${scpExec}" dir="../"><arg line="${file} ${scpTarget}"/></exec>
+    util.runExecTask(SCPExec, file.toString() + SP + SCPTarget, null);
+  }
+
+  private void runCVSExecTask(String task, String dir)
+  {
+    util.runExecTask(CVSExec, "-d " + CVSRoot + " -q " + task, dir); //$NON-NLS-1$ //$NON-NLS-2$
+  }
+  
+  private static boolean isNullString(String str)
+  {
+    return RSSFeedUtil.isNullString(str);
+  }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/services/rss/RSSFeedWatcherTask.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/services/rss/RSSFeedWatcherTask.java
new file mode 100644
index 0000000..085d46c
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/services/rss/RSSFeedWatcherTask.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.services.rss;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.ExecTask;
+
+import org.eclipse.releng.generators.rss.RSSFeedUpdateEntryTask;
+import org.eclipse.releng.util.rss.Messages;
+import org.eclipse.releng.util.rss.RSSFeedUtil;
+
+/**
+ * Parameters: 
+ *   debug - more output to console - eg., 0|1|2
+ *   
+ *   file - path to the XML file that will be published - eg., /path/to/file.to.publish.xml
+ *   feedURL - URL of the feed where it will be published - eg., http://servername/path/to/feed.xml
+ *   
+ *   feedWatchActions - semi-comma-separated list of triplets: 
+ *   	(Xpath to watch for); (what to execute if condition is met); (commandline args to the executable)...
+ *   	eg., to watch for ANY change in the feed and respond by sending email
+ *   		/*[name() = 'feed']/*[name() = 'updated']/text(); sendEmailAlert.sh; null
+ *      eg., to watch for ANY changes in the current build
+ *   		//*[name() = 'entry'][1]/*[name() = 'updated']/text(); sendEmailAlert.sh; null
+ *      eg., to watch for changes in the current build's performance test results on linux-gtk
+ *      	//*[name() = 'entry'][1]/*[name() = 'summary']/*[@type = 'performance'][1]/*[name() = 'results'][@os = 'linux'][@ws = 'gtk']/text(); sendEmailAlert.sh; null
+ *    
+ * @author nickb
+ *
+ */
+public class RSSFeedWatcherTask extends Task {
+
+  private int debug = 0;
+
+  private static final String CL = ":"; //$NON-NLS-1$
+  private static final String DOT = "."; //$NON-NLS-1$
+  private static final String NS = ""; //$NON-NLS-1$
+  private static final String SP = " "; //$NON-NLS-1$
+  
+  private static final String splitter = "[;\t\r\n]+"; //$NON-NLS-1$ 
+
+  private static final String feedWatchActionError = "feedWatchAction.Error"; //$NON-NLS-1$
+  private static final String feedWatchActionOuput = "feedWatchAction.Output"; //$NON-NLS-1$
+  private static final String feedWatchActionResult = "feedWatchAction.Result"; //$NON-NLS-1$
+  private static final String feedWatchActionNewValue = "feedWatchAction.NewValue"; //$NON-NLS-1$
+  private static final String feedWatchActionOldValue = "feedWatchAction.OldValue"; //$NON-NLS-1$
+  private static final String feedWatchActionTheValue = "feedWatchAction.TheValue"; //$NON-NLS-1$
+
+  private static final RSSFeedUtil util = new RSSFeedUtil();
+
+  //required fields
+  private File file;
+  private File tmpFile;
+  private String feedURL;
+  private String[] feedWatchActions = new String[] {};
+
+  //optional
+  public void setDebug(int debug) { this.debug = debug; }
+
+  //required
+  public void setFile(String file) { 
+    if (!isNullString(file)) { 
+      this.file = new File(file); 
+      this.tmpFile = new File(file + ".tmp");  //$NON-NLS-1$
+    }     
+  }
+  public void setFeedURL(String feedURL) { 
+    if (isNullString(feedURL))
+    { System.err.println(Messages.getString("RSSFeedCommon.FeedURLError")); }  //$NON-NLS-1$
+    else
+    { this.feedURL = feedURL; }
+  }
+  public void setFeedWatchActions(String feedWatchActions) {
+    int missingActions = 0;
+    if (!isNullString(feedWatchActions)) { 
+      this.feedWatchActions = feedWatchActions.split(splitter); 
+      missingActions = this.feedWatchActions.length % 3; if (missingActions > 0) { missingActions = 3 - missingActions; }
+    }
+    if (missingActions > 0) {
+      for (int i = 0; i < missingActions; i++)
+      {
+        System.out.println((i==0 && missingActions==2 ? Messages.getString("RSSFeedWatcherTask.WarningNoScriptAction") : Messages.getString("RSSFeedWatcherTask.WarningNoCommandlineParams")) + SP + feedWatchActions ); //$NON-NLS-1$ //$NON-NLS-2$
+        feedWatchActions += "; null"; //$NON-NLS-1$
+      }
+      this.feedWatchActions = feedWatchActions.split(splitter);
+    }
+  }
+
+  // The method executing the task
+  public void execute() throws BuildException {
+    if (debug>0) { util.setDebug(debug); }
+    if (file==null || !file.exists() || !file.isFile()) {
+      // if there's no local copy of the feed, get a copy, then exit with instructions 
+      downloadFeed(file,debug>=0);
+      System.out.println(Messages.getString("RSSFeedWatcherTask.PleaseRunThisTaskLater") + SP + file); //$NON-NLS-1$
+      System.out.println(Messages.getString("RSSFeedWatcherTask.ToTheLatestVersion") + SP + feedURL); //$NON-NLS-1$
+    } else {
+      if (feedWatchActions==null || feedWatchActions.length<1) {
+        System.err.println(Messages.getString("RSSFeedWatcherTask.ErrorNoWatchActions")); //$NON-NLS-1$
+      } else {
+        checkFeed();
+      }
+    }
+  }
+
+  private void checkFeed() {
+    if (file.isDirectory()) {
+      System.err.println(Messages.getString("RSSFeedWatcherTask.ErrorDestinationFileIsADirectory")); //$NON-NLS-1$
+    } else {
+      downloadFeed(tmpFile,debug>0);
+    } 
+
+    if (tmpFile.isFile()) {
+      if (debug>0) { System.out.println(Messages.getString("RSSFeedWatcherTask.Compare") + SP + file + Messages.getString("RSSFeedWatcherTask.with") + tmpFile + CL); } //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+      RSSFeedUpdateEntryTask oldFeedWatcher = null;
+      RSSFeedUpdateEntryTask newFeedWatcher = null;
+      int j=0;
+      
+      for (int i = 0; i < feedWatchActions.length; i+=3)
+      {
+        String xpath = feedWatchActions[i].trim();
+        String action = feedWatchActions[i+1].trim();
+        String commandline = feedWatchActions[i+2].trim();
+
+        oldFeedWatcher = new RSSFeedUpdateEntryTask();
+        oldFeedWatcher.setFile(file.toString());
+        if (debug>0) { oldFeedWatcher.setDebug(debug); }
+        oldFeedWatcher.setXpath(xpath);
+        oldFeedWatcher.execute();
+
+        if (oldFeedWatcher.getFoundNode() != null) {
+          newFeedWatcher = new RSSFeedUpdateEntryTask();
+          newFeedWatcher.setFile(tmpFile.toString());
+          if (debug>0) { newFeedWatcher.setDebug(debug); }
+          newFeedWatcher.setXpath(xpath);
+          newFeedWatcher.execute();
+
+          String oldContent = oldFeedWatcher.getFoundNode().getTextContent();
+          String newContent = newFeedWatcher.getFoundNode().getTextContent();
+
+          if (debug>1) {
+            System.out.println(Messages.getString("RSSFeedWatcherTask.GotOldNodeContents") + CL + SP + oldContent); //$NON-NLS-1$
+            System.out.println(Messages.getString("RSSFeedWatcherTask.GotNewNodeContents") + CL + SP + newContent); //$NON-NLS-1$
+          }
+
+          if (!"null".equals(action)) { //$NON-NLS-1$
+            commandline = 
+              (debug>0?"-debug " + debug + SP:NS) + ("null".equals(commandline)?NS:commandline) + //$NON-NLS-1$ //$NON-NLS-2$ 
+              " -feedURL " + feedURL + //$NON-NLS-1$  
+              " -xpath \"" + xpath + "\"" + //$NON-NLS-1$ //$NON-NLS-2$
+              " -oldvalue \"" + oldContent + "\"" + //$NON-NLS-1$ //$NON-NLS-2$
+              " -newvalue \"" + newContent + "\""; //$NON-NLS-1$ //$NON-NLS-2$
+          }
+
+          // store actual value - either the changed value or the original value (if unchanged)
+          this.getProject().setProperty(feedWatchActionTheValue + DOT + j,!isNullString(newContent)?newContent:oldContent);
+
+          if (newFeedWatcher.getFoundNode() == null || // changed from exists to not exists, or 
+              !oldContent.equals(newContent) // node has changed
+          ) {
+            // collect property from newNode and pass it to THIS task so that the local ant script can see it 
+            if (!isNullString(oldContent)) { this.getProject().setProperty(feedWatchActionOldValue + DOT + j,oldContent); }
+            if (!isNullString(newContent)) { this.getProject().setProperty(feedWatchActionNewValue + DOT + j,newContent); }
+            
+            if (!"null".equals(action)) { //$NON-NLS-1$
+              System.out.println(Messages.getString("RSSFeedWatcherTask.RunExecTask") + CL + SP + action + SP + commandline); //$NON-NLS-1$ 
+              ExecTask exec = util.runExecTask((new File(action)).getAbsolutePath(), commandline, null);
+
+              // collect properties from exec task and pass them to THIS task so that the local ant script can see them 
+              String out = null;
+              
+              out = exec.getProject().getProperty(RSSFeedUtil.RUN_EXEC_TASK_ERROR);
+              if (!isNullString(out)) { this.getProject().setProperty(feedWatchActionError + DOT + j, out); }
+              
+              out = exec.getProject().getProperty(RSSFeedUtil.RUN_EXEC_TASK_RESULT);
+              if (!isNullString(out)) { this.getProject().setProperty(feedWatchActionOuput + DOT + j, out); }
+              
+              out = exec.getProject().getProperty(RSSFeedUtil.RUN_EXEC_TASK_RESULT);
+              if (!RSSFeedUtil.EXPECTED_RESULT.equals(out)) { this.getProject().setProperty(feedWatchActionResult + DOT + j, out); }
+            } 
+          } else {
+            System.out.println(Messages.getString("RSSFeedWatcherTask.NodeUnchanged")); //$NON-NLS-1$
+          }
+        } else {
+          System.out.println(Messages.getString("RSSFeedWatcherTask.NodeNotFound")); //$NON-NLS-1$
+        }
+        j++;
+      }
+
+      try
+      {
+        RSSFeedUtil.transferData(new FileInputStream(tmpFile), new FileOutputStream(file));
+        tmpFile.deleteOnExit();
+      }
+      catch (FileNotFoundException e)
+      {
+        e.printStackTrace();
+      }
+      catch (IOException e)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  private void downloadFeed(File destFile, boolean verbose) 
+  {
+    try
+    {
+      if (verbose) {
+        System.out.println(Messages.getString("RSSFeedWatcherTask.Download") + CL + SP + feedURL); //$NON-NLS-1$
+        System.out.println(Messages.getString("RSSFeedWatcherTask.To") + CL + SP + destFile + SP); //$NON-NLS-1$
+      }
+      RSSFeedUtil.transferData((new URL(feedURL)).openStream(), new FileOutputStream(destFile));
+      if (verbose) {
+        System.out.println(Messages.getString("RSSFeedWatcherTask.Done")); //$NON-NLS-1$
+        System.out.println(SP);
+      }
+    }
+    catch (MalformedURLException e)
+    {
+      e.printStackTrace();
+    }
+    catch (FileNotFoundException e)
+    {
+      e.printStackTrace();
+    }
+    catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  private static boolean isNullString(String str)
+  {
+    return RSSFeedUtil.isNullString(str);
+  }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/Messages.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/Messages.java
new file mode 100644
index 0000000..bd50cc7
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/Messages.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id$
+ * /
+ *******************************************************************************/
+package org.eclipse.releng.util.rss;
+
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+
+public class Messages
+{
+  private static final String BUNDLE_NAME = "org.eclipse.releng.util.rss.messages"; //$NON-NLS-1$
+
+  private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+
+  private Messages()
+  {
+  }
+
+  public static String getString(String key)
+  {
+    try
+    {
+      return RESOURCE_BUNDLE.getString(key);
+    }
+    catch (MissingResourceException e)
+    {
+      return '!' + key + '!';
+    }
+  }
+}
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/RSSFeedUtil.java b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/RSSFeedUtil.java
new file mode 100644
index 0000000..f933f76
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/RSSFeedUtil.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.releng.util.rss;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.types.Commandline.Argument;
+
+/**
+ *
+ *    Helper methods
+ *    
+ * @author nickb
+ *
+ */
+public class RSSFeedUtil {
+
+  private static final String SP = " "; //$NON-NLS-1$
+  private static final String CL = ":"; //$NON-NLS-1$
+  public static final String EXPECTED_RESULT = "0"; //$NON-NLS-1$
+
+  public static final String RUN_EXEC_TASK_ERROR = "runExecTask.Error"; //$NON-NLS-1$
+  public static final String RUN_EXEC_TASK_OUTPUT = "runExecTask.Output"; //$NON-NLS-1$
+  public static final String RUN_EXEC_TASK_RESULT = "runExecTask.Result"; //$NON-NLS-1$
+
+  private int debug = 0;
+
+  /**
+   * A buffer.
+   */
+  private static byte[] buffer = new byte [8192];
+
+  public ExecTask runExecTask(String executable, String commandline, String dir)
+  {
+    if (dir==null) {
+      dir = ".";  //$NON-NLS-1$
+    }
+
+    ExecTask exec = new ExecTask();
+    exec.setExecutable(executable);
+    exec.setResolveExecutable(true);
+    exec.setDir((new File(dir)).getAbsoluteFile());
+    Project project = new Project(); project.setName(executable);
+    exec.setProject(project);
+    exec.setFailIfExecutionFails(true);
+    exec.setFailonerror(true);
+    exec.setErrorProperty(RUN_EXEC_TASK_ERROR);
+    exec.setOutputproperty(RUN_EXEC_TASK_OUTPUT);
+    exec.setResultProperty(RUN_EXEC_TASK_RESULT);
+    exec.setLogError(true);
+
+    if (commandline != null || "".equals(commandline)) { //$NON-NLS-1$
+      Argument execArg = exec.createArg();
+      execArg.setLine(commandline);
+    }
+    try
+    {
+      if (debug>0) { 
+        System.out.println(Messages.getString("RSSFeedPublisherTask.Execute") + SP + executable + (commandline==null?"":SP + commandline)); //$NON-NLS-1$ //$NON-NLS-2$
+      }
+      exec.execute();
+      handleExecTaskReturn(project);
+    }
+    catch (Exception e)
+    {
+      handleExecTaskReturn(project);
+      System.err.println(Messages.getString("RSSFeedPublisherTask.ForProject") + SP + project.getName() + CL); //$NON-NLS-1$
+      e.printStackTrace();
+    }
+    
+    return exec;
+
+  }
+
+  private void handleExecTaskReturn(Project project)
+  {
+    String out = null;
+    
+    out = project.getProperty(RUN_EXEC_TASK_RESULT);
+    if (debug>1) {
+      if (!isNullString(out) && !EXPECTED_RESULT.equals(out)) { 
+        System.err.println(Messages.getString("RSSFeedPublisherTask.Result") + SP + out); //$NON-NLS-1$
+      }
+    }
+    
+    out = project.getProperty(RUN_EXEC_TASK_OUTPUT);
+    if (!isNullString(out)) { 
+      System.out.println(out);
+    }
+    
+    out = project.getProperty(RUN_EXEC_TASK_ERROR);
+    if (!isNullString(out)) {
+      if (debug>1 && out.equals(Messages.getString("RSSFeedPublisherTask.CVSWarning"))) { //$NON-NLS-1$
+        System.out.println(out);
+      } else if (!out.equals(Messages.getString("RSSFeedPublisherTask.CVSWarning"))) { //$NON-NLS-1$
+        System.err.println(Messages.getString("RSSFeedPublisherTask.Error") + SP + out); //$NON-NLS-1$
+      }
+    }
+  }
+
+  /**
+   * Copies all bytes in the given source stream to the given destination
+   * stream. Neither streams are closed. 
+   * 
+   * From: org.eclipse.emf/tests/org.eclipse.emf.test.build/src/org/eclipse/emf/test/build/FileTool.java,v 1.2
+   * 
+   * @param source
+   *            the given source stream
+   * @param destination
+   *            the given destination stream
+   */
+  public static void transferData(InputStream source, OutputStream destination) throws IOException
+  {
+    int bytesRead = 0;
+    while (bytesRead != -1)
+    {
+      bytesRead = source.read(buffer, 0, buffer.length);
+      if (bytesRead != -1)
+      {
+        destination.write(buffer, 0, bytesRead);
+      }
+    }
+  }
+
+  public static boolean isNullString(String str)
+  {
+    return str==null||"".equals(str); //$NON-NLS-1$
+  }
+
+  public void setDebug(int debug)
+  {
+    this.debug = debug;
+  }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/messages.properties b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/messages.properties
new file mode 100644
index 0000000..7a57db3
--- /dev/null
+++ b/bundles/org.eclipse.build.tools/src_rss/org/eclipse/releng/util/rss/messages.properties
@@ -0,0 +1,80 @@
+###############################################################################
+# 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
+###############################################################################
+RSSFeedCommon.FileError=Error: no output file specified for feed, eg., ./feed.xml\!
+RSSFeedWatcherTask.with=\ with 
+RSSFeedWatcherTask.To=To
+RSSFeedWatcherTask.Done=Done.
+RSSFeedCommon.ProjectError=Error: no project name specified, eg., EMF or WTP\!
+RSSFeedCommon.FeedURLError=Error: no feedURL specified, eg., http://server/path/to/feed.xml\!
+RSSFeedCommon.RSSFeedFile=RSS Feed file 
+RSSFeedCommon.ToBePublishedAt=to be published at: 
+RSSFeedCommon.Set=Set
+RSSFeedCommon.XpathError=Error: no xpath string to search for, eg., /*/*[name() = 'entry'][1]/*[name() = 'updated']/text()
+
+RSSFeedCreateFeedTask.Creating=Creating 
+RSSFeedCreateFeedTask.Builds=Builds
+RSSFeedCreateFeedTask.BuildTeam=Build Team
+RSSFeedCreateFeedTask.UnableToWriteToFile=Unable to write to file 
+
+RSSFeedAddEntryTask.BranchError=Warning: no branch specified, eg., 3.2.0\!
+RSSFeedAddEntryTask.BuildIDError=Warning: no buildID specified, eg., S200506071234\!
+RSSFeedAddEntryTask.BuildURLError=Warning: no buildURL specified, eg., http://server/path/to/project/downloads/drops/branch/buildID/\!
+RSSFeedAddEntryTask.AddingEntryTo=Adding entry to 
+
+RSSFeedAddEntryTask.DoesNotExist=does not exist. Creating ...
+RSSFeedAddEntryTask.AnnouncePrefix=[announce] 
+RSSFeedAddEntryTask.IsAvailable=is available
+RSSFeedAddEntryTask.InProperty=in property
+RSSFeedAddEntryTask.AttachNew=Attach new
+RSSFeedAddEntryTask.BranchKeyword=%%branch%%
+RSSFeedAddEntryTask.BuildIDKeyword=%%buildID%%
+RSSFeedAddEntryTask.BuildAliasKeyword=%%buildAlias%%
+RSSFeedAddEntryTask.WrongNumberOfVariables=Wrong number of variables
+RSSFeedAddEntryTask.MustBeMultipleOf5=(must be multiple of 5)
+RSSFeedAddEntryTask.MustBeMultipleOf4=(must be multiple of 4)
+
+RSSFeedUpdateEntryTask.SearchingFor=Searching for
+RSSFeedUpdateEntryTask.ReplacingWith=replacing with
+RSSFeedUpdateEntryTask.DoesNotExist=does not exist. Nothing to do\!
+RSSFeedUpdateEntryTask.DebugFoundAttribute=Found Attribute: 
+RSSFeedUpdateEntryTask.DebugFoundElement=Found Element: 
+RSSFeedUpdateEntryTask.DebugFoundText=Found Text: 
+RSSFeedUpdateEntryTask.XpathNodeNotFound=Xpath Node Not Found\!
+RSSFeedUpdateEntryTask.DebugGotATNode=Got A/T Node: 
+RSSFeedUpdateEntryTask.DebugGotENode=Got E Node: 
+
+RSSFeedPublisherTask.ErrorInvalidFile=Error: invalid feed file specified
+RSSFeedPublisherTask.Error=Error:
+RSSFeedPublisherTask.Publish=Publish
+RSSFeedPublisherTask.Execute=Execute:
+RSSFeedPublisherTask.Result=Result:
+RSSFeedPublisherTask.ForProject=For project
+RSSFeedPublisherTask.UsingCVSRoot=Using CVS Root:
+RSSFeedPublisherTask.UsingCVSPath=Using CVS Path:
+RSSFeedPublisherTask.UsingCVSTemp=Using CVS Temp:
+RSSFeedPublisherTask.PublishToSCPTarget=Publish to SCP Target:
+RSSFeedPublisherTask.ErrorNothingToDo=Error\! Must specify SCP and/or CVS options to publish a feed file. Nothing to do\!
+RSSFeedPublisherTask.ErrorCouldNotDeleteTempFolder=Error: could not delete temp folder
+RSSFeedPublisherTask.CVSWarning=cvs add: use `cvs commit' to add this file permanently
+
+RSSFeedWatcherTask.ErrorNoWatchActions=Error\! Must specify for which feed changes to watch and what actions as response. Nothing to do\!
+RSSFeedWatcherTask.PleaseRunThisTaskLater=Please run this task at some later date to compare reference copy in
+RSSFeedWatcherTask.WarningNoScriptAction=Warning: Assuming no script action for
+RSSFeedWatcherTask.WarningNoCommandlineParams=Warning: Assuming no commandline params for
+RSSFeedWatcherTask.ToTheLatestVersion=to latest version of feed in
+RSSFeedWatcherTask.ErrorDestinationFileIsADirectory=Error\! Destination file exists as a directory\! Cannot proceed.
+RSSFeedWatcherTask.Compare=Compare
+RSSFeedWatcherTask.GotOldNodeContents=Got old node contents
+RSSFeedWatcherTask.GotNewNodeContents=Got new node contents
+RSSFeedWatcherTask.RunExecTask=Run Exec Task
+RSSFeedWatcherTask.NodeUnchanged=Node unchanged. Nothing to do\!
+RSSFeedWatcherTask.NodeNotFound=Node not found. Nothing to do\!
+RSSFeedWatcherTask.Download=Download
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..0d00e40
--- /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.6.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.html b/bundles/org.eclipse.test.performance.ui/html/builds.html
new file mode 100644
index 0000000..e0dcf60
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/builds.html
@@ -0,0 +1,84 @@
+<!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>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..be5b75d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/components.html
@@ -0,0 +1,92 @@
+<!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>
+</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.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/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..45001b0
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/overview.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>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 may have scenario 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 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.5-200906111540/performance/performance.php">Eclipse 3.5.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..ead8fd6
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/html/preferences.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>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 generated results could be puzzled.
+</p><p>
+Here's what the page looks like:
+<p><img src="images/preferences.png" alt="Preferences"/></p>
+
+<h2>Database</h2>
+<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>
+
+<h2>Milestones</h2>
+<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>
+
+<h2>Default dimension</h2>
+<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>Results dimensions</h2>
+<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/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..3623103
--- /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..491cb79
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/plugin.xml
@@ -0,0 +1,84 @@
+<?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>
+   </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..f4cb0cc
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/readme.html
@@ -0,0 +1,77 @@
+<!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>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..9bd6e25
--- /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..64de5c1
--- /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..f576711
--- /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..275555d
--- /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, 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.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>
+ */
+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..24a5939
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/DB_Results.java
@@ -0,0 +1,1299 @@
+/*******************************************************************************
+ * 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.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.core.runtime.Assert;
+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;
+
+/**
+ * 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) {
+		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();
+		for (int i = 0; result.next(); i++) {
+			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++;
+			}
+		}
+		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..7cebe7e
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/db/PerformanceResults.java
@@ -0,0 +1,955 @@
+/*******************************************************************************
+ * 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.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 (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;
+}
+
+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.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.baselinePrefix == null && this.baselineName != null) {
+		// Assume that 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();
+		}
+	}
+
+	// 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..82c1766
--- /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, 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.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;
+}
+
+private 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..c8b21f3
--- /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 writableStatus(StringBuffer buffer, int kind, StringBuffer excluded) {
+	// Write status for scenarios having error
+	if ((getStatus() & ERROR_MASK) != 0) {
+
+		// Get children status
+		StringBuffer childrenBuffer = super.writableStatus(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..a00ed03
--- /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 writableStatus(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][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..a7073a5
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/model/PerformanceResultsElement.java
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * 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.io.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+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.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+
+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;
+}
+
+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 writeStatus(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 = writableStatus(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;
+}
+
+}
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..b3646d3
--- /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 element status in the given stream
+ */
+StringBuffer writableStatus(StringBuffer buffer, int kind, StringBuffer excluded) {
+	int length = this.children.length;
+	for (int i=0; i<length; i++) {
+		this.children[i].writableStatus(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..5957d6a
--- /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 writableStatus(StringBuffer buffer, int kind, StringBuffer excluded) {
+	// Write status for scenarios having error
+	if ((getStatus() & ERROR_MASK) != 0) {
+
+		// Get children status
+		StringBuffer childrenBuffer = super.writableStatus(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/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..3dabb41
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/BuildsView.java
@@ -0,0 +1,659 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.test.internal.performance.results.ui;
+
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Comparator;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.test.internal.performance.results.db.DB_Results;
+import org.eclipse.test.internal.performance.results.model.BuildResultsElement;
+import org.eclipse.test.internal.performance.results.model.ResultsElement;
+import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
+import org.eclipse.test.internal.performance.results.utils.Util;
+import org.eclipse.test.performance.ui.GenerateResults;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+
+/**
+ * View to see all the builds which have performance results stored in the database.
+ * <p>
+ * Typical actions from this view are update local data files with builds results
+ * and generated the HTML pages.
+ * </p>
+ */
+public class BuildsView extends PerformancesView {
+
+	/**
+	 * Action to generate results.
+	 */
+	final class GenerateAction extends Action {
+		IStatus status;
+
+		public void run() {
+
+			// Ask for output directory
+			String resultGenerationDir = BuildsView.this.preferences.get(IPerformancesConstants.PRE_RESULTS_GENERATION_DIR, "");
+			String pathFilter = (BuildsView.this.outputDir == null) ? resultGenerationDir : BuildsView.this.outputDir.getPath();
+			File dir = changeDir(pathFilter, "Select directory to write comparison files");
+			if (dir == null) {
+				return;
+			}
+			BuildsView.this.outputDir = dir;
+			BuildsView.this.preferences.put(IPerformancesConstants.PRE_RESULTS_GENERATION_DIR, dir.getAbsolutePath());
+
+			// Select the reference
+			String[] baselines = BuildsView.this.results.getBaselines();
+			int bLength = baselines.length;
+			String selectedBaseline;
+			switch (bLength) {
+				case 0:
+					// no baseline, nothing to do...
+					selectedBaseline = BuildsView.this.results.getPerformanceResults().getBaselineName();
+					break;
+				case 1:
+					// only one baseline, no selection to do
+					selectedBaseline = baselines[0];
+					break;
+				default:
+					// select the baseline from list
+					ElementListSelectionDialog dialog = new ElementListSelectionDialog(getSite().getShell(), new LabelProvider());
+					dialog.setTitle(getTitleToolTip());
+					dialog.setMessage("Select the baseline to use while generating results:");
+					String[] defaultBaseline = new String[] { baselines[baselines.length - 1] };
+					dialog.setInitialSelections(defaultBaseline);
+					dialog.setElements(baselines);
+					dialog.open();
+					Object[] selected = dialog.getResult();
+					if (selected == null)
+						return;
+					selectedBaseline = (String) selected[0];
+					break;
+			}
+			final String baselineName = selectedBaseline;
+			BuildsView.this.results.getPerformanceResults().setBaselineName(baselineName);
+
+			// Ask for fingerprints
+			final boolean fingerprints = MessageDialog.openQuestion(BuildsView.this.shell, getTitleToolTip(), "Generate only fingerprints?");
+
+			// Generate all selected builds
+			int length = BuildsView.this.buildsResults.length;
+			for (int i = 0; i < length; i++) {
+				generate(i, baselineName, fingerprints);
+			}
+		}
+
+		/*
+		 * Generate the HTML pages.
+		 */
+		private void generate(int i, final String baselineName, final boolean fingerprints) {
+			// Create output directory
+			final String buildName = BuildsView.this.buildsResults[i].getName();
+			final File genDir = new File(BuildsView.this.outputDir, buildName);
+			if (!genDir.exists() && !genDir.mkdir()) {
+				MessageDialog.openError(BuildsView.this.shell, getTitleToolTip(), "Cannot create " + genDir.getPath() + " to generate results!");
+				return;
+			}
+
+			// Create runnable
+			IRunnableWithProgress runnable = new IRunnableWithProgress() {
+
+				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+					try {
+						monitor.beginTask("Generate performance results", 10000);
+						GenerateResults generation = new GenerateResults(BuildsView.this.results.getPerformanceResults(),
+						    buildName,
+						    baselineName,
+						    fingerprints,
+						    BuildsView.this.dataDir,
+						    genDir);
+						GenerateAction.this.status = generation.run(monitor);
+						monitor.done();
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			};
+
+			// Run with progress monitor
+			ProgressMonitorDialog readProgress = new ProgressMonitorDialog(getSite().getShell());
+			try {
+				readProgress.run(true, true, runnable);
+			} catch (InvocationTargetException e) {
+				// skip
+			} catch (InterruptedException e) {
+				// skip
+			}
+
+			// Results
+			if (!this.status.isOK()) {
+				StringWriter swriter = new StringWriter();
+				PrintWriter pwriter = new PrintWriter(swriter);
+				swriter.write(this.status.getMessage());
+				Throwable ex = this.status.getException();
+				if (ex != null) {
+					swriter.write(": ");
+					swriter.write(ex.getMessage());
+					swriter.write('\n');
+					ex.printStackTrace(pwriter);
+				}
+				MessageDialog.open(this.status.getSeverity(),
+				    BuildsView.this.shell,
+				    getTitleToolTip(),
+				    swriter.toString(),
+				    SWT.NONE);
+			}
+		}
+	}
+
+	/**
+	 * Action to update local data files with the performance results of a build.
+	 *
+	 * This may be done lazily (i.e. not done if the local data already knows
+	 * the build) or forced (i.e. done whatever the local data files contain).
+	 */
+	class UpdateBuildAction extends Action {
+
+		boolean force;
+
+		UpdateBuildAction(boolean force) {
+			super();
+			this.force = force;
+		}
+
+		public void run() {
+
+			// Verify that directories are set
+			if (BuildsView.this.dataDir == null) {
+				if (changeDataDir() == null) {
+					if (!MessageDialog.openConfirm(BuildsView.this.shell, getTitleToolTip(), "No local files directory is set, hence the update could not be written! OK to continue?")) {
+						return;
+					}
+				}
+			}
+
+			// Progress dialog
+			IRunnableWithProgress runnable = new IRunnableWithProgress() {
+
+				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+					try {
+						updateBuilds(monitor);
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			};
+			ProgressMonitorDialog readProgress = new ProgressMonitorDialog(getSite().getShell());
+			try {
+				readProgress.run(true, true, runnable);
+			} catch (InvocationTargetException e) {
+				return;
+			} catch (InterruptedException e) {
+				return;
+			}
+
+			// Reset Components and Builds views input
+			refreshInput();
+			getSiblingView().refreshInput();
+		}
+
+		void updateBuilds(IProgressMonitor monitor) {
+			BuildsView.this.updateBuilds(monitor, this.force);
+		}
+	}
+
+	/**
+	 * Action to update local data files with the performance results of all builds.
+	 *
+	 * This may be done lazily (i.e. not done if the local data already knows
+	 * the build) or forced (i.e. done whatever the local data files contain).
+	 */
+	class UpdateAllBuildsAction extends UpdateBuildAction {
+
+		UpdateAllBuildsAction(boolean force) {
+			super(force);
+		}
+//
+//		public boolean isEnabled() {
+//			String[] elements = buildsToUpdate();
+//			return elements != null;
+//		}
+
+		void updateBuilds(IProgressMonitor monitor) {
+			BuildsView.this.updateAllBuilds(monitor, this.force);
+		}
+	}
+
+	/**
+	 * Class to compare builds regarding their date instead of their name.
+	 *
+	 * @see Util#getBuildDate(String)
+	 */
+	class BuildDateComparator implements Comparator {
+		public int compare(Object o1, Object o2) {
+	        String s1 = (String) o1;
+	        String s2 = (String) o2;
+	        return Util.getBuildDate(s1).compareTo(Util.getBuildDate(s2));
+	    }
+	}
+
+	// Views
+	PerformancesView componentsView;
+
+	// Results model
+	BuildResultsElement[] buildsResults;
+
+	// Generation info
+	File outputDir;
+
+	// Actions
+	Action generate;
+	UpdateBuildAction updateBuild, updateAllBuilds;
+//	UpdateBuildAction forceUpdateBuild, forceUpdateAllBuilds;
+
+	// SWT resources
+	Font italicFont;
+
+/*
+ * Default constructor.
+ */
+public BuildsView() {
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+	this.preferences.addPreferenceChangeListener(this);
+}
+
+/*
+ * Compute the list of builds to update based on their status.
+ */
+String[] buildsToUpdate() {
+	Object[] elements = this.results.getBuilds();
+	int length = elements.length;
+	String[] buildsToUpdate = new String[length];
+	int count = 0;
+	for (int i=0; i<length; i++) {
+		BuildResultsElement element = (BuildResultsElement) elements[i];
+		if (element.getStatus() == 0) {
+	        buildsToUpdate[count++] = element.getName();
+		}
+	}
+	if (count == 0) return null;
+	if (count < length) {
+		System.arraycopy(buildsToUpdate, 0, buildsToUpdate = new String[count], 0, count);
+	}
+	return buildsToUpdate;
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.ui.PerformancesView#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+public void createPartControl(Composite parent) {
+	super.createPartControl(parent);
+
+	// Create the viewer
+	this.viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+
+	// Set the content provider: first level is builds list
+	WorkbenchContentProvider contentProvider = new WorkbenchContentProvider() {
+		public Object[] getElements(Object o) {
+			return getBuilds();
+		}
+	};
+	this.viewer.setContentProvider(contentProvider);
+
+	// Set the label provider
+	WorkbenchLabelProvider labelProvider = new WorkbenchLabelProvider() {
+
+		// Set an italic font when no local data have been read
+		public Font getFont(Object element) {
+			Font font = super.getFont(element);
+			if (element instanceof BuildResultsElement) {
+				if (((BuildResultsElement) element).isUnknown()) {
+					if (BuildsView.this.italicFont == null) {
+						FontData[] defaultFont = JFaceResources.getDefaultFont().getFontData();
+						FontData italicFontData = new FontData(defaultFont[0].getName(), defaultFont[0].getHeight(), SWT.ITALIC);
+						BuildsView.this.italicFont = new Font(DEFAULT_DISPLAY, italicFontData);
+					}
+					return BuildsView.this.italicFont;
+				}
+			}
+			return font;
+		}
+
+		// Set font in gray when no local data is available (i.e. local data needs to be updated)
+		public Color getForeground(Object element) {
+			Color color = super.getForeground(element);
+			if (element instanceof BuildResultsElement) {
+				if (!((BuildResultsElement) element).isRead()) {
+					color = DARK_GRAY;
+				}
+			}
+			return color;
+		}
+	};
+	this.viewer.setLabelProvider(labelProvider);
+
+	// Set the children sorter
+	ViewerSorter nameSorter = new ViewerSorter() {
+
+		// Sort children using specific comparison (see the implementation
+		// of the #compareTo(Object) in the ResultsElement hierarchy
+		public int compare(Viewer view, Object e1, Object e2) {
+			if (e2 instanceof ResultsElement) {
+				return ((ResultsElement) e2).compareTo(e1);
+			}
+			return super.compare(view, e1, e2);
+		}
+	};
+	this.viewer.setSorter(nameSorter);
+
+	// Finalize viewer initialization
+	PlatformUI.getWorkbench().getHelpSystem().setHelp(this.viewer.getControl(), "org.eclipse.test.performance.ui.builds");
+	finalizeViewerCreation();
+}
+
+/* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#dispose()
+ */
+public void dispose() {
+	if (this.italicFont != null) {
+		this.italicFont.dispose();
+	}
+	super.dispose();
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.ui.PerformancesView#fillContextMenu(org.eclipse.jface.action.IMenuManager)
+ */
+void fillContextMenu(IMenuManager manager) {
+	super.fillContextMenu(manager);
+	manager.add(this.generate);
+	manager.add(this.updateBuild);
+//	manager.add(this.forceUpdateBuild);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.ui.PerformancesView#fillLocalPullDown(org.eclipse.jface.action.IMenuManager)
+ */
+void fillFiltersDropDown(IMenuManager manager) {
+	super.fillFiltersDropDown(manager);
+	manager.add(this.filterLastBuilds);
+}
+
+/*
+ * Fill the local data drop-down menu
+ */
+void fillLocalDataDropDown(IMenuManager manager) {
+	super.fillLocalDataDropDown(manager);
+	manager.add(new Separator());
+	manager.add(this.updateAllBuilds);
+//	manager.add(this.forceUpdateAllBuilds);
+}
+
+/*
+ * Get all builds from the model.
+ */
+Object[] getBuilds() {
+	if (this.results == null) {
+		initResults();
+	}
+	return this.results.getBuilds();
+}
+
+/*
+ * Return the components view.
+ */
+PerformancesView getSiblingView() {
+	if (this.componentsView == null) {
+		this.componentsView = (PerformancesView) getWorkbenchView("org.eclipse.test.internal.performance.results.ui.ComponentsView");
+	}
+	return this.componentsView;
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.ui.PerformancesView#makeActions()
+ */
+void makeActions() {
+
+	super.makeActions();
+
+	// Generate action
+	this.generate = new GenerateAction();
+	this.generate.setText("&Generate");
+
+	// Update build actions
+	boolean connected = this.preferences.getBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
+	this.updateBuild = new UpdateBuildAction(false);
+	this.updateBuild.setText("&Update from DB");
+	this.updateBuild.setEnabled(connected);
+//	this.forceUpdateBuild = new UpdateBuildAction(true);
+//	this.forceUpdateBuild.setText("Force Update");
+
+	// Update build action
+	this.updateAllBuilds = new UpdateAllBuildsAction(false);
+	this.updateAllBuilds.setText("&Update from DB (all)");
+	this.updateAllBuilds.setEnabled(connected);
+//	this.forceUpdateAllBuilds = new UpdateAllBuildsAction(true);
+//	this.forceUpdateAllBuilds.setText("Force Update all");
+
+	// Set filters default
+	this.filterBaselineBuilds.setChecked(false);
+	this.filterNightlyBuilds.setChecked(false);
+}
+
+/**
+ * Reset the views.
+ */
+public void resetView() {
+
+	boolean debug = true;
+
+	// Look whether database constants has changed or not
+	int eclipseVersion = this.preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
+	boolean connected = this.preferences.getBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
+	String databaseLocation = this.preferences.get(IPerformancesConstants.PRE_DATABASE_LOCATION, IPerformancesConstants.NETWORK_DATABASE_LOCATION);
+	String lastBuild = this.preferences.get(IPerformancesConstants.PRE_LAST_BUILD, null);
+	boolean noLastBuild = lastBuild.length() == 0;
+	if (debug) {
+		System.out.println("Reset View:");
+		System.out.println("	- eclispe version = "+eclipseVersion);
+		System.out.println("	- connected       = "+connected);
+		System.out.println("	- db location     = "+databaseLocation);
+		System.out.println("	- last build      = "+(noLastBuild?"<none>":lastBuild));
+	}
+	final boolean sameVersion = DB_Results.getDbVersion().endsWith(Integer.toString(eclipseVersion));
+	final boolean sameConnection = connected == DB_Results.DB_CONNECTION;
+	final boolean sameDB = sameVersion && databaseLocation.equals(DB_Results.getDbLocation());
+	boolean sameLastBuild = (noLastBuild && LAST_BUILD == null) || lastBuild.equals(LAST_BUILD);
+	if (debug) {
+		System.out.println("	- same version:    "+sameVersion);
+		System.out.println("	- same connection: "+sameConnection);
+		System.out.println("	- same DB:         "+sameDB);
+		System.out.println("	- same last build: "+sameLastBuild);
+	}
+	final PerformancesView siblingView = getSiblingView();
+	if (sameConnection && sameDB) {
+		if (!sameLastBuild) {
+			// Set last build
+			LAST_BUILD = noLastBuild ? null : lastBuild;
+			this.results.setLastBuildName(LAST_BUILD);
+			siblingView.results.setLastBuildName(LAST_BUILD);
+
+			// Reset views content
+			resetInput();
+			siblingView.resetInput();
+
+			// May be read local data now
+			File newDataDir = changeDataDir();
+			if (newDataDir == null) {
+				this.dataDir = null;
+				siblingView.dataDir = null;
+			}
+		}
+		// No database preferences has changed do nothing
+		return;
+	}
+
+	// Update database constants
+	boolean updated = DB_Results.updateDbConstants(connected, eclipseVersion, databaseLocation);
+	if (debug) {
+		System.out.println("	- updated:         "+updated);
+	}
+	if (!connected) {
+		if (!updated) {
+			MessageDialog.openError(this.shell, getTitleToolTip(), "Error while updating database results constants!\nOpen error log to see more details on this error");
+		}
+	} else if (updated) {
+		StringBuffer message = new StringBuffer("Database connection has been correctly ");
+		message.append( connected ? "opened." : "closed.");
+		MessageDialog.openInformation(this.shell, getTitleToolTip(), message.toString());
+	} else {
+		MessageDialog.openError(this.shell, getTitleToolTip(), "The database connection cannot be established!\nOpen error log to see more details on this error");
+		DB_Results.updateDbConstants(false, eclipseVersion, databaseLocation);
+	}
+	setTitleToolTip();
+	siblingView.setTitleToolTip();
+
+	// Refresh view
+	if (sameVersion && sameLastBuild) {
+		// Refresh only builds view as the sibling view (Components) contents is based on local data files contents
+		this.results.resetBuildNames();
+		refreshInput();
+	} else {
+		// Reset views content
+		resetInput();
+		siblingView.resetInput();
+
+		// May be read local data now
+		if (MessageDialog.openQuestion(this.shell, getTitleToolTip(), "Do you want to read local data right now?")) {
+			changeDataDir();
+		} else {
+			this.dataDir = null;
+			siblingView.dataDir = null;
+		}
+	}
+
+	// Update actions
+	this.updateBuild.setEnabled(connected);
+	this.updateAllBuilds.setEnabled(connected);
+}
+
+/*
+ * (non-Javadoc)
+ * @see org.eclipse.test.internal.performance.results.ui.PerformancesView#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
+public void selectionChanged(SelectionChangedEvent event) {
+	super.selectionChanged(event);
+
+	// Update selected element
+	Object selection = this.viewer.getSelection();
+	int length = 0;
+	if (selection instanceof IStructuredSelection) {
+		Object[] elements = ((IStructuredSelection)selection).toArray();
+		length = elements == null ? 0 : elements.length;
+		this.buildsResults = new BuildResultsElement[length];
+		if (length == 0) {
+			this.updateAllBuilds.setText("&Update from DB (all)");
+			return;
+		}
+		for (int i=0; i<length; i++) {
+			this.buildsResults[i] = (BuildResultsElement) elements[i];
+		}
+	} else {
+		return;
+	}
+
+	// Update update build action
+//	boolean enableUpdateBuild = true;
+//	boolean enableGenerate = true;
+	int readBuilds = 0;
+	for (int i=0; i<length; i++) {
+		if (this.buildsResults[i].isRead()) {
+//			enableUpdateBuild = false;
+			readBuilds++;
+		} else {
+//			enableGenerate = false;
+		}
+	}
+//	this.updateBuild.setEnabled(enableUpdateBuild);
+//	this.forceUpdateBuild.setEnabled(!enableUpdateBuild);
+	final boolean force = readBuilds < length;
+	this.updateBuild.force = force;
+	this.updateAllBuilds.force = force;
+	this.updateAllBuilds.setText("&Update from DB");
+
+	// Update generate action
+	boolean enableGenerate = true;
+	if (enableGenerate) {
+		for (int i=0; i<length; i++) {
+			if (this.buildsResults[i].getName().startsWith(DB_Results.getDbBaselinePrefix())) {
+				enableGenerate = false;
+				break;
+			}
+		}
+	}
+	this.generate.setEnabled(enableGenerate);
+}
+
+void updateAllBuilds(IProgressMonitor monitor, boolean force) {
+	if (this.dataDir == null) {
+		changeDataDir();
+	}
+	String[] builds = buildsToUpdate();
+	if (builds == null) {
+		this.results.updateBuild(null, true, this.dataDir, monitor);
+	} else {
+		this.results.updateBuilds(builds, force, this.dataDir, monitor);
+	}
+}
+
+void updateBuilds(IProgressMonitor monitor, boolean force) {
+	if (this.dataDir == null) {
+		changeDataDir();
+	}
+	int length = this.buildsResults.length;
+	String[] builds = new String[length];
+	for (int i = 0; i < length; i++) {
+		builds[i] = this.buildsResults[i].getName();
+	}
+	this.results.updateBuilds(builds, force, this.dataDir, monitor);
+}
+
+}
\ 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..158361d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentResultsView.java
@@ -0,0 +1,531 @@
+/*******************************************************************************
+ * 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.StringTokenizer;
+
+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 {
+
+	// Constants
+	private static final String ORG_ECLIPSE = "org.eclipse.";
+
+	// 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();
+	}
+
+	// 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..06b77d3
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/ComponentsView.java
@@ -0,0 +1,513 @@
+/*******************************************************************************
+ * 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.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+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.dialogs.MessageDialog;
+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
+	PerformancesView buildsView;
+	ComponentResultsView componentResultsView = null;
+
+	// Internal
+	Set expandedComponents = new HashSet();
+	File resultsDir = null;
+
+	// Actions
+	Action filterAdvancedScenarios;
+	Action writeStatus;
+
+	// SWT resources
+	Font boldFont;
+
+	// Write Status
+	static int WRITE_STATUS;
+
+/**
+ * Default constructor.
+ */
+public ComponentsView() {
+//	this.onlyFingerprintsImageDescriptor = ImageDescriptor.createFromFile(getClass(), "filter_ps.gif");
+	super();
+
+	// Get preferences
+	this.preferences = new InstanceScope().getNode(IPerformancesConstants.PLUGIN_ID);
+
+	// Init status
+	WRITE_STATUS = this.preferences.getInt(IPerformancesConstants.PRE_WRITE_STATUS, IPerformancesConstants.DEFAULT_WRITE_STATUS);
+
+}
+
+/*
+ * (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) {
+//				Action fingerprints = ComponentsView.this.filterNonFingerprints;
+//				if (fingerprints != null && !fingerprints.isChecked()) {
+				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();
+	}
+//	JFaceResources.getResources().destroyImage(this.onlyFingerprintsImageDescriptor);
+	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(this.filterLastBuilds);
+	manager.add(new Separator());
+	manager.add(this.filterAdvancedScenarios);
+}
+
+void fillLocalPullDown(IMenuManager manager) {
+	super.fillLocalPullDown(manager);
+	manager.add(new Separator());
+	manager.add(this.writeStatus);
+}
+
+/*
+ * 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 = (PerformancesView) 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)");
+
+	// Write status
+	this.writeStatus = new Action("Write status") {
+		public void run() {
+
+			// Get write directory
+			String filter = (ComponentsView.this.resultsDir == null) ? null : ComponentsView.this.resultsDir.getPath();
+			final File writeDir = changeDir(filter, "Select a directory to write the status");
+			if (writeDir != null) {
+				writeStatus(writeDir);
+			}
+        }
+	};
+	this.writeStatus.setEnabled(true);
+	this.writeStatus.setToolTipText("Write component status to a file");
+
+	// 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);
+	}
+
+	// Write status
+	if (propertyName.equals(IPerformancesConstants.PRE_WRITE_STATUS)) {
+		WRITE_STATUS = newValue == null ? IPerformancesConstants.DEFAULT_WRITE_STATUS : Integer.parseInt((String)newValue);
+	}
+
+	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);
+	} else {
+		String dir = this.viewState.getString(IPerformancesConstants.PRE_WRITE_RESULTS_DIR);
+		if (dir != null) {
+			this.resultsDir = new File(dir);
+		}
+	}
+
+	// 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) {
+	if (this.resultsDir != null) {
+		memento.putString(IPerformancesConstants.PRE_WRITE_RESULTS_DIR, this.resultsDir.getPath());
+	}
+	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);
+		}
+	}
+}
+
+protected void writeStatus(File writeDir) {
+		this.resultsDir = writeDir;
+		if (this.filterAdvancedScenarios.isChecked()) {
+			writeDir = new File(writeDir, "fingerprints");
+		} else {
+			writeDir = new File(writeDir, "all");
+		}
+		writeDir.mkdir();
+		if ((WRITE_STATUS & IPerformancesConstants.STATUS_VALUES) != 0) {
+			writeDir = new File(writeDir, "values");
+		}
+		int buildsNumber = WRITE_STATUS & IPerformancesConstants.STATUS_BUILDS_NUMBER_MASK;
+		if (buildsNumber > 1) {
+			writeDir = new File(writeDir, Integer.toString(buildsNumber));
+		}
+		writeDir.mkdirs();
+		String prefix = this.results.getName();
+		File resultsFile = new File(writeDir, prefix+".log");
+		File exclusionDir = new File(writeDir, "excluded");
+		exclusionDir.mkdir();
+		File exclusionFile = new File(exclusionDir, prefix+".log");
+		if (resultsFile.exists()) {
+			int i=0;
+			File saveDir = new File(writeDir, "save");
+			saveDir.mkdir();
+			while (true) {
+				String newFileName = prefix+"_";
+				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.writeStatus(resultsFile, WRITE_STATUS);
+		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/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..eb0b54a
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPerspective.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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");
+
+	// 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
+
+	// 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..d6bfb1d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferenceInitializer.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * 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);
+
+	// 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.V35_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.V36_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..1c4407f
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformanceResultsPreferencePage.java
@@ -0,0 +1,1216 @@
+/*******************************************************************************
+ * 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.Arrays;
+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;
+
+	// 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");
+
+		// 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");
+
+		// Last build
+		StringBuffer tooltip = new StringBuffer("Select the last build to display performance results\n");
+		tooltip.append("If set then performance results won't be displayed for any build after this date...");
+		String tooltipText = tooltip.toString();
+		Composite compositeLastBuild = createComposite(parent, 3, 1);
+	//	this.lastBuildCheckBox = createCheckBox(compositeLastBuild, "Until last build");
+		createLabel(compositeLastBuild, "Last build: ", false);
+		this.lastBuildCombo = createCombo(compositeLastBuild);
+		this.lastBuildCombo.setEditable(false);
+		this.lastBuildCombo.setToolTipText(tooltipText);
+		this.lastBuildCombo.add("");
+		initBuildsList();
+
+		// Default dimension layout
+		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...");
+		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();
+}
+
+/**
+ * @return The dialog title
+ */
+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);
+	}
+}
+
+/*
+ * Init he contents of the dimensions list controls.
+ */
+private void initBuildsList() {
+	String[] builds = DB_Results.getBuilds();
+	Arrays.sort(builds, Util.BUILD_DATE_COMPARATOR);
+	int length = builds.length;
+	for (int i=length-1; i>=0; i--) {
+		this.lastBuildCombo.add(builds[i]);
+	}
+}
+
+/**
+ * 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 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 last build
+	String lastBuild = store.getDefaultString(PRE_LAST_BUILD);
+//	if (lastBuild.length() == 0) {
+//		this.lastBuildCheckBox.setSelection(false);
+//		this.lastBuildCombo.setEnabled(false);
+//	} else {
+//		this.lastBuildCombo.setEnabled(true);
+//	}
+	this.lastBuildCombo.setText(lastBuild);
+
+	// 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 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 last build
+	String lastBuild = store.getString(PRE_LAST_BUILD);
+//	if (lastBuild.length() == 0) {
+//		this.lastBuildCheckBox.setSelection(false);
+//		this.lastBuildCombo.setEnabled(false);
+//	} else {
+//		this.lastBuildCombo.setEnabled(true);
+//	}
+	this.lastBuildCombo.setText(lastBuild);
+
+	// 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++);
+	}
+	*/
+}
+
+/**
+ * @param writeStatus The observed status to write
+ */
+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)) {
+							// already existing milestone, leave silently
+							if (MessageDialog.openQuestion(getShell(), getDialogTitle(), "Do you want to select milestone "+milestoneDate+" as the last build?")) {
+								String builds[] = this.lastBuildCombo.getItems();
+								int bLength = builds.length;
+								String milestone = milestoneDate.substring(milestoneDate.indexOf('-')+1);
+								for (int b=0; b<bLength; b++) {
+									if (builds[b].length() > 0 && Util.getBuildDate(builds[b]).equals(milestone)) {
+										this.lastBuildCombo.select(b);
+										break;
+									}
+								}
+							}
+							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
+		final String databaseLocation = this.databaseLocationCombo.getText();
+		char version = databaseLocation.charAt(databaseLocation.length()-1);
+
+		// 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 < 1 ) {
+				this.statusBuildsToConfirm.setText("1");
+			} 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);
+
+	// 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 last build
+	String lastBuild = this.lastBuildCombo.getText();
+	store.putValue(PRE_LAST_BUILD, lastBuild);
+
+	// 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..470ab35
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/ui/PerformancesView.java
@@ -0,0 +1,710 @@
+/*******************************************************************************
+ * 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.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.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.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;
+        }
+	};
+	static String LAST_BUILD;
+	final static ViewerFilter FILTER_LAST_BUILDS = new ViewerFilter() {
+		public boolean select(Viewer v, Object parentElement, Object element) {
+			if (LAST_BUILD != null && element instanceof BuildResultsElement) {
+				BuildResultsElement buildElement = (BuildResultsElement) element;
+				return buildElement.isBefore(LAST_BUILD);
+			}
+	        return true;
+        }
+	};
+	Set viewFilters = new HashSet();
+
+	// SWT resources
+	Shell shell;
+	Display display;
+	TreeViewer viewer;
+	IPropertySheetPage propertyPage;
+
+	// Data info
+	File dataDir;
+
+	// Views
+	IMemento viewState;
+
+	// Results model information
+	PerformanceResultsElement results;
+
+	// Actions
+	Action changeDataDir;
+	Action filterBaselineBuilds;
+	Action filterNightlyBuilds;
+	Action filterOldBuilds;
+	Action filterLastBuilds;
+//	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);
+
+	// Init last build
+	String lastBuild = this.preferences.get(IPerformancesConstants.PRE_LAST_BUILD, null);
+	LAST_BUILD = lastBuild == null || lastBuild.length() == 0 ? null : lastBuild;
+}
+
+File changeDataDir() {
+	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 (LAST_BUILD != null) {
+				if (!MessageDialog.openConfirm(PerformancesView.this.shell, getTitleToolTip(), "Only builds before "+LAST_BUILD+" will be taken into account!\nDo you want to continue?")) {
+					return null;
+				}
+			}
+
+			// Read local files
+			readLocalFiles();
+
+			// Refresh views
+			refreshInput();
+			PerformancesView resultsView = getSiblingView();
+			resultsView.refreshInput();
+			return resultsView.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 fingerprints scenarios action run.
+ */
+void filterLastBuilds(boolean filter, boolean updatePreference) {
+	if (filter) {
+		this.viewFilters.add(FILTER_LAST_BUILDS);
+	} else {
+		this.viewFilters.remove(FILTER_LAST_BUILDS);
+	}
+	this.preferences.putBoolean(IPerformancesConstants.PRE_FILTER_LAST_BUILDS, filter);
+	updateFilters();
+}
+
+/*
+ * 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();
+			}
+		}
+	}
+}
+
+/*
+ * Make common actions to performance views.
+ */
+void makeActions() {
+
+	// Change data dir action
+	this.changeDataDir = new Action("&Read...") {
+		public void run() {
+			changeDataDir();
+		}
+	};
+	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)");
+
+	// Filter non-important builds action
+	this.filterLastBuilds = new Action("&Last Builds", IAction.AS_CHECK_BOX) {
+		public void run() {
+			filterLastBuilds(isChecked(), true/*update preference*/);
+		}
+	};
+	final String lastBuild = this.preferences.get(IPerformancesConstants.PRE_LAST_BUILD, null);
+	this.filterLastBuilds.setChecked(false);
+	if (lastBuild == null) {
+		this.filterLastBuilds.setEnabled(false);
+	} else {
+		this.filterLastBuilds.setToolTipText("Filter last builds (i.e. after "+lastBuild+" build)");
+	}
+}
+
+/* (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();
+//	String newValue = (String) event.getNewValue();
+
+	// Eclipse version change
+	if (propertyName.equals(IPerformancesConstants.PRE_ECLIPSE_VERSION)) {
+//		int eclipseVersion = newValue == null ? IPerformancesConstants.DEFAULT_ECLIPSE_VERSION : Integer.parseInt(newValue);
+//		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);
+//		setTitleToolTip();
+	}
+
+	// Database location change
+	if (propertyName.equals(IPerformancesConstants.PRE_DATABASE_LOCATION)) {
+//		boolean connected = this.preferences.getBoolean(IPerformancesConstants.PRE_DATABASE_CONNECTION, IPerformancesConstants.DEFAULT_DATABASE_CONNECTION);
+//		int eclipseVersion = this.preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION);
+//		DB_Results.updateDbConstants(connected, eclipseVersion, newValue);
+//		setTitleToolTip();
+	}
+
+	// Database connection
+	if (propertyName.equals(IPerformancesConstants.PRE_DATABASE_CONNECTION)) {
+//		boolean connected = newValue == null ? IPerformancesConstants.DEFAULT_DATABASE_CONNECTION : newValue.equals(Boolean.TRUE);
+//		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);
+//		DB_Results.updateDbConstants(connected, eclipseVersion, databaseLocation);
+//		setTitleToolTip();
+	}
+
+	// Last build
+	if (propertyName.equals(IPerformancesConstants.PRE_LAST_BUILD)) {
+//		if (newValue == null || newValue.length() == 0) {
+//			this.filterLastBuilds.setEnabled(false);
+//			LAST_BUILD = null;
+//		} else {
+//			this.filterLastBuilds.setEnabled(true);
+//			this.filterLastBuilds.setToolTipText("Filter last builds (i.e. after "+newValue+" build)");
+//			LAST_BUILD = newValue;
+//		}
+	}
+}
+
+/*
+ * Read local files
+ */
+void readLocalFiles() {
+
+	// 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, LAST_BUILD);
+				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 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);
+		}
+	}
+
+	// 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);
+	}
+
+	// Filter last builds action state
+	checked = this.preferences.getBoolean(IPerformancesConstants.PRE_FILTER_LAST_BUILDS, IPerformancesConstants.DEFAULT_FILTER_LAST_BUILDS);
+	this.filterLastBuilds.setChecked(checked);
+	if (checked) {
+		this.viewFilters.add(FILTER_LAST_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
+	}
+}
+
+/*
+ * (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..f04d5b8
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/IPerformancesConstants.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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_LAST_BUILD = PREFIX + "last.build"; //$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$
+    public static final String PRE_FILTER_LAST_BUILDS = PREFIX + "filter.last.builds"; //$NON-NLS-1$
+
+	// Other constants
+	public static final int ECLIPSE_MAINTENANCE_VERSION = 35;
+	public static final int ECLIPSE_DEVELOPMENT_VERSION = 36;
+
+	// 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_LAST_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;
+
+	// Default milestones nowadays
+	public static final String[] V36_MILESTONES = new String[] {
+		"M1-200908060100",
+		"M2-200909170100",
+		"M3-200910301201",
+		"M4-200912101301",
+		"M5-201001291300",
+		"M6-201003121448",
+	};
+	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..5199381
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/internal/performance/results/utils/Util.java
@@ -0,0 +1,557 @@
+/*******************************************************************************
+ * 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();
+
+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':
+				throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is no longer supported!");
+			case '5':
+				MILESTONES = V35_MILESTONES;
+				break;
+			case '6':
+				MILESTONES = V36_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$
+
+/**
+ * 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#V35_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..0a283c6
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/GenerateResults.java
@@ -0,0 +1,1050 @@
+/*******************************************************************************
+ * 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.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.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;
+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(PerformanceResults results, String current, String baseline, boolean fingerprints, File data, File output) {
+	this.dataDir = data;
+	this.outputDir = output;
+	this.genFingerPrints = fingerprints;
+	this.genAll = !fingerprints;
+	this.performanceResults = results;
+	this.printStream = System.out;
+	setDefaults(current, baseline);
+}
+
+/*
+ * 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 run((IProgressMonitor) null);
+}
+
+/**
+ * Run the generation using a progress monitor.
+ * Note that all necessary information to generate properly must be set before
+ * calling this method
+ *
+ * @see #run(String[])
+ */
+public IStatus run(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);
+	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...");
+		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]+"...");
+			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();
+		}
+	}
+
+	// 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..9278a1c
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java
@@ -0,0 +1,469 @@
+/*******************************************************************************
+ * 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);
+		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()+"...");
+
+			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..864ff8a
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/toc.xml
@@ -0,0 +1,16 @@
+<?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>
+	<topic label="Verification" href="html/verification.html"/> 
+</toc>