prep for update site
diff --git a/org.eclipse.wtp.releng.webupdatesite/build-home/buildJar.xml b/org.eclipse.wtp.releng.webupdatesite/build-home/buildJar.xml
index 56545a4..bdec36b 100644
--- a/org.eclipse.wtp.releng.webupdatesite/build-home/buildJar.xml
+++ b/org.eclipse.wtp.releng.webupdatesite/build-home/buildJar.xml
@@ -1,39 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project default="create_run_jar" name="Create Runnable Jar for Project org.eclipse.wtp.releng.tools">
+<project default="create_run_jar" name="Create Runnable Jar for Project org.eclipse.wtp.releng.webupdatesite">
     <!--this file was created by Eclipse Runnable JAR Export Wizard-->
     <!--ANT 1.7 is required                                        -->
     <target name="create_run_jar">
         <jar destfile="D:/builds/Workspaces/wtpPureHead/org.eclipse.wtp.releng.webupdatesite/build-home/featureFileCreate.jar" filesetmanifest="mergewithoutmain">
             <manifest>
                 <attribute name="Built-By" value="${user.name}"/>
-                <attribute name="Main-Class" value="org.eclipse.wtp.releng.tools.UpdateFeatureUpdateFile"/>
+                <attribute name="Main-Class" value="org.eclipse.wtp.releng.tools.CreateFeatureUpdateFile"/>
                 <attribute name="Class-Path" value="."/>
             </manifest>
-            <fileset dir="D:/builds/Workspaces/wtpPureHead/org.eclipse.wtp.releng.tools/bin"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-antlr.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-apache-bcel.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-apache-bsf.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-apache-log4j.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-apache-oro.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-apache-regexp.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-apache-resolver.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-commons-logging.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-commons-net.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-jai.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-javamail.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-jdepend.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-jmf.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-jsch.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-junit.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-launcher.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-netrexx.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-nodeps.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-starteam.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-stylebook.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-swing.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-trax.jar"/>
-            <zipfileset excludes="META-INF/*.SF" src="D:/builds/targets/RC3/eclipse/plugins/org.apache.ant_1.7.0.v200803061910/lib/ant-weblogic.jar"/>
+            <fileset dir="D:/builds/Workspaces/wtpPureHead/org.eclipse.wtp.releng.webupdatesite/bin"/>
         </jar>
     </target>
 </project>
diff --git a/org.eclipse.wtp.releng.webupdatesite/build-home/featureFileCreate.jar b/org.eclipse.wtp.releng.webupdatesite/build-home/featureFileCreate.jar
index 6042781..5d249c6 100644
--- a/org.eclipse.wtp.releng.webupdatesite/build-home/featureFileCreate.jar
+++ b/org.eclipse.wtp.releng.webupdatesite/build-home/featureFileCreate.jar
Binary files differ
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/GenerateMirrorTags.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/GenerateMirrorTags.java
new file mode 100644
index 0000000..91971b4
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/GenerateMirrorTags.java
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * Copyright (c) 2004 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.callisto.tools;
+
+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.Writer;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.eclipse.callisto.tools.utils.FullJarNameParser;
+import org.eclipse.callisto.tools.utils.LineInfo;
+import org.eclipse.callisto.tools.utils.ProjectInfoPair;
+import org.eclipse.callisto.tools.utils.Version;
+
+public class GenerateMirrorTags {
+
+	private FullJarNameParser jarNameParser = new FullJarNameParser();
+
+	private static final String QUOTE = "\""; //$NON-NLS-1$
+
+	private static final String ARCHIVE_PATH = "<archive path="; //$NON-NLS-1$
+
+	private static final String SLASH = "/"; //$NON-NLS-1$
+
+	private static final char CHAR_BASKSLASH = '\\';
+
+	private static final char CHAR_SLASH = '/';
+
+	private static final String PLUGINS = "plugins"; //$NON-NLS-1$
+
+	private static final String FEATURES = "features"; //$NON-NLS-1$
+
+	private static final String IGNORE = "/home/data/httpd/download.eclipse.org/"; //$NON-NLS-1$
+
+	private static final boolean DEBUG = false;
+
+	private static final String urlCONST = " url=\""; //$NON-NLS-1$
+
+	private static final String nearestMirrorURL = "http://www.eclipse.org/downloads/download.php?r=1&amp;file=/"; //$NON-NLS-1$
+
+	private static final String relativeURL = "../../"; //$NON-NLS-1$
+
+	private static final String ENDTAG = "/>"; //$NON-NLS-1$
+
+	private static final String EOL = System.getProperty("line.separator"); //$NON-NLS-1$
+
+	static final String PATH_SEPARATOR = System.getProperty("path.separator"); //$NON-NLS-1$
+
+	private static final String DATADIR = "data"; //$NON-NLS-1$
+
+	private static final boolean VERBOSE = true;
+
+	private Hashtable pluginTable = new Hashtable();
+
+	private Hashtable featureTable = new Hashtable();
+
+	private ArrayList featureList = new ArrayList();
+
+	private ArrayList pluginList = new ArrayList();
+
+	public static void main(String[] args) {
+
+		System.out.println();
+		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
+		System.out.println("\t" + dateFormat.format(new Date())); //$NON-NLS-1$
+		System.out.println();
+
+		System.out.println("\tSystem Properties for some interesting locations"); //$NON-NLS-1$
+		System.out.println();
+		System.out.println("\t\tuser.dir: \t\t" + System.getProperty("user.dir")); //$NON-NLS-1$ //$NON-NLS-2$
+		System.out.println("\t\tuser.home: \t\t" + System.getProperty("user.home")); //$NON-NLS-1$ //$NON-NLS-2$
+		System.out.println("\t\tjava.io.tmpdir: \t" + System.getProperty("java.io.tmpdir")); //$NON-NLS-1$ //$NON-NLS-2$
+		System.out.println();
+		System.out.println();
+
+		try {
+
+			String propertyFileName = null;
+			String[] filenames = null;
+
+			if (args.length == 0) {
+				propertyFileName = new String("updateDirs.properties"); //$NON-NLS-1$
+				Properties updateDirectories = new Properties();
+
+				String userDirectory = System.getProperty("user.dir"); //$NON-NLS-1$
+				File propertyDir = new File(userDirectory);
+				File file = new File(propertyDir, propertyFileName);
+				InputStream inProperties = null;
+				String propertiesSource = null;
+				if (file.exists()) {
+					inProperties = new FileInputStream(file);
+					propertiesSource = file.getAbsolutePath();
+				}
+				else {
+					inProperties = GenerateMirrorTags.class.getClassLoader().getResourceAsStream(propertyFileName);
+					propertiesSource = "jar: " + propertyFileName; //$NON-NLS-1$
+				}
+
+				System.out.println("\tUsing properties from " + EOL + "\t\t" + propertiesSource); //$NON-NLS-1$ //$NON-NLS-2$
+				updateDirectories.load(inProperties);
+
+
+				filenames = initFileNamesFromProperties(updateDirectories);
+
+			}
+			else {
+				filenames = args;
+			}
+			new GenerateMirrorTags().generateFromPathNames(filenames);
+
+
+		}
+		catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+		catch (IOException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	private static String[] initFileNamesFromProperties(Properties updateDirectories) {
+		String[] result = new String[0];
+		ArrayList nameProperties = new ArrayList();
+		Enumeration allProperties = updateDirectories.keys();
+		while (allProperties.hasMoreElements()) {
+			String propertyname = (String) allProperties.nextElement();
+			String dirname = null;
+			if (propertyname.startsWith("updateDir")) { //$NON-NLS-1$
+				dirname = updateDirectories.getProperty(propertyname);
+				nameProperties.add(dirname);
+			}
+		}
+		if (nameProperties.size() > 0) {
+			result = new String[nameProperties.size()];
+			for (int iProp = 0; iProp < nameProperties.size(); iProp++) {
+				result[iProp] = (String) nameProperties.get(iProp);
+			}
+		}
+		return result;
+	}
+
+	private int totalTags;
+
+	private String UNDERSCORE = "_"; //$NON-NLS-1$
+
+	private boolean doNearestMirror = false;
+
+	private boolean doRelativeURL = true;
+
+	private void generateFromPathNames(String[] pathnames) throws IOException {
+
+		int nPathNames = pathnames.length;
+		System.out.println();
+		System.out.print("\tTotal PathNames to be processed: " + nPathNames); //$NON-NLS-1$
+		System.out.println();
+
+		for (int iPath = 0; iPath < nPathNames; iPath++) {
+			String pathname = pathnames[iPath];
+
+			File mainDirectory = new File(pathname);
+			if (!mainDirectory.exists()) {
+				System.out.println("\n\tSkipping a path from property file, that did not exist: " + mainDirectory); //$NON-NLS-1$
+			}
+			else {
+				if (!mainDirectory.isDirectory()) {
+					System.err.println("We expect to get directory names as the input."); //$NON-NLS-1$
+				}
+				else {
+
+					pluginTable.clear();
+					featureTable.clear();
+					featureList.clear();
+					pluginList.clear();
+
+					String[] members = mainDirectory.list();
+
+					for (int jMember = 0; jMember < members.length; jMember++) {
+
+						String member = members[jMember];
+
+						if (DEBUG) {
+							System.out.println(member);
+						}
+
+						if (FEATURES.equals(member)) {
+							File featureDirectory = new File(mainDirectory, member);
+							String[] featureNames = featureDirectory.list();
+
+							for (int iFeatureName = 0; iFeatureName < featureNames.length; iFeatureName++) {
+								String featureName = featureNames[iFeatureName];
+
+								if (DEBUG) {
+									System.out.println(featureName);
+								}
+
+								jarNameParser.parse(featureName);
+								String projectId = jarNameParser.getProjectString();
+								String versionString = jarNameParser.getVersionString();
+								Version version = createVersionFromString(featureName, projectId, versionString);
+								if (version != null) {
+									storeInTable(featureTable, projectId, version, featureDirectory + SLASH + featureName);
+									storeInList(featureList, projectId, version, featureDirectory + SLASH + featureName);
+								}
+							}
+						}
+						else if (PLUGINS.equals(member)) {
+							File pluginDirectory = new File(mainDirectory, member);
+							String[] pluginNames = pluginDirectory.list();
+
+							for (int iPluginName = 0; iPluginName < pluginNames.length; iPluginName++) {
+								String pluginName = pluginNames[iPluginName];
+
+								if (DEBUG) {
+									System.out.println(pluginName);
+								}
+
+								jarNameParser.parse(pluginName);
+								String projectId = jarNameParser.getProjectString();
+								String versionString = jarNameParser.getVersionString();
+								Version version = createVersionFromString(pluginName, projectId, versionString);
+								if (version != null) {
+									storeInTable(pluginTable, projectId, version, pluginDirectory + SLASH + pluginName);
+									storeInList(pluginList, projectId, version, pluginDirectory + SLASH + pluginName);
+								}
+							}
+						}
+
+					}
+
+					String newFileName = newFileName(pathname);
+
+					createTagFileMR(newFileName + "-MR"); //$NON-NLS-1$
+					createTagFileALL(newFileName + "-All"); //$NON-NLS-1$
+
+				}
+
+			}
+		}
+
+	}
+
+	private Version createVersionFromString(String wholeName, String projectId, String versionString) {
+		Version result = null;
+		try {
+			result = new Version(versionString);
+		}
+		catch (NumberFormatException e) {
+			System.out.println();
+			System.out.println(" Number Format Exception creating Version"); //$NON-NLS-1$
+			System.out.println("featureName: " + wholeName); //$NON-NLS-1$
+			System.out.println("projectId: " + projectId); //$NON-NLS-1$
+			System.out.println("versionString: " + versionString); //$NON-NLS-1$
+			System.out.println();
+		}
+		return result;
+	}
+
+	private void createTagFileMR(String filename) throws IOException {
+
+		File dataDir = ensureDataDirectory();
+
+		File file = new File(dataDir, filename);
+		FileWriter writer = new FileWriter(file);
+
+		Enumeration features = featureTable.keys();
+		while (features.hasMoreElements()) {
+			String id = (String) features.nextElement();
+			LineInfo lineInfo = (LineInfo) featureTable.get(id);
+			createTag(writer, id, FEATURES, lineInfo.getLine());
+		}
+
+		Enumeration plugins = pluginTable.keys();
+		while (plugins.hasMoreElements()) {
+			String id = (String) plugins.nextElement();
+			LineInfo lineInfo = (LineInfo) pluginTable.get(id);
+			createTag(writer, id, PLUGINS, lineInfo.getLine());
+		}
+		writer.close();
+
+		printSummary(filename, featureTable.size(), pluginTable.size());
+	}
+
+	private void printSummary(String filename, int nFeatures, int nPlugins) {
+		if (VERBOSE) {
+			System.out.println();
+			System.out.println("\t\t" + filename); //$NON-NLS-1$
+			System.out.println("\t\t\t" + "number of features: " + nFeatures); //$NON-NLS-1$ //$NON-NLS-2$
+			System.out.println("\t\t\t" + " number of plugins: " + nPlugins); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	private void createTagFileALL(String filename) throws IOException {
+
+		File dataDir = ensureDataDirectory();
+
+		File file = new File(dataDir, filename);
+		FileWriter writer = new FileWriter(file);
+
+		Iterator features = featureList.iterator();
+		writeTags(writer, features, FEATURES);
+
+		Iterator plugins = pluginList.iterator();
+		writeTags(writer, plugins, PLUGINS);
+
+		writer.close();
+
+		printSummary(filename, featureList.size(), pluginList.size());
+
+	}
+
+	private File ensureDataDirectory() throws IOException {
+		String writeDirectory = System.getProperty("user.dir"); //$NON-NLS-1$
+		File writeDir = new File(writeDirectory);
+		File dataDir = new File(writeDir, DATADIR);
+		if (!dataDir.exists()) {
+			boolean success = dataDir.mkdirs();
+			if (!success) {
+				throw new IOException("Could not create directory: " + dataDir.getName()); //$NON-NLS-1$
+			}
+		}
+		return dataDir;
+	}
+
+	private void writeTags(FileWriter writer, Iterator iterator, String type) throws IOException {
+		while (iterator.hasNext()) {
+			ProjectInfoPair info = (ProjectInfoPair) iterator.next();
+			String id = info.getProjectId();
+			LineInfo lineInfo = info.getLineInfo();
+			createTag(writer, id, type, lineInfo.getLine());
+		}
+	}
+
+	private String newFileName(String filename) {
+		// System.out.println("pathname processed: " + filename);
+		String name = filename;
+		if (name.startsWith(IGNORE)) {
+			name = name.substring(IGNORE.length());
+		}
+		name = name.replace(CHAR_SLASH, '-');
+		name = name.replace(CHAR_BASKSLASH, '-');
+		if (name.startsWith("-")) { //$NON-NLS-1$
+			name = name.substring(1);
+		}
+		if (!name.endsWith("-")) { //$NON-NLS-1$
+			name = name + "-"; //$NON-NLS-1$
+		}
+		name = name + "Tags" + "." + "xmlinc"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		// System.out.println("filename created: " + name);
+		return name;
+	}
+
+	private void storeInTable(Hashtable table, String projectId, Version version, String lineParam) {
+
+		String lineToStore = lineParam.replace(CHAR_BASKSLASH, CHAR_SLASH);
+		if (lineToStore.startsWith(IGNORE)) {
+			lineToStore = lineToStore.substring(IGNORE.length());
+		}
+
+		if (notInSkipList(lineToStore)) {
+			// if see if project in table at all, if not store it. If so,
+			// update
+			// version and line, if higher.
+			Object data = table.get(projectId);
+			if (data == null) {
+				LineInfo lineInfo = new LineInfo(version, lineToStore);
+				table.put(projectId, lineInfo);
+			}
+			else {
+				LineInfo lineinfo = (LineInfo) data;
+				Version currentVersion = lineinfo.getVersion();
+				if (version.compareTo(currentVersion) > 0) {
+					LineInfo newlineInfo = new LineInfo(version, lineToStore);
+					table.put(projectId, newlineInfo);
+					if (DEBUG) {
+						System.out.println("\tVersion Replaced: " + projectId); //$NON-NLS-1$
+						System.out.println("\t\t" + version + " greater then " + currentVersion); //$NON-NLS-1$ //$NON-NLS-2$
+					}
+				}
+			}
+		}
+
+	}
+
+	private void storeInList(ArrayList list, String projectId, Version version, String lineParam) {
+
+		String lineToStore = lineParam.replace(CHAR_BASKSLASH, CHAR_SLASH);
+		if (lineToStore.startsWith(IGNORE)) {
+			lineToStore = lineToStore.substring(IGNORE.length());
+		}
+
+		if (notInSkipList(lineToStore)) {
+			LineInfo newlineInfo = new LineInfo(version, lineToStore);
+			ProjectInfoPair projectInfoPair = new ProjectInfoPair(projectId, newlineInfo);
+			list.add(projectInfoPair);
+		}
+
+	}
+
+	private boolean notInSkipList(String directory) {
+		boolean result = false;
+		if (directory != null && directory.length() > 0) {
+			if (contains(directory, ".tests_") || contains(directory, ".tests.") || contains(directory, "validation.test") || contains(directory, "validation.sample") || contains(directory, "tests.feature")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+				result = false;
+			}
+			else {
+				result = true;
+			}
+		}
+		return result;
+	}
+
+	private boolean contains(String directory, String string) {
+		return (-1 < directory.indexOf(string));
+	}
+
+	private void createTag(Writer writer, String directory, String type, String line) throws IOException {
+		jarNameParser.parse(line);
+		String versionString = jarNameParser.getVersionString();
+		String tagStart = ARCHIVE_PATH + QUOTE + type + SLASH + directory + UNDERSCORE + versionString + ".jar" + QUOTE; //$NON-NLS-1$
+
+		String urlAttrib = null;
+		String avoidDoubleSlashLine = line;
+		if (avoidDoubleSlashLine.startsWith(SLASH)) {
+			avoidDoubleSlashLine = avoidDoubleSlashLine.substring(1);
+		}
+		if (doNearestMirror) {
+			urlAttrib = urlCONST + nearestMirrorURL + avoidDoubleSlashLine + QUOTE + ENDTAG;
+		}
+		else if (doRelativeURL) {
+			urlAttrib = urlCONST + relativeURL + avoidDoubleSlashLine + QUOTE + ENDTAG;
+		}
+		else {
+			throw new IllegalStateException("url type is not specified correctly"); //$NON-NLS-1$
+		}
+
+		writer.write(EOL);
+		writer.write("\t" + tagStart + EOL); //$NON-NLS-1$
+		writer.write("\t\t" + urlAttrib + EOL); //$NON-NLS-1$
+		writer.write(EOL);
+		writer.flush();
+
+		totalTags++;
+
+	}
+}
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/Messages.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/Messages.java
new file mode 100644
index 0000000..fe83c4b
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/Messages.java
@@ -0,0 +1,22 @@
+package org.eclipse.callisto.tools;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class Messages {
+	private static final String BUNDLE_NAME = "org.eclipse.callisto.tools.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/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/SiteFileUpdater.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/SiteFileUpdater.java
new file mode 100644
index 0000000..2c7c579
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/SiteFileUpdater.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     
+ *******************************************************************************/
+
+package org.eclipse.callisto.tools;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.xml.parsers.DocumentBuilder;
+
+import org.eclipse.callisto.tools.utils.CommonXML;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * The purpose of this utility is to take a site.xml file that's automatically
+ * created during the mirror command operations, and "merge" it with an
+ * authored site.xml file that uses different categories or descriptions that
+ * the automated one. The "merge" is only that any new URL's or version
+ * numbers are mapped to their corresponding authored versions, so rest of
+ * authored version is untouched, and can be used as "the" site.xml for the
+ * mirrored site.
+ */
+
+public class SiteFileUpdater {
+
+	public static void main(String[] args) {
+
+		if (args.length != 2) {
+			System.out.println();
+			System.out.println("    Usage: file-to-merge-into file-to-merge-from"); //$NON-NLS-1$
+			System.out.println("           The file-to-merge-into will be overwritten"); //$NON-NLS-1$
+			System.out.println();
+		}
+		else {
+			try {
+				String oldFileName = args[0];
+				String newFileName = args[1];
+				SiteFileUpdater siteFileUpdater = new SiteFileUpdater();
+				// old file is the authored file, with old feature versions
+				FileInputStream oldFile;
+				oldFile = new FileInputStream(oldFileName);
+				// new file has correct feature versions and url's from mirror
+				// command
+				FileInputStream newFile = new FileInputStream(newFileName);
+				Document mergedDom = siteFileUpdater.merge(oldFile, newFile);
+				oldFile.close();
+				newFile.close();
+
+				// outfile will contain "merge" xml files
+				FileOutputStream outfile = new FileOutputStream(oldFileName);
+				CommonXML.serialize(mergedDom, outfile);
+
+				outfile.close();
+				System.out.println("\tOutput to: " + oldFileName); //$NON-NLS-1$
+			}
+			catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+
+	}
+
+	private Document merge(FileInputStream oldFile, FileInputStream newFile) throws IOException {
+
+		DocumentBuilder documentBuilder = CommonXML.getDocumentBuilder();
+		Document mergedDom = null;
+		try {
+			Document oldDom = documentBuilder.parse(oldFile);
+
+			Document newDom = documentBuilder.parse(newFile);
+			mergedDom = merge(oldDom, newDom);
+		}
+		catch (SAXException e) {
+			e.printStackTrace();
+		}
+		return mergedDom;
+
+
+	}
+
+	private Document merge(Document oldDom, Document newDom) {
+
+		Document result = oldDom;
+		NodeList oldNodeList = oldDom.getElementsByTagName("feature"); //$NON-NLS-1$
+		NodeList newNodeList = newDom.getElementsByTagName("feature"); //$NON-NLS-1$
+
+		// go through each of the existing features, if there's a new one with
+		// same ID,
+		// then update old one with url and version.
+		int nNodes = oldNodeList.getLength();
+
+		for (int i = 0; i < nNodes; i++) {
+			Node node = oldNodeList.item(i);
+			NamedNodeMap attributeMap = node.getAttributes();
+			Node oldIdAttribute = attributeMap.getNamedItem("id"); //$NON-NLS-1$
+
+			Node matchingNode = findMatchingNodeById(newNodeList, oldIdAttribute);
+			if (matchingNode != null) {
+				// so, found match, update url and version
+				Node newurl = matchingNode.getAttributes().getNamedItem("url"); //$NON-NLS-1$
+				Node newversion = matchingNode.getAttributes().getNamedItem("version"); //$NON-NLS-1$
+				Attr newurlAttr = node.getOwnerDocument().createAttribute("url"); //$NON-NLS-1$
+				Attr newversionAttr = node.getOwnerDocument().createAttribute("version"); //$NON-NLS-1$
+				newurlAttr.setValue(newurl.getNodeValue());
+				newversionAttr.setNodeValue(newversion.getNodeValue());
+				attributeMap.setNamedItem(newurlAttr);
+				attributeMap.setNamedItem(newversionAttr);
+			}
+
+		}
+
+		return result;
+	}
+
+	private Node findMatchingNodeById(NodeList nodeList, Node attribute) {
+		Node result = null;
+		ArrayList matches = new ArrayList();
+		String idValue = attribute.getNodeValue();
+
+		for (int i = 0; i < nodeList.getLength(); i++) {
+			Node potentialnode = nodeList.item(i);
+			Node potentialMatch = potentialnode.getAttributes().getNamedItem(Messages.getString("SiteFileUpdater.10")); //$NON-NLS-1$
+			if (potentialMatch != null) {
+				String potentialIdValue = potentialMatch.getNodeValue();
+				if (idValue.equals(potentialIdValue)) {
+					matches.add(potentialnode);
+				}
+			}
+		}
+		if (matches.size() > 1) {
+			System.out.println(Messages.getString("SiteFileUpdater.11") + idValue); //$NON-NLS-1$
+			System.out.println("              The last one in list was used, but sites(s) should be cleaned up so only newest one included"); //$NON-NLS-1$
+			for (int i = 0; i < matches.size(); i++) {
+				Node match = (Node) matches.get(i);
+				Node version = match.getAttributes().getNamedItem("version"); //$NON-NLS-1$
+
+				System.out.println("        " + version.getNodeValue()); //$NON-NLS-1$
+			}
+
+		}
+		if (matches.size() > 0) {
+			result = (Node) matches.get(matches.size() - 1);
+		}
+		return result;
+	}
+}
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/messages.properties b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/messages.properties
new file mode 100644
index 0000000..b536465
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/messages.properties
@@ -0,0 +1,2 @@
+SiteFileUpdater.10=id
+SiteFileUpdater.11=\ \ \ \ \ WARNING: there were mulitple matches for 
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/CommonXML.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/CommonXML.java
new file mode 100644
index 0000000..3029f1c
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/CommonXML.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2004 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.callisto.tools.utils;
+
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+
+/**
+ */
+public class CommonXML {
+
+	/**
+	 * Provides a DocumentBuilder capable of creating a DOM Document from
+	 * input. Sets the desired characteristics for this instance, 
+	 * 	validating=false
+	 *  ignoringComments=false
+	 *  ignoringElementContentWhitespace=false
+	 * 
+	 * Example usage: Document document = null; try { DocumentBuilder builder =
+	 * CommonXML.getDocumentBuilder(); if (builder != null) { InputStream fis =
+	 * new FileInputStream(getFilename()); document = builder.parse(new
+	 * InputSource(fis)); } else { Logger.log(Logger.ERROR, "Couldn't obtain a
+	 * DocumentBuilder"); //$NON-NLS-1$ } } catch (FileNotFoundException e) { //
+	 * typical of new workspace, don't log it document = null; } catch
+	 * (IOException e) { Logger.logException("Could not load document", e);
+	 * //$NON-NLS-1$ return definitions; } catch (SAXException e) {
+	 * Logger.logException("Could not load document", e); //$NON-NLS-1$ return
+	 * definitions; }
+	 * 
+	 * @return DocumentBuilder
+	 */
+	public synchronized static DocumentBuilder getDocumentBuilder() {
+		DocumentBuilder result = null;
+		try {
+			DocumentBuilderFactory instance = DocumentBuilderFactory.newInstance();
+			instance.setValidating(false);
+			instance.setIgnoringComments(false);
+			instance.setIgnoringElementContentWhitespace(false);
+			
+			result = instance.newDocumentBuilder();
+			
+			
+		}
+		catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		}
+		return result;
+	}
+
+	public synchronized static DocumentBuilder getDocumentBuilder(boolean validating) {
+		DocumentBuilder result = null;
+		try {
+			DocumentBuilderFactory instance = DocumentBuilderFactory.newInstance();
+			instance.setValidating(validating);
+			result = instance.newDocumentBuilder();
+		}
+		catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		}
+		return result;
+	}
+
+	/**
+	 * Transforms a DOM document into a lightly-formatted UTF-16 represntation
+	 * and outputs it to an outputstream
+	 * 
+	 * @param document
+	 * @param ostream
+	 * @throws IOException
+	 */
+	public static void serialize(Document document, OutputStream ostream) throws IOException {
+		Source domSource = new DOMSource(document);
+		try {
+			Transformer serializer = TransformerFactory.newInstance().newTransformer();
+			try {
+				serializer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
+				serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$
+				serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
+				
+				//Properties prop = serializer.getOutputProperties();
+				//prop.list(System.out);
+			}
+			catch (IllegalArgumentException e) {
+				e.printStackTrace();
+			}
+			serializer.transform(domSource, new StreamResult(ostream));
+		}
+		catch (TransformerConfigurationException e) {
+			throw new IOException(e.getMessage());
+		}
+		catch (TransformerFactoryConfigurationError e) {
+			throw new IOException(e.getMessage());
+		}
+		catch (TransformerException e) {
+			throw new IOException(e.getMessage());
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/FullJarNameParser.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/FullJarNameParser.java
new file mode 100644
index 0000000..a641ea5
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/FullJarNameParser.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     
+ *******************************************************************************/
+
+package org.eclipse.callisto.tools.utils;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FullJarNameParser {
+
+	private static final boolean DEBUG = false; 
+	// simplified pattern: (ID) '_' (N '.' M '.' O '.' S) '.jar'
+	private String START_GROUP = "(";//$NON-NLS-1$
+	private String END_GROUP = ")"; //$NON-NLS-1$
+	private String UNDERSCORE = "_"; //$NON-NLS-1$
+	private String BACKSLASH = "\\"; //$NON-NLS-1$
+	private String LITERAL_PERIOD = BACKSLASH + "."; //$NON-NLS-1$
+	private String ANYDIGITS = BACKSLASH + "d" + "*"; //$NON-NLS-1$ //$NON-NLS-2$
+	private String ANY = ".*"; //$NON-NLS-1$
+	private Pattern pattern = Pattern.compile(START_GROUP + ANY + END_GROUP + UNDERSCORE + START_GROUP + START_GROUP + ANYDIGITS + END_GROUP + LITERAL_PERIOD + START_GROUP + ANYDIGITS + END_GROUP + LITERAL_PERIOD + START_GROUP + ANYDIGITS + END_GROUP + START_GROUP + LITERAL_PERIOD + ANY + END_GROUP + "?" + END_GROUP + ".jar"); //$NON-NLS-1$ //$NON-NLS-2$
+
+	private String projectString;
+	private String versionString;
+
+
+
+	public FullJarNameParser() {
+		super();
+	}
+
+	public boolean parse(String line) {
+		boolean result = false;
+		projectString = ""; //$NON-NLS-1$
+		projectString = ""; //$NON-NLS-1$
+		Matcher matcher = pattern.matcher(line);
+
+		if (!matcher.matches()) {
+			System.out.println();
+			System.out.println("\tthe line did not match parse rule: "); //$NON-NLS-1$
+			System.out.println("\t" + line); //$NON-NLS-1$
+			System.out.println();
+			result = false;
+		}
+		else {
+
+			projectString = matcher.group(1);
+			versionString = matcher.group(2);
+			if (DEBUG) {
+				System.out.println(projectString);
+				System.out.println(versionString);
+				System.out.println();
+			}
+			result = true;
+		}
+		return result;
+	}
+
+
+
+	public String getProjectString() {
+		return projectString;
+	}
+
+
+
+	public String getVersionString() {
+		return versionString;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/LineInfo.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/LineInfo.java
new file mode 100644
index 0000000..3b75bf4
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/LineInfo.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2004 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.callisto.tools.utils;
+
+
+public class LineInfo {
+
+	private String lineInfo;
+	private Version version;
+	
+	public LineInfo(Version version, String line) {
+		this.version = version;
+		this.lineInfo = line;
+	}
+	
+	public Version getVersion() {
+		return version;
+	}
+	public String getLine() {
+		return lineInfo;
+	}
+}
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/ProjectInfoPair.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/ProjectInfoPair.java
new file mode 100644
index 0000000..cad8bee
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/ProjectInfoPair.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2004 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.callisto.tools.utils;
+
+
+public class ProjectInfoPair {
+	private String projectId;
+	private LineInfo lineInfo;
+	public LineInfo getLineInfo() {
+		return lineInfo;
+	}
+	public String getProjectId() {
+		return projectId;
+	}
+	public ProjectInfoPair(String projectId, LineInfo lineInfo) {
+		this.projectId = projectId;
+		this.lineInfo = lineInfo;
+	}
+
+}
diff --git a/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/Version.java b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/Version.java
new file mode 100644
index 0000000..d3311a7
--- /dev/null
+++ b/org.eclipse.wtp.releng.webupdatesite/src/org/eclipse/callisto/tools/utils/Version.java
@@ -0,0 +1,356 @@
+package org.eclipse.callisto.tools.utils;
+
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+/**
+ * Temporary "copy" of class
+ * 
+ * Version identifier for bundles and packages.
+ * 
+ * <p>
+ * Version identifiers have four components.
+ * <ol>
+ * <li>Major version. A non-negative integer.</li>
+ * <li>Minor version. A non-negative integer.</li>
+ * <li>Micro version. A non-negative integer.</li>
+ * <li>Qualifier. A text string. See <code>Version(String)</code> for the
+ * format of the qualifier string.</li>
+ * </ol>
+ * 
+ * <p>
+ * <code>Version</code> objects are immutable.
+ * 
+ * @version $Revision: 1.1 $
+ * @since 1.3
+ */
+
+public class Version implements Comparable {
+	private final int major;
+	private final int minor;
+	private final int micro;
+	private final String qualifier;
+	private static final String SEPARATOR = "."; //$NON-NLS-1$
+
+	/**
+	 * The empty version "0.0.0". Equivalent to calling
+	 * <code>new Version(0,0,0)</code>.
+	 */
+	public static final Version emptyVersion = new Version(0, 0, 0);
+
+	/**
+	 * Creates a version identifier from the specified numerical components.
+	 * 
+	 * <p>
+	 * The qualifier is set to the empty string.
+	 * 
+	 * @param major
+	 *            Major component of the version identifier.
+	 * @param minor
+	 *            Minor component of the version identifier.
+	 * @param micro
+	 *            Micro component of the version identifier.
+	 * @throws IllegalArgumentException
+	 *             If the numerical components are negative.
+	 */
+	public Version(int major, int minor, int micro) {
+		this(major, minor, micro, null);
+	}
+
+	/**
+	 * Creates a version identifier from the specifed components.
+	 * 
+	 * @param major
+	 *            Major component of the version identifier.
+	 * @param minor
+	 *            Minor component of the version identifier.
+	 * @param micro
+	 *            Micro component of the version identifier.
+	 * @param qualifier
+	 *            Qualifier component of the version identifier. If
+	 *            <code>null</code> is specified, then the qualifier will be
+	 *            set to the empty string.
+	 * @throws IllegalArgumentException
+	 *             If the numerical components are negative or the qualifier
+	 *             string is invalid.
+	 */
+	public Version(int major, int minor, int micro, String qualifier) {
+		if (qualifier == null) {
+			qualifier = ""; //$NON-NLS-1$
+		}
+
+		this.major = major;
+		this.minor = minor;
+		this.micro = micro;
+		this.qualifier = qualifier;
+		validate();
+	}
+
+	/**
+	 * Created a version identifier from the specified string.
+	 * 
+	 * <p>
+	 * Here is the grammar for version strings.
+	 * 
+	 * <pre>
+	 *  version ::= major('.'minor('.'micro('.'qualifier)?)?)?
+	 *  major ::= digit+
+	 *  minor ::= digit+
+	 *  micro ::= digit+
+	 *  qualifier ::= (alpha|digit|'_'|'-')+
+	 *  digit ::= [0..9]
+	 *  alpha ::= [a..zA..Z]
+	 * </pre>
+	 * 
+	 * There must be no whitespace in version.
+	 * 
+	 * @param version
+	 *            String representation of the version identifier.
+	 * @throws IllegalArgumentException
+	 *             If <code>version</code> is improperly formatted.
+	 */
+	public Version(String version) {
+		int major = 0;
+		int minor = 0;
+		int micro = 0;
+		String qualifier = ""; //$NON-NLS-1$
+
+		if (version == null) {
+			throw new IllegalArgumentException("version string can not be null"); //$NON-NLS-1$
+		}
+		if (version.length() == 0) {
+			throw new IllegalArgumentException("version string can not be empty"); //$NON-NLS-1$
+		}
+
+		try {
+			StringTokenizer st = new StringTokenizer(version, SEPARATOR, true);
+			major = Integer.parseInt(st.nextToken());
+
+			if (st.hasMoreTokens()) {
+				st.nextToken(); // consume delimiter
+				minor = Integer.parseInt(st.nextToken());
+
+				if (st.hasMoreTokens()) {
+					st.nextToken(); // consume delimiter
+					micro = Integer.parseInt(st.nextToken());
+
+					if (st.hasMoreTokens()) {
+						st.nextToken(); // consume delimiter
+						qualifier = st.nextToken();
+
+						if (st.hasMoreTokens()) {
+							throw new IllegalArgumentException("invalid format: " + version); //$NON-NLS-1$
+						}
+					}
+				}
+			}
+		}
+		catch (NoSuchElementException e) {
+			throw new IllegalArgumentException("invalid format: " + version); //$NON-NLS-1$
+		}
+
+		this.major = major;
+		this.minor = minor;
+		this.micro = micro;
+		this.qualifier = qualifier;
+		validate();
+	}
+
+	/**
+	 * Called by the Version constructors to validate the version components.
+	 * 
+	 * @throws IllegalArgumentException
+	 *             If the numerical components are negative or the qualifier
+	 *             string is invalid.
+	 */
+	private void validate() {
+		if (major < 0) {
+			throw new IllegalArgumentException("negative major"); //$NON-NLS-1$
+		}
+		if (minor < 0) {
+			throw new IllegalArgumentException("negative minor"); //$NON-NLS-1$
+		}
+		if (micro < 0) {
+			throw new IllegalArgumentException("negative micro"); //$NON-NLS-1$
+		}
+		int length = qualifier.length();
+		for (int i = 0; i < length; i++) {
+			if ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".indexOf(qualifier.charAt(i)) == -1) { //$NON-NLS-1$
+				throw new IllegalArgumentException("invalid qualifier"); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/**
+	 * Parses a version identifier from the specified string.
+	 * 
+	 * <p>
+	 * See <code>Version(String)</code> for the format of the version
+	 * string.
+	 * 
+	 * @param version
+	 *            String representation of the version identifier. Leading and
+	 *            trailing whitespace will be ignored.
+	 * @return A <code>Version</code> object representing the version
+	 *         identifier. If <code>version</code> is <code>null</code> or
+	 *         the empty string then <code>emptyVersion</code> will be
+	 *         returned.
+	 * @throws IllegalArgumentException
+	 *             If <code>version</code> is improperly formatted.
+	 */
+	public static Version parseVersion(String version) {
+		if (version == null) {
+			return emptyVersion;
+		}
+
+		version = version.trim();
+		if (version.length() == 0) {
+			return emptyVersion;
+		}
+
+		return new Version(version);
+	}
+
+	/**
+	 * Returns the major component of this version identifier.
+	 * 
+	 * @return The major component.
+	 */
+	public int getMajor() {
+		return major;
+	}
+
+	/**
+	 * Returns the minor component of this version identifier.
+	 * 
+	 * @return The minor component.
+	 */
+	public int getMinor() {
+		return minor;
+	}
+
+	/**
+	 * Returns the micro component of this version identifier.
+	 * 
+	 * @return The micro component.
+	 */
+	public int getMicro() {
+		return micro;
+	}
+
+	/**
+	 * Returns the qualifier component of this version identifier.
+	 * 
+	 * @return The qualifier component.
+	 */
+	public String getQualifier() {
+		return qualifier;
+	}
+
+	/**
+	 * Returns the string representation of this version identifier.
+	 * 
+	 * <p>
+	 * The format of the version string will be <code>major.minor.micro</code>
+	 * if qualifier is the empty string or
+	 * <code>major.minor.micro.qualifier</code> otherwise.
+	 * 
+	 * @return The string representation of this version identifier.
+	 */
+	public String toString() {
+		String base = major + SEPARATOR + minor + SEPARATOR + micro;
+		if (qualifier.length() == 0) {
+			return base;
+		}
+		else {
+			return base + SEPARATOR + qualifier;
+		}
+	}
+
+	/**
+	 * Returns a hash code value for the object.
+	 * 
+	 * @return An integer which is a hash code value for this object.
+	 */
+	public int hashCode() {
+		return (major << 24) + (minor << 16) + (micro << 8) + qualifier.hashCode();
+	}
+
+	/**
+	 * Compares this <code>Version</code> object to another object.
+	 * 
+	 * <p>
+	 * A version is considered to be <b>equal to </b> another version if the
+	 * major, minor and micro components are equal and the qualifier component
+	 * is equal (using <code>String.equals</code>).
+	 * 
+	 * @param object
+	 *            The <code>Version</code> object to be compared.
+	 * @return <code>true</code> if <code>object</code> is a
+	 *         <code>Version</code> and is equal to this object;
+	 *         <code>false</code> otherwise.
+	 */
+	public boolean equals(Object object) {
+		if (object == this) { // quicktest
+			return true;
+		}
+
+		if (!(object instanceof Version)) {
+			return false;
+		}
+
+		Version other = (Version) object;
+		return (major == other.major) && (minor == other.minor) && (micro == other.micro) && qualifier.equals(other.qualifier);
+	}
+
+	/**
+	 * Compares this <code>Version</code> object to another object.
+	 * 
+	 * <p>
+	 * A version is considered to be <b>less than </b> another version if its
+	 * major component is less than the other version's major component, or
+	 * the major components are equal and its minor component is less than the
+	 * other version's minor component, or the major and minor components are
+	 * equal and its micro component is less than the other version's micro
+	 * component, or the major, minor and micro components are equal and it's
+	 * qualifier component is less than the other version's qualifier
+	 * component (using <code>String.compareTo</code>).
+	 * 
+	 * <p>
+	 * A version is considered to be <b>equal to</b> another version if the
+	 * major, minor and micro components are equal and the qualifier component
+	 * is equal (using <code>String.compareTo</code>).
+	 * 
+	 * @param object
+	 *            The <code>Version</code> object to be compared.
+	 * @return A negative integer, zero, or a positive integer if this object
+	 *         is less than, equal to, or greater than the specified
+	 *         <code>Version</code> object.
+	 * @throws ClassCastException
+	 *             If the specified object is not a <code>Version</code>.
+	 */
+	public int compareTo(Object object) {
+		if (object == this) { // quicktest
+			return 0;
+		}
+
+		Version other = (Version) object;
+
+		int result = major - other.major;
+		if (result != 0) {
+			return result;
+		}
+
+		result = minor - other.minor;
+		if (result != 0) {
+			return result;
+		}
+
+		result = micro - other.micro;
+		if (result != 0) {
+			return result;
+		}
+
+		return qualifier.compareTo(other.qualifier);
+	}
+}
\ No newline at end of file