*** empty log message ***
diff --git a/update/org.eclipse.update.configurator/.classpath b/update/org.eclipse.update.configurator/.classpath
new file mode 100644
index 0000000..065ac06
--- /dev/null
+++ b/update/org.eclipse.update.configurator/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/update/org.eclipse.update.configurator/.cvsignore b/update/org.eclipse.update.configurator/.cvsignore
new file mode 100644
index 0000000..c5e82d7
--- /dev/null
+++ b/update/org.eclipse.update.configurator/.cvsignore
@@ -0,0 +1 @@
+bin
\ No newline at end of file
diff --git a/update/org.eclipse.update.configurator/.options b/update/org.eclipse.update.configurator/.options
new file mode 100644
index 0000000..403dd4a
--- /dev/null
+++ b/update/org.eclipse.update.configurator/.options
@@ -0,0 +1,8 @@
+# Debugging options for the org.eclipse.update.configurator plugin
+
+# Turn on general debugging for the plugin.
+org.eclipse.update.configurator/debug=false
+
+# Turns on debug for the plugin converter.
+org.eclipse.update.configurator/converter/debug=false
+
diff --git a/update/org.eclipse.update.configurator/.project b/update/org.eclipse.update.configurator/.project
new file mode 100644
index 0000000..d860656
--- /dev/null
+++ b/update/org.eclipse.update.configurator/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.update.configurator</name>
+	<comment></comment>
+	<projects>
+		<project>org.eclipse.core.boot</project>
+		<project>org.eclipse.core.runtime</project>
+		<project>org.eclipse.core.runtime.osgi</project>
+		<project>org.eclipse.osgi</project>
+		<project>org.eclipse.osgi.util</project>
+	</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.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/update/org.eclipse.update.configurator/META-INF/MANIFEST.MF b/update/org.eclipse.update.configurator/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1a3bc9b
--- /dev/null
+++ b/update/org.eclipse.update.configurator/META-INF/MANIFEST.MF
@@ -0,0 +1,24 @@
+Bundle-Name: org.eclipse.update.configurator

+Bundle-GlobalName: org.eclipse.update.configurator

+Bundle-Description: Eclipse Update Configurator

+Bundle-Copyright: Open Source Materials - Eclipse 

+Bundle-Version: 3.0.0

+Bundle-DocUrl: http://www.eclipse.org

+Build-Information: ${build-information}

+Bundle-Activator: 

+ org.eclipse.update.configurator.ConfigurationActivator

+Bundle-ClassPath: 

+ configurator.jar

+Import-Package: 

+ org.osgi.framework,

+ org.osgi.util.tracker,

+ org.osgi.service.startlevel,

+ org.osgi.service.packageadmin,

+ org.eclipse.osgi.service.environment

+Export-Package:

+ org.eclipse.update.configurator

+Provide-Package: 

+ org.eclipse.update.configurator

+Require-Bundle: 

+ org.eclipse.core.runtime.osgi,

+ org.apache.xerces;optional=true

diff --git a/update/org.eclipse.update.configurator/build.properties b/update/org.eclipse.update.configurator/build.properties
new file mode 100644
index 0000000..26fc617
--- /dev/null
+++ b/update/org.eclipse.update.configurator/build.properties
@@ -0,0 +1,6 @@
+bin.includes = .options,\
+               plugin.properties,\
+               plugin.xml,\
+               configurator.jar,\
+               META-INF/
+source.configurator.jar = src/
diff --git a/update/org.eclipse.update.configurator/plugin.properties b/update/org.eclipse.update.configurator/plugin.properties
new file mode 100644
index 0000000..22d0828
--- /dev/null
+++ b/update/org.eclipse.update.configurator/plugin.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2000, 2003 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
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+pluginName= Install/Update Core
+providerName= Eclipse.org
+fragmentNameWin= Install/Update Core for Windows
+fragmentNameLinux= Install/Update Core for Linux
+
+
diff --git a/update/org.eclipse.update.configurator/plugin.xml b/update/org.eclipse.update.configurator/plugin.xml
new file mode 100644
index 0000000..fbeb811
--- /dev/null
+++ b/update/org.eclipse.update.configurator/plugin.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin
+  name="%pluginName"
+  id="org.eclipse.update.configurator"
+  version="3.0.0"
+  provider-name="%providerName">
+
+  <runtime>
+    <library name="configurator.jar">
+      <export name="*"/>
+    </library>   
+  </runtime>
+
+  <requires>
+    <import plugin="org.eclipse.osgi"/>
+    <import plugin="org.eclipse.osgi.util"/>
+    <import plugin="org.eclipse.core.runtime.osgi"/>
+  </requires>
+</plugin>
\ No newline at end of file
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/ConfigurationActivator.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/ConfigurationActivator.java
new file mode 100644
index 0000000..8c620c6
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/ConfigurationActivator.java
@@ -0,0 +1,368 @@
+/*******************************************************************************
+ * Copyright (c) 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.service.environment.DebugOptions;
+import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.osgi.framework.*;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class ConfigurationActivator implements BundleActivator {
+	private final static String DEFAULT_CONVERTER = "org.eclipse.update.configurator.migration.PluginConverter"; //$NON-NLS-1$
+	public static String PI_CONFIGURATOR = "org.eclipse.update.configurator";
+	// debug options
+	public static String OPTION_DEBUG = PI_CONFIGURATOR + "/debug";
+	public static String OPTION_DEBUG_CONVERTER = PI_CONFIGURATOR + "/converter/debug";
+	// debug values
+	public static boolean DEBUG = false;
+	public static boolean DEBUG_CONVERTER = false;
+
+	private static BundleContext context;
+	private ServiceTracker platformTracker;
+	private ServiceRegistration configurationFactorySR;
+	private String[] allArgs;
+
+	// location used to put the generated manfests
+	private String cacheLocation = (String) System.getProperties().get("osgi.manifest.cache"); //PASCAL Need to set this value somewhere (probably from boot)
+	private IPluginConverter converter;
+	private Set ignore;
+	private BundleListener reconcilerListener;
+	private IPlatform platform;
+	private PlatformConfiguration configuration;
+
+	public void start(BundleContext ctx) throws Exception {
+		context = ctx;
+		obtainArgs();
+		initialize();
+		//Short cut, if the configuration has not changed
+		String application = configuration.getApplicationIdentifier();
+
+		//TODO Hack until PDE changes the default application that they are setting
+		if("org.eclipse.ui.workbench".equals(System.getProperties().get("eclipse.application"))) { //$NON-NLS-1$ //$NON-NLS-2$
+			System.setProperty("eclipse.application", "org.eclipse.ui.ide.workbench"); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		if (!(application.equals(PlatformConfiguration.RECONCILER_APP) || System.getProperties().get("osgi.dev") != null))
+			if (System.getProperty("eclipse.application") == null) {
+				System.setProperty("eclipse.application", application);
+				return;
+			}
+		loadOptions();
+		if (DEBUG)
+			System.out.println("Starting update configurator...");
+		computeIgnoredBundles();
+		loadConverter();
+		installBundles();
+	}
+
+	private void initialize() {
+		platform = acquirePlatform();
+		String metaPath = platform.getLocation().append(".metadata").toOSString();
+		URL installURL = platform.getInstallURL();
+		configurationFactorySR = context.registerService(IPlatformConfigurationFactory.class.getName(), new PlatformConfigurationFactory(), null);
+		configuration = getPlatformConfiguration(allArgs, metaPath, installURL);
+	}
+
+	private void computeIgnoredBundles() {
+		String ignoreList = System.getProperty("eclipse.ignore", "org.eclipse.osgi,org.eclipse.core.boot,org.eclipse.core.runtime.adaptor");
+		ignore = new HashSet();
+		StringTokenizer tokenizer = new StringTokenizer(ignoreList, ",");
+		while (tokenizer.hasMoreTokens())
+			ignore.add(tokenizer.nextToken().trim());
+	}
+	private boolean shouldIgnore(String bundleName) {
+		if (ignore == null)
+			return false;
+		StringTokenizer tokenizer = new StringTokenizer(bundleName, "._");
+		String partialName = "";
+		while (tokenizer.hasMoreTokens()) {
+			partialName += tokenizer.nextToken();
+			if (ignore.contains(partialName))
+				return true;
+			partialName += ".";
+		}
+		return false;
+	}
+	private void loadConverter() {
+		// TODO look at making this an extension
+		String converterClassName = System.getProperty("eclipse.manifestconverter", DEFAULT_CONVERTER);
+		if (converterClassName == null)
+			return;
+		Class converterClass;
+		try {
+			converterClass = Class.forName(converterClassName);
+		} catch (ClassNotFoundException e) {
+			return;
+		}
+		try {
+			converter = (IPluginConverter) converterClass.newInstance();
+		} catch (InstantiationException e1) {
+			return;
+		} catch (IllegalAccessException e1) {
+			return;
+		} catch (ClassCastException cce) {
+			return;
+		}
+	}
+	private void obtainArgs() {
+		// all this is only to get the application args		
+		EnvironmentInfo envInfo = null;
+		ServiceReference envInfoSR = context.getServiceReference(EnvironmentInfo.class.getName());
+		if (envInfoSR != null)
+			envInfo = (EnvironmentInfo) context.getService(envInfoSR);
+		if (envInfo == null)
+			throw new IllegalStateException();
+		this.allArgs = envInfo.getAllArgs();
+		// we have what we want - release the service
+		context.ungetService(envInfoSR);
+	}
+
+	public void stop(BundleContext ctx) throws Exception {
+		releasePlatform();
+		configurationFactorySR.unregister();
+	}
+
+	private void releasePlatform() {
+		if (platformTracker == null)
+			return;
+		platformTracker.close();
+		platformTracker = null;
+	}
+	private IPlatform acquirePlatform() {
+		if (platformTracker == null) {
+			platformTracker = new ServiceTracker(context, IPlatform.class.getName(), null);
+			platformTracker.open();
+		}
+		IPlatform result = (IPlatform) platformTracker.getService();
+		while (result == null) {
+			try {
+				platformTracker.waitForService(1000);
+				result = (IPlatform) platformTracker.getService();
+			} catch (InterruptedException ie) {
+			}
+		}
+		return result;
+	}
+
+	private void installBundles() {
+		String metaPath = platform.getLocation().append(".metadata").toOSString();
+		URL installURL = platform.getInstallURL();
+		ServiceReference reference = context.getServiceReference(StartLevel.class.getName());
+		StartLevel start = null;
+		if (reference != null)
+			start = (StartLevel) context.getService(reference);
+		try {
+			URL[] plugins = configuration.getPluginPath();
+			ArrayList installed = new ArrayList(plugins.length);
+			for (int i = 0; i < plugins.length; i++) {
+				String location = plugins[i].toExternalForm();
+				try {
+					checkOrGenerateManifest(location);
+					location = "reference:" + location.substring(0, location.lastIndexOf('/'));
+					if (!isInstalled(location)) {
+						Bundle target = context.installBundle(location);
+						installed.add(target);
+						if (start != null)
+							start.setBundleStartLevel(target, 4);
+					}
+				} catch (Exception e) {
+					System.err.println("Ignoring bundle at: " + location);
+					System.err.println(e.getMessage());
+				}
+			}
+			context.ungetService(reference);
+			refreshPackages((Bundle[]) installed.toArray(new Bundle[installed.size()]));
+			if (System.getProperty("eclipse.application") == null || System.getProperty("eclipse.application").equals(PlatformConfiguration.RECONCILER_APP))
+				System.setProperty("eclipse.application", configuration.getApplicationIdentifier());
+			//			if (config.getApplicationIdentifier().equals(PlatformConfiguration.RECONCILER_APP) ) {
+			//				reconcilerListener = reconcilerListener();
+			//				context.addBundleListener(reconcilerListener);
+			//			}
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} finally {
+			releasePlatform();
+		}
+	}
+
+	private BundleListener reconcilerListener() {
+		return new BundleListener() {
+			public void bundleChanged(BundleEvent event) {
+				String buid = event.getBundle().getGlobalName();
+				if (event.getType() == BundleEvent.STOPPED && buid != null && buid.equals("org.eclipse.update.core"))
+					runPostReconciler();
+			}
+		};
+	}
+
+	private void runPostReconciler() {
+		Runnable postReconciler = new Runnable() {
+			public void run() {
+				try {
+					Bundle apprunner = context.getBundle("org.eclipse.core.applicationrunner");
+					apprunner.stop();
+					context.removeBundleListener(reconcilerListener);
+					try {
+						PlatformConfiguration.shutdown();
+					} catch (IOException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					}
+					installBundles();
+					apprunner.start();
+				} catch (BundleException be) {
+					be.printStackTrace();
+				}
+			}
+		};
+		new Thread(postReconciler, "Post reconciler").start();
+	}
+	/**
+	 * @param location
+	 */
+	private void checkOrGenerateManifest(String pluginManifestLocationURL) {
+		if (converter == null)
+			return;
+		String pluginManifestLocation = null;
+		try {
+			pluginManifestLocation = new URL(pluginManifestLocationURL).getPath();
+		} catch (MalformedURLException e) {
+			return;
+		}
+		File pluginDir = new File(pluginManifestLocation).getParentFile();
+		if (shouldIgnore(pluginDir.getName()))
+			return;
+		File manifest = new File(pluginDir, "META-INF/MANIFEST.MF");
+		if (manifest.exists())
+			return;
+		// bail if the install location is not writable and we don't know where else to write to
+		if (cacheLocation == null)
+			return;
+		File generationLocation = new File(cacheLocation, computeFileName(pluginDir.getPath()) + ".MF");
+		if (generationLocation.exists())
+			return;
+		if (!converter.convertManifest(pluginDir, generationLocation))
+			System.out.println(pluginDir + " manifest generation failed");
+	}
+	/*
+	 * Derives a file name corresponding to a path:
+	 * c:\autoexec.bat -> c__autoexec.bat
+	 */
+	private String computeFileName(String filePath) {
+		StringBuffer newName = new StringBuffer(filePath);
+		for (int i = 0; i < filePath.length(); i++) {
+			char c = newName.charAt(i);
+			if (c == ':' || c == '/' || c == '\\')
+				newName.setCharAt(i, '_');
+		}
+		return newName.toString();
+	}
+	/**
+	 * This is a major hack to try to get the reconciler application running. However we should find a way to not run it.
+	 * @param args
+	 * @param metaPath
+	 * @return
+	 */
+	private PlatformConfiguration getPlatformConfiguration(String[] args, String metaPath, URL installURL) {
+		try {
+			PlatformConfiguration.startup(args, null, null, metaPath, installURL);
+		} catch (Exception e) {
+			if (platformTracker != null) {
+				String message = e.getMessage();
+				if (message == null)
+					message = "";
+				IStatus status = new Status(IStatus.ERROR, IPlatform.PI_RUNTIME, IStatus.OK, message, e);
+				((IPlatform) platformTracker.getService()).getLog(context.getBundle()).log(status);
+			}
+		}
+		return PlatformConfiguration.getCurrent();
+
+	}
+
+	/**
+	 * Do PackageAdmin.refreshPackages() in a synchronous way.  After installing
+	 * all the requested bundles we need to do a refresh and want to ensure that 
+	 * everything is done before returning.
+	 * @param bundles
+	 */
+	private void refreshPackages(Bundle[] bundles) {
+		if (bundles.length == 0)
+			return;
+		ServiceReference packageAdminRef = context.getServiceReference(PackageAdmin.class.getName());
+		PackageAdmin packageAdmin = null;
+		if (packageAdminRef != null) {
+			packageAdmin = (PackageAdmin) context.getService(packageAdminRef);
+			if (packageAdmin == null)
+				return;
+		}
+		// TODO this is such a hack it is silly.  There are still cases for race conditions etc
+		// but this should allow for some progress...
+		final Object semaphore = new Object();
+		FrameworkListener listener = new FrameworkListener() {
+			public void frameworkEvent(FrameworkEvent event) {
+				if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED)
+					synchronized (semaphore) {
+						semaphore.notifyAll();
+					}
+			}
+		};
+		context.addFrameworkListener(listener);
+		packageAdmin.refreshPackages(bundles);
+		synchronized (semaphore) {
+			try {
+				semaphore.wait();
+			} catch (InterruptedException e) {
+			}
+		}
+		context.removeFrameworkListener(listener);
+		context.ungetService(packageAdminRef);
+	}
+
+	private boolean isInstalled(String location) {
+		Bundle[] installed = context.getBundles();
+		for (int i = 0; i < installed.length; i++) {
+			Bundle bundle = installed[i];
+			if (location.equalsIgnoreCase(bundle.getLocation()))
+				return true;
+		}
+		return false;
+	}
+
+	private void loadOptions() {
+		// all this is only to get the application args		
+		DebugOptions service = null;
+		ServiceReference reference = context.getServiceReference(DebugOptions.class.getName());
+		if (reference != null)
+			service = (DebugOptions) context.getService(reference);
+		if (service == null)
+			return;
+		try {
+			DEBUG = service.getBooleanOption(OPTION_DEBUG, false);
+			if (!DEBUG)
+				return;
+			DEBUG_CONVERTER = service.getBooleanOption(OPTION_DEBUG_CONVERTER, false);
+		} finally {
+			// we have what we want - release the service
+			context.ungetService(reference);
+		}
+	}
+	public static BundleContext getBundleContext() {
+		return context;
+	}
+}
\ No newline at end of file
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPlatformConfiguration.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPlatformConfiguration.java
new file mode 100644
index 0000000..2c31cbe
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPlatformConfiguration.java
@@ -0,0 +1,566 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Platform configuration interface. Represents the runtime
+ * configuration used by the Eclipse platform. Any configuration
+ * changes do not take effect until next startup of the Eclipse
+ * platform
+ * 
+ * @since 2.0
+ */
+public interface IPlatformConfiguration {
+
+	/**
+	 * Configuration entry representing an install site.
+	 * 
+	 * @since 2.0 
+	 */
+	public interface ISiteEntry {
+
+		/**
+		 * Returns the URL for this site
+		 * 
+		 * @return site url
+		 * @since 2.0
+		 */
+		public URL getURL();
+
+		/**
+		 * Returns the policy for this site
+		 * 
+		 * @return site policy
+		 * @since 2.0
+		 */
+		public ISitePolicy getSitePolicy();
+
+		/**
+		 * Sets the site policy
+		 * 
+		 * @param policy site policy
+		 * @since 2.0
+		 */
+		public void setSitePolicy(ISitePolicy policy);
+
+		/**
+		 * Returns a list of features visible on the site. Note, that this is simply a 
+		 * reflection of the site content. The features may or may not be actually configured.
+		 * 
+		 * @return an array of feature entries, or an empty array if no features are found.
+		 * A feature entry is returned as a path relative to the site URL
+		 * @since 2.0
+		 */
+		public String[] getFeatures();
+
+		/**
+		 * Returns a list of plug-ins visible on the site. Note, that this is simply a 
+		 * reflection of the site content and the current policy for the site. The plug-ins
+		 * may or may not end up being used by Eclipse (depends on which plug-in are 
+		 * actually bound by the platform).
+		 * 
+		 * @return an array of plug-in entries, or an empty array if no plug-ins are found.
+		 * A plug-in entry is returned as a path relative to the site URL		 * 
+		 * @since 2.0
+		 */
+		public String[] getPlugins();
+
+		/**
+		 * Returns a stamp reflecting the current state of the site. If called repeatedly,
+		 * returns the same value as long as no changes were made to the site (changes to
+		 * features or plugins).
+		 * 
+		 * @return site change stamp
+		 * @since 2.0
+		 */
+		public long getChangeStamp();
+
+		/**
+		 * Returns a stamp reflecting the current state of the features on the site. 
+		 * If called repeatedly, returns the same value as long as no changes were made to
+		 * features on the site.
+		 * 
+		 * @return site features change stamp 
+		 * @since 2.0
+		 */
+		public long getFeaturesChangeStamp();
+
+		/**
+		 * Returns a stamp reflecting the current state of the plug-ins on the site. 
+		 * If called repeatedly, returns the same value as long as no changes were made to
+		 * plug-ins on the site.
+		 * 
+		 * @return site plug-ins change stamp 
+		 * @since 2.0
+		 */
+		public long getPluginsChangeStamp();
+
+		/**
+		 * Returns an indication whether the site can be updated.
+		 * 
+		 * @return <code>true</code> if site can be updated, <code>false</code> otherwise
+		 * @since 2.0
+		 */
+		public boolean isUpdateable();
+
+		/**
+		 * Returns an indication whether the site represents an install site
+		 * that has been linked via a native installer (using the links/<linkfile>
+		 * mechanism)
+		 * 
+		 * @return <code>true</code> if the site is linked, <code>false</code> otherwise
+		 * @since 2.0
+		 */
+		public boolean isNativelyLinked();
+	}
+
+	/**
+	 * Site policy. The site policy object determines how plug-ins
+	 * contained on the site are processed during startup. In general,
+	 * there are 3 ways of configuring a site policy
+	 * <ul>
+	 * <li>explicitly specify which plug-ins are to be included at
+	 * startup (type==USER_INCLUDE). Any other plug-ins located
+	 * at the site are ignored at startup. This is typically the best
+	 * policy when using remote sites where the user wishes
+	 * to retain explicit control over the plug-ins that are included
+	 * from such site.
+	 * <li>explicitly specify which plug-ins are to be excluded at
+	 * startup (type==USER-EXCLUDE). All other plug-ins located
+	 * at the site are used at startup. This policy requires that
+	 * the site support an access "protocol" that allows plug-in
+	 * discovery. In general, these are sites defined using the "file"
+	 * URL protocol. This is typically the best policy for local
+	 * install sites (on the user system).
+	 * </ul>
+	 * 
+	 * @since 2.0
+	 */
+	public interface ISitePolicy {
+
+		/**
+		 * Policy type constants.
+		 */
+
+		/** 
+		 * User-defined inclusion list. The list associated with this
+		 * policy type is interpreted as path entries to included plugin.xml
+		 * or fragment.xml <b>relative</b> to the site URL
+		 */
+		public static final int USER_INCLUDE = 0;
+
+		/**
+		 * User-defined exclusion list. The list associated with this
+		 * policy type is interpreted as path entries to excluded plugin.xml
+		 * or fragment.xml <b>relative</b> to the site URL
+		 */
+		public static final int USER_EXCLUDE = 1;
+
+		/**
+		 * Return policy type
+		 * 
+		 * @return policy type
+		 * @since 2.0
+		 */
+		public int getType();
+
+		/**
+		 * Return policy inclusion/ exclusion list
+		 * 
+		 * @return the list as an array
+		 * @since 2.0
+		 */
+		public String[] getList();
+
+		/**
+		 * Set new policy list. The list entries are interpreted based on the policy
+		 * type. See description of the policy type constants for details.
+		 * 
+		 * @param list policy inclusion/ exclusion list as an array.
+		 * Returns an empty array if there are no entries.
+		 * @see #USER_INCLUDE
+		 * @see #USER_EXCLUDE
+		 * @since 2.0
+		 */
+		public void setList(String[] list);
+	}
+
+	/**
+	 * Feature entry.
+	 * Represents runtime "hints" about configured features.
+	 * The information is used during execution to locate the
+	 * correct attribution information for a feature. Note,
+	 * that a typical configuration can declare multiple feature
+	 * entries. At execution time, only one can be selected as
+	 * the active primary feature. This is determined based on 
+	 * specified command line arguments or computed defaults.
+	 * 
+	 * @since 2.0
+	 */
+	public interface IFeatureEntry {
+
+		/**
+		 * Returns feature identifier.
+		 * @return feature identifier
+		 * @since 2.0
+		 */
+		public String getFeatureIdentifier();
+
+		/**
+		 * Returns the currently configured version for the feature.
+		 * @return feature version (as string), or <code>null</code>
+		 * @since 2.0
+		 */
+		public String getFeatureVersion();
+
+		/**
+		 * Returns the identifier of the feature plug-in for this feature entry.
+			 *  Note,      that there is no guarantee that a feature in fact
+			 * supplies a corresponding feature plugin, so the result can be
+			 * <code>null</code>. Also, if supplied, there is no guarantee that the
+			 * plugin will in fact be loaded into the plug-in registry at runtime
+			 * (due to rules and constraint checking performed by the registry
+			 * loading support). Consequently code making use of this method must
+			 * handle these conditions.
+		 * @return feature identifier (as string), or <code>null</code>
+		 * @since 2.1
+		 */
+		public String getFeaturePluginIdentifier();
+
+		/**
+		 * Returns the version of the feature plug-in for this feature
+		 * entry. Note, that there is no guarantee that a feature in fact
+		 * supplies a corresponding feature plugin, so the result can be
+		 * <code>null</code>. Also, if supplied, there is no guarantee that the
+		 * plugin will in fact be loaded into the plug-in registry at runtime
+		 * (due to rules and constraint checking performed by the registry
+		 * loading support). Consequently code making use of this method must
+		 * handle these conditions.
+		 * @return feature version (as string), or <code>null</code>
+		 * @since 2.0
+		 */
+		public String getFeaturePluginVersion();
+
+		/**
+		 * Returns the application to run when this feature is the
+		 * primary feature.
+		 * @return application identifier, or <code>null</code> 
+		 * @since 2.0
+		 */
+		public String getFeatureApplication();
+
+		/**
+		 * Returns URLs to the feature "root" locations. The root
+		 * URLs are install locations of the feature plugin and its
+		 * fragments.
+		 *
+		 * @return array of URLs, or an empty array
+		 * @since 2.0
+		 */
+		public URL[] getFeatureRootURLs();
+
+		/**
+		 * Returns an indication whether this feature has been defined
+		 * to act as a primary feature.
+		 * @return <code>true</code> if the feature can be primary,
+		 * <code>false</code> otherwise.
+		 * @since 2.0
+		 */
+		public boolean canBePrimary();
+	}
+
+	/**
+	 * Create a site entry
+	 *
+	 * @param url site URL
+	 * @param policy site policy
+	 * @return created site entry
+	 * @since 2.0
+	 */
+	public ISiteEntry createSiteEntry(URL url, ISitePolicy policy);
+
+	/**
+	 * Create a site policy. The policy determines the way the site
+	 * plug-in are processed at startpu
+	 *
+	 * @param type policy type
+	 * @param list an array of site-relative paths representing the
+	 * inclusion/ exclusion list
+	 * @return created site policy entry
+	 * @since 2.0
+	 */
+	public ISitePolicy createSitePolicy(int type, String[] list);
+
+	/**
+	 * Create a feature entry
+	 * @param id feature identifier. Must not be <code>null</code>.
+	 * @param version feature version (as String). Can be <code>null</code>.
+	 * @param pluginVersion version of the feature plugin (as String). Can be
+	 * <code>null</code>.
+	 * @param primary <code>true</code> if the feature is defined as a primary
+	 * feature, otherwise <code>false</code>.
+	 * @param application identifier of the application to run when 
+	 * this feature is the primary feature. Can be <code>null</code>.
+	 * If specified, the identifier must represent a valid extension 
+	 * registered in the <code>org.eclipse.core.runtime.applications</code>
+	 * extension point.
+	 * @param an array of URLs to feature root directories.
+	 * These are URLs to install locations for the feature plugin
+	 * and its fragments. Can be <code>null</code>.
+	 * @return create feature entry
+	 * @since 2.0
+	 */
+	public IFeatureEntry createFeatureEntry(String id, String version, String pluginVersion, boolean primary, String application, URL[] root);
+
+	/**
+	 * Create a feature entry
+	 * @param id feature identifier. Must not be <code>null</code>.
+	 * @param version feature version (as String). Can be <code>null</code>.
+	 * @param pluginIdentifier identifier of the feature plugin (as String). Can
+	 * be <code>null</code>.
+	 * @param  pluginVersion  version of the feature plugin (as String). Can be
+	 * <code>null</code>.
+	 * @param primary <code>true</code> if the feature is defined as a primary
+	 * feature, otherwise <code>false</code>.
+	 * @param application identifier of the application to run when
+	 * this feature is the primary feature. Can be <code>null</code>.
+	 * If specified, the identifier must represent a valid extension
+	 * registered in the <code>org.eclipse.core.runtime.applications</code>
+	 * extension point.
+	 * @param an array of URLs to feature root directories.
+	 * These are URLs to install locations for the feature plugin
+	 * and its fragments. Can be <code>null</code>.
+	 * @return create feature entry
+	 * @since 2.1
+	 */
+	public IFeatureEntry createFeatureEntry(String id, String version, String pluginIdentifier, String pluginVersion, boolean primary, String application, URL[] root);
+
+	/**
+	 * Configures the specified site entry. If a site entry with the
+	 * same site URL is already configured, the entry is <b>not</b> replaced.
+	 * 
+	 * @param entry site entry 
+	 * @since 2.0
+	 */
+	public void configureSite(ISiteEntry entry);
+
+	/**
+	 * Configures the specified site entry. If a site entry with the
+	 * same site URL is already configured, the replacement behavior for
+	 * the entry can be specified.
+	 * 
+	 * @param entry site entry 
+	 * @param  flag indicating whether an existing configured entry with
+	 * the same URL should be replaced (<code>true</code>) or not (<code>false</code>).
+	 * @since 2.0
+	 */
+	public void configureSite(ISiteEntry entry, boolean replace);
+
+	/**
+	 * Unconfigures the specified entry. Does not do anything if the entry
+	 * is not configured.
+	 * 
+	 * @param entry site entry
+	 * @since 2.0
+	 */
+	public void unconfigureSite(ISiteEntry entry);
+
+	/**
+	 * Returns configured site entries
+	 * 
+	 * @return array of site entries. Returns an empty array if no sites are
+	 * configured
+	 * @since 2.0
+	 */
+	public ISiteEntry[] getConfiguredSites();
+
+	/**
+	 * Returns a site entry matching the specified URL
+	 * 
+	 * @param url site url
+	 * @return matching site entry, or <code>null</code> if no match found
+	 * @since 2.0
+	 */
+	public ISiteEntry findConfiguredSite(URL url);
+
+	/**
+	 * Configures the feature entry.
+	 * If another feature entry with the same feature identifier 
+	 * already exists, it is replaced.
+	 * @param entry feature entry
+	 * @since 2.0
+	 */
+	public void configureFeatureEntry(IFeatureEntry entry);
+
+	/**
+	 * Unconfigures the specified feature entry if it exists.
+	 * @param entry feature entry
+	 * @since 2.0
+	 */
+	public void unconfigureFeatureEntry(IFeatureEntry entry);
+
+	/**
+	 * Returns a list of configured feature entries.
+	 * @return array or entries, or an empty array if no entries
+	 * are configured
+	 * @since 2.0
+	 */
+	public IFeatureEntry[] getConfiguredFeatureEntries();
+
+	/**
+	 * Locates the specified feature entry.
+	 * @param id feature identifier
+	 * @return ferature entry, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public IFeatureEntry findConfiguredFeatureEntry(String id);
+
+	/**
+	 * Returns the URL location of the configuration information
+	 * 
+	 * @return configuration location URL, or <code>null</code> if the
+	 * configuration location could not be determined.
+	 * @since 2.0
+	 */
+	public URL getConfigurationLocation();
+
+	/**
+	 * Returns a stamp reflecting the current state of the configuration. If called repeatedly,
+	 * returns the same value as long as no changes were made to the configuration (changes to
+	 * sites, features or plugins).
+	 * 
+	 * @return configuration change stamp
+	 * @since 2.0
+	 */
+	public long getChangeStamp();
+
+	/**
+	 * Returns a stamp reflecting the current state of the features in the configuration. 
+	 * If called repeatedly, returns the same value as long as no changes were made to
+	 * features in the configuration.
+	 * 
+	 * @return configuration features change stamp 
+	 * @since 2.0
+	 */
+	public long getFeaturesChangeStamp();
+
+	/**
+	 * Returns a stamp reflecting the current state of the plug-ins in the configuration. 
+	 * If called repeatedly, returns the same value as long as no changes were made to
+	 * plug-ins in the configuration.
+	 * 
+	 * @return configuration plug-ins change stamp 
+	 * @since 2.0
+	 */
+	public long getPluginsChangeStamp();
+
+	/**
+	 * Returns the identifier of the configured primary feature. A primary feature
+	 * is used to specify product customization information for a running instance
+	 * of Eclipse. 
+	 * 
+	 * @return primary feature identifier, or <code>null</code> if none configured
+	 * @since 2.0
+	 */
+	public String getPrimaryFeatureIdentifier();
+
+	/**
+	 * Computes the plug-in path for this configuration. The result includes all plug-ins
+	 * visible on each of the configured sites based on each site policy.
+	 * 
+	 * @return an array of plug-in path elements (full URL entries), or an empty array.
+	 * @since 2.0
+	 */
+	public URL[] getPluginPath();
+
+	/**
+	 * Returns an array of bootstrap plugin identifiers whose
+	 * location needs to be explicitly identified in the configuration.
+	 * 
+	 * @return an array of identifiers, or empty array
+	 * otherwise
+	 * @since 2.0
+	 */
+	public String[] getBootstrapPluginIdentifiers();
+
+	/**
+	 * Sets the location of a bootstrap plugin.
+	 * 
+	 * @see IPlatformConfiguration#getBootstrapPluginIdentifiers()
+	 * @param id plugin identifier. Must match one of the entries returned
+	 * by getBootstrapPluginIdentifiers()
+	 * @param location
+	 * @since 2.0
+	 */
+	public void setBootstrapPluginLocation(String id, URL location);
+
+	/**
+	 * Returns an indication whether the configuration can be updated.
+	 * 
+	 * @return <code>true</code> if configuration can be updated, <code>false</code> 
+	 * otherwise
+	 * @since 2.0
+	 */
+	public boolean isUpdateable();
+
+	/**
+	 * Returns an indication whether the configuration is transient. A transient
+	 * configuration typically represents a scenario where the configuration
+	 * was computed for a single instantiation of the platform and is not
+	 * guaranteed to be valid on subsequent instantiations.
+	 * 
+	 * @return <code>true</code> if configuration is transient, <code>false</code> 
+	 * otherwise
+	 * @since 2.0
+	 */
+	public boolean isTransient();
+
+	/**
+	 * Indicates whether the configuration is transient or not. A transient
+	 * configuration typically represents a scenario where the configuration
+	 * was computed for a single instantiation of the platform and is not
+	 * guaranteed to be valid on subsequent instantiations. This method has
+	 * no effect if called on the current platform configuration.
+	 * 
+	 * @see BootLoader#getCurrentPlatformConfiguration()
+	 * @param value <code>true</code> if configuration is transient, <code>false</code> 
+	 * otherwise
+	 * @since 2.0
+	 */
+	public void isTransient(boolean value);
+
+	/**
+	 * Called to refresh the configuration information. In particular,
+	 * causes change stamps to be recomputed based on the current
+	 * configuration state, and updates the lists of available plug-ins.
+	 * @since 2.0
+	 */
+	public void refresh();
+
+	/**
+	 * Called to save the configuration information
+	 * @since 2.0
+	 */
+	public void save() throws IOException;
+
+	/**
+	 * Called to save the configuration information in the
+	 * specified location
+	 * 
+	 * @param url save location.
+	 * @since 2.0
+	 */
+	public void save(URL url) throws IOException;
+
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPlatformConfigurationFactory.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPlatformConfigurationFactory.java
new file mode 100644
index 0000000..3fcdebf
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPlatformConfigurationFactory.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.io.IOException;
+import java.net.URL;
+
+public interface IPlatformConfigurationFactory {
+	/**
+	 * Returns the current platform configuration.
+	 * 
+	 * @return platform configuration used in current instance of platform
+	 */	
+	public IPlatformConfiguration getCurrentPlatformConfiguration();
+	/**
+	 * Returns a platform configuration object, optionally initialized with previously saved
+	 * configuration information.
+	 * 
+	 * @param url location of previously save configuration information. If <code>null</code>
+	 * is specified, an empty configuration object is returned
+	 * @return platform configuration used in current instance of platform
+	 */	
+	public IPlatformConfiguration getPlatformConfiguration(URL url) throws IOException;
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPluginConverter.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPluginConverter.java
new file mode 100644
index 0000000..5652156
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/IPluginConverter.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.io.File;
+
+public interface IPluginConverter {
+	/**
+	 * Converts a plug-in/fragment into a bundle (host/fragment).
+	 * @param pluginLocation
+	 * @param bundleManifestLocation
+	 * @return <code>true</coed>
+	 */
+	public boolean convertManifest(File pluginLocation, File bundleManifestLocation);
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/PlatformConfiguration.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/PlatformConfiguration.java
new file mode 100644
index 0000000..534c583
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/PlatformConfiguration.java
@@ -0,0 +1,2929 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.eclipse.core.internal.boot.PlatformURLConnection;
+import org.eclipse.core.internal.boot.PlatformURLHandler;
+
+public class PlatformConfiguration implements IPlatformConfiguration {
+
+	private static PlatformConfiguration currentPlatformConfiguration = null;
+
+	private URL configLocation;
+	private URL rootLocation;
+	private HashMap sites;
+	private HashMap externalLinkSites; // used to restore prior link site state
+	private HashMap cfgdFeatures;
+	private HashMap bootPlugins;
+	private String defaultFeature;
+	private long lastChangeStamp;
+	private long changeStamp;
+	private boolean changeStampIsValid = false;
+	private long lastFeaturesChangeStamp;
+	private long featuresChangeStamp;
+	private boolean featuresChangeStampIsValid = false;
+	private long lastPluginsChangeStamp;
+	private long pluginsChangeStamp;
+	private boolean pluginsChangeStampIsValid = false;
+	private boolean featureChangesConfigured = false;
+	private boolean transientConfig = false;
+	private File cfgLockFile;
+	private RandomAccessFile cfgLockFileRAF;
+	private BootDescriptor runtimeDescriptor;
+
+	private static String cmdConfiguration = null;
+	private static String cmdFeature = null;
+	private static String cmdApplication = null;
+	private static URL cmdPlugins = null;
+	private static boolean cmdInitialize = false;
+	private static boolean cmdFirstUse = false;
+	private static boolean cmdUpdate = false;
+	private static boolean cmdNoUpdate = false;
+	private static boolean cmdDev = false;
+
+	static boolean DEBUG = false;
+
+	private static final String BOOT_XML = "boot.xml"; //$NON-NLS-1$
+	private static final String BOOT_PLUGIN_ID = "org.eclipse.core.boot"; //$NON-NLS-1$
+	private static final String RUNTIME_PLUGIN_ID = "org.eclipse.core.runtime"; //$NON-NLS-1$
+
+	private static final String ECLIPSE = "eclipse"; //$NON-NLS-1$
+	private static final String PLUGINS = "plugins"; //$NON-NLS-1$
+	private static final String FEATURES = "features"; //$NON-NLS-1$
+	private static final String CONFIG_DIR = ".config"; //$NON-NLS-1$
+	private static final String CONFIG_FILE = CONFIG_DIR + "/platform.cfg"; //$NON-NLS-1$
+	private static final String CONFIG_FILE_INIT = "install.ini"; //$NON-NLS-1$
+	private static final String CONFIG_FILE_LOCK_SUFFIX = ".lock"; //$NON-NLS-1$
+	private static final String CONFIG_FILE_TEMP_SUFFIX = ".tmp"; //$NON-NLS-1$
+	private static final String CONFIG_FILE_BAK_SUFFIX = ".bak"; //$NON-NLS-1$
+	private static final String CHANGES_MARKER = ".newupdates"; //$NON-NLS-1$
+	private static final String LINKS = "links"; //$NON-NLS-1$
+	private static final String PLUGIN_XML = "plugin.xml"; //$NON-NLS-1$
+	private static final String FRAGMENT_XML = "fragment.xml"; //$NON-NLS-1$
+	private static final String FEATURE_XML = "feature.xml"; //$NON-NLS-1$
+	private static final String PRODUCT_SITE_MARKER = ".eclipseproduct"; //$NON-NLS-1$
+	private static final String PRODUCT_SITE_ID = "id"; //$NON-NLS-1$
+	private static final String PRODUCT_SITE_VERSION = "version"; //$NON-NLS-1$
+
+	private static final String[] BOOTSTRAP_PLUGINS = { "org.eclipse.core.boot" }; //$NON-NLS-1$
+	private static final String CFG_BOOT_PLUGIN = "bootstrap"; //$NON-NLS-1$
+	private static final String CFG_SITE = "site"; //$NON-NLS-1$
+	private static final String CFG_URL = "url"; //$NON-NLS-1$
+	private static final String CFG_POLICY = "policy"; //$NON-NLS-1$
+	private static final String[] CFG_POLICY_TYPE = { "USER-INCLUDE", "USER-EXCLUDE" }; //$NON-NLS-1$ //$NON-NLS-2$
+	private static final String CFG_POLICY_TYPE_UNKNOWN = "UNKNOWN"; //$NON-NLS-1$
+	private static final String CFG_LIST = "list"; //$NON-NLS-1$
+	private static final String CFG_STAMP = "stamp"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_STAMP = "stamp.features"; //$NON-NLS-1$
+	private static final String CFG_PLUGIN_STAMP = "stamp.plugins"; //$NON-NLS-1$
+	private static final String CFG_UPDATEABLE = "updateable"; //$NON-NLS-1$
+	private static final String CFG_LINK_FILE = "linkfile"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY = "feature"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_DEFAULT = "feature.default.id"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_ID = "id"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_PRIMARY = "primary"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_VERSION = "version"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_PLUGIN_VERSION = "plugin-version"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_PLUGIN_IDENTIFIER = "plugin-identifier"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_APPLICATION = "application"; //$NON-NLS-1$
+	private static final String CFG_FEATURE_ENTRY_ROOT = "root"; //$NON-NLS-1$
+
+	private static final String INIT_DEFAULT_FEATURE_ID = "feature.default.id"; //$NON-NLS-1$
+	private static final String INIT_DEFAULT_PLUGIN_ID = "feature.default.plugin.id"; //$NON-NLS-1$
+	private static final String INIT_DEFAULT_FEATURE_APPLICATION = "feature.default.application"; //$NON-NLS-1$
+	private static final String DEFAULT_FEATURE_ID = "org.eclipse.platform"; //$NON-NLS-1$
+	private static final String DEFAULT_FEATURE_APPLICATION = "org.eclipse.ui.workbench"; //$NON-NLS-1$
+
+	private static final String CFG_VERSION = "version"; //$NON-NLS-1$
+	private static final String CFG_TRANSIENT = "transient"; //$NON-NLS-1$
+	private static final String VERSION = "2.1"; //$NON-NLS-1$
+	private static final String EOF = "eof"; //$NON-NLS-1$
+	private static final int CFG_LIST_LENGTH = 10;
+
+	private static final int DEFAULT_POLICY_TYPE = ISitePolicy.USER_EXCLUDE;
+	private static final String[] DEFAULT_POLICY_LIST = new String[0];
+
+	private static final String LINK_PATH = "path"; //$NON-NLS-1$
+	private static final String LINK_READ = "r"; //$NON-NLS-1$
+	private static final String LINK_READ_WRITE = "rw"; //$NON-NLS-1$
+
+	private static final String CMD_CONFIGURATION = "-configuration"; //$NON-NLS-1$
+	private static final String CMD_FEATURE = "-feature"; //$NON-NLS-1$
+	private static final String CMD_APPLICATION = "-application"; //$NON-NLS-1$
+	private static final String CMD_PLUGINS = "-plugins"; //$NON-NLS-1$
+	private static final String CMD_UPDATE = "-update"; //$NON-NLS-1$
+	private static final String CMD_INITIALIZE = "-initialize"; //$NON-NLS-1$
+	private static final String CMD_FIRSTUSE = "-firstuse"; //$NON-NLS-1$
+	private static final String CMD_NO_UPDATE = "-noupdate"; //$NON-NLS-1$
+	private static final String CMD_NEW_UPDATES = "-newUpdates"; //$NON-NLS-1$
+	private static final String CMD_DEV = "-dev"; // triggers -noupdate //$NON-NLS-1$
+
+	protected static final String RECONCILER_APP = "org.eclipse.update.core.reconciler"; //$NON-NLS-1$
+
+	private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+	private static URL installURL;
+
+	public class SiteEntry implements IPlatformConfiguration.ISiteEntry {
+
+		private URL url; // this is the external URL for the site
+		private URL resolvedURL; // this is the resolved URL used internally
+		private ISitePolicy policy;
+		private boolean updateable = true;
+		private ArrayList features;
+		private ArrayList plugins;
+		private PlatformConfiguration parent;
+		private long lastChangeStamp;
+		private long changeStamp;
+		private boolean changeStampIsValid = false;
+		private long lastFeaturesChangeStamp;
+		private long featuresChangeStamp;
+		private boolean featuresChangeStampIsValid = false;
+		private long lastPluginsChangeStamp;
+		private long pluginsChangeStamp;
+		private boolean pluginsChangeStampIsValid = false;
+		private String linkFileName = null;
+
+		private SiteEntry() {
+		}
+		private SiteEntry(URL url, ISitePolicy policy, PlatformConfiguration parent) {
+			if (url == null)
+				throw new IllegalArgumentException();
+
+			if (policy == null)
+				throw new IllegalArgumentException();
+
+			if (parent == null)
+				throw new IllegalArgumentException();
+
+			this.url = url;
+			this.policy = policy;
+			this.parent = parent;
+			this.features = null;
+			this.plugins = null;
+			this.resolvedURL = this.url;
+			if (url.getProtocol().equals(PlatformURLHandler.PROTOCOL)) {
+				try {
+					resolvedURL = resolvePlatformURL(url); // 19536
+				} catch (IOException e) {
+					// will use the baseline URL ...
+				}
+			}
+		}
+
+		/*
+		 * @see ISiteEntry#getURL()
+		 */
+		public URL getURL() {
+			return url;
+		}
+
+		/*
+		* @see ISiteEntry#getSitePolicy()
+		*/
+		public ISitePolicy getSitePolicy() {
+			return policy;
+		}
+
+		/*
+		 * @see ISiteEntry#setSitePolicy(ISitePolicy)
+		 */
+		public synchronized void setSitePolicy(ISitePolicy policy) {
+			if (policy == null)
+				throw new IllegalArgumentException();
+			this.policy = policy;
+		}
+
+		/*
+		 * @see ISiteEntry#getFeatures()
+		 */
+		public String[] getFeatures() {
+			return getDetectedFeatures();
+		}
+
+		/*
+		 * @see ISiteEntry#getPlugins()
+		 */
+		public String[] getPlugins() {
+
+			ISitePolicy policy = getSitePolicy();
+
+			if (policy.getType() == ISitePolicy.USER_INCLUDE)
+				return policy.getList();
+
+			if (policy.getType() == ISitePolicy.USER_EXCLUDE) {
+				ArrayList detectedPlugins = new ArrayList(Arrays.asList(getDetectedPlugins()));
+				String[] excludedPlugins = policy.getList();
+				for (int i = 0; i < excludedPlugins.length; i++) {
+					if (detectedPlugins.contains(excludedPlugins[i]))
+						detectedPlugins.remove(excludedPlugins[i]);
+				}
+				return (String[]) detectedPlugins.toArray(new String[0]);
+			}
+
+			// bad policy type
+			return new String[0];
+		}
+
+		/*
+		 * @see ISiteEntry#getChangeStamp()
+		 */
+		public long getChangeStamp() {
+			if (!changeStampIsValid)
+				computeChangeStamp();
+			return changeStamp;
+		}
+
+		/*
+		 * @see ISiteEntry#getFeaturesChangeStamp()
+		 */
+		public long getFeaturesChangeStamp() {
+			if (!featuresChangeStampIsValid)
+				computeFeaturesChangeStamp();
+			return featuresChangeStamp;
+		}
+
+		/*
+		 * @see ISiteEntry#getPluginsChangeStamp()
+		 */
+		public long getPluginsChangeStamp() {
+			if (!pluginsChangeStampIsValid)
+				computePluginsChangeStamp();
+			return pluginsChangeStamp;
+		}
+
+		/*
+		 * @see ISiteEntry#isUpdateable()
+		 */
+		public boolean isUpdateable() {
+			return updateable;
+		}
+
+		/*
+		 * @see ISiteEntry#isNativelyLinked()
+		 */
+		public boolean isNativelyLinked() {
+			return isExternallyLinkedSite();
+		}
+
+		private String[] detectFeatures() {
+
+			// invalidate stamps ... we are doing discovery
+			changeStampIsValid = false;
+			featuresChangeStampIsValid = false;
+			parent.changeStampIsValid = false;
+			parent.featuresChangeStampIsValid = false;
+
+			features = new ArrayList();
+
+			if (!supportsDetection(resolvedURL))
+				return new String[0];
+
+			// locate feature entries on site
+			File siteRoot = new File(resolvedURL.getFile().replace('/', File.separatorChar));
+			File root = new File(siteRoot, FEATURES);
+
+			String[] list = root.list();
+			String path;
+			File plugin;
+			for (int i = 0; list != null && i < list.length; i++) {
+				path = list[i] + File.separator + FEATURE_XML;
+				plugin = new File(root, path);
+				if (!plugin.exists()) {
+					continue;
+				}
+				features.add(FEATURES + "/" + path.replace(File.separatorChar, '/')); //$NON-NLS-1$
+			}
+			if (DEBUG) {
+				debug(resolvedURL.toString() + " located  " + features.size() + " feature(s)"); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			return (String[]) features.toArray(new String[0]);
+		}
+
+		private String[] detectPlugins() {
+
+			// invalidate stamps ... we are doing discovery
+			changeStampIsValid = false;
+			pluginsChangeStampIsValid = false;
+			parent.changeStampIsValid = false;
+			parent.pluginsChangeStampIsValid = false;
+
+			plugins = new ArrayList();
+
+			if (!supportsDetection(resolvedURL))
+				return new String[0];
+
+			// locate plugin entries on site
+			File root = new File(resolvedURL.getFile().replace('/', File.separatorChar) + PLUGINS);
+			String[] list = root.list();
+			String path;
+			File plugin;
+			for (int i = 0; list != null && i < list.length; i++) {
+				path = list[i] + File.separator + PLUGIN_XML;
+				plugin = new File(root, path);
+				if (!plugin.exists()) {
+					path = list[i] + File.separator + FRAGMENT_XML;
+					plugin = new File(root, path);
+					if (!plugin.exists())
+						continue;
+				}
+				plugins.add(PLUGINS + "/" + path.replace(File.separatorChar, '/')); //$NON-NLS-1$
+			}
+			if (DEBUG) {
+				debug(resolvedURL.toString() + " located  " + plugins.size() + " plugin(s)"); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			return (String[]) plugins.toArray(new String[0]);
+		}
+
+		private synchronized String[] getDetectedFeatures() {
+			if (features == null)
+				return detectFeatures();
+			else
+				return (String[]) features.toArray(new String[0]);
+		}
+
+		private synchronized String[] getDetectedPlugins() {
+			if (plugins == null)
+				return detectPlugins();
+			else
+				return (String[]) plugins.toArray(new String[0]);
+		}
+
+		private URL getResolvedURL() {
+			return resolvedURL;
+		}
+
+		private void computeChangeStamp() {
+			computeFeaturesChangeStamp();
+			computePluginsChangeStamp();
+			changeStamp = resolvedURL.hashCode() ^ featuresChangeStamp ^ pluginsChangeStamp;
+			changeStampIsValid = true;
+		}
+
+		private synchronized void computeFeaturesChangeStamp() {
+			if (featuresChangeStampIsValid)
+				return;
+
+			long start = 0;
+			if (DEBUG)
+				start = (new Date()).getTime();
+			String[] features = getFeatures();
+			featuresChangeStamp = computeStamp(features);
+			featuresChangeStampIsValid = true;
+			if (DEBUG) {
+				long end = (new Date()).getTime();
+				debug(resolvedURL.toString() + " feature stamp: " + featuresChangeStamp + ((featuresChangeStamp == lastFeaturesChangeStamp) ? " [no changes]" : " [was " + lastFeaturesChangeStamp + "]") + " in " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+			}
+		}
+
+		private synchronized void computePluginsChangeStamp() {
+			if (pluginsChangeStampIsValid)
+				return;
+
+			long start = 0;
+			if (DEBUG)
+				start = (new Date()).getTime();
+			String[] plugins = getPlugins();
+			pluginsChangeStamp = computeStamp(plugins);
+			pluginsChangeStampIsValid = true;
+			if (DEBUG) {
+				long end = (new Date()).getTime();
+				debug(resolvedURL.toString() + " plugin stamp: " + pluginsChangeStamp + ((pluginsChangeStamp == lastPluginsChangeStamp) ? " [no changes]" : " [was " + lastPluginsChangeStamp + "]") + " in " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+			}
+		}
+
+		private long computeStamp(String[] targets) {
+
+			long result = 0;
+			if (!supportsDetection(resolvedURL)) {
+				// NOTE:  this path should not be executed until we support running
+				//        from an arbitrary URL (in particular from http server). For
+				//        now just compute stamp across the list of names. Eventually
+				//        when general URLs are supported we need to do better (factor
+				//        in at least the existence of the target). However, given this
+				//        code executes early on the startup sequence we need to be
+				//        extremely mindful of performance issues.
+				for (int i = 0; i < targets.length; i++)
+					result ^= targets[i].hashCode();
+				if (DEBUG)
+					debug("*WARNING* computing stamp using URL hashcodes only"); //$NON-NLS-1$
+			} else {
+				// compute stamp across local targets
+				String rootPath = resolvedURL.getFile().replace('/', File.separatorChar);
+				if (!rootPath.endsWith(File.separator))
+					rootPath += File.separator;
+				File rootFile = new File(rootPath);
+				if (rootFile.exists()) {
+					File f = null;
+					for (int i = 0; i < targets.length; i++) {
+						f = new File(rootFile, targets[i]);
+						if (f.exists())
+							result ^= f.getAbsolutePath().hashCode() ^ f.lastModified() ^ f.length();
+					}
+				}
+			}
+
+			return result;
+		}
+
+		private boolean isExternallyLinkedSite() {
+			return (linkFileName != null && !linkFileName.trim().equals("")); //$NON-NLS-1$
+		}
+
+		private synchronized void refresh() {
+			// reset computed values. Will be updated on next access.
+			lastChangeStamp = changeStamp;
+			lastFeaturesChangeStamp = featuresChangeStamp;
+			lastPluginsChangeStamp = pluginsChangeStamp;
+			changeStampIsValid = false;
+			featuresChangeStampIsValid = false;
+			pluginsChangeStampIsValid = false;
+			features = null;
+			plugins = null;
+		}
+
+	}
+
+	public class SitePolicy implements IPlatformConfiguration.ISitePolicy {
+
+		private int type;
+		private String[] list;
+
+		private SitePolicy() {
+		}
+		private SitePolicy(int type, String[] list) {
+			if (type != ISitePolicy.USER_INCLUDE && type != ISitePolicy.USER_EXCLUDE)
+				throw new IllegalArgumentException();
+			this.type = type;
+
+			if (list == null)
+				this.list = new String[0];
+			else
+				this.list = list;
+		}
+
+		/*
+		 * @see ISitePolicy#getType()
+		 */
+		public int getType() {
+			return type;
+		}
+
+		/*
+		* @see ISitePolicy#getList()
+		*/
+		public String[] getList() {
+			return list;
+		}
+
+		/*
+		 * @see ISitePolicy#setList(String[])
+		 */
+		public synchronized void setList(String[] list) {
+			if (list == null)
+				this.list = new String[0];
+			else
+				this.list = list;
+		}
+
+	}
+
+	public class FeatureEntry implements IPlatformConfiguration.IFeatureEntry {
+		private String id;
+		private String version;
+		private String pluginVersion;
+		private String application;
+		private URL[] root;
+		private boolean primary;
+		private String pluginIdentifier;
+
+		private FeatureEntry(String id, String version, String pluginIdentifier, String pluginVersion, boolean primary, String application, URL[] root) {
+			if (id == null)
+				throw new IllegalArgumentException();
+			this.id = id;
+			this.version = version;
+			this.pluginVersion = pluginVersion;
+			this.pluginIdentifier = pluginIdentifier;
+			this.primary = primary;
+			this.application = application;
+			this.root = (root == null ? new URL[0] : root);
+		}
+
+		private FeatureEntry(String id, String version, String pluginVersion, boolean primary, String application, URL[] root) {
+			this(id, version, id, pluginVersion, primary, application, root);
+		}
+
+		/*
+		 * @see IFeatureEntry#getFeatureIdentifier()
+		 */
+		public String getFeatureIdentifier() {
+			return id;
+		}
+
+		/*
+		 * @see IFeatureEntry#getFeatureVersion()
+		 */
+		public String getFeatureVersion() {
+			return version;
+		}
+
+		/*
+		 * @see IFeatureEntry#getFeaturePluginVersion()
+		 */
+		public String getFeaturePluginVersion() {
+			return pluginVersion;
+		}
+
+		/*
+		 * @see IFeatureEntry#getFeatureApplication()
+		 */
+		public String getFeatureApplication() {
+			return application;
+		}
+
+		/*
+		 * @see IFeatureEntry#getFeatureRootURLs()
+		 */
+		public URL[] getFeatureRootURLs() {
+			return root;
+		}
+
+		/*
+		 * @see IFeatureEntry#canBePrimary()
+		 */
+		public boolean canBePrimary() {
+			return primary;
+		}
+		/*
+		 * @see IFeatureEntry#getFeaturePluginIdentifier()
+		 */
+		public String getFeaturePluginIdentifier() {
+			return pluginIdentifier;
+		}
+
+	}
+
+	private class VersionedIdentifier {
+		private String identifier = ""; //$NON-NLS-1$
+		private int major = 0;
+		private int minor = 0;
+		private int service = 0;
+		private String qualifier = ""; //$NON-NLS-1$
+
+		private static final String VER_SEPARATOR = "."; //$NON-NLS-1$
+		private static final String ID_SEPARATOR = "_"; //$NON-NLS-1$
+
+		public static final int LESS_THAN = -1;
+		public static final int EQUAL = 0;
+		public static final int EQUIVALENT = 1;
+		public static final int COMPATIBLE = 2;
+		public static final int GREATER_THAN = 3;
+
+		public VersionedIdentifier(String s) {
+			if (s == null || (s = s.trim()).equals("")) //$NON-NLS-1$
+				return;
+
+			int loc = s.lastIndexOf(ID_SEPARATOR);
+			if (loc != -1) {
+				this.identifier = s.substring(0, loc);
+				String version = s.substring(loc + 1);
+				parseVersion(version);
+			} else
+				this.identifier = s;
+		}
+
+		public boolean equalIdentifiers(VersionedIdentifier id) {
+			if (id == null)
+				return identifier == null;
+			else
+				return id.identifier.equals(identifier);
+		}
+
+		public int compareVersion(VersionedIdentifier id) {
+
+			if (id == null) {
+				if (major == 0 && minor == 0 && service == 0)
+					return -1;
+				else
+					return 1;
+			}
+
+			if (major > id.major)
+				return GREATER_THAN;
+			if (major < id.major)
+				return LESS_THAN;
+			if (minor > id.minor)
+				return COMPATIBLE;
+			if (minor < id.minor)
+				return LESS_THAN;
+			if (service > id.service)
+				return EQUIVALENT;
+			if (service < id.service)
+				return LESS_THAN;
+			return compareQualifiers(qualifier, id.qualifier);
+		}
+
+		private int compareQualifiers(String q1, String q2) {
+			int result = q1.compareTo(q2);
+			if (result < 0)
+				return LESS_THAN;
+			else if (result > 0)
+				return EQUIVALENT;
+			else
+				return EQUAL;
+		}
+
+		private void parseVersion(String v) {
+			if (v == null || (v = v.trim()).equals("")) //$NON-NLS-1$
+				return;
+
+			try {
+				StringTokenizer st = new StringTokenizer(v, VER_SEPARATOR);
+				ArrayList elements = new ArrayList(4);
+
+				while (st.hasMoreTokens()) {
+					elements.add(st.nextToken());
+				}
+
+				if (elements.size() >= 1)
+					this.major = (new Integer((String) elements.get(0))).intValue();
+				if (elements.size() >= 2)
+					this.minor = (new Integer((String) elements.get(1))).intValue();
+				if (elements.size() >= 3)
+					this.service = (new Integer((String) elements.get(2))).intValue();
+				if (elements.size() >= 4)
+					this.qualifier = removeWhiteSpace((String) elements.get(3));
+
+			} catch (Exception e) {
+				// use what we got so far ...
+			}
+		}
+
+		private String removeWhiteSpace(String s) {
+			char[] chars = s.trim().toCharArray();
+			boolean whitespace = false;
+			for (int i = 0; i < chars.length; i++) {
+				if (Character.isWhitespace(chars[i])) {
+					chars[i] = '_';
+					whitespace = true;
+				}
+			}
+			return whitespace ? new String(chars) : s;
+		}
+	}
+
+	/*
+	 * Element selector for use with "tiny" parser. Parser callers supply
+	 * concrete selectors
+	 */
+	public interface Selector {
+
+		/*
+		 * Method is called to pre-select a specific xml type. Pre-selected
+		 * elements are then fully parsed and result in calls to full
+		 * select method.
+		 * @return <code>true</code> is the element should be considered,
+		 * <code>false</code> otherwise
+		 */
+		public boolean select(String entry);
+
+		/*
+		 * Method is called with a fully parsed element.
+		 * @return <code>true</code> to select this element and terminate the parse,
+		 * <code>false</code> otherwise
+		 */
+		public boolean select(String element, HashMap attributes);
+	}
+
+	/*
+	 * "Tiny" xml parser. Performs a rudimentary parse of a well-formed xml file.
+	 * Is specifically geared to parsing plugin.xml files of "bootstrap" plug-ins
+	 * during the platform startup sequence before full xml plugin is available.
+	 */
+	public static class Parser {
+
+		private ArrayList elements = new ArrayList();
+
+		/*
+		 * Construct parser for the specified file
+		 */
+		public Parser(File file) {
+			try {
+				load(new FileInputStream(file));
+			} catch (Exception e) {
+				// continue ... actual parsing will report errors
+			}
+		}
+
+		/*
+		 * Construct parser for the specified URL
+		 */
+		public Parser(URL url) {
+			try {
+				load(url.openStream());
+			} catch (Exception e) {
+				// continue ... actual parsing will report errors
+			}
+		}
+
+		/*
+		 * Return selected elements as an (attribute-name, attribute-value) map.
+		 * The name of the selected element is returned as the value of entry with
+		 * name "<element>".
+		 * @return attribute map for selected element, or <code>null</code>
+		 */
+		public HashMap getElement(Selector selector) {
+			if (selector == null)
+				return null;
+
+			String element;
+			for (int i = 0; i < elements.size(); i++) {
+				// make pre-parse selector call
+				element = (String) elements.get(i);
+				if (selector.select(element)) {
+					// parse selected entry
+					HashMap attributes = new HashMap();
+					String elementName;
+					int j;
+					// parse out element name
+					for (j = 0; j < element.length(); j++) {
+						if (Character.isWhitespace(element.charAt(j)))
+							break;
+					}
+					if (j >= element.length()) {
+						elementName = element;
+					} else {
+						elementName = element.substring(0, j);
+						element = element.substring(j);
+						// parse out attributes
+						StringTokenizer t = new StringTokenizer(element, "=\""); //$NON-NLS-1$
+						boolean isKey = true;
+						String key = ""; //$NON-NLS-1$
+						while (t.hasMoreTokens()) {
+							String token = t.nextToken().trim();
+							if (!token.equals("")) { //$NON-NLS-1$
+								// collect (key, value) pairs
+								if (isKey) {
+									key = token;
+									isKey = false;
+								} else {
+									attributes.put(key, token);
+									isKey = true;
+								}
+							}
+						}
+					}
+					// make post-parse selector call
+					if (selector.select(elementName, attributes)) {
+						attributes.put("<element>", elementName); //$NON-NLS-1$
+						return attributes;
+					}
+				}
+			}
+			return null;
+		}
+
+		private void load(InputStream is) {
+			if (is == null)
+				return;
+
+			// read file
+			StringBuffer xml = new StringBuffer(4096);
+			char[] iobuf = new char[4096];
+			InputStreamReader r = null;
+			try {
+				r = new InputStreamReader(is);
+				int len = r.read(iobuf, 0, iobuf.length);
+				while (len != -1) {
+					xml.append(iobuf, 0, len);
+					len = r.read(iobuf, 0, iobuf.length);
+				}
+			} catch (Exception e) {
+				return;
+			} finally {
+				if (r != null)
+					try {
+						r.close();
+					} catch (IOException e) {
+						// ignore
+					}
+			}
+
+			// parse out element tokens
+			String xmlString = xml.toString();
+			StringTokenizer t = new StringTokenizer(xmlString, "<>"); //$NON-NLS-1$
+			while (t.hasMoreTokens()) {
+				String token = t.nextToken().trim();
+				if (!token.equals("")) //$NON-NLS-1$
+					elements.add(token);
+			}
+		}
+	}
+
+	public static class BootDescriptor {
+		private String id;
+		private String version;
+		private String[] libs;
+		private URL dir;
+
+		public BootDescriptor(String id, String version, String[] libs, URL dir) {
+			this.id = id;
+			this.version = version;
+			this.libs = libs;
+			this.dir = dir;
+		}
+
+		public String getId() {
+			return id;
+		}
+
+		public String getVersion() {
+			return version;
+		}
+
+		public String[] getLibraries() {
+			return libs;
+		}
+
+		public URL getPluginDirectoryURL() {
+			return dir;
+		}
+	}
+
+	private PlatformConfiguration(String configArg, String metaPath, URL pluginPath) throws IOException {
+		this.sites = new HashMap();
+		this.externalLinkSites = new HashMap();
+		this.cfgdFeatures = new HashMap();
+		this.bootPlugins = new HashMap();
+
+		// Determine configuration URL to use (based on command line argument)
+		URL configURL = null;
+		if (configArg != null && !configArg.trim().equals("")) { //$NON-NLS-1$
+			configURL = new URL(configArg);
+		}
+
+		// initialize configuration
+		boolean createRootSite = (pluginPath == null);
+		initializeCurrent(configURL, metaPath, createRootSite);
+
+		// merge in any plugin-path entries (converted to site(s))
+		if (pluginPath != null) {
+			updateConfigurationFromPlugins(pluginPath);
+		}
+
+		// pick up any first-time default settings (relative to install location)
+		loadInitializationAttributes();
+
+		// Detect external links. These are "soft link" to additional sites. The link
+		// files are usually provided by external installation programs. They are located
+		// relative to this configuration URL.
+		configureExternalLinks();
+
+		// Validate sites in the configuration. Causes any sites that do not exist to
+		// be removed from the configuration
+		validateSites();
+
+		// compute differences between configuration and actual content of the sites
+		// (base sites and link sites)
+		computeChangeStamp();
+
+		// determine which plugins we will use to start the rest of the "kernel"
+		// (need to get core.runtime matching the executing core.boot and
+		// xerces matching the selected core.runtime)
+		//		locateDefaultPlugins();
+	}
+
+	PlatformConfiguration(URL url) throws IOException {
+		this.sites = new HashMap();
+		this.externalLinkSites = new HashMap();
+		this.cfgdFeatures = new HashMap();
+		this.bootPlugins = new HashMap();
+		initialize(url);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#createSiteEntry(URL, ISitePolicy)
+	 */
+	public ISiteEntry createSiteEntry(URL url, ISitePolicy policy) {
+		return new PlatformConfiguration.SiteEntry(url, policy, this);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#createSitePolicy(int, String[])
+	 */
+	public ISitePolicy createSitePolicy(int type, String[] list) {
+		return new PlatformConfiguration.SitePolicy(type, list);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#createFeatureEntry(String, String, String, boolean, String, URL)
+	 */
+	public IFeatureEntry createFeatureEntry(String id, String version, String pluginVersion, boolean primary, String application, URL[] root) {
+		return new PlatformConfiguration.FeatureEntry(id, version, pluginVersion, primary, application, root);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#createFeatureEntry(String, String, String,
+	 * String, boolean, String, URL)
+	 */
+	public IFeatureEntry createFeatureEntry(String id, String version, String pluginIdentifier, String pluginVersion, boolean primary, String application, URL[] root) {
+		return new PlatformConfiguration.FeatureEntry(id, version, pluginIdentifier, pluginVersion, primary, application, root);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#configureSite(ISiteEntry)
+	 */
+	public void configureSite(ISiteEntry entry) {
+		configureSite(entry, false);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#configureSite(ISiteEntry, boolean)
+	 */
+	public synchronized void configureSite(ISiteEntry entry, boolean replace) {
+
+		if (entry == null)
+			return;
+
+		URL url = entry.getURL();
+		if (url == null)
+			return;
+		String key = url.toExternalForm();
+
+		if (sites.containsKey(key) && !replace)
+			return;
+
+		sites.put(key, entry);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#unconfigureSite(ISiteEntry)
+	 */
+	public synchronized void unconfigureSite(ISiteEntry entry) {
+		if (entry == null)
+			return;
+
+		URL url = entry.getURL();
+		if (url == null)
+			return;
+		String key = url.toExternalForm();
+
+		sites.remove(key);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getConfiguredSites()
+	 */
+	public ISiteEntry[] getConfiguredSites() {
+		if (sites.size() == 0)
+			return new ISiteEntry[0];
+
+		return (ISiteEntry[]) sites.values().toArray(new ISiteEntry[0]);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#findConfiguredSite(URL)
+	 */
+	public ISiteEntry findConfiguredSite(URL url) {
+		if (url == null)
+			return null;
+		String key = url.toExternalForm();
+
+		ISiteEntry result = (ISiteEntry) sites.get(key);
+		if (result == null) // retry with decoded URL string
+			result = (ISiteEntry) sites.get(URLDecoder.decode(key));
+		return result;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#configureFeatureEntry(IFeatureEntry)
+	 */
+	public synchronized void configureFeatureEntry(IFeatureEntry entry) {
+		if (entry == null)
+			return;
+
+		String key = entry.getFeatureIdentifier();
+		if (key == null)
+			return;
+
+		cfgdFeatures.put(key, entry);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#unconfigureFeatureEntry(IFeatureEntry)
+	 */
+	public synchronized void unconfigureFeatureEntry(IFeatureEntry entry) {
+		if (entry == null)
+			return;
+
+		String key = entry.getFeatureIdentifier();
+		if (key == null)
+			return;
+
+		cfgdFeatures.remove(key);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getConfiguredFeatureEntries()
+	 */
+	public IFeatureEntry[] getConfiguredFeatureEntries() {
+		if (cfgdFeatures.size() == 0)
+			return new IFeatureEntry[0];
+
+		return (IFeatureEntry[]) cfgdFeatures.values().toArray(new IFeatureEntry[0]);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#findConfiguredFeatureEntry(String)
+	 */
+	public IFeatureEntry findConfiguredFeatureEntry(String id) {
+		if (id == null)
+			return null;
+
+		return (IFeatureEntry) cfgdFeatures.get(id);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getConfigurationLocation()
+	 */
+	public URL getConfigurationLocation() {
+		return configLocation;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getChangeStamp()
+	 */
+	public long getChangeStamp() {
+		if (!changeStampIsValid)
+			computeChangeStamp();
+		return changeStamp;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getFeaturesChangeStamp()
+	 */
+	public long getFeaturesChangeStamp() {
+		if (!featuresChangeStampIsValid)
+			computeFeaturesChangeStamp();
+		return featuresChangeStamp;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getPluginsChangeStamp()
+	 */
+	public long getPluginsChangeStamp() {
+		if (!pluginsChangeStampIsValid)
+			computePluginsChangeStamp();
+		return pluginsChangeStamp;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getApplicationIdentifier()
+	 */
+	public String getApplicationIdentifier() {
+
+		if (cmdInitialize) {
+			// we are running post-install initialization. Force
+			// running of the reconciler
+			return RECONCILER_APP;
+		}
+
+		if (featuresChangeStamp != lastFeaturesChangeStamp) {
+			// we have detected feature changes ... see if we need to reconcile
+			boolean update = !cmdNoUpdate || cmdUpdate;
+			if (update)
+				return RECONCILER_APP;
+		}
+
+		// "normal" startup ... run specified application
+		return getApplicationIdentifierInternal();
+	}
+
+	private String getApplicationIdentifierInternal() {
+
+		if (cmdApplication != null) // application was specified
+			return cmdApplication;
+		else {
+			// if -feature was not specified use the default feature
+			String feature = cmdFeature;
+			if (feature == null)
+				feature = defaultFeature;
+
+			// lookup application for feature (specified or defaulted)
+			if (feature != null) {
+				IFeatureEntry fe = findConfiguredFeatureEntry(feature);
+				if (fe != null) {
+					if (fe.getFeatureApplication() != null)
+						return fe.getFeatureApplication();
+				}
+			}
+		}
+
+		// return hardcoded default if we failed
+		return DEFAULT_FEATURE_APPLICATION;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getPrimaryFeatureIdentifier()
+	 */
+	public String getPrimaryFeatureIdentifier() {
+
+		if (cmdFeature != null) // -feature was specified on command line
+			return cmdFeature;
+
+		// feature was not specified on command line
+		if (defaultFeature != null)
+			return defaultFeature; // return customized default if set
+		else
+			return DEFAULT_FEATURE_ID; // return hardcoded default
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getPluginPath()
+	 */
+	public URL[] getPluginPath() {
+		ArrayList path = new ArrayList();
+		if (DEBUG)
+			debug("computed plug-in path:"); //$NON-NLS-1$
+
+		ISiteEntry[] sites = getConfiguredSites();
+		URL pathURL;
+		for (int i = 0; i < sites.length; i++) {
+			String[] plugins = sites[i].getPlugins();
+			for (int j = 0; j < plugins.length; j++) {
+				try {
+					pathURL = new URL(((SiteEntry) sites[i]).getResolvedURL(), plugins[j]);
+					path.add(pathURL);
+					if (DEBUG)
+						debug("   " + pathURL.toString()); //$NON-NLS-1$
+				} catch (MalformedURLException e) {
+					// skip entry ...
+					if (DEBUG)
+						debug("   bad URL: " + e); //$NON-NLS-1$
+				}
+			}
+		}
+		return (URL[]) path.toArray(new URL[0]);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#getBootstrapPluginIdentifiers()
+	 */
+	public String[] getBootstrapPluginIdentifiers() {
+		return BOOTSTRAP_PLUGINS;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#setBootstrapPluginLocation(String, URL)
+	 */
+	public void setBootstrapPluginLocation(String id, URL location) {
+		String[] ids = getBootstrapPluginIdentifiers();
+		for (int i = 0; i < ids.length; i++) {
+			if (ids[i].equals(id)) {
+				bootPlugins.put(id, location.toExternalForm());
+				break;
+			}
+		}
+	}
+
+	/*
+	 * @see IPlatformConfiguration#isUpdateable()
+	 */
+	public boolean isUpdateable() {
+		return true;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#isTransient()
+	 */
+	public boolean isTransient() {
+		return transientConfig;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#isTransient(boolean)
+	 */
+	public void isTransient(boolean value) {
+		//		if (this != BootLoader.getCurrentPlatformConfiguration())
+		//			transientConfig = value;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#refresh()
+	 */
+	public synchronized void refresh() {
+		// Reset computed values. Will be lazily refreshed
+		// on next access
+		ISiteEntry[] sites = getConfiguredSites();
+		for (int i = 0; i < sites.length; i++) {
+			// reset site entry
+			 ((SiteEntry) sites[i]).refresh();
+		}
+		// reset configuration entry.
+		lastChangeStamp = changeStamp;
+		lastFeaturesChangeStamp = featuresChangeStamp;
+		lastPluginsChangeStamp = pluginsChangeStamp;
+		changeStampIsValid = false;
+		featuresChangeStampIsValid = false;
+		pluginsChangeStampIsValid = false;
+	}
+
+	/*
+	 * @see IPlatformConfiguration#save()
+	 */
+	public void save() throws IOException {
+		if (isUpdateable())
+			save(configLocation);
+	}
+
+	/*
+	 * @see IPlatformConfiguration#save(URL)
+	 */
+	public synchronized void save(URL url) throws IOException {
+		if (url == null)
+			throw new IOException(Policy.bind("cfig.unableToSave.noURL")); //$NON-NLS-1$
+
+		PrintWriter w = null;
+		OutputStream os = null;
+		if (!url.getProtocol().equals("file")) { //$NON-NLS-1$
+			// not a file protocol - attempt to save to the URL
+			URLConnection uc = url.openConnection();
+			uc.setDoOutput(true);
+			os = uc.getOutputStream();
+			w = new PrintWriter(os);
+			try {
+				write(w);
+			} finally {
+				w.close();
+			}
+		} else {
+			// file protocol - do safe i/o
+			File cfigFile = new File(url.getFile().replace('/', File.separatorChar));
+			File cfigDir = cfigFile.getParentFile();
+			if (cfigDir != null)
+				cfigDir.mkdirs();
+
+			// first save the file as temp
+			File cfigTmp = new File(cfigFile.getAbsolutePath() + CONFIG_FILE_TEMP_SUFFIX);
+			os = new FileOutputStream(cfigTmp);
+			w = new PrintWriter(os);
+			try {
+				write(w);
+			} finally {
+				w.close();
+			}
+
+			// make sure we actually succeeded saving the whole configuration.
+			InputStream is = new FileInputStream(cfigTmp);
+			Properties tmpProps = new Properties();
+			try {
+				tmpProps.load(is);
+				if (!EOF.equals(tmpProps.getProperty(EOF))) {
+					throw new IOException(Policy.bind("cfig.unableToSave", cfigTmp.getAbsolutePath())); //$NON-NLS-1$
+				}
+			} finally {
+				is.close();
+			}
+
+			// make the saved config the "active" one
+			File cfigBak = new File(cfigFile.getAbsolutePath() + CONFIG_FILE_BAK_SUFFIX);
+			cfigBak.delete(); // may have old .bak due to prior failure
+
+			if (cfigFile.exists())
+				cfigFile.renameTo(cfigBak);
+
+			// at this point we have old config (if existed) as "bak" and the
+			// new config as "tmp".
+			boolean ok = cfigTmp.renameTo(cfigFile);
+			if (ok) {
+				// at this point we have the new config "activated", and the old
+				// config (if it existed) as "bak"
+				cfigBak.delete(); // clean up
+			} else {
+				// this codepath represents a tiny failure window. The load processing
+				// on startup will detect missing config and will attempt to start
+				// with "tmp" (latest), then "bak" (the previous). We can also end up
+				// here if we failed to rename the current config to "bak". In that
+				// case we will restart with the previous state.
+				throw new IOException(Policy.bind("cfig.unableToSave", cfigTmp.getAbsolutePath())); //$NON-NLS-1$
+			}
+		}
+	}
+
+	public BootDescriptor getPluginBootDescriptor(String id) {
+		// return the plugin descriptor for the specified plugin. This method
+		// is used during boot processing to obtain information about "kernel" plugins
+		// whose class loaders must be created prior to the plugin registry being
+		// available (ie. loaders needed to create the plugin registry).
+
+		if (RUNTIME_PLUGIN_ID.equals(id))
+			return runtimeDescriptor;
+		else
+			return null;
+	}
+
+	static PlatformConfiguration getCurrent() {
+		return currentPlatformConfiguration;
+	}
+
+	/**
+	 * Create and initialize the current platform configuration
+	 * @param cmdArgs command line arguments (startup and boot arguments are
+	 * already consumed)
+	 * @param r10plugins plugin-path URL as passed on the BootLoader.run(...)
+	 * or BootLoader.startup(...) method. Supported for R1.0 compatibility
+	 * @param r10apps application identifies as passed on the BootLoader.run(...)
+	 * method. Supported for R1.0 compatibility.
+	 * @param metaPath path to the platform metadata area
+	 */
+	static synchronized String[] startup(String[] cmdArgs, URL r10plugins, String r10app, String metaPath, URL installURL) throws Exception {
+		PlatformConfiguration.installURL = installURL;
+
+		// if BootLoader was invoked directly (rather than via Main), it is possible
+		// to have the plugin-path and application set in 2 ways: (1) via an explicit
+		// argument on the invocation method, or (2) via a command line argument (passed
+		// into this method as the argument String[]). If specified, the explicit
+		// values are used even if the command line arguments were specified as well.
+		cmdPlugins = r10plugins; // R1.0 compatibility
+		cmdApplication = r10app; // R1.0 compatibility
+
+		// process command line arguments
+		String[] passthruArgs = processCommandLine(cmdArgs);
+		if (cmdDev)
+			cmdNoUpdate = true; // force -noupdate when in dev mode (eg. PDE)
+
+		// create current configuration
+		if (currentPlatformConfiguration == null)
+			currentPlatformConfiguration = new PlatformConfiguration(cmdConfiguration, metaPath, cmdPlugins);
+
+		// check if we will be forcing reconciliation
+		passthruArgs = checkForFeatureChanges(passthruArgs, currentPlatformConfiguration);
+
+		// check if we should indicate new changes
+		passthruArgs = checkForNewUpdates(currentPlatformConfiguration, passthruArgs);
+
+		return passthruArgs;
+	}
+
+	static synchronized void shutdown() throws IOException {
+
+		// save platform configuration
+		PlatformConfiguration config = getCurrent();
+		if (config != null) {
+			try {
+				config.save();
+			} catch (IOException e) {
+				if (DEBUG)
+					debug("Unable to save configuration " + e.toString()); //$NON-NLS-1$
+				// will recover on next startup
+			}
+			config.clearConfigurationLock();
+		}
+	}
+
+	private synchronized void initializeCurrent(URL url, String metaPath, boolean createRootSite) throws IOException {
+		// FIXME: commented out for now. Remove if not needed.
+		//boolean concurrentUse = false;
+
+		if (cmdInitialize) {
+			// we are running post-install initialization (-install command
+			// line argument). Ignore any configuration URL passed in.
+			// Force the configuration to be saved in the install location.
+			// Allow an existing configuration to be re-initialized.
+			url = new URL(getInstallURL(), CONFIG_FILE); // if we fail here, return exception
+			// FIXME: commented out for now. Remove if not needed. 
+			// I left the call to #getConfigurationLock in just in case
+			// calling it has useful side effect. If not, then it can be removed too.
+			//concurrentUse = getConfigurationLock(url);
+			getConfigurationLock(url);
+
+			resetInitializationConfiguration(url); // [20111]
+			if (createRootSite)
+				configureSite(getRootSite());
+			if (DEBUG)
+				debug("Initializing configuration " + url.toString()); //$NON-NLS-1$
+			configLocation = url;
+			verifyPath(configLocation);
+			return;
+		}
+
+		if (url != null) {
+			// configuration URL was specified. Use it (if exists), or create one
+			// in specified location
+
+			// check concurrent use lock
+			// FIXME: might not need this method call.
+			getConfigurationLock(url);
+
+			// try loading the configuration
+			try {
+				load(url);
+				if (DEBUG)
+					debug("Using configuration " + url.toString()); //$NON-NLS-1$
+			} catch (IOException e) {
+				cmdFirstUse = true;
+				if (createRootSite)
+					configureSite(getRootSite());
+				if (DEBUG)
+					debug("Creating configuration " + url.toString()); //$NON-NLS-1$
+			}
+			configLocation = url;
+			verifyPath(configLocation);
+			return;
+
+		} else {
+			// configuration URL was not specified. Default behavior is to look
+			// for configuration in the workspace meta area. If not found, look
+			// for pre-initialized configuration in the installation location.
+			// If it is found it is used as the initial configuration. Otherwise
+			// a new configuration is created. In either case the resulting
+			// configuration is written into the default state area.
+			// The default state area is computed as follows:
+			// 1) We store the config state relative to the 'eclipse' directory if possible
+			// 2) If this directory is read-only OR 
+			//    if shared install is desired (using command line argument -shared), 
+			//    we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home> 
+			//    is unique for each local user, and <application-id> is the one 
+			//    defined in .eclipseproduct marker file. If .eclipseproduct does not
+			//    exist, use "eclipse" as the application-id.
+
+			//	if we fail here, return exception
+			URL defaultStateURL = getDefaultStateLocation();
+			URL cfigURL = new URL(defaultStateURL, CONFIG_FILE);
+
+			// check concurrent use lock
+			// FIXME: might not need this method call
+			getConfigurationLock(cfigURL);
+
+			// if we can load it, use it
+			try {
+				load(cfigURL);
+				configLocation = cfigURL;
+				verifyPath(configLocation);
+				if (DEBUG)
+					debug("Using configuration " + configLocation.toString()); //$NON-NLS-1$
+				return;
+			} catch (IOException e) {
+				cmdFirstUse = true; // we are creating new configuration
+			}
+
+			// failed to load, see if we can find pre-initialized configuration.
+			// Don't attempt this initialization when self-hosting (is unpredictable)
+			if (createRootSite) {
+				try {
+					url = new URL(getInstallURL(), CONFIG_FILE);
+					load(url);
+					// pre-initialized config loaded OK ... copy any remaining update metadata
+					// Only copy if the default config location is not the install location
+					if (getInstallURL() != defaultStateURL)
+						copyInitializedState(getInstallURL(), defaultStateURL.getFile(), CONFIG_DIR);
+					configLocation = cfigURL; // config in default location is the right URL
+					verifyPath(configLocation);
+					if (DEBUG) {
+						debug("Using configuration " + configLocation.toString()); //$NON-NLS-1$
+						debug("Initialized from    " + url.toString()); //$NON-NLS-1$
+					}
+					return;
+				} catch (IOException e) {
+					// continue ...
+				}
+			}
+
+			// if load failed, initialize with default site info
+			if (createRootSite)
+				configureSite(getRootSite());
+			configLocation = cfigURL;
+			verifyPath(configLocation);
+			if (DEBUG)
+				debug("Creating configuration " + configLocation.toString()); //$NON-NLS-1$
+			return;
+		}
+	}
+
+	private synchronized void initialize(URL url) throws IOException {
+		if (url == null) {
+			if (DEBUG)
+				debug("Creating empty configuration object"); //$NON-NLS-1$
+			return;
+		}
+
+		load(url);
+		configLocation = url;
+		if (DEBUG)
+			debug("Using configuration " + configLocation.toString()); //$NON-NLS-1$
+	}
+
+	private ISiteEntry getRootSite() {
+		// create default site entry for the root
+		ISitePolicy defaultPolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
+		URL siteURL = null;
+		try {
+			siteURL = new URL(PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + "/" + "base" + "/"); //$NON-NLS-1$ //$NON-NLS-2$ // try using platform-relative URL
+		} catch (MalformedURLException e) {
+			siteURL = getInstallURL(); // ensure we come up ... use absolute file URL
+		}
+		ISiteEntry defaultSite = createSiteEntry(siteURL, defaultPolicy);
+		return defaultSite;
+	}
+
+	private void resetInitializationConfiguration(URL url) throws IOException {
+		// [20111]
+		if (!supportsDetection(url))
+			return; // can't do ...
+
+		URL resolved = resolvePlatformURL(url);
+		File initCfg = new File(resolved.getFile().replace('/', File.separatorChar));
+		File initDir = initCfg.getParentFile();
+		resetInitializationLocation(initDir);
+	}
+
+	private void resetInitializationLocation(File dir) {
+		// [20111]
+		if (dir == null || !dir.exists() || !dir.isDirectory())
+			return;
+		File[] list = dir.listFiles();
+		for (int i = 0; i < list.length; i++) {
+			if (list[i].isDirectory())
+				resetInitializationLocation(list[i]);
+			list[i].delete();
+		}
+	}
+
+	private boolean getConfigurationLock(URL url) {
+		if (configurationInWorkspace(url))
+			return false;
+
+		if (!url.getProtocol().equals("file")) //$NON-NLS-1$
+			return false;
+
+		verifyPath(url);
+		String cfgName = url.getFile().replace('/', File.separatorChar);
+		String lockName = cfgName + CONFIG_FILE_LOCK_SUFFIX;
+		cfgLockFile = new File(lockName);
+
+		//if the lock file already exists, try to delete,
+		//assume failure means another eclipse has it open
+		if (cfgLockFile.exists())
+			cfgLockFile.delete();
+		if (cfgLockFile.exists()) {
+			throw new RuntimeException(Policy.bind("cfig.inUse", cfgName, lockName)); //$NON-NLS-1$
+		}
+
+		// OK so far ... open the lock file so other instances will fail
+		try {
+			cfgLockFileRAF = new RandomAccessFile(cfgLockFile, "rw"); //$NON-NLS-1$
+			cfgLockFileRAF.writeByte(0);
+		} catch (IOException e) {
+			throw new RuntimeException(Policy.bind("cfig.failCreateLock", cfgName)); //$NON-NLS-1$
+		}
+
+		return false;
+	}
+
+	private void clearConfigurationLock() {
+		try {
+			if (cfgLockFileRAF != null) {
+				cfgLockFileRAF.close();
+				cfgLockFileRAF = null;
+			}
+		} catch (IOException e) {
+			// ignore ...
+		}
+		if (cfgLockFile != null) {
+			cfgLockFile.delete();
+			cfgLockFile = null;
+		}
+	}
+
+	private boolean configurationInWorkspace(URL url) {
+		// the configuration file is now in the workspace, so return true
+		return true;
+	}
+
+	private void computeChangeStamp() {
+		computeFeaturesChangeStamp();
+		computePluginsChangeStamp();
+		changeStamp = featuresChangeStamp ^ pluginsChangeStamp;
+		changeStampIsValid = true;
+	}
+
+	private void computeFeaturesChangeStamp() {
+		if (featuresChangeStampIsValid)
+			return;
+
+		long result = 0;
+		ISiteEntry[] sites = getConfiguredSites();
+		for (int i = 0; i < sites.length; i++) {
+			result ^= sites[i].getFeaturesChangeStamp();
+		}
+		featuresChangeStamp = result;
+		featuresChangeStampIsValid = true;
+	}
+
+	private void computePluginsChangeStamp() {
+		if (pluginsChangeStampIsValid)
+			return;
+
+		long result = 0;
+		ISiteEntry[] sites = getConfiguredSites();
+		for (int i = 0; i < sites.length; i++) {
+			result ^= sites[i].getPluginsChangeStamp();
+		}
+		pluginsChangeStamp = result;
+		pluginsChangeStampIsValid = true;
+	}
+
+	private void configureExternalLinks() {
+		URL linkURL = getInstallURL();
+		if (!supportsDetection(linkURL))
+			return;
+
+		try {
+			linkURL = new URL(linkURL, LINKS + "/"); //$NON-NLS-1$
+		} catch (MalformedURLException e) {
+			// skip bad links ...
+			if (DEBUG)
+				debug("Unable to obtain link URL"); //$NON-NLS-1$
+			return;
+		}
+
+		File linkDir = new File(linkURL.getFile());
+		File[] links = linkDir.listFiles();
+		if (links == null || links.length == 0) {
+			if (DEBUG)
+				debug("No links detected in " + linkURL.toExternalForm()); //$NON-NLS-1$
+			return;
+		}
+
+		for (int i = 0; i < links.length; i++) {
+			if (links[i].isDirectory())
+				continue;
+			if (DEBUG)
+				debug("Link file " + links[i].getAbsolutePath()); //$NON-NLS-1$
+			Properties props = new Properties();
+			FileInputStream is = null;
+			try {
+				is = new FileInputStream(links[i]);
+				props.load(is);
+				configureExternalLinkSites(links[i], props);
+			} catch (IOException e) {
+				// skip bad links ...
+				if (DEBUG)
+					debug("   unable to load link file " + e); //$NON-NLS-1$
+				continue;
+			} finally {
+				if (is != null) {
+					try {
+						is.close();
+					} catch (IOException e) {
+						// ignore ...
+					}
+				}
+			}
+		}
+	}
+
+	private void configureExternalLinkSites(File linkFile, Properties props) {
+		String path = props.getProperty(LINK_PATH);
+		if (path == null) {
+			if (DEBUG)
+				debug("   no path definition"); //$NON-NLS-1$
+			return;
+		}
+
+		String link;
+		boolean updateable = true;
+		URL siteURL;
+		SiteEntry linkSite;
+		ISitePolicy linkSitePolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
+
+		// parse out link information
+		if (path.startsWith(LINK_READ + " ")) { //$NON-NLS-1$
+			updateable = false;
+			link = path.substring(2).trim();
+		} else if (path.startsWith(LINK_READ_WRITE + " ")) { //$NON-NLS-1$
+			link = path.substring(3).trim();
+		} else {
+			link = path;
+		}
+
+		// 	make sure we have a valid link specification
+		try {
+			if (!link.endsWith(File.separator))
+				link += File.separator;
+			File target = new File(link + ECLIPSE);
+			link = "file:" + target.getAbsolutePath().replace(File.separatorChar, '/'); //$NON-NLS-1$
+			if (!link.endsWith("/")) //$NON-NLS-1$
+				link += "/"; // sites must be directories //$NON-NLS-1$
+			siteURL = new URL(link);
+		} catch (MalformedURLException e) {
+			// ignore bad links ...
+			if (DEBUG)
+				debug("  bad URL " + e); //$NON-NLS-1$
+			return;
+		}
+
+		// process the link
+		linkSite = (SiteEntry) externalLinkSites.get(siteURL);
+		if (linkSite != null) {
+			// we already have a site for this link target, update it if needed
+			linkSite.updateable = updateable;
+			linkSite.linkFileName = linkFile.getAbsolutePath();
+		} else {
+			// this is a link to a new target so create site for it
+			linkSite = (SiteEntry) createSiteEntry(siteURL, linkSitePolicy);
+			linkSite.updateable = updateable;
+			linkSite.linkFileName = linkFile.getAbsolutePath();
+		}
+
+		// configure the new site
+		// NOTE: duplicates are not replaced (first one in wins)
+		configureSite(linkSite);
+		if (DEBUG)
+			debug("   " + (updateable ? "R/W -> " : "R/O -> ") + siteURL.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+
+	/*
+	 * compute site(s) from plugin-path and merge into specified configuration
+	 */
+	private void updateConfigurationFromPlugins(URL file) throws IOException {
+
+		// get the actual plugin path
+		URL[] pluginPath = Utils.getPluginPath(file);
+		if (pluginPath == null || pluginPath.length == 0)
+			return;
+
+		// create a temp configuration and populate it based on plugin path
+		PlatformConfiguration tempConfig = new PlatformConfiguration((URL) null);
+		for (int i = 0; i < pluginPath.length; i++) {
+			String entry = pluginPath[i].toExternalForm();
+			String sitePortion;
+			String pluginPortion;
+			int ix;
+			if (entry.endsWith("/")) { //$NON-NLS-1$
+				// assume directory path in the form <site>/plugins/
+				// look for -------------------------------^
+				ix = findEntrySeparator(entry, 2); // second from end
+				sitePortion = entry.substring(0, ix + 1);
+				pluginPortion = entry.substring(ix + 1);
+				if (!pluginPortion.equals("plugins/")) //$NON-NLS-1$
+					continue; // unsupported entry ... skip it ("fragments/" are handled)
+				pluginPortion = null;
+			} else {
+				// assume full path in the form <site>/<pluginsDir>/<some.plugin>/plugin.xml
+				// look for --------------------------^
+				ix = findEntrySeparator(entry, 3); // third from end
+				sitePortion = entry.substring(0, ix + 1);
+				pluginPortion = entry.substring(ix + 1);
+			}
+			if (ix == -1)
+				continue; // bad entry ... skip it
+
+			URL siteURL = null;
+			try {
+				siteURL = new URL(sitePortion);
+				if (siteURL.getProtocol().equals("file")) { //$NON-NLS-1$
+					File sf = new File(siteURL.getFile());
+					String sfn = sf.getAbsolutePath().replace(File.separatorChar, '/');
+					if (!sfn.endsWith("/")) //$NON-NLS-1$
+						sfn += "/"; //$NON-NLS-1$
+					siteURL = new URL("file:" + sfn); //$NON-NLS-1$
+				}
+			} catch (MalformedURLException e) {
+				continue; // bad entry ... skip it
+			}
+
+			// configure existing site or create a new one for the entry
+			ISiteEntry site = tempConfig.findConfiguredSite(siteURL);
+			ISitePolicy policy;
+			if (site == null) {
+				// new site
+				if (pluginPortion == null)
+					policy = tempConfig.createSitePolicy(ISitePolicy.USER_EXCLUDE, null);
+				else
+					policy = tempConfig.createSitePolicy(ISitePolicy.USER_INCLUDE, new String[] { pluginPortion });
+				site = tempConfig.createSiteEntry(siteURL, policy);
+				tempConfig.configureSite(site);
+			} else {
+				// existing site
+				policy = site.getSitePolicy();
+				if (policy.getType() == ISitePolicy.USER_EXCLUDE)
+					continue; // redundant entry ... skip it
+				if (pluginPortion == null) {
+					// directory entry ... change policy to exclusion (with empty list)
+					policy = tempConfig.createSitePolicy(ISitePolicy.USER_EXCLUDE, null);
+				} else {
+					// explicit entry ... add it to the inclusion list
+					ArrayList list = new ArrayList(Arrays.asList(policy.getList()));
+					list.add(pluginPortion);
+					policy = tempConfig.createSitePolicy(ISitePolicy.USER_INCLUDE, (String[]) list.toArray(new String[0]));
+				}
+				site.setSitePolicy(policy);
+			}
+		}
+
+		// merge resulting site(s) into the specified configuration
+		ISiteEntry[] tempSites = tempConfig.getConfiguredSites();
+		for (int i = 0; i < tempSites.length; i++) {
+			configureSite(tempSites[i], true /*replace*/
+			);
+		}
+	}
+
+	private void validateSites() {
+
+		// check to see if all sites are valid. Remove any sites that do not exist.
+		SiteEntry[] list = (SiteEntry[]) sites.values().toArray(new SiteEntry[0]);
+		for (int i = 0; i < list.length; i++) {
+			URL siteURL = list[i].getResolvedURL();
+			if (!supportsDetection(siteURL))
+				continue;
+
+			File siteRoot = new File(siteURL.getFile().replace('/', File.separatorChar));
+			if (!siteRoot.exists()) {
+				unconfigureSite(list[i]);
+				if (DEBUG)
+					debug("Site " + siteURL + " does not exist ... removing from configuration"); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+	}
+
+	private void copyInitializedState(URL source, String target, String dir) {
+		try {
+			if (!source.getProtocol().equals("file")) //$NON-NLS-1$
+				return; // need to be able to do "dir"
+
+			copy(new File(source.getFile()), new File(target), dir);
+
+		} catch (IOException e) {
+			// this is an optimistic copy. If we fail, the state will be reconciled
+			// when the update manager is triggered.
+		}
+	}
+
+	private void copy(File srcDir, File tgtDir, String extraPath) throws IOException {
+		File src = new File(srcDir, extraPath);
+		File tgt = new File(tgtDir, extraPath);
+
+		if (src.isDirectory()) {
+			// copy content of directories
+			tgt.mkdir();
+			String[] list = src.list();
+			if (list == null)
+				return;
+			for (int i = 0; i < list.length; i++) {
+				copy(srcDir, tgtDir, extraPath + File.separator + list[i]);
+			}
+		} else {
+			// copy individual files
+			FileInputStream is = null;
+			FileOutputStream os = null;
+			try {
+				is = new FileInputStream(src);
+				os = new FileOutputStream(tgt);
+				byte[] buff = new byte[1024];
+				int count = is.read(buff);
+				while (count != -1) {
+					os.write(buff, 0, count);
+					count = is.read(buff);
+				}
+			} catch (IOException e) {
+				// continue ... update reconciler will have to reconstruct state
+			} finally {
+				if (is != null)
+					try {
+						is.close();
+					} catch (IOException e) {
+						// ignore ...
+					}
+				if (os != null)
+					try {
+						os.close();
+					} catch (IOException e) {
+						// ignore ...
+					}
+			}
+		}
+	}
+
+	private void load(URL url) throws IOException {
+
+		if (url == null)
+			throw new IOException(Policy.bind("cfig.unableToLoad.noURL")); //$NON-NLS-1$
+
+		// try to load saved configuration file (watch for failed prior save())
+		Properties props = null;
+		IOException originalException = null;
+		try {
+			props = loadProperties(url, null); // try to load config file
+		} catch (IOException e1) {
+			originalException = e1;
+			try {
+				props = loadProperties(url, CONFIG_FILE_TEMP_SUFFIX); // check for failures on save
+			} catch (IOException e2) {
+				try {
+					props = loadProperties(url, CONFIG_FILE_BAK_SUFFIX); // check for failures on save
+				} catch (IOException e3) {
+					throw originalException; // we tried, but no config here ...
+				}
+			}
+		}
+
+		// check version
+		String v = props.getProperty(CFG_VERSION);
+		if (!VERSION.equals(v)) {
+			// the state is invalid, delete any files under the directory
+			// bug 33493
+			resetUpdateManagerState(url);
+			throw new IOException(Policy.bind("cfig.badVersion", v)); //$NON-NLS-1$
+		}
+
+		// load simple properties
+		defaultFeature = loadAttribute(props, CFG_FEATURE_ENTRY_DEFAULT, null);
+
+		String flag = loadAttribute(props, CFG_TRANSIENT, null);
+		if (flag != null) {
+			if (flag.equals("true")) //$NON-NLS-1$
+				transientConfig = true;
+			else
+				transientConfig = false;
+		}
+
+		String stamp = loadAttribute(props, CFG_STAMP, null);
+		if (stamp != null) {
+			try {
+				lastChangeStamp = Long.parseLong(stamp);
+			} catch (NumberFormatException e) {
+				// ignore bad attribute ...
+			}
+		}
+
+		stamp = loadAttribute(props, CFG_FEATURE_STAMP, null);
+		if (stamp != null) {
+			try {
+				lastFeaturesChangeStamp = Long.parseLong(stamp);
+			} catch (NumberFormatException e) {
+				// ignore bad attribute ...
+			}
+		}
+
+		stamp = loadAttribute(props, CFG_PLUGIN_STAMP, null);
+		if (stamp != null) {
+			try {
+				lastPluginsChangeStamp = Long.parseLong(stamp);
+			} catch (NumberFormatException e) {
+				// ignore bad attribute ...
+			}
+		}
+
+		// load bootstrap entries
+		String[] ids = getBootstrapPluginIdentifiers();
+		for (int i = 0; i < ids.length; i++) {
+			bootPlugins.put(ids[i], loadAttribute(props, CFG_BOOT_PLUGIN + "." + ids[i], null)); //$NON-NLS-1$
+		}
+
+		// load feature entries
+		IFeatureEntry fe = loadFeatureEntry(props, CFG_FEATURE_ENTRY + ".0", null); //$NON-NLS-1$
+		for (int i = 1; fe != null; i++) {
+			configureFeatureEntry(fe);
+			fe = loadFeatureEntry(props, CFG_FEATURE_ENTRY + "." + i, null); //$NON-NLS-1$
+		}
+
+		// load site properties
+		SiteEntry root = (SiteEntry) getRootSite();
+		String rootUrlString = root.getURL().toExternalForm();
+		SiteEntry se = (SiteEntry) loadSite(props, CFG_SITE + ".0", null); //$NON-NLS-1$
+		for (int i = 1; se != null; i++) {
+
+			// check if we are forcing "first use" processing with an existing
+			// platform.cfg. In this case ignore site entry that represents
+			// the platform install, and use a root site entry in its place.
+			// This ensures we do not get messed up by an exclusion list that
+			// is read from the prior state.
+			if (cmdFirstUse && rootUrlString.equals(se.getURL().toExternalForm()))
+				se = root;
+
+			if (!se.isExternallyLinkedSite())
+				configureSite(se);
+			else
+				// remember external link site state, but do not configure at this point
+				externalLinkSites.put(se.getURL(), se);
+			se = (SiteEntry) loadSite(props, CFG_SITE + "." + i, null); //$NON-NLS-1$
+		}
+	}
+
+	private Properties loadProperties(URL url, String suffix) throws IOException {
+
+		// figure out what we will be loading
+		if (suffix != null && !suffix.equals("")) //$NON-NLS-1$
+			url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile() + suffix);
+
+		// try to load saved configuration file
+		Properties props = new Properties();
+		InputStream is = null;
+		try {
+			is = url.openStream();
+			props.load(is);
+			// check to see if we have complete config file
+			if (!EOF.equals(props.getProperty(EOF))) {
+				throw new IOException(Policy.bind("cfig.unableToLoad.incomplete", url.toString())); //$NON-NLS-1$
+			}
+		} finally {
+			if (is != null) {
+				try {
+					is.close();
+				} catch (IOException e) {
+					// ignore ...
+				}
+			}
+		}
+		return props;
+	}
+
+	private ISiteEntry loadSite(Properties props, String name, ISiteEntry dflt) {
+
+		String urlString = loadAttribute(props, name + "." + CFG_URL, null); //$NON-NLS-1$
+		if (urlString == null)
+			return dflt;
+
+		URL url = null;
+		try {
+			url = new URL(urlString);
+		} catch (MalformedURLException e) {
+			return dflt;
+		}
+
+		int policyType;
+		String[] policyList;
+		String typeString = loadAttribute(props, name + "." + CFG_POLICY, null); //$NON-NLS-1$
+		if (typeString == null) {
+			policyType = DEFAULT_POLICY_TYPE;
+			policyList = DEFAULT_POLICY_LIST;
+		} else {
+			int i;
+			for (i = 0; i < CFG_POLICY_TYPE.length; i++) {
+				if (typeString.equals(CFG_POLICY_TYPE[i])) {
+					break;
+				}
+			}
+			if (i >= CFG_POLICY_TYPE.length) {
+				policyType = DEFAULT_POLICY_TYPE;
+				policyList = DEFAULT_POLICY_LIST;
+			} else {
+				policyType = i;
+				policyList = loadListAttribute(props, name + "." + CFG_LIST, new String[0]); //$NON-NLS-1$
+			}
+		}
+
+		ISitePolicy sp = createSitePolicy(policyType, policyList);
+		SiteEntry site = (SiteEntry) createSiteEntry(url, sp);
+
+		String stamp = loadAttribute(props, name + "." + CFG_STAMP, null); //$NON-NLS-1$
+		if (stamp != null) {
+			try {
+				site.lastChangeStamp = Long.parseLong(stamp);
+			} catch (NumberFormatException e) {
+				// ignore bad attribute ...
+			}
+		}
+
+		stamp = loadAttribute(props, name + "." + CFG_FEATURE_STAMP, null); //$NON-NLS-1$
+		if (stamp != null) {
+			try {
+				site.lastFeaturesChangeStamp = Long.parseLong(stamp);
+			} catch (NumberFormatException e) {
+				// ignore bad attribute ...
+			}
+		}
+
+		stamp = loadAttribute(props, name + "." + CFG_PLUGIN_STAMP, null); //$NON-NLS-1$
+		if (stamp != null) {
+			try {
+				site.lastPluginsChangeStamp = Long.parseLong(stamp);
+			} catch (NumberFormatException e) {
+				// ignore bad attribute ...
+			}
+		}
+
+		String flag = loadAttribute(props, name + "." + CFG_UPDATEABLE, null); //$NON-NLS-1$
+		if (flag != null) {
+			if (flag.equals("true")) //$NON-NLS-1$
+				site.updateable = true;
+			else
+				site.updateable = false;
+		}
+
+		String linkname = loadAttribute(props, name + "." + CFG_LINK_FILE, null); //$NON-NLS-1$
+		if (linkname != null && !linkname.equals("")) { //$NON-NLS-1$
+			site.linkFileName = linkname.replace('/', File.separatorChar);
+		}
+
+		return site;
+	}
+
+	private IFeatureEntry loadFeatureEntry(Properties props, String name, IFeatureEntry dflt) {
+		String id = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_ID, null); //$NON-NLS-1$
+		if (id == null)
+			return dflt;
+		String version = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_VERSION, null); //$NON-NLS-1$
+		String pluginVersion = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_PLUGIN_VERSION, null); //$NON-NLS-1$
+		if (pluginVersion == null)
+			pluginVersion = version;
+		String pluginIdentifier = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_PLUGIN_IDENTIFIER, null); //$NON-NLS-1$
+		if (pluginIdentifier == null)
+			pluginIdentifier = id;
+		String application = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_APPLICATION, null); //$NON-NLS-1$
+		ArrayList rootList = new ArrayList();
+
+		// get install locations
+		String rootString = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_ROOT + ".0", null); //$NON-NLS-1$ //$NON-NLS-2$
+		for (int i = 1; rootString != null; i++) {
+			try {
+				URL rootEntry = new URL(rootString);
+				rootList.add(rootEntry);
+			} catch (MalformedURLException e) {
+				// skip bad entries ...
+			}
+			rootString = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_ROOT + "." + i, null); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		URL[] roots = (URL[]) rootList.toArray(new URL[0]);
+
+		// get primary flag
+		boolean primary = false;
+		String flag = loadAttribute(props, name + "." + CFG_FEATURE_ENTRY_PRIMARY, null); //$NON-NLS-1$
+		if (flag != null) {
+			if (flag.equals("true")) //$NON-NLS-1$
+				primary = true;
+		}
+		return createFeatureEntry(id, version, pluginIdentifier, pluginVersion, primary, application, roots);
+	}
+
+	private String[] loadListAttribute(Properties props, String name, String[] dflt) {
+		ArrayList list = new ArrayList();
+		String value = loadAttribute(props, name + ".0", null); //$NON-NLS-1$
+		if (value == null)
+			return dflt;
+
+		for (int i = 1; value != null; i++) {
+			loadListAttributeSegment(list, value);
+			value = loadAttribute(props, name + "." + i, null); //$NON-NLS-1$
+		}
+		return (String[]) list.toArray(new String[0]);
+	}
+
+	private void loadListAttributeSegment(ArrayList list, String value) {
+
+		if (value == null)
+			return;
+
+		StringTokenizer tokens = new StringTokenizer(value, ","); //$NON-NLS-1$
+		String token;
+		while (tokens.hasMoreTokens()) {
+			token = tokens.nextToken().trim();
+			if (!token.equals("")) //$NON-NLS-1$
+				list.add(token);
+		}
+		return;
+	}
+
+	private String loadAttribute(Properties props, String name, String dflt) {
+		String prop = props.getProperty(name);
+		if (prop == null)
+			return dflt;
+		else
+			return prop.trim();
+	}
+
+	private void loadInitializationAttributes() {
+
+		// look for the product initialization file relative to the install location
+		URL url = getInstallURL();
+
+		// load any initialization attributes. These are the default settings for
+		// key attributes (eg. default primary feature) supplied by the packaging team.
+		// They are always reloaded on startup to pick up any changes due to
+		// "native" updates.
+		Properties initProps = new Properties();
+		InputStream is = null;
+		try {
+			URL initURL = new URL(url, CONFIG_FILE_INIT);
+			is = initURL.openStream();
+			initProps.load(is);
+			if (DEBUG)
+				debug("Defaults from " + initURL.toExternalForm()); //$NON-NLS-1$
+		} catch (IOException e) {
+			return; // could not load default settings
+		} finally {
+			if (is != null) {
+				try {
+					is.close();
+				} catch (IOException e) {
+					// ignore ...
+				}
+			}
+		}
+
+		// use default settings if supplied
+		String initId = loadAttribute(initProps, INIT_DEFAULT_FEATURE_ID, null);
+		if (initId != null) {
+			String application = loadAttribute(initProps, INIT_DEFAULT_FEATURE_APPLICATION, null);
+			String initPluginId = loadAttribute(initProps, INIT_DEFAULT_PLUGIN_ID, null);
+			if (initPluginId == null)
+				initPluginId = initId;
+			IFeatureEntry fe = findConfiguredFeatureEntry(initId);
+
+			if (fe == null) {
+				// bug 26896 : setup optimistic reconciliation if the primary feature has changed or is new
+				cmdFirstUse = true;
+				// create entry if not exists
+				fe = createFeatureEntry(initId, null, initPluginId, null, true, application, null);
+			} else
+				// update existing entry with new info
+				fe = createFeatureEntry(initId, fe.getFeatureVersion(), fe.getFeaturePluginIdentifier(), fe.getFeaturePluginVersion(), fe.canBePrimary(), application, fe.getFeatureRootURLs());
+			configureFeatureEntry(fe);
+			defaultFeature = initId;
+			if (DEBUG) {
+				debug("    Default primary feature: " + defaultFeature); //$NON-NLS-1$
+				if (application != null)
+					debug("    Default application    : " + application); //$NON-NLS-1$
+			}
+		}
+	}
+
+	private HashMap getImport(URL entry, String id) {
+		if (id == null)
+			return null;
+		final String fId = id;
+
+		// parse out the import element attributes
+		Selector importSel = new Selector() {
+				// parse out import attributes
+	public boolean select(String element) {
+				if (element.startsWith("import")) //$NON-NLS-1$
+					return true;
+				else
+					return false;
+			}
+			public boolean select(String element, HashMap attributes) {
+				if (attributes == null)
+					return false;
+				String plugin = (String) attributes.get("plugin"); //$NON-NLS-1$
+				return fId.equals(plugin);
+			}
+		};
+		Parser p = new Parser(entry);
+		return p.getElement(importSel);
+	}
+
+	private BootDescriptor createPluginBootDescriptor(URL entry) {
+		if (entry == null)
+			return null;
+
+		// selector for plugin element
+		Selector pluginSel = new Selector() {
+			public boolean select(String element) {
+				if (element.startsWith("plugin")) //$NON-NLS-1$
+					return true;
+				else
+					return false;
+			}
+			public boolean select(String element, HashMap attributes) {
+				return true;
+			}
+		};
+
+		// selector for library elements
+		final ArrayList libs = new ArrayList();
+		Selector librarySel = new Selector() {
+			public boolean select(String element) {
+				if (element.startsWith("library")) //$NON-NLS-1$
+					return true;
+				else
+					return false;
+			}
+			public boolean select(String element, HashMap attributes) {
+				if (attributes == null)
+					return false;
+				String lib = (String) attributes.get("name"); //$NON-NLS-1$
+				if (lib != null)
+					libs.add(lib);
+				return false; // accumulate all library elements
+			}
+		};
+
+		// parse out descriptor information
+		Parser p = new Parser(entry);
+		String id = null;
+		String version = null;
+		HashMap attributes = p.getElement(pluginSel);
+		if (attributes != null) {
+			id = (String) attributes.get("id"); //$NON-NLS-1$
+			version = (String) attributes.get("version"); //$NON-NLS-1$
+		}
+		if (id == null)
+			id = ""; //$NON-NLS-1$
+		if (version == null)
+			version = "0.0.0"; //$NON-NLS-1$
+
+		p.getElement(librarySel);
+		String[] libraries = (String[]) libs.toArray(new String[0]);
+
+		String dir = entry.getFile();
+		int ix = dir.lastIndexOf("/"); //$NON-NLS-1$
+		dir = dir.substring(0, ix + 1);
+		URL dirURL = null;
+		try {
+			dirURL = new URL(entry.getProtocol(), entry.getHost(), entry.getPort(), dir);
+		} catch (MalformedURLException e) {
+			// continue ...
+		}
+
+		// return boot descriptor for the plugin
+		return new BootDescriptor(id, version, libraries, dirURL);
+	}
+
+	private URL getPluginPath(HashMap importElement) {
+		// return the plugin path element for the specified import element
+
+		if (importElement == null)
+			return null;
+
+		// determine which plugin we are looking for
+		VersionedIdentifier id;
+		String pid = (String) importElement.get("plugin"); //$NON-NLS-1$
+		String version = (String) importElement.get("version"); //$NON-NLS-1$
+		String match = (String) importElement.get("match"); //$NON-NLS-1$
+		if (pid == null)
+			return null; // bad <import> element
+		if (version == null)
+			id = new VersionedIdentifier(pid);
+		else {
+			id = new VersionedIdentifier(pid + "_" + version); //$NON-NLS-1$
+			if (match == null)
+				match = "compatible"; //$NON-NLS-1$
+		}
+
+		// search plugins on all configured sites
+		ISiteEntry[] sites = getConfiguredSites();
+		if (sites == null || sites.length == 0)
+			return null;
+
+		VersionedIdentifier savedVid = id; // initialize with baseline we are looking for
+		String savedEntry = null;
+		URL savedURL = null;
+		for (int j = 0; j < sites.length; j++) {
+			String[] plugins = sites[j].getPlugins();
+			for (int i = 0; plugins != null && i < plugins.length; i++) {
+				// look for best match.
+				// The entries are in the form <path>/<pluginDir>/plugin.xml
+				// look for -------------------------^
+				int ix = findEntrySeparator(plugins[i], 2); // second from end
+				if (ix == -1)
+					continue; // bad entry ... skip
+				String pluginDir = plugins[i].substring(ix + 1);
+				ix = pluginDir.indexOf("/"); //$NON-NLS-1$
+				if (ix != -1)
+					pluginDir = pluginDir.substring(0, ix);
+				if (pluginDir.equals("")) //$NON-NLS-1$
+					continue; // bad entry ... skip
+
+				// compare the candidate plugin using the matching rule
+				VersionedIdentifier vid = new VersionedIdentifier(pluginDir);
+				if (vid.equalIdentifiers(id)) {
+
+					// check if we have suffixed directory. If not (eg. self-hosting)
+					// we need to actually parse the plugin.xml to get its version
+					if (pluginDir.indexOf("_") == -1) { //$NON-NLS-1$
+						URL xmlURL = null;
+						try {
+							xmlURL = new URL(((SiteEntry) sites[j]).getResolvedURL(), plugins[i]);
+						} catch (MalformedURLException e) {
+							continue; // bad URL ... skip
+						}
+
+						// parse out the plugin element attributes
+						final String fpid = pid;
+						Selector versionSel = new Selector() {
+								// parse out plugin attributes
+	public boolean select(String element) {
+								if (element.startsWith("plugin")) //$NON-NLS-1$
+									return true;
+								else
+									return false;
+							}
+							public boolean select(String element, HashMap attributes) {
+								if (attributes == null)
+									return false;
+								String plugin = (String) attributes.get("id"); //$NON-NLS-1$
+								return fpid.equals(plugin);
+							}
+						};
+						Parser p = new Parser(xmlURL);
+						HashMap attributes = p.getElement(versionSel);
+						if (attributes == null)
+							continue; // bad xml ... skip
+						String pluginVersion;
+						if ((pluginVersion = (String) attributes.get("version")) == null) //$NON-NLS-1$
+							continue; // bad xml ... skip
+						pluginDir += "_" + pluginVersion; //$NON-NLS-1$
+						vid = new VersionedIdentifier(pluginDir);
+					}
+
+					// do the comparison
+					int result;
+					if ((result = vid.compareVersion(savedVid)) >= 0) {
+						if ("greaterOrEqual".equals(match)) { //$NON-NLS-1$
+							if (result > VersionedIdentifier.GREATER_THAN)
+								continue;
+						} else if ("compatible".equals(match)) { //$NON-NLS-1$
+							if (result > VersionedIdentifier.COMPATIBLE)
+								continue;
+						} else if ("equivalent".equals(match)) { //$NON-NLS-1$
+							if (result > VersionedIdentifier.EQUIVALENT)
+								continue;
+						} else if ("perfect".equals(match)) { //$NON-NLS-1$
+							if (result > VersionedIdentifier.EQUAL)
+								continue;
+						} else if (result > VersionedIdentifier.GREATER_THAN)
+							continue; // use the latest
+						savedVid = vid;
+						savedEntry = plugins[i];
+						savedURL = ((SiteEntry) sites[j]).getResolvedURL();
+					}
+				}
+			}
+		}
+
+		if (savedEntry == null)
+			return null;
+
+		try {
+			return new URL(savedURL, savedEntry);
+		} catch (MalformedURLException e) {
+			return null;
+		}
+	}
+
+	private void write(PrintWriter w) {
+		// write header
+		w.println("# " + (new Date()).toString()); //$NON-NLS-1$
+		writeAttribute(w, CFG_VERSION, VERSION);
+		if (transientConfig)
+			writeAttribute(w, CFG_TRANSIENT, "true"); //$NON-NLS-1$
+		w.println(""); //$NON-NLS-1$
+
+		// write global attributes
+		writeAttribute(w, CFG_STAMP, Long.toString(getChangeStamp()));
+		writeAttribute(w, CFG_FEATURE_STAMP, Long.toString(getFeaturesChangeStamp()));
+		writeAttribute(w, CFG_PLUGIN_STAMP, Long.toString(getPluginsChangeStamp()));
+
+		// write out bootstrap entries
+		String[] ids = getBootstrapPluginIdentifiers();
+		for (int i = 0; i < ids.length; i++) {
+			String location = (String) bootPlugins.get(ids[i]);
+			if (location != null)
+				writeAttribute(w, CFG_BOOT_PLUGIN + "." + ids[i], location); //$NON-NLS-1$
+		}
+
+		// write out feature entries
+		w.println(""); //$NON-NLS-1$
+		writeAttribute(w, CFG_FEATURE_ENTRY_DEFAULT, defaultFeature);
+		IFeatureEntry[] feats = getConfiguredFeatureEntries();
+		for (int i = 0; i < feats.length; i++) {
+			writeFeatureEntry(w, CFG_FEATURE_ENTRY + "." + Integer.toString(i), feats[i]); //$NON-NLS-1$
+		}
+
+		// write out site entries
+		SiteEntry[] list = (SiteEntry[]) sites.values().toArray(new SiteEntry[0]);
+		for (int i = 0; i < list.length; i++) {
+			writeSite(w, CFG_SITE + "." + Integer.toString(i), list[i]); //$NON-NLS-1$
+		}
+
+		// write end-of-file marker
+		writeAttribute(w, EOF, EOF);
+	}
+
+	private void writeSite(PrintWriter w, String id, SiteEntry entry) {
+
+		// write site separator
+		w.println(""); //$NON-NLS-1$
+
+		// write out site settings
+		writeAttribute(w, id + "." + CFG_URL, entry.getURL().toString()); //$NON-NLS-1$
+		writeAttribute(w, id + "." + CFG_STAMP, Long.toString(entry.getChangeStamp())); //$NON-NLS-1$
+		writeAttribute(w, id + "." + CFG_FEATURE_STAMP, Long.toString(entry.getFeaturesChangeStamp())); //$NON-NLS-1$
+		writeAttribute(w, id + "." + CFG_PLUGIN_STAMP, Long.toString(entry.getPluginsChangeStamp())); //$NON-NLS-1$
+		writeAttribute(w, id + "." + CFG_UPDATEABLE, entry.updateable ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		if (entry.linkFileName != null && !entry.linkFileName.trim().equals("")) //$NON-NLS-1$
+			writeAttribute(w, id + "." + CFG_LINK_FILE, entry.linkFileName.trim().replace(File.separatorChar, '/')); //$NON-NLS-1$
+
+		// write out site policy
+		int type = entry.getSitePolicy().getType();
+		String typeString = CFG_POLICY_TYPE_UNKNOWN;
+		try {
+			typeString = CFG_POLICY_TYPE[type];
+		} catch (IndexOutOfBoundsException e) {
+			// ignore bad attribute ...
+		}
+		writeAttribute(w, id + "." + CFG_POLICY, typeString); //$NON-NLS-1$
+		writeListAttribute(w, id + "." + CFG_LIST, entry.getSitePolicy().getList()); //$NON-NLS-1$
+	}
+
+	private void writeFeatureEntry(PrintWriter w, String id, IFeatureEntry entry) {
+
+		// write feature entry separator
+		w.println(""); //$NON-NLS-1$
+
+		// write out feature entry settings
+		writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_ID, entry.getFeatureIdentifier()); //$NON-NLS-1$
+		if (entry.canBePrimary())
+			writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_PRIMARY, "true"); //$NON-NLS-1$ //$NON-NLS-2$
+		writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_VERSION, entry.getFeatureVersion()); //$NON-NLS-1$
+		if (entry.getFeatureVersion() != null && !entry.getFeatureVersion().equals(entry.getFeaturePluginVersion()))
+			writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_PLUGIN_VERSION, entry.getFeaturePluginVersion()); //$NON-NLS-1$
+		if (entry.getFeatureIdentifier() != null && !entry.getFeatureIdentifier().equals(entry.getFeaturePluginIdentifier()))
+			writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_PLUGIN_IDENTIFIER, entry.getFeaturePluginIdentifier()); //$NON-NLS-1$
+		writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_APPLICATION, entry.getFeatureApplication()); //$NON-NLS-1$
+		URL[] roots = entry.getFeatureRootURLs();
+		for (int i = 0; i < roots.length; i++) {
+			// write our as individual attributes (is easier for Main.java to read)
+			writeAttribute(w, id + "." + CFG_FEATURE_ENTRY_ROOT + "." + i, roots[i].toExternalForm()); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	private void writeListAttribute(PrintWriter w, String id, String[] list) {
+		if (list == null || list.length == 0)
+			return;
+
+		String value = ""; //$NON-NLS-1$
+		int listLen = 0;
+		int listIndex = 0;
+		for (int i = 0; i < list.length; i++) {
+			if (listLen != 0)
+				value += ","; //$NON-NLS-1$
+			else
+				value = ""; //$NON-NLS-1$
+			value += list[i];
+
+			if (++listLen >= CFG_LIST_LENGTH) {
+				writeAttribute(w, id + "." + Integer.toString(listIndex++), value); //$NON-NLS-1$
+				listLen = 0;
+			}
+		}
+		if (listLen != 0)
+			writeAttribute(w, id + "." + Integer.toString(listIndex), value); //$NON-NLS-1$
+	}
+
+	private void writeAttribute(PrintWriter w, String id, String value) {
+		if (value == null || value.trim().equals("")) //$NON-NLS-1$
+			return;
+		w.println(id + "=" + escapedValue(value)); //$NON-NLS-1$
+	}
+
+	private String escapedValue(String value) {
+		// if required, escape property values as \\uXXXX
+		StringBuffer buf = new StringBuffer(value.length() * 2); // assume expansion by less than factor of 2
+		for (int i = 0; i < value.length(); i++) {
+			char character = value.charAt(i);
+			if (character == '\\' || character == '\t' || character == '\r' || character == '\n' || character == '\f') {
+				// handle characters requiring leading \
+				buf.append('\\');
+				buf.append(character);
+			} else if ((character < 0x0020) || (character > 0x007e)) {
+				// handle characters outside base range (encoded)
+				buf.append('\\');
+				buf.append('u');
+				buf.append(HEX[(character >> 12) & 0xF]); // first nibble
+				buf.append(HEX[(character >> 8) & 0xF]); // second nibble
+				buf.append(HEX[(character >> 4) & 0xF]); // third nibble
+				buf.append(HEX[character & 0xF]); // fourth nibble
+			} else {
+				// handle base characters
+				buf.append(character);
+			}
+		}
+		return buf.toString();
+	}
+
+	private static String[] checkForFeatureChanges(String[] args, PlatformConfiguration cfg) {
+		String original = cfg.getApplicationIdentifierInternal();
+		String actual = cfg.getApplicationIdentifier();
+
+		if (original.equals(actual))
+			// base startup of specified application
+			return args;
+		else {
+			// Will run reconciler.
+			// Re-insert -application argument with original app and optionally
+			// force "first use" processing
+			int newArgCnt = cmdFirstUse ? 3 : 2;
+			String[] newArgs = new String[args.length + newArgCnt];
+			newArgs[0] = CMD_APPLICATION;
+			newArgs[1] = original;
+			if (cmdFirstUse)
+				newArgs[2] = CMD_FIRSTUSE;
+			System.arraycopy(args, 0, newArgs, newArgCnt, args.length);
+			if (DEBUG)
+				debug("triggering reconciliation ..."); //$NON-NLS-1$
+			return newArgs;
+		}
+	}
+
+	private static String[] checkForNewUpdates(IPlatformConfiguration cfg, String[] args) {
+		try {
+			URL markerURL = new URL(cfg.getConfigurationLocation(), CHANGES_MARKER);
+			File marker = new File(markerURL.getFile());
+			if (!marker.exists())
+				return args;
+
+			// indicate -newUpdates
+			marker.delete();
+			String[] newArgs = new String[args.length + 1];
+			newArgs[0] = CMD_NEW_UPDATES;
+			System.arraycopy(args, 0, newArgs, 1, args.length);
+			return newArgs;
+		} catch (MalformedURLException e) {
+			return args;
+		}
+	}
+
+	private static String[] processCommandLine(String[] args) throws Exception {
+		int[] configArgs = new int[100];
+		configArgs[0] = -1; // need to initialize the first element to something that could not be an index.
+		int configArgIndex = 0;
+		for (int i = 0; i < args.length; i++) {
+			boolean found = false;
+
+			// check for args without parameters (i.e., a flag arg)
+
+			// look for forced "first use" processing (triggered by stale
+			// bootstrap information)
+			if (args[i].equalsIgnoreCase(CMD_FIRSTUSE)) {
+				cmdFirstUse = true;
+				found = true;
+			}
+
+			// look for the update flag
+			if (args[i].equalsIgnoreCase(CMD_UPDATE)) {
+				cmdUpdate = true;
+				found = true;
+			}
+
+			// look for the no-update flag
+			if (args[i].equalsIgnoreCase(CMD_NO_UPDATE)) {
+				cmdNoUpdate = true;
+				found = true;
+			}
+
+			// look for the initialization flag
+			if (args[i].equalsIgnoreCase(CMD_INITIALIZE)) {
+				cmdInitialize = true;
+				continue; // do not remove from command line
+			}
+
+			// look for the development mode flag ... triggers no-update
+			if (args[i].equalsIgnoreCase(CMD_DEV)) {
+				cmdDev = true;
+				continue; // do not remove from command line
+			}
+
+			if (found) {
+				configArgs[configArgIndex++] = i;
+				continue;
+			}
+
+			// check for args with parameters. If we are at the last argument or if the next one
+			// has a '-' as the first character, then we can't have an arg with a parm so continue.
+
+			if (i == args.length - 1 || args[i + 1].startsWith("-")) { //$NON-NLS-1$
+				continue;
+			}
+
+			String arg = args[++i];
+
+			// look for the platform configuration to use.
+			if (args[i - 1].equalsIgnoreCase(CMD_CONFIGURATION)) {
+				found = true;
+				cmdConfiguration = arg;
+			}
+
+			// look for the feature to use for customization.
+			if (args[i - 1].equalsIgnoreCase(CMD_FEATURE)) {
+				found = true;
+				cmdFeature = arg;
+			}
+
+			// look for the application to run.  Only use the value from the
+			// command line if the application identifier was not explicitly
+			// passed on BootLoader.run(...) invocation.
+			if (args[i - 1].equalsIgnoreCase(CMD_APPLICATION)) {
+				found = true;
+				if (cmdApplication == null)
+					cmdApplication = arg;
+			}
+
+			// R1.0 compatibility
+			// look for the plugins location to use.  Only use the value from the
+			// command line if the plugins location was not explicitly passed on
+			// BootLoader.run(...) or BootLoader.startup(...) invocation.
+			if (args[i - 1].equalsIgnoreCase(CMD_PLUGINS)) {
+				found = true;
+				// if the arg can be made into a URL use it. Otherwise assume that
+				// it is a file path so make a file URL.
+				try {
+					if (cmdPlugins == null)
+						cmdPlugins = new URL(arg);
+				} catch (MalformedURLException e) {
+					try {
+						cmdPlugins = new URL("file:" + arg.replace(File.separatorChar, '/')); //$NON-NLS-1$
+					} catch (MalformedURLException e2) {
+						throw e; // rethrow original exception
+					}
+				}
+			}
+
+			// done checking for args.  Remember where an arg was found
+			if (found) {
+				configArgs[configArgIndex++] = i - 1;
+				configArgs[configArgIndex++] = i;
+			}
+		}
+
+		// remove all the arguments consumed by this argument parsing
+		if (configArgIndex == 0)
+			return args;
+		String[] passThruArgs = new String[args.length - configArgIndex];
+		configArgIndex = 0;
+		int j = 0;
+		for (int i = 0; i < args.length; i++) {
+			if (i == configArgs[configArgIndex])
+				configArgIndex++;
+			else
+				passThruArgs[j++] = args[i];
+		}
+		return passThruArgs;
+	}
+
+	private static int findEntrySeparator(String pathEntry, int cnt) {
+		for (int i = pathEntry.length() - 1; i >= 0; i--) {
+			if (pathEntry.charAt(i) == '/') {
+				if (--cnt == 0)
+					return i;
+			}
+		}
+		return -1;
+	}
+
+	private static String[] stringListToArray(String prop, String separator) {
+		if (prop == null || prop.trim().equals("")) //$NON-NLS-1$
+			return new String[0];
+		ArrayList list = new ArrayList();
+		StringTokenizer tokens = new StringTokenizer(prop, separator);
+		while (tokens.hasMoreTokens()) {
+			String token = tokens.nextToken().trim();
+			if (!token.equals("")) //$NON-NLS-1$
+				list.add(token);
+		}
+		return list.isEmpty() ? new String[0] : (String[]) list.toArray(new String[0]);
+	}
+
+	private static boolean supportsDetection(URL url) {
+		String protocol = url.getProtocol();
+		if (protocol.equals("file")) //$NON-NLS-1$
+			return true;
+		else if (protocol.equals(PlatformURLHandler.PROTOCOL)) {
+			URL resolved = null;
+			try {
+				resolved = resolvePlatformURL(url); // 19536
+			} catch (IOException e) {
+				return false; // we tried but failed to resolve the platform URL
+			}
+			return resolved.getProtocol().equals("file"); //$NON-NLS-1$
+		} else
+			return false;
+	}
+
+	private static void verifyPath(URL url) {
+		String protocol = url.getProtocol();
+		String path = null;
+		if (protocol.equals("file")) //$NON-NLS-1$
+			path = url.getFile();
+		else if (protocol.equals(PlatformURLHandler.PROTOCOL)) {
+			URL resolved = null;
+			try {
+				resolved = resolvePlatformURL(url); // 19536
+				if (resolved.getProtocol().equals("file")) //$NON-NLS-1$
+					path = resolved.getFile();
+			} catch (IOException e) {
+				// continue ...
+			}
+		}
+
+		if (path != null) {
+			File dir = new File(path).getParentFile();
+			if (dir != null)
+				dir.mkdirs();
+		}
+	}
+
+	private static URL resolvePlatformURL(URL url) throws IOException {
+		// 19536
+		if (url.getProtocol().equals(PlatformURLHandler.PROTOCOL)) {
+			URLConnection connection = url.openConnection();
+			if (connection instanceof PlatformURLConnection) {
+				url = ((PlatformURLConnection) connection).getResolvedURL();
+			} else {
+				//				connection = new PlatformURLBaseConnection(url);
+				//				url = ((PlatformURLConnection)connection).getResolvedURL();
+				url = getInstallURL();
+			}
+		}
+		return url;
+	}
+
+	private static void debug(String s) {
+		System.out.println("PlatformConfig: " + s); //$NON-NLS-1$
+	}
+
+	private void resetUpdateManagerState(URL url) throws IOException {
+		// [20111]
+		if (!supportsDetection(url))
+			return; // can't do ...
+
+		// find directory where the platform configuration file is	
+		URL resolved = resolvePlatformURL(url);
+		File initCfg = new File(resolved.getFile().replace('/', File.separatorChar));
+		File initDir = initCfg.getParentFile();
+
+		// Find the Update Manager State directory
+		if (initDir == null || !initDir.exists() || !initDir.isDirectory())
+			return;
+		String temp = initCfg.getName() + ".metadata"; //$NON-NLS-1$
+		File UMDir = new File(initDir, temp + '/');
+
+		// Attempt to rename it
+		if (UMDir == null || !UMDir.exists() || !UMDir.isDirectory())
+			return;
+		Date now = new Date();
+		boolean renamed = UMDir.renameTo(new File(initDir, temp + now.getTime() + '/'));
+
+		if (!renamed)
+			resetInitializationLocation(UMDir);
+	}
+
+	private static URL getInstallURL() {
+		return installURL;
+	}
+
+	private URL getDefaultStateLocation() throws IOException {
+		// 1) We store the config state relative to the 'eclipse' directory if possible
+		// 2) If this directory is read-only 
+		//    we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home> 
+		//    is unique for each local user, and <application-id> is the one 
+		//    defined in .eclipseproduct marker file. If .eclipseproduct does not
+		//    exist, use "eclipse" as the application-id.
+
+		URL installURL = getInstallURL();
+		File installDir = new File(installURL.getFile());
+
+		if ("file".equals(installURL.getProtocol()) && installDir.canWrite()) { //$NON-NLS-1$
+			if (DEBUG)
+				debug("Using the installation directory."); //$NON-NLS-1$
+			return installURL;
+		} else {
+			if (DEBUG)
+				debug("Using the user.home location."); //$NON-NLS-1$
+			String appName = "." + ECLIPSE; //$NON-NLS-1$
+			File eclipseProduct = new File(installDir, PRODUCT_SITE_MARKER);
+			if (eclipseProduct.exists()) {
+				Properties props = new Properties();
+				props.load(new FileInputStream(eclipseProduct));
+				String appId = props.getProperty(PRODUCT_SITE_ID);
+				if (appId == null || appId.trim().length() == 0)
+					appId = ECLIPSE;
+				String appVersion = props.getProperty(PRODUCT_SITE_VERSION);
+				if (appVersion == null || appVersion.trim().length() == 0)
+					appVersion = ""; //$NON-NLS-1$
+				appName += File.separator + appId + "_" + appVersion; //$NON-NLS-1$
+			}
+
+			String userHome = System.getProperty("user.home"); //$NON-NLS-1$
+			File configDir = new File(userHome, appName);
+			configDir.mkdirs();
+			return configDir.toURL();
+		}
+	}
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/PlatformConfigurationFactory.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/PlatformConfigurationFactory.java
new file mode 100644
index 0000000..be9bc34
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/PlatformConfigurationFactory.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.io.IOException;
+import java.net.URL;
+
+
+public class PlatformConfigurationFactory implements IPlatformConfigurationFactory {
+	public IPlatformConfiguration getCurrentPlatformConfiguration() {
+		return PlatformConfiguration.getCurrent();
+	}
+	public IPlatformConfiguration getPlatformConfiguration(URL url) throws IOException {
+		return new PlatformConfiguration(url);
+	}
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/Policy.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/Policy.java
new file mode 100644
index 0000000..58bb52c
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/Policy.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.text.MessageFormat;
+import java.util.*;
+
+public class Policy {
+	private static String bundleName = "org.eclipse.update.configurator.messages"; //$NON-NLS-1$
+	private static ResourceBundle bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
+
+	/**
+	 * Lookup the message with the given ID in this catalog 
+	 */
+	public static String bind(String id) {
+		return bind(id, (String[]) null);
+	}
+	/**
+	 * Lookup the message with the given ID in this catalog and bind its
+	 * substitution locations with the given string.
+	 */
+	public static String bind(String id, String binding) {
+		return bind(id, new String[] { binding });
+	}
+	/**
+	 * Lookup the message with the given ID in this catalog and bind its
+	 * substitution locations with the given strings.
+	 */
+	public static String bind(String id, String binding1, String binding2) {
+		return bind(id, new String[] { binding1, binding2 });
+	}
+
+	/**
+	 * Lookup the message with the given ID in this catalog and bind its
+	 * substitution locations with the given string values.
+	 */
+	public static String bind(String id, String[] bindings) {
+		if (id == null)
+			return "No message available"; //$NON-NLS-1$
+		String message = null;
+		try {
+			message = bundle.getString(id);
+		} catch (MissingResourceException e) {
+			// If we got an exception looking for the message, fail gracefully by just returning
+			// the id we were looking for.  In most cases this is semi-informative so is not too bad.
+			return "Missing message: " + id + " in: " + bundleName; //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		if (bindings == null)
+			return message;
+		return MessageFormat.format(message, bindings);
+	}
+	/**
+	 * Print a debug message to the console. If the given boolean is <code>true</code> then
+	 * pre-pend the message with the current date.
+	 */
+	public static void debug(boolean includeDate, String message) {
+		if (includeDate)
+			message = new Date(System.currentTimeMillis()).toString() + " - " + message; //$NON-NLS-1$
+		System.out.println(message);
+	}
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/Utils.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/Utils.java
new file mode 100644
index 0000000..6701051
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/Utils.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.core.internal.boot.PlatformURLBaseConnection;
+
+public class Utils {
+	private static final String PLUGIN_PATH = ".plugin-path"; //$NON-NLS-1$
+
+	/*
+	 * This method is retained for R1.0 compatibility because it is defined as API.
+	 * It's function matches the API description (returns <code>null</code> when
+	 * argument URL is <code>null</code> or cannot be read).
+	 */
+	public static URL[] getPluginPath(URL pluginPathLocation /*R1.0 compatibility*/
+	) {
+		InputStream input = null;
+		// first try and see if the given plugin path location exists.
+		if (pluginPathLocation == null)
+			return null;
+		try {
+			input = pluginPathLocation.openStream();
+		} catch (IOException e) {
+			//fall through
+		}
+
+		// if the given path was null or did not exist, look for a plugin path
+		// definition in the install location.
+		if (input == null)
+			try {
+				URL url = new URL(PlatformURLBaseConnection.PLATFORM_URL_STRING + PLUGIN_PATH);
+				input = url.openStream();
+			} catch (MalformedURLException e) {
+				//fall through
+			} catch (IOException e) {
+				//fall through
+			}
+
+		// nothing was found at the supplied location or in the install location
+		if (input == null)
+			return null;
+		// if we found a plugin path definition somewhere so read it and close the location.
+		URL[] result = null;
+		try {
+			try {
+				result = readPluginPath(input);
+			} finally {
+				input.close();
+			}
+		} catch (IOException e) {
+			//let it return null on failure to read
+		}
+		return result;
+	}
+
+	private static URL[] readPluginPath(InputStream input) {
+		Properties ini = new Properties();
+		try {
+			ini.load(input);
+		} catch (IOException e) {
+			return null;
+		}
+		Vector result = new Vector(5);
+		for (Enumeration groups = ini.propertyNames(); groups.hasMoreElements();) {
+			String group = (String) groups.nextElement();
+			for (StringTokenizer entries = new StringTokenizer(ini.getProperty(group), ";"); entries.hasMoreElements();) { //$NON-NLS-1$
+				String entry = (String) entries.nextElement();
+				if (!entry.equals("")) //$NON-NLS-1$
+					try {
+						result.addElement(new URL(entry));
+					} catch (MalformedURLException e) {
+						//intentionally ignore bad URLs
+						System.err.println(Policy.bind("ignore.plugin", entry)); //$NON-NLS-1$
+					}
+			}
+		}
+		return (URL[]) result.toArray(new URL[result.size()]);
+	}
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/messages.properties b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/messages.properties
new file mode 100644
index 0000000..8dc04df
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/messages.properties
@@ -0,0 +1,43 @@
+###############################################################################
+# Copyright (c) 2000, 2003 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
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+### Boot plugin message catalog
+
+ok = OK
+
+### URL
+url.badVariant=Unsupported \"platform:\" protocol variation {0}
+url.invalidURL=Invalid "platform:" URL {0}
+url.createConnection=Unable to create connection {0}
+url.noaccess=Unable to access URL as local {0}.
+
+### Platform Configuration
+cfig.inUse=\nConfiguration file \"{0}\" is in use by another instance of the platform, \n\
+	or there was a failure in deleting the old lock file.  If no other platform instances are running, \n\
+	delete file \"{1}\" and try starting the platform again.
+cfig.failCreateLock=Failed to create lock for configuration file \"{0}\"
+cfig.badUrlArg=Bad -configuration argument \"{0}\"
+cfig.unableToLoad.incomplete=Unable to load {0}
+cfig.unableToLoad.noURL=Configuration file location not specified
+cfig.unableToSave.noURL=Configuration file save location not specified
+cfig.unableToSave=Unable to save configuration file \"{0}\"
+cfig.badVersion=Unsupported configuration version \"{0}\"
+
+platform.running=The Platform is already running.
+platform.mustNotBeRunning=The Platform must not be running.
+platform.notRunning=The Platform is not running.
+
+ignore.plugin=Ignoring invalid plug-in location: {0}.
+application.notFound=Application not found: {0}.
+error.fatal=Fatal Error: Unable to determine platform installation URL {0}.
+error.boot=Fatal Error: Unable to locate boot.xml file for executing org.eclipse.core.boot.
+error.runtime=Fatal Error: Unable to locate matching org.eclipse.core.runtime plug-in.
+error.xerces=Fatal Error: Unable to locate matching org.apache.xerces plug-in.
+error.badNL=Bad value: \"{0}\" for NL. Using system default.
\ No newline at end of file
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/IPluginInfo.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/IPluginInfo.java
new file mode 100644
index 0000000..26170e5
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/IPluginInfo.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator.migration;
+
+import java.util.Map;
+import java.util.Set;
+
+public interface IPluginInfo {
+	public Map getLibraries();
+	public String[] getLibrariesName();
+	public String[] getRequires();
+	public String getMasterId();
+	public String getMasterVersion();
+	public String getPluginClass();
+	public String getUniqueId();
+	public String getVersion();
+	public boolean isFragment();
+	public Set getPackageFilters();
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/PluginConverter.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/PluginConverter.java
new file mode 100644
index 0000000..d009370
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/PluginConverter.java
@@ -0,0 +1,435 @@
+/*******************************************************************************
+ * Copyright (c) 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator.migration;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import org.eclipse.update.configurator.ConfigurationActivator;
+import org.eclipse.update.configurator.IPluginConverter;
+import org.osgi.framework.Constants;
+import org.xml.sax.SAXException;
+
+public class PluginConverter implements IPluginConverter {
+	private static final String LEGACY = "Legacy"; //$NON-NLS-1$
+	private static final String PLUGIN = "Plugin-Class"; //$NON-NLS-1$
+
+	protected String devPathSpec;
+	private IPluginInfo pluginInfo;
+	private File bundleManifestLocation;
+	private File pluginLocation;
+	private PrintWriter out;
+
+	private static final String[] ARCH_LIST = { org.eclipse.osgi.service.environment.Constants.ARCH_PA_RISC, org.eclipse.osgi.service.environment.Constants.ARCH_PPC, org.eclipse.osgi.service.environment.Constants.ARCH_SPARC, org.eclipse.osgi.service.environment.Constants.ARCH_X86 };
+	private static final String[] OS_LIST =
+		{
+			org.eclipse.osgi.service.environment.Constants.OS_AIX,
+			org.eclipse.osgi.service.environment.Constants.OS_HPUX,
+			org.eclipse.osgi.service.environment.Constants.OS_LINUX,
+			org.eclipse.osgi.service.environment.Constants.OS_MACOSX,
+			org.eclipse.osgi.service.environment.Constants.OS_QNX,
+			org.eclipse.osgi.service.environment.Constants.OS_SOLARIS,
+			org.eclipse.osgi.service.environment.Constants.OS_WIN32 };
+	private static final String[] WS_LIST =
+		{ org.eclipse.osgi.service.environment.Constants.WS_CARBON, org.eclipse.osgi.service.environment.Constants.WS_GTK, org.eclipse.osgi.service.environment.Constants.WS_MOTIF, org.eclipse.osgi.service.environment.Constants.WS_PHOTON, org.eclipse.osgi.service.environment.Constants.WS_WIN32 };
+
+	public PluginConverter() {
+		super();
+		devPathSpec = System.getProperty("osgi.dev");
+	}
+
+	public void convertManifest(IPluginInfo pluginInfo, File pluginLocation, File bundleManifestLocation) {
+		this.pluginInfo = pluginInfo;
+		this.pluginLocation = pluginLocation;
+		this.bundleManifestLocation = bundleManifestLocation;
+		generate();
+	}
+
+	public boolean convertManifest(File pluginLocation, File bundleManifestLocation) {
+		/* parse the plugin manifest to find out:
+		 * - the plug-in unique identifier
+		 * - the plug-in version
+		 * - runtime/libraries entries
+		 * - the plug-in class
+		 * - the master plugin (for a fragment)
+		 */
+		this.pluginLocation = pluginLocation;
+		this.bundleManifestLocation = bundleManifestLocation;
+		File pluginManifestLocation = new File(pluginLocation, "plugin.xml");
+		if (!pluginManifestLocation.isFile()) {
+			pluginManifestLocation = new File(pluginLocation, "fragment.xml");
+			if (!pluginManifestLocation.isFile())
+				// location does not contain a plugin manifest
+				return false;
+		}
+		try {
+			pluginInfo = new PluginParser().parse(pluginManifestLocation.toString());
+			generate();
+		} catch (IOException e) {
+			//TODO: what to do here? 
+			e.printStackTrace();
+			return false;
+		} catch (SAXException e) {
+			//TODO: what to do here?		
+			e.printStackTrace();
+			return false;
+		}
+		return true;
+	}
+
+	protected void generate() {
+		openFile();
+		generateHeaders();
+		generateClasspath();
+		generateLegacy();
+		generateActivator();
+		generatePluginClass();
+		generateProvidePackage();
+		generateImports();
+		generateRequireBundle();
+		closeFile();
+		if (ConfigurationActivator.DEBUG_CONVERTER)
+			System.out.println("Generated manifest on-the-fly at: " + bundleManifestLocation);
+	}
+
+	private void generateRequireBundle() {
+		String[] requiredBundles = pluginInfo.getRequires();
+		if (requiredBundles == null && !pluginInfo.getUniqueId().equals("org.eclipse.core.runtime")) // to avoid cycles
+			requiredBundles = new String[] { "org.eclipse.core.runtime" };
+
+		writeEntry(Constants.REQUIRE_BUNDLE, requiredBundles);
+	}
+
+	private void generateLegacy() {
+		writeEntry(LEGACY, "true");
+	}
+
+	private void generateImports() {
+		// TODO We should not need DynamicImport-Package or Import-Package
+		// writeEntry(Constants.DYNAMICIMPORT_PACKAGE, "*");
+	}
+
+	private void openFile() {
+		try {
+			bundleManifestLocation.getParentFile().mkdirs();
+			bundleManifestLocation.createNewFile();
+		} catch (IOException e) {
+			System.err.println("error creating directory writing manifest for " + pluginInfo.getUniqueId());
+			return;
+		}
+		BufferedOutputStream outputFile = null;
+		try {
+			outputFile = new BufferedOutputStream(new FileOutputStream(bundleManifestLocation));
+		} catch (FileNotFoundException e) {
+			System.err.println("error writing manifest for " + pluginInfo.getUniqueId());
+			return;
+		}
+		out = new PrintWriter(outputFile);
+		// so it is easy to tell which ones are generated
+		out.println("Generated: true");
+	}
+
+	private void writeEntry(String key, Collection value) {
+		if (value == null || value.size() == 0)
+			return;
+		if (value.size() == 1) {
+			out.println(key + ": " + value.iterator().next());
+			return;
+		}
+		key = key + ": ";
+		out.println(key);
+		out.print(' ');
+		boolean first = true;
+		for (Iterator i = value.iterator(); i.hasNext();) {
+			if (first)
+				first = false;
+			else {
+				out.println(',');
+				out.print(' ');
+			}
+			out.print(i.next());
+		}
+		out.println();
+	}
+
+	private void writeEntry(String key, String[] value) {
+		if (value == null || value.length == 0)
+			return;
+		if (value.length == 1) {
+			out.println(key + ": " + value[0]);
+			return;
+		}
+		key = key + ": ";
+		out.println(key);
+		out.print(' ');
+		boolean first = true;
+		for (int i = 0; i < value.length; i++) {
+			if (first)
+				first = false;
+			else {
+				out.println(',');
+				out.print(' ');
+			}
+			out.print(value[i]);
+		}
+		out.println();
+	}
+
+	private void writeEntry(String key, String value) {
+		if (value != null && value.length() > 0)
+			out.println(key + ": " + value);
+	}
+
+	private void closeFile() {
+		out.close();
+	}
+
+	private void generateHeaders() {
+		writeEntry(Constants.BUNDLE_NAME, pluginInfo.getUniqueId());
+		writeEntry(Constants.BUNDLE_VERSION, pluginInfo.getVersion());
+		writeEntry(Constants.BUNDLE_GLOBALNAME, pluginInfo.getUniqueId());
+	}
+	private void generateProvidePackage() {
+		StringBuffer providePackage = new StringBuffer();
+		Set exports = getExports();
+		if (exports != null) {
+			Iterator iter = exports.iterator();
+			boolean firstPkg = true;
+			while (iter.hasNext()) {
+				String pkg = (String) iter.next();
+				if (firstPkg) {
+					providePackage.append("\n ");
+					firstPkg = false;
+				} else {
+					providePackage.append(",\n ");
+				}
+				providePackage.append(pkg);
+			}
+			writeEntry(Constants.PROVIDE_PACKAGE, providePackage.toString());
+		}
+
+		if (pluginInfo.isFragment()) {
+			StringBuffer hostBundle = new StringBuffer();
+
+			hostBundle.append(pluginInfo.getMasterId()).append("; ");
+			hostBundle.append(Constants.BUNDLE_VERSION_ATTRIBUTE).append("=");
+			hostBundle.append(pluginInfo.getMasterVersion());
+
+			writeEntry(Constants.HOST_BUNDLE, hostBundle.toString());
+		}
+	}
+
+	private ArrayList getLibrariesExpandingVariables(String libraryPath, boolean filter) {
+		String var = hasPrefix(libraryPath);
+		if (var == null) {
+			ArrayList returnValue = new ArrayList(1);
+			returnValue.add(libraryPath);
+			return returnValue;
+		}
+		if (var.equals("ws")) {
+			return findWSJars(pluginLocation, libraryPath, filter);
+		}
+		if (var.equals("os")) {
+			return findOSJars(pluginLocation, libraryPath, filter);
+		}
+		return new ArrayList(0);
+	}
+
+	private void generateClasspath() {
+		String[] libraries = pluginInfo.getLibrariesName();
+		ArrayList expandedPath = new ArrayList();
+		for (int i = 0; i < libraries.length; i++) {
+			expandedPath.addAll(getLibrariesExpandingVariables(libraries[i], true));
+		}
+		writeEntry(Constants.BUNDLE_CLASSPATH, expandedPath);
+	}
+
+	private ArrayList findWSJars(File pluginRoot, String path, boolean filter) {
+		path = path.substring(4);
+		ArrayList found = new ArrayList(0);
+		for (int i = 0; i < WS_LIST.length; i++) {
+			String searchedPath = "ws/" + WS_LIST[i] + path;
+			if (new File(pluginRoot, searchedPath).exists()) {
+				found.add(searchedPath + (filter ? ";(ws=" + WS_LIST[i] + ")" : ""));
+			}
+		}
+		return found;
+	}
+
+	private ArrayList findOSJars(File pluginRoot, String path, boolean filter) {
+		path = path.substring(4);
+		ArrayList found = new ArrayList(0);
+		for (int i = 0; i < OS_LIST.length; i++) {
+			//look for os/osname/path
+			String searchedPath = "os/" + OS_LIST[i] + "/" + path;
+			if (new File(pluginRoot, searchedPath).exists())
+				found.add(searchedPath + (filter ? ";(os=" + WS_LIST[i] + ")" : ""));
+
+			//look for os/osname/archname/path
+			for (int j = 0; j < ARCH_LIST.length; j++) {
+				searchedPath = "os/" + OS_LIST[i] + "/" + ARCH_LIST[j] + "/" + path;
+				if (new File(pluginRoot, searchedPath).exists()) {
+					found.add(searchedPath + (filter ? ";(& (os=" + WS_LIST[i] + ") (arch=" + ARCH_LIST[j] + ")" : ""));
+				}
+			}
+		}
+		return found;
+	}
+	//return a String representing the string found between the $s
+	private String hasPrefix(String libPath) {
+		if (libPath.startsWith("$ws$"))
+			return "ws";
+		if (libPath.startsWith("$os$"))
+			return "os";
+		if (libPath.startsWith("$nl$"))
+			return "nl";
+		return null;
+	}
+
+	private void generateActivator() {
+		if (!pluginInfo.isFragment())
+			writeEntry(Constants.BUNDLE_ACTIVATOR, "org.eclipse.core.runtime.compatibility.PluginActivator");
+	}
+
+	private void generatePluginClass() {
+		writeEntry(PLUGIN, pluginInfo.getPluginClass());
+	}
+
+	private Set filterExport(Collection exportToFilter, Collection filter) {
+		if (filter == null || filter.contains("*"))
+			return (Set) exportToFilter;
+
+		Set filteredExport = new HashSet(exportToFilter.size());
+		for (Iterator iter = exportToFilter.iterator(); iter.hasNext();) {
+			String anExport = (String) iter.next();
+
+			for (Iterator iter2 = filter.iterator(); iter2.hasNext();) {
+				String aFilter = (String) iter2.next();
+				if (anExport.startsWith(aFilter)) {
+					filteredExport.add(anExport);
+				}
+			}
+		}
+		return filteredExport;
+	}
+
+	private Set getExportsFromJAR(File jarFile) {
+		Set names = new HashSet();
+		JarFile file = null;
+		try {
+			file = new JarFile(jarFile);
+		} catch (IOException e) {
+			System.err.println("Ignore " + jarFile);
+			return names;
+		}
+
+		//Run through the entries
+		for (Enumeration enum = file.entries(); enum.hasMoreElements();) {
+			JarEntry entry = (JarEntry) enum.nextElement();
+			String name = entry.getName();
+
+			int lastSlash = name.lastIndexOf("/");
+			//Ignore folders that do not contain files
+			if (lastSlash != -1 && lastSlash != name.length() - 1) {
+				if (name.lastIndexOf(' ') == -1 && !name.equalsIgnoreCase("meta-inf/manifest.mf"))
+					names.add(name.substring(0, lastSlash).replace('/', '.'));
+			}
+		}
+		return names;
+	}
+
+	private Set getExportsFromDir(File location) {
+		return getExportsFromDir(location, "");
+	}
+
+	private Set getExportsFromDir(File location, String packageName) {
+		String prefix = (packageName.length() > 0) ? (packageName + '.') : "";
+		File[] files = location.listFiles();
+		Set exportedPaths = new HashSet();
+		boolean containsFile = false;
+		for (int i = 0; i < files.length; i++) {
+			if (files[i].isDirectory())
+				exportedPaths.addAll(getExportsFromDir(files[i], prefix + files[i].getName()));
+			else
+				containsFile = true;
+		}
+		if (containsFile && packageName.length() > 0)
+			exportedPaths.add(packageName);
+		return exportedPaths;
+	}
+
+	private Set getExports() {
+		Map libs = pluginInfo.getLibraries();
+		if (libs == null)
+			return null;
+
+		// Based on similar code from EclipseStarter
+		// Check the osgi.dev property to see if dev classpath entries have been defined.
+		//		String osgiDev = System.getProperty("osgi.dev");
+		String[] devClassPath = null;
+		if (devPathSpec != null) {
+			// Add each dev classpath entry
+			Vector tokens = new Vector(6);
+			StringTokenizer t = new StringTokenizer(devPathSpec, ",");
+			while (t.hasMoreTokens()) {
+				String token = t.nextToken();
+				if (!token.equals("")) {
+					tokens.addElement(token);
+				}
+			}
+			devClassPath = new String[tokens.size()];
+			tokens.toArray(devClassPath);
+		}
+
+		// add the dev. time classpath entries
+		List starExport = new ArrayList(1);
+		starExport.add("*");
+		if (devClassPath != null) {
+			for (int i = 0; i < devClassPath.length; i++) {
+				libs.put(devClassPath[i], starExport);
+			}
+		}
+		Set result = new HashSet(7);
+		Set libEntries = libs.entrySet();
+		for (Iterator iter = libEntries.iterator(); iter.hasNext();) {
+			Map.Entry element = (Map.Entry) iter.next();
+			List filter = (List) element.getValue();
+			if (filter.size() == 0) //If the library is not exported, then ignore it
+				continue;
+
+			File libraryLocation = new File(pluginLocation, (String) element.getKey());
+			Set exports = null;
+			if (libraryLocation.exists()) {
+				if (libraryLocation.isFile())
+					exports = filterExport(getExportsFromJAR(libraryLocation), filter); //TODO Need to handle $xx$ variables
+				else if (libraryLocation.isDirectory())
+					exports = filterExport(getExportsFromDir(libraryLocation), filter);
+			} else {
+				ArrayList expandedLibs = getLibrariesExpandingVariables((String) element.getKey(), false);
+				exports = new HashSet();
+				for (Iterator iterator = expandedLibs.iterator(); iterator.hasNext();) {
+					String libName = (String) iterator.next();
+					File libFile = new File(pluginLocation, libName);
+					if (libFile.isFile()) {
+						exports.addAll(filterExport(getExportsFromJAR(libFile), filter));
+					}
+				}
+			}
+
+			if (exports != null) {
+				result.addAll(exports);
+			}
+		}
+		return result;
+	}
+
+}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/PluginParser.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/PluginParser.java
new file mode 100644
index 0000000..8fb8a89
--- /dev/null
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/configurator/migration/PluginParser.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.configurator.migration;
+
+import java.io.IOException;
+import java.util.*;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.eclipse.update.configurator.ConfigurationActivator;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class PluginParser extends DefaultHandler {
+	/**
+	 * Manifest schema version from "eclipse" processing directive, or
+	 * <code>null</code> if none.
+	 * @since 3.0
+	 */
+	private String schemaVersion = null;
+	private PluginInfo manifestInfo = new PluginInfo();
+	private List exportStatement;
+
+	// model parser
+	private ServiceReference parserReference;
+
+	public PluginParser() {
+		super();
+	}
+
+	public PluginInfo parse(String fileLocation) throws IOException, SAXException {
+		SAXParserFactory factory = acquireXMLParsing();
+		if (factory == null)
+			return null;
+		try {
+			factory.setNamespaceAware(true);
+			factory.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
+			factory.setValidating(false);
+			factory.newSAXParser().parse(fileLocation, this);
+		} catch (ParserConfigurationException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} finally {
+			releaseXMLParsing();
+		}
+		return manifestInfo;
+	}
+
+	public void startElement(String uri, String elementName, String qName, Attributes attributes) throws SAXException {
+		if (elementName.equals("plugin") || elementName.equals("fragment")) {
+			manifestInfo.pluginId = attributes.getValue("", "id");
+			manifestInfo.version = attributes.getValue("", "version");
+			manifestInfo.pluginClass = attributes.getValue("", "class");
+			manifestInfo.schemaVersion = schemaVersion;
+			if (elementName.equals("fragment")) {
+				manifestInfo.masterPluginId = attributes.getValue("", "plugin-id");
+				manifestInfo.masterVersion = attributes.getValue("", "plugin-version");
+			}
+		} else if (elementName.equals("library")) {
+			if (manifestInfo.libraries == null)
+				manifestInfo.libraries = new HashMap();
+			exportStatement = new ArrayList();
+			manifestInfo.libraries.put(attributes.getValue("", "name"), exportStatement);
+		} else if (elementName.equals("import")) {
+			if (manifestInfo.requires == null) {
+				manifestInfo.requires = new ArrayList();
+				// to avoid cycles
+				if (!manifestInfo.pluginId.equals("org.eclipse.core.runtime"))
+					manifestInfo.requires.add("org.eclipse.core.runtime");
+			}
+			String plugin = attributes.getValue("", "plugin");
+			if (plugin == null)
+				return;
+
+			if (plugin.equals("org.eclipse.core.boot") || plugin.equals("org.eclipse.core.runtime"))
+				return;
+
+			String version = attributes.getValue("", "version");
+			String export = attributes.getValue("", "export");
+			String optional = attributes.getValue("", "optional");
+			String match = attributes.getValue("", "match");
+			String modImport = plugin;
+			if (version != null) {
+				modImport += "; " + Constants.BUNDLE_VERSION_ATTRIBUTE + "=" + version;
+			}
+			if (export != null) {
+				modImport += "; " + Constants.PROVIDE_PACKAGES_ATTRIBUTE + "=" + export;
+			}
+			if (optional != null) {
+				modImport += ";" + Constants.OPTIONAL_ATTRIBUTE + "=" + "true";
+			}
+			if (match != null) {
+				modImport += ";" + Constants.VERSION_MATCH_ATTRIBUTE + "=";
+				if (match.equalsIgnoreCase("perfect")) {
+					modImport += Constants.VERSION_MATCH_PERFECT;
+				} else if (match.equalsIgnoreCase("equivalent")) {
+					modImport += Constants.VERSION_MATCH_EQUIVALENT;
+				} else if (match.equalsIgnoreCase("compatible")) {
+					modImport += Constants.VERSION_MATCH_COMPATIBLE;
+				} else if (match.equalsIgnoreCase("greaterOrEqual")) {
+					modImport += Constants.VERSION_MATCH_GREATERTHANOREQUAL;
+				}
+			}
+			manifestInfo.requires.add(modImport);
+		} else if (elementName.equals("packages")) {
+			//packages filtering is done globally
+			if (manifestInfo.filters == null)
+				manifestInfo.filters = new HashSet(4);
+			String prefixString = attributes.getValue("", "prefixes");
+			StringTokenizer tok = new StringTokenizer(prefixString, ",");
+			while (tok.hasMoreTokens()) {
+				manifestInfo.filters.add(tok.nextToken().trim());
+			}
+		} else if (elementName.equals("export")) {
+			//Add the export to the last library encountered
+			String exportFilter = (String) attributes.getValue("", "name");
+			StringTokenizer tok = new StringTokenizer(exportFilter, ",");
+			while (tok.hasMoreTokens()) {
+				exportStatement.add(tok.nextToken().trim());
+			}
+		}
+	}
+
+	private SAXParserFactory acquireXMLParsing() {
+		parserReference = ConfigurationActivator.getBundleContext().getServiceReference("javax.xml.parsers.SAXParserFactory");
+		if (parserReference == null)
+			return null;
+		return (SAXParserFactory) ConfigurationActivator.getBundleContext().getService(parserReference);
+	}
+
+	private void releaseXMLParsing() {
+		if (parserReference != null)
+			ConfigurationActivator.getBundleContext().ungetService(parserReference);
+	}
+
+	public static void main(String[] args) throws Exception {
+		for (int i = 0; i < args.length; i++)
+			System.out.println(new PluginParser().parse(args[i]));
+	}
+
+	public class PluginInfo implements IPluginInfo {
+		private String schemaVersion;
+		private String pluginId;
+		private String version;
+		// TODO libraries and requires should be ordered.
+		private Map libraries; //represent the libraries and their export statement
+		private ArrayList requires;
+		private String pluginClass;
+		private String masterPluginId;
+		private String masterVersion;
+		private Set filters;
+
+		public boolean isFragment() {
+			return masterPluginId != null;
+		}
+		public String toString() {
+			return "plugin-id: " + pluginId + "  version: " + version + " libraries: " + libraries + " class:" + pluginClass + " master: " + masterPluginId + " master-version: " + masterVersion + " requires: " + requires;
+		}
+		public Map getLibraries() {
+			if (libraries == null)
+				return new HashMap(0);
+			return libraries;
+		}
+
+		public String[] getRequires() {
+			if (requires == null)
+				return new String[] { "org.eclipse.core.runtime" };
+
+			if (schemaVersion == null) {
+				//Add elements on the requirement list of ui and help. 
+				for (int i = 0; i < requires.size(); i++) {
+					if ("org.eclipse.ui".equals(requires.get(i))) { //$NON-NLS-1$
+						requires.add(i + 1, "org.eclipse.ui.workbench.texteditor;" + Constants.OPTIONAL_ATTRIBUTE + "=" + "true");
+						requires.add(i + 1, "org.eclipse.ui.jface.text;" + Constants.OPTIONAL_ATTRIBUTE + "=" + "true");
+						requires.add(i + 1, "org.eclipse.ui.editors;" + Constants.OPTIONAL_ATTRIBUTE + "=" + "true");
+						requires.add(i + 1, "org.eclipse.ui.views;" + Constants.OPTIONAL_ATTRIBUTE + "=" + "true");
+						requires.add(i + 1, "org.eclipse.ui.ide;" + Constants.OPTIONAL_ATTRIBUTE + "=" + "true");
+					} else if ("org.eclipse.help".equals(requires.get(i))) { //$NON-NLS-1$
+						requires.add(i + 1, "org.eclipse.help.base;" + Constants.OPTIONAL_ATTRIBUTE + "=" + "true");
+					}
+				}
+			}
+
+			String[] requireBundles = new String[requires.size()];
+			requires.toArray(requireBundles);
+			return requireBundles;
+		}
+
+		public String getMasterId() {
+			return masterPluginId;
+		}
+		public String getMasterVersion() {
+			return masterVersion;
+		}
+		public String getPluginClass() {
+			return pluginClass;
+		}
+		public String getUniqueId() {
+			return pluginId;
+		}
+		public String getVersion() {
+			return version;
+		}
+		public Set getPackageFilters() {
+			return filters;
+		}
+		public String[] getLibrariesName() {
+			if (libraries == null)
+				return new String[0];
+			Set names = libraries.keySet();
+			return (String[]) names.toArray(new String[names.size()]);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.xml.sax.ContentHandler#processingInstruction
+	 * @since 3.0
+	 */
+	public void processingInstruction(String target, String data) throws SAXException {
+		// Since 3.0, a processing instruction of the form <?eclipse version="3.0"?> at
+		// the start of the manifest file is used to indicate the plug-in manifest
+		// schema version in effect. Pre-3.0 (i.e., 2.1) plug-in manifest files do not
+		// have one of these, and this is how we can distinguish the manifest of a
+		// pre-3.0 plug-in from a post-3.0 one (for compatibility tranformations).
+		if (target.equalsIgnoreCase("eclipse")) { //$NON-NLS-1$
+			// just the presence of this processing instruction indicates that this
+			// plug-in is at least 3.0
+			schemaVersion = "3.0"; //$NON-NLS-1$
+			StringTokenizer tokenizer = new StringTokenizer(data, "=\""); //$NON-NLS-1$
+			while (tokenizer.hasMoreTokens()) {
+				String token = tokenizer.nextToken();
+				if (token.equalsIgnoreCase("version")) { //$NON-NLS-1$
+					if (!tokenizer.hasMoreTokens()) {
+						break;
+					}
+					schemaVersion = tokenizer.nextToken();
+					break;
+				}
+			}
+		}
+	}
+}