New code for startup and model
diff --git a/update/org.eclipse.update.configurator/plugin.xml b/update/org.eclipse.update.configurator/plugin.xml
index a08ff4d..941a7cd 100644
--- a/update/org.eclipse.update.configurator/plugin.xml
+++ b/update/org.eclipse.update.configurator/plugin.xml
@@ -18,4 +18,4 @@
     <import plugin="org.eclipse.osgi.util"/>
     <import plugin="org.eclipse.core.runtime"/>
   </requires>
-</plugin>
\ No newline at end of file
+</plugin>
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/BootDescriptor.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/BootDescriptor.java
index 557d6f2..bc63b75 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/BootDescriptor.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/BootDescriptor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/ConfigurationActivator.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/ConfigurationActivator.java
index 9c94ca3..bcd167b 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/ConfigurationActivator.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/ConfigurationActivator.java
@@ -16,18 +16,18 @@
 
 import org.eclipse.core.runtime.*;
 import org.eclipse.osgi.service.debug.*;
-import org.eclipse.osgi.service.environment.*;
 import org.eclipse.update.configurator.*;
 import org.osgi.framework.*;
 import org.osgi.service.packageadmin.*;
 import org.osgi.service.startlevel.*;
 import org.osgi.util.tracker.*;
 
-public class ConfigurationActivator implements BundleActivator {
+public class ConfigurationActivator implements BundleActivator, IBundleGroupProvider {
 
 	public static String PI_CONFIGURATOR = "org.eclipse.update.configurator";
 	public static final String INSTALL_LOCATION = "osgi.installLocation";
 	public static final String LAST_CONFIG_STAMP = "last.config.stamp";
+	
 	// debug options
 	public static String OPTION_DEBUG = PI_CONFIGURATOR + "/debug";
 	// debug values
@@ -38,8 +38,6 @@
 	private static BundleContext context;
 	private ServiceTracker platformTracker;
 	private ServiceRegistration configurationFactorySR;
-	private String[] allArgs;
-	private BundleListener reconcilerListener;
 	private IPlatform platform;
 	private PlatformConfiguration configuration;
 	
@@ -54,7 +52,6 @@
 
 	public void start(BundleContext ctx) throws Exception {
 		context = ctx;
-		obtainArgs();
 		initialize();
 		//Short cut, if the configuration has not changed
 		String application = configuration.getApplicationIdentifier();
@@ -64,7 +61,9 @@
 			System.setProperty("eclipse.application", "org.eclipse.ui.ide.workbench"); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 	
-		if (lastTimeStamp==configuration.getChangeStamp() && !(application.equals(PlatformConfiguration.RECONCILER_APP) || System.getProperties().get("osgi.dev") != null)) {
+//		if (lastTimeStamp==configuration.getChangeStamp() && !(application.equals(PlatformConfiguration.RECONCILER_APP) || System.getProperties().get("osgi.dev") != null)) {
+		if (lastTimeStamp==configuration.getChangeStamp() && System.getProperties().get("osgi.dev") == null) {
+				
 			Utils.debug("Same last time stamp *****");
 			
 			if (System.getProperty("eclipse.application") == null) {
@@ -77,6 +76,7 @@
 		Utils.debug("Starting update configurator...");
 
 		installBundles();
+		platform.registerBundleGroupProvider(this);
 	}
 
 
@@ -91,7 +91,7 @@
 		installURL = platform.getInstallURL();
 		configArea = platform.getConfigurationLocation().toOSString();
 		configurationFactorySR = context.registerService(IPlatformConfigurationFactory.class.getName(), new PlatformConfigurationFactory(), null);
-		configuration = getPlatformConfiguration(allArgs, installURL, configArea);
+		configuration = getPlatformConfiguration(installURL, configArea);
 		if (configuration == null)
 			throw Utils.newCoreException("Cannot create configuration in " + configArea, null);
 
@@ -106,19 +106,6 @@
 	}
 
 
-	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 {
 		// quick fix (hack) for bug 47861
 		try {
@@ -207,12 +194,9 @@
 			}
 			context.ungetService(reference);
 			refreshPackages((Bundle[]) toRefresh.toArray(new Bundle[toRefresh.size()]));
-			if (System.getProperty("eclipse.application") == null || System.getProperty("eclipse.application").equals(PlatformConfiguration.RECONCILER_APP))
+//			if (System.getProperty("eclipse.application") == null || System.getProperty("eclipse.application").equals(PlatformConfiguration.RECONCILER_APP))
+			if (System.getProperty("eclipse.application") == null)
 				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();
@@ -261,39 +245,6 @@
 		}
 		return (Bundle[])bundlesToUninstall.toArray(new Bundle[bundlesToUninstall.size()]);
 	}
-			
-	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.getBundles("org.eclipse.core.applicationrunner")[0];
-					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();
-	}
 
 	/**
 	 * This is a major hack to try to get the reconciler application running. However we should find a way to not run it.
@@ -301,9 +252,9 @@
 	 * @param metaPath
 	 * @return
 	 */
-	private PlatformConfiguration getPlatformConfiguration(String[] args, URL installURL, String configPath) {
+	private PlatformConfiguration getPlatformConfiguration(URL installURL, String configPath) {
 		try {
-			PlatformConfiguration.startup(args, null, installURL, configPath);
+			PlatformConfiguration.startup(installURL, configPath);
 		} catch (Exception e) {
 			if (platformTracker != null) {
 				String message = e.getMessage();
@@ -334,6 +285,7 @@
 		}
 		// TODO this is such a hack it is silly.  There are still cases for race conditions etc
 		// but this should allow for some progress...
+		// (patch from John A.)
 		final boolean[] flag = new boolean[] {false};
 		FrameworkListener listener = new FrameworkListener() {
 			public void frameworkEvent(FrameworkEvent event) {
@@ -399,7 +351,7 @@
 	public static BundleContext getBundleContext() {
 		return context;
 	}
-	
+		
 	public static URL getInstallURL() {
 		if (installURL == null)
 			try {
@@ -409,4 +361,21 @@
 			}
 			return installURL;
 	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroupProvider#getName()
+	 */
+	public String getName() {
+		return Messages.getString("BundleGroupProvider");
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroupProvider#getBundleGroups()
+	 */
+	public IBundleGroup[] getBundleGroups() {
+		if (configuration == null)
+			return new IBundleGroup[0];
+		else {
+			return (FeatureEntry[])configuration.getConfiguredFeatureEntries();
+		}
+	}
 }
\ No newline at end of file
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureEntry.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureEntry.java
index daeae83..9e326c1 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureEntry.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureEntry.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
@@ -12,10 +12,13 @@
 
 import java.net.*;
 
+import org.eclipse.core.runtime.*;
 import org.eclipse.update.configurator.*;
+import org.osgi.framework.*;
+import org.w3c.dom.*;
 
 
-public class FeatureEntry implements IPlatformConfiguration.IFeatureEntry {
+public class FeatureEntry implements IPlatformConfiguration.IFeatureEntry, IConfigurationConstants, IBundleGroup {
 	private String id;
 	private String version;
 	private String pluginVersion;
@@ -23,6 +26,7 @@
 	private URL[] root;
 	private boolean primary;
 	private String pluginIdentifier;
+	private String url;
 
 	public FeatureEntry(String id, String version, String pluginIdentifier, String pluginVersion, boolean primary, String application, URL[] root) {
 		if (id == null)
@@ -40,6 +44,23 @@
 		this(id, version, id, pluginVersion, primary, application, root);
 	}
 
+	/**
+	 * Sets the url string (relative to the site url)
+	 * @param url
+	 */
+	public void setURL(String url) {
+		this.url = url;
+	}
+	
+	/**
+	 * @return the feature url (relative to the site): features/org.eclipse.platform/
+	 */
+	public String getURL() {
+//		if (url == null)
+//			url = FEATURES + "/" + id + "_" + version + "/";
+		return url;
+	}
+	
 	/*
 	 * @see IFeatureEntry#getFeatureIdentifier()
 	 */
@@ -88,4 +109,81 @@
 		return pluginIdentifier;
 	}
 
+	public Element toXML(Document doc) {
+	
+		Element featureElement = doc.createElement(CFG_FEATURE_ENTRY);		
+		// write out feature entry settings
+		if (getFeatureIdentifier() != null)
+			featureElement.setAttribute(CFG_FEATURE_ENTRY_ID, getFeatureIdentifier()); 
+		if (canBePrimary())
+			featureElement.setAttribute(CFG_FEATURE_ENTRY_PRIMARY, "true");
+		if (getFeatureVersion() != null)
+			featureElement.setAttribute(CFG_FEATURE_ENTRY_VERSION, getFeatureVersion()); 
+		if (getFeaturePluginVersion() != null && !getFeaturePluginVersion().equals(getFeatureVersion()) && getFeaturePluginVersion().length() > 0)
+			featureElement.setAttribute(CFG_FEATURE_ENTRY_PLUGIN_VERSION, getFeaturePluginVersion()); 
+		if (getFeaturePluginIdentifier() != null && !getFeaturePluginIdentifier().equals(getFeatureIdentifier()))
+			featureElement.setAttribute(CFG_FEATURE_ENTRY_PLUGIN_IDENTIFIER, getFeaturePluginIdentifier());
+		if (getFeatureApplication() != null)
+			featureElement.setAttribute(CFG_FEATURE_ENTRY_APPLICATION, getFeatureApplication());
+		if (getURL() != null)
+			featureElement.setAttribute(CFG_URL, getURL());
+		
+		URL[] roots = getFeatureRootURLs();
+		for (int i=0; i<roots.length; i++) {
+			String root = roots[i].toExternalForm();
+			if (root != null && root.trim().length() > 0){
+				Element rootElement = doc.createElement(CFG_FEATURE_ENTRY_ROOT);
+				rootElement.appendChild(doc.createTextNode(root));
+				featureElement.appendChild(rootElement);
+			}
+		}
+		
+		return featureElement;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroup#getBundles()
+	 */
+	public Bundle getBundles() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroup#getDescription()
+	 */
+	public String getDescription() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroup#getIdentifier()
+	 */
+	public String getIdentifier() {
+		return id;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroup#getName()
+	 */
+	public String getName() {
+		return id + "_" + version;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroup#getProperty(java.lang.String)
+	 */
+	public String getProperty(String key) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroup#getProviderName()
+	 */
+	public String getProviderName() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IBundleGroup#getVersion()
+	 */
+	public String getVersion() {
+		return version;
+	}
 }
\ No newline at end of file
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureParser.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureParser.java
index a6417ee..0625e25 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureParser.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/FeatureParser.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
@@ -15,7 +15,6 @@
 import java.net.*;
 
 import javax.xml.parsers.*;
-import org.eclipse.update.configurator.*;
 
 import org.xml.sax.*;
 import org.xml.sax.helpers.*;
@@ -36,6 +35,7 @@
 
 	private SAXParser parser;
 	private FeatureEntry feature;
+	private URL url;
 
 	private final static SAXParserFactory parserFactory =
 		SAXParserFactory.newInstance();
@@ -67,8 +67,9 @@
 	 * @exception IOException
 	 * @since 2.0
 	 */
-	public IPlatformConfiguration.IFeatureEntry parse(URL featureURL) {
+	public FeatureEntry parse(URL featureURL) {
 		try {
+			this.url = featureURL;
 			InputStream in = featureURL.openStream();
 			parser.parse(new InputSource(in), this);
 		} catch (SAXException e) {;
@@ -127,18 +128,15 @@
 
 			//TODO rootURLs
 			feature = new FeatureEntry(id, ver, plugin, "", isPrimary, application, null );
+			if ("file".equals(url.getProtocol())) {
+				File f = new File(url.getFile().replace('/', File.separatorChar));
+				feature.setURL("features" + "/" + f.getParentFile().getName() + "/");// + f.getName());
+			} else {
+				feature.setURL(url.toExternalForm());
+			}
 
 			Utils.
-				debug("End process DefaultFeature tag: id:" //$NON-NLS-1$
-				+id + " ver:" //$NON-NLS-1$
-				+ver); //$NON-NLS-1$
-//			Utils.debug("End process DefaultFeature tag: image:" + imageURL); //$NON-NLS-1$
-//			Utils.debug("End process DefaultFeature tag: ws:" //$NON-NLS-1$
-//				+ws + " os:" //$NON-NLS-1$
-//				+os + " nl:" //$NON-NLS-1$
-//				+nl + " application:" //$NON-NLS-1$
-//				+application);
-			
+				debug("End process DefaultFeature tag: id:" +id + " ver:" +ver + " url:" + feature.getURL()); 	
 		}
 	}
 
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/InstalledSiteParser.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/InstalledSiteParser.java
deleted file mode 100644
index f59972d..0000000
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/InstalledSiteParser.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*******************************************************************************
- * 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.internal.configurator;
-import java.io.*;
-import java.net.*;
-
-import org.eclipse.core.runtime.*;
-import org.eclipse.update.configurator.*;
-import org.xml.sax.*;
-
-public class InstalledSiteParser {
-	
-	// private when parsing file system
-	private SiteEntry site;
-	
-	private static FeatureParser featureParser = new FeatureParser();
-
-	public InstalledSiteParser() {
-	}
-	
-	/*
-	 * @see ISiteFactory#createSite(URL,boolean)
-	 */
-	public IPlatformConfiguration.ISiteEntry parse(URL url) throws CoreException{
-		InputStream siteStream = null;
-	
-		try {
-			// if url points to a directory
-			// attempt to parse site.xml
-			String path = url.getFile();
-			File siteLocation = new File(path);
-			if (!siteLocation.isDirectory() || !siteLocation.exists())
-				throw Utils.newCoreException(Messages.getString("InstalledSiteParser.DirectoryDoesNotExist", siteLocation.getAbsolutePath()), null);
-			
-			site = parseSite(siteLocation);
-
-// TODO			site.resolve(url, url);
-	
-			// Do not set read only as may install in it
-			//site.markReadOnly();
-//		} catch (MalformedURLException e) {
-//			throw Utils.newCoreException(Policy.bind("InstalledSiteParser.UnableToCreateURL", url == null ? "" : url.toExternalForm()), e);
-//			//$NON-NLS-1$
-		} finally {
-			try {
-				if (siteStream != null)
-					siteStream.close();
-			} catch (IOException e) {
-			}
-		}
-		return site;
-	}
-	/**
-	 * Method parseSite.
-	 */
-	private SiteEntry parseSite(File directory) throws CoreException {
-
-		this.site = new SiteEntry();
-	
-		parseInstalledFeatures(directory);
-		parseInstalledPlugins(directory);
-
-		return site;
-
-	}
-
-	/**
-	 * Method parseFeature.
-	 * @throws CoreException
-	 */
-	private void parseInstalledFeatures(File directory) throws CoreException {
-
-		File featuresDir = new File(directory, "features");
-		if (featuresDir.exists()) {
-
-			URL featureURL;
-
-			// handle the installed features under the features directory
-			File[] dirs = featuresDir.listFiles(new FileFilter() {
-				public boolean accept(File f) {
-					boolean valid = f.isDirectory() && (new File(f,"feature.xml").exists());
-					if (!valid)
-						System.out.println("Unable to find feature.xml in directory:" + f.getAbsolutePath());
-					return valid;
-				}
-			});
-			try {
-				for (int index = 0; index < dirs.length; index++) {
-					File featureXML = new File(dirs[index], "feature.xml");
-					featureURL = featureXML.toURL();
-					IPlatformConfiguration.IFeatureEntry featureEntry = featureParser.parse(featureURL);
-					site.addFeatureEntry(featureEntry);
-				}
-			} catch (MalformedURLException e) {
-				throw Utils.newCoreException(Messages.getString("InstalledSiteParser.UnableToCreateURLForFile", featuresDir.getAbsolutePath()), e);
-				//$NON-NLS-1$
-			}
-		}
-	}
-
-
-
-	/**
-	 * Method parsePlugins.
-	 * 
-	 * look into each plugin/fragment directory, crack the plugin.xml open (or fragment.xml ???)
-	 * get id and version, calculate URL...	
-	 * 
-	 * @return VersionedIdentifier
-	 * @throws CoreException
-	 */
-	private void parseInstalledPlugins(File directory) throws CoreException {
-
-		File pluginPath = new File(directory, "plugins");
-		File pluginFile = null;
-
-		try {
-			if (pluginPath.exists()) {
-				File[] files = pluginPath.listFiles();
-				PluginParser parser = new PluginParser();
-				for (int i = 0; i < files.length; i++) {
-					if (files[i].isDirectory()) {
-
-						if (!(pluginFile = new File(files[i], "plugin.xml")).exists()) { //$NON-NLS-1$
-							pluginFile = new File(files[i], "fragment.xml"); //$NON-NLS-1$
-						}
-
-						if (pluginFile != null && pluginFile.exists() && !pluginFile.isDirectory()) {
-							PluginEntry entry = parser.parse(new FileInputStream(pluginFile));
-							site.addPluginEntry(entry);
-						}
-					} // files[i] is a directory
-				}
-			} // path is a directory
-		} catch (IOException e) {
-			String pluginFileString = (pluginFile == null) ? null : pluginFile.getAbsolutePath();
-			throw Utils.newCoreException(Messages.getString("InstalledSiteParser.ErrorAccessing", pluginFileString), e);
-			//$NON-NLS-1$
-		} catch (SAXException e) {
-			String pluginFileString = (pluginFile == null) ? null : pluginFile.getAbsolutePath();
-			throw Utils.newCoreException(Messages.getString("InstalledSiteParser.ErrorParsingFile", pluginFileString), e);
-			//$NON-NLS-1$
-		}
-
-	}
-
-
-//	/**
-//	* Method parseFeature.
-//	* @throws CoreException
-//	*/
-//	private void parsePackagedFeatures(File directory) throws CoreException {
-//
-//		// FEATURES
-//		File featureDir = new File(directory, Site.DEFAULT_FEATURE_PATH);
-//		if (featureDir.exists()) {
-//			String[] dir;
-//			FeatureReference featureRef;
-//			URL featureURL;
-//			File currentFeatureFile;
-//			String newFilePath = null;
-//
-//			try {
-//				// only list JAR files
-//				dir = featureDir.list(FeaturePackagedContentProvider.filter);
-//				for (int index = 0; index < dir.length; index++) {
-//
-//					// check if the JAR file contains a feature.xml
-//					currentFeatureFile = new File(featureDir, dir[index]);
-//					JarContentReference ref = new JarContentReference("", currentFeatureFile);
-//					ContentReference result = null;
-//					try {
-//						result = ref.peek(Feature.FEATURE_XML, null, null);
-//					} catch (IOException e) {
-//						UpdateCore.warn("Exception retrieving feature.xml in file:" + currentFeatureFile, e);
-//					}
-//					if (result == null) {
-//						UpdateCore.warn("Unable to find feature.xml in file:" + currentFeatureFile);
-//					} else {
-//						featureURL = currentFeatureFile.toURL();
-//						// PERF: remove code
-//						//SiteFileFactory archiveFactory = new SiteFileFactory();
-//						featureRef = new FeatureReference(site, featureURL.toExternalForm());
-//						site.addFeatureReference(featureRef);
-//					}
-//				}
-//			} catch (MalformedURLException e) {
-//				throw Utilities.newCoreException(Policy.bind("SiteFileFactory.UnableToCreateURLForFile", newFilePath), e);
-//				//$NON-NLS-1$
-//			}
-//		}
-//	}
-	
-//	/**
-//	 * 
-//	 */
-//	private void parsePackagedPlugins(File pluginDir) throws CoreException {
-//
-//		File file = null;
-//		String[] dir;
-//
-//		ContentReference ref = null;
-//		String refString = null;
-//
-//		try {
-//			if (pluginDir.exists()) {
-//				dir = pluginDir.list(FeaturePackagedContentProvider.filter);
-//				for (int i = 0; i < dir.length; i++) {
-//					file = new File(pluginDir, dir[i]);
-//					JarContentReference jarReference = new JarContentReference(null, file);
-//					ref = jarReference.peek("plugin.xml", null, null); //$NON-NLS-1$
-//					if (ref == null)
-//						jarReference.peek("fragment.xml", null, null); //$NON-NLS-1$
-//
-//					refString = (ref == null) ? null : ref.asURL().toExternalForm();
-//
-//					if (ref != null) {
-//						PluginEntry entry = new PluginParser().parse(ref.getInputStream());
-//						addParsedPlugin(entry,file);
-//					} //ref!=null
-//				} //for
-//			}
-//
-//		} catch (IOException e) {
-//			throw Utilities.newCoreException(Policy.bind("SiteFileFactory.ErrorAccessing", refString), e);
-//			//$NON-NLS-1$
-//		} catch (SAXException e) {
-//			throw Utilities.newCoreException(Policy.bind("SiteFileFactory.ErrorParsingFile", refString), e);
-//			//$NON-NLS-1$
-//		}
-//	}
-}
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Messages.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Messages.java
index 4a58aee..c05f524 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Messages.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Messages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfiguration.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfiguration.java
index d69b497..1e414b2 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfiguration.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfiguration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
@@ -11,6 +11,7 @@
 package org.eclipse.update.internal.configurator;
 
 import java.io.*;
+import java.lang.reflect.*;
 import java.net.*;
 import java.util.*;
 
@@ -20,45 +21,48 @@
 import javax.xml.transform.stream.*;
 
 import org.eclipse.core.internal.boot.*;
+import org.eclipse.core.runtime.*;
 import org.eclipse.update.configurator.*;
 import org.w3c.dom.*;
 
-public class PlatformConfiguration implements IPlatformConfiguration {
+/**
+ * This class is responsible for providing the features and plugins (bundles) to 
+ * the runtime. Configuration data is stored in the .config/platform.xml file.
+ * When eclipse starts, it tries to load the config info from platform.xml.
+ * If the file does not exist, then it also tries to read it from a temp or backup file.
+ * If this does not succeed, a platform.xml is created by inspecting the eclipse 
+ * installation directory (its features and plugin folders).
+ * If platform.xml already exists, a check is made to see when it was last modified
+ * and whether there are any file system changes that are newer (users may manually unzip 
+ * features and plugins). In this case, the newly added features and plugins are picked up.
+ * A check for existence of features and plugins is also performed, to detect deletions.
+ */
+public class PlatformConfiguration implements IPlatformConfiguration, IConfigurationConstants {
 
 	private static PlatformConfiguration currentPlatformConfiguration = null;
 	private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
 	private static final TransformerFactory transformerFactory = TransformerFactory.newInstance();
 
+	private Configuration config;
 	private URL configLocation;
-	private HashMap sites;
+//	private HashMap sites;
 	private HashMap externalLinkSites; // used to restore prior link site state
-	private HashMap cfgdFeatures;
 	private HashMap bootPlugins;
-	private String defaultFeature;
 	private long changeStamp;
-	private boolean changeStampIsValid = false;
-	private long lastFeaturesChangeStamp;
+//	private boolean changeStampIsValid = false;
+//	private long lastFeaturesChangeStamp;
 	private long featuresChangeStamp;
-	private boolean featuresChangeStampIsValid = false;
-	private long lastPluginsChangeStamp;
+	private boolean featuresChangeStampIsValid;
+//	private long lastPluginsChangeStamp;
 	private long pluginsChangeStamp;
-	private boolean pluginsChangeStampIsValid = false;
-	private boolean transientConfig = false;
+	private boolean pluginsChangeStampIsValid;
 	private File cfgLockFile;
 	private RandomAccessFile cfgLockFileRAF;
-
-	private static String cmdFeature = null;
-	private static String cmdApplication = 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;
-	private static boolean cmdNoLinkedConfig = true;
+	private boolean isNew; // true when created out of parsing files
 
 	private static final String ECLIPSE = "eclipse"; //$NON-NLS-1$
 	private static final String CONFIG_DIR = ".config"; //$NON-NLS-1$
-	private static final String CONFIG_NAME = "platform.cfg"; //$NON-NLS-1$
+	private static final String CONFIG_NAME = "platform.xml"; //$NON-NLS-1$
 	private static final String CONFIG_FILE = CONFIG_DIR + "/" + CONFIG_NAME; //$NON-NLS-1$
 	private static final String CONFIG_FILE_INIT = "install.ini"; //$NON-NLS-1$
 	private static final String CONFIG_INI = "config.ini"; //NON-NLS-1$
@@ -67,29 +71,9 @@
 	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[] BOOTSTRAP_PLUGINS = { "org.eclipse.core.boot" }; //$NON-NLS-1$
-	private static final String CFG = "config"; //$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[] BOOTSTRAP_PLUGINS = { "org.eclipse.core.boot" }; //$NON-NLS-1$
+	private static final String[] BOOTSTRAP_PLUGINS = {}; //$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$
@@ -97,42 +81,21 @@
 	private static final String DEFAULT_FEATURE_ID = "org.eclipse.platform"; //$NON-NLS-1$
 	private static final String DEFAULT_FEATURE_APPLICATION = "org.eclipse.ui.ide.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_FEATURE = "-feature"; //$NON-NLS-1$
-	private static final String CMD_APPLICATION = "-application"; //$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$
-	private static final String CMD_NO_LINKED_CONFIG = "-no_linked_config";
-
-	public 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;
 	
 
-	private PlatformConfiguration(String configPath) throws IOException {
-		this.sites = new HashMap();
+	private PlatformConfiguration(String configPath) throws CoreException, IOException {
+//		this.sites = new HashMap();
 		this.externalLinkSites = new HashMap();
-		this.cfgdFeatures = new HashMap();
 		this.bootPlugins = new HashMap();
-
+		
+		this.config = null;
+		
 		// initialize configuration
 		initializeCurrent(configPath);
 
@@ -150,7 +113,10 @@
 
 		// compute differences between configuration and actual content of the sites
 		// (base sites and link sites)
-		computeChangeStamp();
+		// Note: when the config is transient (generated by PDE, etc.) we don't reconcile
+		changeStamp = computeChangeStamp();
+		if (changeStamp > config.getDate().getTime() && !isTransient())
+			reconcile();
 
 		// determine which plugins we will use to start the rest of the "kernel"
 		// (need to get core.runtime matching the executing core.boot and
@@ -158,10 +124,9 @@
 		//		locateDefaultPlugins();
 	}
 
-	PlatformConfiguration(URL url) throws IOException {
-		this.sites = new HashMap();
+	PlatformConfiguration(URL url) throws Exception {
+//		this.sites = new HashMap();
 		this.externalLinkSites = new HashMap();
-		this.cfgdFeatures = new HashMap();
 		this.bootPlugins = new HashMap();
 		initialize(url);
 	}
@@ -170,7 +135,7 @@
 	 * @see IPlatformConfiguration#createSiteEntry(URL, ISitePolicy)
 	 */
 	public ISiteEntry createSiteEntry(URL url, ISitePolicy policy) {
-		return new SiteEntry(url, policy, this);
+		return new SiteEntry(url, policy);
 	}
 
 	/*
@@ -209,16 +174,23 @@
 
 		if (entry == null)
 			return;
-
+	
 		URL url = entry.getURL();
 		if (url == null)
 			return;
 		String key = url.toExternalForm();
 
-		if (sites.containsKey(key) && !replace)
+//		if (sites.containsKey(key) && !replace)
+//			return;
+		
+		if (config.getSiteEntry(key) != null && !replace)
 			return;
-
-		sites.put(key, entry);
+		
+//
+//		sites.put(key, entry);
+		
+		if (entry instanceof SiteEntry)
+			config.addSiteEntry(key, (SiteEntry)entry);
 	}
 
 	/*
@@ -233,28 +205,52 @@
 			return;
 		String key = url.toExternalForm();
 
-		sites.remove(key);
+//		sites.remove(key);
+		
+		if (entry instanceof SiteEntry)
+			config.removeSiteEntry(key);
 	}
 
 	/*
 	 * @see IPlatformConfiguration#getConfiguredSites()
 	 */
 	public ISiteEntry[] getConfiguredSites() {
-		if (sites.size() == 0)
+//		if (sites.size() == 0)
+//			return new ISiteEntry[0];
+//
+//		return (ISiteEntry[]) sites.values().toArray(new ISiteEntry[0]);
+		if (config == null)
 			return new ISiteEntry[0];
-
-		return (ISiteEntry[]) sites.values().toArray(new ISiteEntry[0]);
+		
+		SiteEntry[] sites = config.getSites();
+		ArrayList enabledSites = new ArrayList(sites.length);
+		for (int i=0; i<sites.length; i++) {
+			if (sites[i].isEnabled())
+				enabledSites.add(sites[i]);
+		}
+		return (ISiteEntry[])enabledSites.toArray(new ISiteEntry[enabledSites.size()]);
 	}
 
 	/*
 	 * @see IPlatformConfiguration#findConfiguredSite(URL)
 	 */
 	public ISiteEntry findConfiguredSite(URL url) {
+		return findConfiguredSite(url, true);
+	}
+	
+	/**
+	 * 
+	 * @param url site url
+	 * @param checkPlatformURL if true, check for url format that is platform:/...
+	 * @return
+	 */
+	public SiteEntry findConfiguredSite(URL url, boolean checkPlatformURL) {
 		if (url == null)
 			return null;
 		String key = url.toExternalForm();
 
-		ISiteEntry result = (ISiteEntry) sites.get(key);
+//		ISiteEntry result = (ISiteEntry) sites.get(key);
+		SiteEntry result = config.getSiteEntry(key);
 		try {
 			key = URLDecoder.decode(key, "UTF-8");
 		} catch (UnsupportedEncodingException e) {
@@ -262,8 +258,16 @@
 		}
 		
 		if (result == null) // retry with decoded URL string
-			result = (ISiteEntry) sites.get(key);
+//			result = (ISiteEntry) sites.get(key);
+			result = config.getSiteEntry(key);
 			
+		if (result == null && checkPlatformURL) {
+			try {
+				result = findConfiguredSite(Utils.asPlatformURL(url), false);
+			} catch (Exception e) {
+				//ignore
+			}
+		}
 		return result;
 	}
 
@@ -278,7 +282,16 @@
 		if (key == null)
 			return;
 
-		cfgdFeatures.put(key, entry);
+		// we should check each site and find where the feature is
+		// located and then configure it
+		if (config == null)
+			config = new Configuration();
+
+		SiteEntry defaultSite = config.getSiteEntry(PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + "/" + "base" + "/");
+		if (defaultSite != null) {
+			defaultSite.addFeatureEntry(entry);
+		}
+		// else, do nothing (we need a site)
 	}
 
 	/*
@@ -292,17 +305,21 @@
 		if (key == null)
 			return;
 
-		cfgdFeatures.remove(key);
+		config.unconfigureFeatureEntry(entry);
 	}
 
 	/*
 	 * @see IPlatformConfiguration#getConfiguredFeatureEntries()
 	 */
 	public IFeatureEntry[] getConfiguredFeatureEntries() {
-		if (cfgdFeatures.size() == 0)
-			return new IFeatureEntry[0];
-
-		return (IFeatureEntry[]) cfgdFeatures.values().toArray(new IFeatureEntry[0]);
+		ArrayList configFeatures = new ArrayList();
+		SiteEntry[] sites = config.getSites();
+		for (int i=0; i<sites.length; i++) {
+			FeatureEntry[] features = sites[i].getFeatureEntries();
+			for (int j=0; j<features.length; j++)
+				configFeatures.add(features[j]);
+		}
+		return (IFeatureEntry[])configFeatures.toArray(new IFeatureEntry[configFeatures.size()]);
 	}
 
 	/*
@@ -312,7 +329,13 @@
 		if (id == null)
 			return null;
 
-		return (IFeatureEntry) cfgdFeatures.get(id);
+		SiteEntry[] sites = config.getSites();
+		for (int i=0; i<sites.length; i++) {
+			FeatureEntry f = sites[i].getFeatureEntry(id);
+			if (f != null)
+				return f;
+		}
+		return null;
 	}
 
 	/*
@@ -326,27 +349,30 @@
 	 * @see IPlatformConfiguration#getChangeStamp()
 	 */
 	public long getChangeStamp() {
-		if (!changeStampIsValid)
-			computeChangeStamp();
-		return changeStamp;
+//		if (!changeStampIsValid)
+//			computeChangeStamp();
+//		return changeStamp;
+		return 0;
 	}
 
 	/*
 	 * @see IPlatformConfiguration#getFeaturesChangeStamp()
 	 */
 	public long getFeaturesChangeStamp() {
-		if (!featuresChangeStampIsValid)
-			computeFeaturesChangeStamp();
-		return featuresChangeStamp;
+//		if (!featuresChangeStampIsValid)
+//			computeFeaturesChangeStamp();
+//		return featuresChangeStamp;
+		return 0;
 	}
 
 	/*
 	 * @see IPlatformConfiguration#getPluginsChangeStamp()
 	 */
 	public long getPluginsChangeStamp() {
-		if (!pluginsChangeStampIsValid)
-			computePluginsChangeStamp();
-		return pluginsChangeStamp;
+//		if (!pluginsChangeStampIsValid)
+//			computePluginsChangeStamp();
+//		return pluginsChangeStamp;
+		return 0;
 	}
 
 	/*
@@ -354,40 +380,32 @@
 	 */
 	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;
-		}
+//		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() {
+		String feature = config.getDefaultFeature();
 
-		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();
-				}
+		// lookup application for feature (specified or defaulted)
+		if (feature != null) {
+			IFeatureEntry fe = findConfiguredFeatureEntry(feature);
+			if (fe != null) {
+				if (fe.getFeatureApplication() != null)
+					return fe.getFeatureApplication();
 			}
 		}
 
@@ -400,10 +418,8 @@
 	 */
 	public String getPrimaryFeatureIdentifier() {
 		String primaryFeatureId = null;
-		if (cmdFeature != null) // -feature was specified on command line
-			primaryFeatureId = cmdFeature;
-		else if (defaultFeature != null)
-			primaryFeatureId = defaultFeature; // return customized default if set
+		if (config.getDefaultFeature() != null)
+			primaryFeatureId = config.getDefaultFeature(); // return customized default if set
 		else
 			primaryFeatureId = DEFAULT_FEATURE_ID; // return hardcoded default
 		
@@ -450,13 +466,13 @@
 	 * @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;
-			}
-		}
+//		String[] ids = getBootstrapPluginIdentifiers();
+//		for (int i = 0; i < ids.length; i++) {
+//			if (ids[i].equals(id)) {
+//				bootPlugins.put(id, location.toExternalForm());
+//				break;
+//			}
+//		}
 	}
 
 	/*
@@ -470,15 +486,18 @@
 	 * @see IPlatformConfiguration#isTransient()
 	 */
 	public boolean isTransient() {
-		return transientConfig;
+		if (config != null)
+			return config.isTransient();
+		else
+			return false;
 	}
 
 	/*
 	 * @see IPlatformConfiguration#isTransient(boolean)
 	 */
 	public void isTransient(boolean value) {
-		//		if (this != BootLoader.getCurrentPlatformConfiguration())
-		//			transientConfig = value;
+		if (this != getCurrent() && config != null)
+			config.setTransient(value);
 	}
 
 	/*
@@ -493,11 +512,11 @@
 			 ((SiteEntry) sites[i]).refresh();
 		}
 		// reset configuration entry.
-		lastFeaturesChangeStamp = featuresChangeStamp;
-		lastPluginsChangeStamp = pluginsChangeStamp;
-		changeStampIsValid = false;
-		featuresChangeStampIsValid = false;
-		pluginsChangeStampIsValid = false;
+//		lastFeaturesChangeStamp = featuresChangeStamp;
+//		lastPluginsChangeStamp = pluginsChangeStamp;
+//		changeStampIsValid = false;
+//		featuresChangeStampIsValid = false;
+//		pluginsChangeStampIsValid = false;
 	}
 
 	/*
@@ -515,18 +534,19 @@
 		if (url == null)
 			throw new IOException(Messages.getString("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);
+				saveAsXML(os);
+				config.setDirty(false);
+			} catch (CoreException e) {
+				throw new IOException(Messages.getString("cfig.unableToSave", url.toExternalForm())); //$NON-NLS-1$
 			} finally {
-				w.close();
+				os.close();
 			}
 		} else {
 			// file protocol - do safe i/o
@@ -537,30 +557,34 @@
 			if (cfigDir != null)
 				cfigDir.mkdirs();
 
+			// Backup old file
+			File oldConfigFile = new File(cfigDir, CONFIG_NAME);
+			if (oldConfigFile.exists()){
+				File backupDir = new File(cfigDir, "history");
+				if (!backupDir.exists())
+					backupDir.mkdir();
+				File preservedFile = new File(backupDir, String.valueOf(oldConfigFile.lastModified())+".xml");
+				copy(oldConfigFile, preservedFile);
+				preservedFile.setLastModified(oldConfigFile.lastModified());
+			}
+			
 			// If config.ini does not exist, generate it
 			writeConfigIni(cfigDir);
 			
 			// 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);
-				//saveAsXML(new File(cfigDir, "platform.xml"));
+				saveAsXML(os);
+				// set file time stamp to match that of the config element
+				cfigTmp.setLastModified(config.getDate().getTime());
+				// make the change stamp to be the same as the config file
+				changeStamp = config.getDate().getTime();
+//				changeStampIsValid = true;
+			} catch (CoreException e) {
+				throw new IOException(Messages.getString("cfig.unableToSave", cfigTmp.getAbsolutePath())); //$NON-NLS-1$
 			} 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(Messages.getString("cfig.unableToSave", cfigTmp.getAbsolutePath())); //$NON-NLS-1$
-				}
-			} finally {
-				is.close();
+				os.close();
 			}
 
 			// make the saved config the "active" one
@@ -611,27 +635,15 @@
 	 * @param r10apps application identifies as passed on the BootLoader.run(...)
 	 * method. Supported for R1.0 compatibility.
 	 */
-	public static synchronized String[] startup(String[] cmdArgs, String r10app, URL installURL, String configPath) throws Exception {
+	public static synchronized void startup(URL installURL, String configPath) throws Exception {
 		PlatformConfiguration.installURL = installURL;
-
-		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)
+		if (currentPlatformConfiguration == null) {
 			currentPlatformConfiguration = new PlatformConfiguration(configPath);
-
-		// check if we will be forcing reconciliation
-		passthruArgs = checkForFeatureChanges(passthruArgs, currentPlatformConfiguration);
-
-		// check if we should indicate new changes
-		passthruArgs = checkForNewUpdates(currentPlatformConfiguration, passthruArgs);
-
-		return passthruArgs;
+			if (currentPlatformConfiguration.isNew)
+				currentPlatformConfiguration.save();
+		}
 	}
 
 	public static synchronized void shutdown() throws IOException {
@@ -639,11 +651,17 @@
 		// save platform configuration
 		PlatformConfiguration config = getCurrent();
 		if (config != null) {
-			try {
-				config.save();
-			} catch (IOException e) {
-				Utils.debug("Unable to save configuration " + e.toString()); //$NON-NLS-1$
-				// will recover on next startup
+			// only save if there are changes in the config
+			// TODO clean this up when merging with the rest of update code
+			long lastStamp = config.config.getDate().getTime();
+			long computedStamp = config.computeChangeStamp();
+			if (config.config.isDirty() || computedStamp > lastStamp) {
+				try {
+					config.save();
+				} catch (IOException e) {
+					Utils.debug("Unable to save configuration " + e.toString()); //$NON-NLS-1$
+					// will recover on next startup
+				}
 			}
 			config.clearConfigurationLock();
 		}
@@ -653,27 +671,6 @@
 		// 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 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]
-
-			configureSite(getRootSite());
-			Utils.debug("Initializing configuration " + url.toString()); //$NON-NLS-1$
-			configLocation = url;
-			verifyPath(configLocation);
-			return;
-		}
-
 		// Configuration URL was is specified by the OSGi layer. 
 		// Default behavior is to look
 		// for configuration in the specified meta area. If not found, look
@@ -691,54 +688,72 @@
 
 			// try loading the configuration
 			try {
-				load(configFileURL);
+				config = loadConfig(configFileURL);
 				Utils.debug("Using configuration " + configFileURL.toString()); //$NON-NLS-1$
-			} catch (IOException e) {
+			} catch (Exception e) {
 				// failed to load, see if we can find pre-initialized configuration.
 				// Don't attempt this initialization when self-hosting (is unpredictable)
 				try {
 					URL sharedConfigDirURL = new URL(getInstallURL(), CONFIG_DIR);
 					URL sharedConfigFileURL = new URL(getInstallURL(), CONFIG_FILE);
 
-					load(sharedConfigFileURL);
+					config = loadConfig(sharedConfigFileURL);
 					
 					// pre-initialized config loaded OK ... copy any remaining update metadata
 					// Only copy if the default config location is not the install location
 					if (!sharedConfigDirURL.equals(configDirURL)) {
-//						if (cmdNoLinkedConfig)
-//							// need to link config info instead of using a copy
-//							linkInitializedState(sharedConfigDirURL, configPath);
-//						else
+						if (true)
+							// need to link config info instead of using a copy
+							linkInitializedState(config, sharedConfigDirURL, configFileURL);
+						else
 							// copy config info
 							copyInitializedState(sharedConfigDirURL, configPath);
 						
 						Utils.debug("Configuration initialized from    " + sharedConfigDirURL.toString()); //$NON-NLS-1$
 					}
 					return;
-				} catch (IOException ioe) {
-					cmdFirstUse = true;
-					// we are creating new configuration
-					configureSite(getRootSite());
+				} catch (Exception ioe) {
+					Utils.debug("Creating default configuration from " + configFileURL.toExternalForm());
+					createDefaultConfiguration(configFileURL);
 				}
 			}
 		} finally {
 			configLocation = configFileURL;
+			if (config.getURL() == null)
+				config.setURL(configFileURL);
 			verifyPath(configLocation);
 			Utils.debug("Creating configuration " + configFileURL.toString()); //$NON-NLS-1$
 		}
 	}
 
-	private synchronized void initialize(URL url) throws IOException {
+	private synchronized void initialize(URL url) throws Exception {
 		if (url == null) {
+			config = new Configuration();
+			config.setURL(url);
 			Utils.debug("Creating empty configuration object"); //$NON-NLS-1$
 			return;
 		}
 
-		load(url);
+		config = loadConfig(url);
 		configLocation = url;
 		Utils.debug("Using configuration " + configLocation.toString()); //$NON-NLS-1$
 	}
 
+	private void createDefaultConfiguration(URL url)throws IOException{
+		// we are creating new configuration
+		config = new Configuration();
+		config.setURL(url);
+		SiteEntry defaultSite = (SiteEntry)getRootSite();
+		configureSite(defaultSite);
+		try {
+			// parse the site directory to discover features
+			defaultSite.loadFromDisk();
+			isNew = true;
+		} catch (CoreException e1) {
+			Utils.log("Cannot load default site " + defaultSite.getResolvedURL());
+			return;
+		}
+	}
 	private ISiteEntry getRootSite() {
 		// create default site entry for the root
 		ISitePolicy defaultPolicy = createSitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
@@ -819,37 +834,41 @@
 		}
 	}
 
-	private void computeChangeStamp() {
-		computeFeaturesChangeStamp();
-		computePluginsChangeStamp();
-		changeStamp = featuresChangeStamp ^ pluginsChangeStamp;
-		changeStampIsValid = true;
+	private long computeChangeStamp() {
+		featuresChangeStamp = computeFeaturesChangeStamp();
+		pluginsChangeStamp = computePluginsChangeStamp();
+		changeStamp = Math.max(featuresChangeStamp, pluginsChangeStamp);
+		// round off to seconds
+		changeStamp = (changeStamp/1000)*1000;
+		return changeStamp;
 	}
 
-	private void computeFeaturesChangeStamp() {
+	private long computeFeaturesChangeStamp() {
 		if (featuresChangeStampIsValid)
-			return;
+			return featuresChangeStamp;
 
 		long result = 0;
-		ISiteEntry[] sites = getConfiguredSites();
+		ISiteEntry[] sites = config.getSites();
 		for (int i = 0; i < sites.length; i++) {
-			result ^= sites[i].getFeaturesChangeStamp();
+			result = Math.max(result, sites[i].getFeaturesChangeStamp());
 		}
 		featuresChangeStamp = result;
 		featuresChangeStampIsValid = true;
+		return featuresChangeStamp;
 	}
 
-	private void computePluginsChangeStamp() {
+	private long computePluginsChangeStamp() {
 		if (pluginsChangeStampIsValid)
-			return;
+			return pluginsChangeStamp;
 
 		long result = 0;
-		ISiteEntry[] sites = getConfiguredSites();
+		ISiteEntry[] sites = config.getSites();
 		for (int i = 0; i < sites.length; i++) {
-			result ^= sites[i].getPluginsChangeStamp();
+			result = Math.max(result, sites[i].getPluginsChangeStamp());
 		}
 		pluginsChangeStamp = result;
 		pluginsChangeStampIsValid = true;
+		return pluginsChangeStamp;
 	}
 
 	private void configureExternalLinks() {
@@ -955,7 +974,7 @@
 	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]);
+		SiteEntry[] list = config.getSites();
 		for (int i = 0; i < list.length; i++) {
 			URL siteURL = list[i].getResolvedURL();
 			if (!supportsDetection(siteURL))
@@ -969,24 +988,23 @@
 		}
 	}
 	
-	private void linkInitializedState(URL source, String target) {
+	private void linkInitializedState(Configuration sharedConfig, URL sharedConfigURL, URL newConfigURL) {
 		try {
-			if (!source.getProtocol().equals("file")) //$NON-NLS-1$
-				return; // need to be able to do "dir"
+			URL oldConfigIniURL = new URL(sharedConfigURL, CONFIG_INI);
+			URL newConfigIniURL = new URL(newConfigURL, CONFIG_INI);
+			if (!newConfigIniURL.getProtocol().equals("file")) //$NON-NLS-1$
+				return; // need to be able to do write
 
-			copy(new File(source.getFile()), new File(target));
-			// modify config.ini and platform.cfg to only link original files
-			File configIni = new File(target, CONFIG_INI);
+			// modify config.ini and platform.xml to only link original files
+			File configIni = new File(newConfigIniURL.getFile());
 			Properties props = new Properties();
-			props.put("link", new URL(source, CONFIG_DIR+"/"+CONFIG_INI));
+			props.put(CFG_SHARED_URL, oldConfigIniURL.toExternalForm());
 			props.store(new FileOutputStream(configIni), "Linked configuration");
 			
-			File platformCfg = new File(target, CONFIG_NAME);
-			props = new Properties();
-			props.put(CFG_VERSION, VERSION);
-			props.put("link", new URL(source, CONFIG_DIR+"/"+CONFIG_NAME));
-			props.store(new FileOutputStream(platformCfg), "Linked configuration");
-
+			config = new Configuration(new Date());
+			config.setURL(newConfigURL);
+			config.setLinkedConfig(sharedConfig);
+			isNew = true;
 		} catch (IOException e) {
 			// this is an optimistic copy. If we fail, the state will be reconciled
 			// when the update manager is triggered.
@@ -1000,7 +1018,7 @@
 				return; // need to be able to do "dir"
 
 			copy(new File(source.getFile()), new File(target));
-			
+
 		} catch (IOException e) {
 			// this is an optimistic copy. If we fail, the state will be reconciled
 			// when the update manager is triggered.
@@ -1080,272 +1098,50 @@
 					// ignore ...
 				}
 		}
-	}
-
-	private void load(URL url) throws IOException {
-
+	}		
+	private Configuration loadConfig(URL url) throws Exception {
 		if (url == null)
 			throw new IOException(Messages.getString("cfig.unableToLoad.noURL")); //$NON-NLS-1$
 
 		// try to load saved configuration file (watch for failed prior save())
-		Properties props = null;
-		IOException originalException = null;
+		ConfigurationParser parser = null;
 		try {
-			props = loadProperties(url, null); // try to load config file
-		} catch (IOException e1) {
+			parser = new ConfigurationParser();
+		} catch (InvocationTargetException e) {
+			throw (Exception)e.getTargetException();
+		}
+		
+		config = null;
+		Exception originalException = null;
+		try {
+			config = parser.parse(url);
+		} catch (Exception e1) {
+			// check for save failures, so open temp and backup configurations
 			originalException = e1;
 			try {
-				props = loadProperties(url, CONFIG_FILE_TEMP_SUFFIX); // check for failures on save
-			} catch (IOException e2) {
+				URL tempURL = new URL(url.toExternalForm()+CONFIG_FILE_TEMP_SUFFIX);
+				config = parser.parse(tempURL); 
+			} catch (Exception e2) {
 				try {
-					props = loadProperties(url, CONFIG_FILE_BAK_SUFFIX); // check for failures on save
+					URL backupUrl = new URL(url.toExternalForm()+CONFIG_FILE_BAK_SUFFIX);
+					config = parser.parse(backupUrl);
 				} 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(Messages.getString("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_FEATURE_STAMP, null);
-		if (stamp != null) {
-			try {
-				lastFeaturesChangeStamp = Long.parseLong(stamp);
-			} catch (NumberFormatException e) {
-				// ignore bad attribute ...
+		if (config != null) {
+			SiteEntry[] sites = config.getSites();
+			for (int i=0; i<sites.length; i++) {
+				configureSite(sites[i]);
+				IFeatureEntry[] features = sites[i].getFeatureEntries();
+				for (int j=0; j<features.length; j++)
+					configureFeatureEntry(features[j]);
 			}
 		}
-
-		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$
-		}
+		return config;
 	}
-
-	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();
-			if (is == null)
-				throw new IOException();
-			props.load(is);
-			// check to see if we have complete config file
-			if (!EOF.equals(props.getProperty(EOF))) {
-				throw new IOException(Messages.getString("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_FEATURE_STAMP, null); //$NON-NLS-1$
-		if (stamp != null) {
-			try {
-				site.setLastFeaturesChangeStamp(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.setLastPluginsChangeStamp(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.setUpdateable(true);
-			else
-				site.setUpdateable(false);
-		}
-
-		String linkname = loadAttribute(props, name + "." + CFG_LINK_FILE, null); //$NON-NLS-1$
-		if (linkname != null && !linkname.equals("")) { //$NON-NLS-1$
-			site.setLinkFileName(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);
@@ -1394,300 +1190,40 @@
 
 			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 (config != null)
+				config.setDefaultFeature(initId);
 			if (ConfigurationActivator.DEBUG) {
-				Utils.debug("    Default primary feature: " + defaultFeature); //$NON-NLS-1$
+				Utils.debug("    Default primary feature: " + initId); //$NON-NLS-1$
 				if (application != null)
 					Utils.debug("    Default application    : " + application); //$NON-NLS-1$
 			}
 		}
 	}
 
-	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.isUpdateable() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-		if (entry.isExternallyLinkedSite()) //$NON-NLS-1$
-			writeAttribute(w, id + "." + CFG_LINK_FILE, entry.getLinkFileName().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);
-			Utils.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 no-linked-config flag
-			if (args[i].equalsIgnoreCase(CMD_NO_LINKED_CONFIG)) {
-				cmdNoLinkedConfig = 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 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;
-			}
-
-			// 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 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;
+//		}
+//	}
 
 	public static boolean supportsDetection(URL url) {
 		String protocol = url.getProtocol();
@@ -1743,90 +1279,49 @@
 		return url;
 	}
 
-	private void resetUpdateManagerState(URL url) throws IOException {
-		// [20111]
-		if (!supportsDetection(url))
-			return; // can't do ...
+//	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);
+//	}
 
-		// 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() {
+	public static URL getInstallURL() {
 		return installURL;
 	}
 	
-	public void invalidateFeaturesChangeStamp() {
-		changeStampIsValid = false;
-		featuresChangeStampIsValid = false;
-	}
-	
-	public void invalidatePluginsChangeStamp() {
-		changeStampIsValid = false;
-		pluginsChangeStampIsValid = false;
-	}
-	
-	private void saveAsXML(File platformXML) {
+	private void saveAsXML(OutputStream stream) throws CoreException {	
 		try {
 			DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder();
 			Document doc = docBuilder.newDocument();
+
+			if (config == null)
+				throw Utils.newCoreException("Configuration cannot be saved because it does not exist",null);
 			
-			doc.appendChild(doc.createProcessingInstruction("date", "\""+(new Date()).toString()+"\""));
-			
-			Element configElement = doc.createElement(CFG);
+			config.setDate(new Date());
+			Element configElement = config.toXML(doc);
 			doc.appendChild(configElement);
-			configElement.setAttribute(CFG_VERSION, VERSION);
-			if (transientConfig)
-				configElement.setAttribute(CFG_TRANSIENT, "true"); //$NON-NLS-1$
-
-			// collect global attributes
-			configElement.setAttribute(CFG_STAMP, Long.toString(getChangeStamp()));
-			configElement.setAttribute(CFG_FEATURE_STAMP, Long.toString(getFeaturesChangeStamp()));
-			configElement.setAttribute(CFG_PLUGIN_STAMP, Long.toString(getPluginsChangeStamp()));
-
-			// collect bootstrap entries
-			String[] ids = getBootstrapPluginIdentifiers();
-			for (int i = 0; i < ids.length; i++) {
-				String location = (String) bootPlugins.get(ids[i]);
-				if (location != null) {
-					Element bootPlugin = doc.createElement(CFG_BOOT_PLUGIN);
-					bootPlugin.setNodeValue(location);
-					configElement.appendChild(bootPlugin);
-				}
-			}
-
-			// collect feature entries
-			configElement.setAttribute(CFG_FEATURE_ENTRY_DEFAULT, defaultFeature);
-			IFeatureEntry[] feats = getConfiguredFeatureEntries();
-			for (int i = 0; i < feats.length; i++) {
-				saveFeatureAsXML(feats[i], configElement); 
-			}
-
-			// collect site entries
-			SiteEntry[] list = (SiteEntry[]) sites.values().toArray(new SiteEntry[0]);
-			for (int i = 0; i < list.length; i++) {
-				saveSiteAsXML(list[i], configElement); 
-			}
 
 			// Write out to a file
-			FileOutputStream stream = new FileOutputStream(platformXML);
 			
 			Transformer transformer=transformerFactory.newTransformer();
 			transformer.setOutputProperty(OutputKeys.METHOD, "xml");
@@ -1837,98 +1332,22 @@
 
 			transformer.transform(source,result);
 			stream.close();
-		} catch (DOMException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (FileNotFoundException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (TransformerConfigurationException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (IllegalArgumentException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (ParserConfigurationException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (TransformerException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (IOException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
 		} catch (Exception e) {
-			e.printStackTrace();
-		} 
+			throw Utils.newCoreException("", e);
+		}  
 	}
 	
-	
-	/**
-	 * Saves state as xml content in a given parent element
-	 * @param parent
-	 */
-	private void saveSiteAsXML(SiteEntry site, Element parent) {
-		Document doc = parent.getOwnerDocument();
-		Element siteElement = doc.createElement(CFG_SITE);
-		parent.appendChild(siteElement);
-		
-		if (site.getURL().toString() != null)
-			siteElement.setAttribute(CFG_URL, site.getURL().toString());
-		siteElement.setAttribute(CFG_STAMP, Long.toString(site.getChangeStamp()));
-		siteElement.setAttribute(CFG_FEATURE_STAMP, Long.toString(site.getFeaturesChangeStamp()));
-		siteElement.setAttribute(CFG_PLUGIN_STAMP, Long.toString(site.getPluginsChangeStamp()));
-		siteElement.setAttribute(CFG_UPDATEABLE, site.isUpdateable() ? "true" : "false");
-		if (site.isExternallyLinkedSite()) 
-			siteElement.setAttribute(CFG_LINK_FILE, site.getLinkFileName().trim().replace(File.separatorChar, '/')); 
-
-		int type = site.getSitePolicy().getType();
-		String typeString = CFG_POLICY_TYPE_UNKNOWN;
-		try {
-			typeString = CFG_POLICY_TYPE[type];
-		} catch (IndexOutOfBoundsException e) {
-			// ignore bad attribute ...
+	private void reconcile() throws CoreException {
+		long lastChange = config.getDate().getTime();
+		SiteEntry[] sites = config.getSites();
+		for (int s=0; s<sites.length; s++) {
+			long siteTimestamp = sites[s].getChangeStamp();
+			if (siteTimestamp > lastChange) 
+				sites[s].loadFromDisk();
 		}
-		siteElement.setAttribute(CFG_POLICY, typeString); 
-		String[] list = site.getSitePolicy().getList();
-		for (int i=0; i<list.length; i++) {
-			Element listElement = doc.createElement(CFG_LIST);
-			listElement.setNodeValue(list[i]);
-			siteElement.appendChild(listElement);
-		}
-		// note: we don't save features inside the site element.
 	}
 	
-	/**
-	 * Saves state as xml content in a given parent element
-	 * @param parent
-	 */
-	private void saveFeatureAsXML(IFeatureEntry feature, Element parent) {
-		Document doc = parent.getOwnerDocument();
-		Element featureElement = doc.createElement(CFG_FEATURE_ENTRY);
-		parent.appendChild(featureElement);
-	
-		// write out feature entry settings
-		if (feature.getFeatureIdentifier() != null)
-			featureElement.setAttribute(CFG_FEATURE_ENTRY_ID, feature.getFeatureIdentifier()); 
-		if (feature.canBePrimary())
-			featureElement.setAttribute(CFG_FEATURE_ENTRY_PRIMARY, "true");
-		if (feature.getFeatureVersion() != null)
-			featureElement.setAttribute(CFG_FEATURE_ENTRY_VERSION, feature.getFeatureVersion()); 
-		if (feature.getFeaturePluginVersion() != null && !feature.getFeaturePluginVersion().equals(feature.getFeatureVersion()))
-			featureElement.setAttribute(CFG_FEATURE_ENTRY_PLUGIN_VERSION, feature.getFeaturePluginVersion()); 
-		if (feature.getFeaturePluginIdentifier() != null && !feature.getFeaturePluginIdentifier().equals(feature.getFeatureIdentifier()))
-			featureElement.setAttribute(CFG_FEATURE_ENTRY_PLUGIN_IDENTIFIER, feature.getFeaturePluginIdentifier());
-		if (feature.getFeatureApplication() != null)
-			featureElement.setAttribute(CFG_FEATURE_ENTRY_APPLICATION, feature.getFeatureApplication()); 
-		URL[] roots = feature.getFeatureRootURLs();
-		for (int i=0; i<roots.length; i++) {
-			String root = roots[i].toExternalForm();
-			if (root != null && root.trim().length() > 0){
-				Element rootElement = doc.createElement(CFG_FEATURE_ENTRY_ROOT);
-				rootElement.appendChild(doc.createTextNode(root));
-				featureElement.appendChild(rootElement);
-			}
-		}
+	public Configuration getConfiguration() {
+		return config;
 	}
 }
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfigurationFactory.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfigurationFactory.java
index ddd4447..80c4457 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfigurationFactory.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PlatformConfigurationFactory.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
@@ -22,6 +22,13 @@
 		return PlatformConfiguration.getCurrent();
 	}
 	public IPlatformConfiguration getPlatformConfiguration(URL url) throws IOException {
-		return new PlatformConfiguration(url);
+		try {
+			return new PlatformConfiguration(url);
+		} catch (Exception e) {
+			if(e instanceof IOException)
+				throw (IOException)e;
+			else
+				throw new IOException(e.getMessage());
+		}
 	}
 }
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginEntry.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginEntry.java
index f9177ee..7b0d698 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginEntry.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginEntry.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
@@ -19,11 +19,26 @@
 	private String pluginVersion;
 	private boolean isFragment = false;
 	private VersionedIdentifier versionId;
+	private String url;
 	
 	public PluginEntry() {
 		super();
 	}
 
+	/**
+	 * @return url relative to the site location: plugins/org.eclipse.foo/plugin.xml
+	 * Note: to do: we should probably only use plugins/org.eclipse.foo/ in the future
+	 */
+	public String getURL() {
+		return url;
+	}
+	
+	/**
+	 * url is relative to the site
+	 */
+	public void setURL(String url) {
+		this.url = url;
+	}
 
 	/**
 	 * Returns the plug-in identifier for this entry.
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginParser.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginParser.java
index 2f760bf..338ed57 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginParser.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/PluginParser.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
@@ -22,17 +22,12 @@
  * Parse default feature.xml
  */
 
-public class PluginParser extends DefaultHandler {
+public class PluginParser extends DefaultHandler implements IConfigurationConstants {
 	private final static SAXParserFactory parserFactory =
 		SAXParserFactory.newInstance();
 	private SAXParser parser;
-	private String id = null;
-	private String version = null;
 	private PluginEntry pluginEntry;
 
-	private static final String PLUGIN = "plugin"; //$NON-NLS-1$
-	private static final String FRAGMENT = "fragment"; //$NON-NLS-1$
-
 	private class ParseCompleteException extends SAXException {
 		public ParseCompleteException(String arg0) {
 			super(arg0);
@@ -57,18 +52,15 @@
 	/**
 	 * @since 2.0
 	 */
-	public synchronized PluginEntry parse(InputStream in) throws SAXException, IOException {
+	public synchronized PluginEntry parse(File pluginFile) throws SAXException, IOException {
 		try {
 			pluginEntry = new PluginEntry();
-			parser.parse(new InputSource(in), this);
+			pluginEntry.setURL(PLUGINS + "/" + pluginFile.getParentFile().getName() + "/" );
+			parser.parse(new InputSource(new FileInputStream(pluginFile)), this);
 		} catch (ParseCompleteException e) {
 			// expected, we stopped the parsing when we have the information we need
 			/// no need to pursue the parsing
 		}
-
-		if (id == null || id.trim().length() == 0)
-			id = "_no_id_";
-		pluginEntry.setVersionedIdentifier(new VersionedIdentifier(id, version));
 		return pluginEntry;
 	}
 
@@ -79,13 +71,13 @@
 
 		String tag = localName.trim();
 
-		if (tag.equalsIgnoreCase(PLUGIN)) {
+		if (tag.equalsIgnoreCase(CFG_PLUGIN)) {
 			pluginEntry.isFragment(false);			
 			processPlugin(attributes);
 			return;
 		}
 
-		if (tag.equalsIgnoreCase(FRAGMENT)) {
+		if (tag.equalsIgnoreCase(CFG_FRAGMENT)) {
 			pluginEntry.isFragment(true);			
 			processPlugin(attributes);
 			return;
@@ -96,8 +88,13 @@
 	 * process plugin entry info
 	 */
 	private void processPlugin(Attributes attributes) throws ParseCompleteException {
-		id = attributes.getValue("id"); //$NON-NLS-1$
-		version = attributes.getValue("version"); //$NON-NLS-1$
+		String id = attributes.getValue("id"); //$NON-NLS-1$
+		String version = attributes.getValue("version"); //$NON-NLS-1$
+		if (id == null || id.trim().length() == 0)
+			id = "_no_id_";
+		pluginEntry.setVersionedIdentifier(new VersionedIdentifier(id, version));
+		
+		// stop parsing now
 		throw new ParseCompleteException(""); //$NON-NLS-1$
 	}
 }
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SiteEntry.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SiteEntry.java
index c19bcc9..547a47b 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SiteEntry.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SiteEntry.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
@@ -15,53 +15,48 @@
 import java.util.*;
 
 import org.eclipse.core.internal.boot.*;
+import org.eclipse.core.runtime.*;
 import org.eclipse.update.configurator.*;
 import org.eclipse.update.configurator.IPlatformConfiguration.*;
+import org.w3c.dom.*;
+import org.xml.sax.*;
 
 
-public class SiteEntry implements IPlatformConfiguration.ISiteEntry {
-	private static final String PLUGINS = "plugins"; //$NON-NLS-1$
-	private static final String FEATURES = "features"; //$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$
-	
+public class SiteEntry implements IPlatformConfiguration.ISiteEntry, IConfigurationConstants{	
 	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 featureEntries = new ArrayList();
-	private ArrayList pluginEntries = new ArrayList();
-	private ArrayList features;
-	private ArrayList plugins;
-	private PlatformConfiguration parent;
+	private Map featureEntries;
+	private ArrayList pluginEntries;
 	private long changeStamp;
-	private boolean changeStampIsValid;
 	private long lastFeaturesChangeStamp;
 	private long featuresChangeStamp;
-	private boolean featuresChangeStampIsValid;
 	private long lastPluginsChangeStamp;
 	private long pluginsChangeStamp;
-	private boolean pluginsChangeStampIsValid;
 	private String linkFileName;
+	private boolean enabled = true;
+	
+	private static FeatureParser featureParser = new FeatureParser();
+	private static PluginParser pluginParser = new PluginParser();
 
-	public SiteEntry() {
+	public SiteEntry(URL url) {
+		this(url,null);
 	}
-	public SiteEntry(URL url, ISitePolicy policy, PlatformConfiguration parent) {
+	
+	public SiteEntry(URL url, ISitePolicy policy) {
 		if (url == null)
-			throw new IllegalArgumentException();
-
+			try {
+				url = new URL(PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + "/" + "base" + "/"); //$NON-NLS-1$ //$NON-NLS-2$ // try using platform-relative URL
+			} catch (MalformedURLException e) {
+				url = PlatformConfiguration.getInstallURL(); // ensure we come up ... use absolute file URL
+			}
+			
 		if (policy == null)
-			throw new IllegalArgumentException();
-
-		if (parent == null)
-			throw new IllegalArgumentException();
+			policy = new SitePolicy(DEFAULT_POLICY_TYPE, DEFAULT_POLICY_LIST);
 
 		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 {
@@ -130,7 +125,7 @@
 	 * @see ISiteEntry#getChangeStamp()
 	 */
 	public long getChangeStamp() {
-		if (!changeStampIsValid)
+		if (changeStamp == 0)
 			computeChangeStamp();
 		return changeStamp;
 	}
@@ -139,7 +134,7 @@
 	 * @see ISiteEntry#getFeaturesChangeStamp()
 	 */
 	public long getFeaturesChangeStamp() {
-		if (!featuresChangeStampIsValid)
+		if (featuresChangeStamp == 0)
 			computeFeaturesChangeStamp();
 		return featuresChangeStamp;
 	}
@@ -148,7 +143,7 @@
 	 * @see ISiteEntry#getPluginsChangeStamp()
 	 */
 	public long getPluginsChangeStamp() {
-		if (!pluginsChangeStampIsValid)
+		if (pluginsChangeStamp == 0)
 			computePluginsChangeStamp();
 		return pluginsChangeStamp;
 	}
@@ -175,118 +170,185 @@
 		return resolvedURL;
 	}
 	
-	private String[] detectFeatures() {
-
+	/**
+	 * Detect new features (timestamp > current site timestamp)
+	 * and validates existing features (they might have been removed)
+	 */
+	private void detectFeatures() {
 		// invalidate stamps ... we are doing discovery
-		invalidateFeaturesChangeStamp();
+//		invalidateFeaturesChangeStamp();
 
-		features = new ArrayList();
+		if (featureEntries != null)
+			validateFeatureEntries();
+		else
+			featureEntries = new HashMap();
 
 		if (!PlatformConfiguration.supportsDetection(resolvedURL))
-			return new String[0];
+			return;
 
 		// 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;
+		File featuresDir = new File(siteRoot, FEATURES);
+		if (featuresDir.exists()) {
+			// handle the installed features under the features directory
+			File[] dirs = featuresDir.listFiles(new FileFilter() {
+				public boolean accept(File f) {
+					boolean valid = f.isDirectory() && (new File(f,FEATURE_XML).exists());
+					if (!valid)
+						System.out.println("Unable to find feature.xml in directory:" + f.getAbsolutePath());
+					return valid;
+				}
+			});
+		
+			for (int index = 0; index < dirs.length; index++) {
+				try {
+					File featureXML = new File(dirs[index], FEATURE_XML);
+					if (featureXML.lastModified() <= featuresChangeStamp &&
+						dirs[index].lastModified() <= featuresChangeStamp)
+						continue;
+					URL featureURL = featureXML.toURL();
+					FeatureEntry featureEntry = featureParser.parse(featureURL);
+					addFeatureEntry(featureEntry);
+				} catch (MalformedURLException e) {
+					Utils.log(Messages.getString("InstalledSiteParser.UnableToCreateURLForFile", featuresDir.getAbsolutePath()));
+					//$NON-NLS-1$
+				}
 			}
-			features.add(FEATURES + "/" + path.replace(File.separatorChar, '/')); //$NON-NLS-1$
 		}
-		Utils.debug(resolvedURL.toString() + " located  " + features.size() + " feature(s)"); //$NON-NLS-1$ //$NON-NLS-2$
-
-		return (String[]) features.toArray(new String[0]);
+		
+		Utils.debug(resolvedURL.toString() + " located  " + featureEntries.size() + " feature(s)"); //$NON-NLS-1$ //$NON-NLS-2$
 	}
-
-	private String[] detectPlugins() {
-
+	
+	/**
+	 * Detect new plugins (timestamp > current site timestamp)
+	 * and validates existing plugins (they might have been removed)
+	 */
+	private void detectPlugins() {
 		// invalidate stamps ... we are doing discovery
-		invalidatePluginsChangeStamp();
+//		invalidatePluginsChangeStamp();
 
-		plugins = new ArrayList();
+		if (pluginEntries != null)
+			validatePluginEntries();
+		else
+			pluginEntries = new ArrayList();
 
 		if (!PlatformConfiguration.supportsDetection(resolvedURL))
-			return new String[0];
+			return;
 
 		// 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;
+		File root = new File(resolvedURL.getFile().replace('/', File.separatorChar));
+		File pluginsDir = new File(root, PLUGINS);
+
+		if (pluginsDir.exists()) {
+			File[] files = pluginsDir.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				File pluginFile = null;
+				try {
+					if (!(pluginFile = new File(files[i], PLUGIN_XML)).exists()) { //$NON-NLS-1$
+						pluginFile = new File(files[i], FRAGMENT_XML); //$NON-NLS-1$
+					}
+
+					if (pluginFile != null && pluginFile.exists() && !pluginFile.isDirectory()) {
+						
+						// TODO in the future, assume that the timestamps are not reliable,
+						// or that the user manually modified an existing plugin, so
+						// the apparently modifed plugin may actually be configured already.
+						// We will need to double check for this. END to do.
+						
+						if (pluginFile.lastModified() <= pluginsChangeStamp)
+							continue;
+						PluginEntry entry =	pluginParser.parse(pluginFile);
+						addPluginEntry(entry);
+					}
+				} catch (IOException e) {
+					String pluginFileString = (pluginFile == null) ? null : pluginFile.getAbsolutePath();
+					Utils.log(Messages.getString("InstalledSiteParser.ErrorAccessing",pluginFileString)); //$NON-NLS-1$
+				} catch (SAXException e) {
+					String pluginFileString = (pluginFile == null) ? null : pluginFile.getAbsolutePath();
+					Utils.log(Messages.getString("InstalledSiteParser.ErrorParsingFile", pluginFileString)); //$NON-NLS-1$
+				}
 			}
-			plugins.add(PLUGINS + "/" + path.replace(File.separatorChar, '/')); //$NON-NLS-1$
-		}
-		Utils.debug(resolvedURL.toString() + " located  " + plugins.size() + " plugin(s)"); //$NON-NLS-1$ //$NON-NLS-2$
-
-		return (String[]) plugins.toArray(new String[0]);
+		} 
+		
+		Utils.debug(resolvedURL.toString() + " located  " + pluginEntries.size() + " plugin(s)"); //$NON-NLS-1$ //$NON-NLS-2$
 	}
 
+	/**
+	 * @return list of feature url's (relative to site)
+	 */
 	private synchronized String[] getDetectedFeatures() {
-		if (features == null)
-			return detectFeatures();
-		else
-			return (String[]) features.toArray(new String[0]);
+		if (featureEntries == null)
+			detectFeatures();
+		String[] features = new String[featureEntries.size()];
+		Iterator iterator = featureEntries.values().iterator();
+		for (int i=0; i<features.length; i++)
+			features[i] = ((FeatureEntry)iterator.next()).getURL();
+		return features;
 	}
 
+	/**
+	 * @return list of plugin url's (relative to site)
+	 */
 	private synchronized String[] getDetectedPlugins() {
-		if (plugins == null)
-			return detectPlugins();
-		else
-			return (String[]) plugins.toArray(new String[0]);
+		if (pluginEntries == null)
+			detectPlugins();
+		
+		String[] plugins = new String[pluginEntries.size()];
+		for (int i=0; i<plugins.length; i++)
+			plugins[i] = ((PluginEntry)pluginEntries.get(i)).getURL();
+		return plugins;
 	}
 
 	private void computeChangeStamp() {
-		computeFeaturesChangeStamp();
-		computePluginsChangeStamp();
-		changeStamp = resolvedURL.hashCode() ^ featuresChangeStamp ^ pluginsChangeStamp;
-		changeStampIsValid = true;
+		changeStamp = Math.max(computeFeaturesChangeStamp(), computePluginsChangeStamp());
+//		changeStampIsValid = true;
 	}
 
-	private synchronized void computeFeaturesChangeStamp() {
-		if (featuresChangeStampIsValid)
-			return;
+	private synchronized long computeFeaturesChangeStamp() {
+		if (featuresChangeStamp > 0)
+			return featuresChangeStamp;
 
 		long start = 0;
 		if (ConfigurationActivator.DEBUG)
 			start = (new Date()).getTime();
 		String[] features = getFeatures();
-		featuresChangeStamp = computeStamp(features);
-		featuresChangeStampIsValid = true;
+	
+		// compute stamp for the features directory
+		long dirStamp = 0;
+		if (PlatformConfiguration.supportsDetection(resolvedURL)) {
+			File root = new File(resolvedURL.getFile().replace('/', File.separatorChar));
+			File featuresDir = new File(root, FEATURES);
+			dirStamp = featuresDir.lastModified();
+		}
+		featuresChangeStamp = Math.max(dirStamp, computeStamp(features));
 		if (ConfigurationActivator.DEBUG) {
 			long end = (new Date()).getTime();
 			Utils.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$
 		}
+		return featuresChangeStamp;
 	}
 
-	private synchronized void computePluginsChangeStamp() {
-		if (pluginsChangeStampIsValid)
-			return;
+	private synchronized long computePluginsChangeStamp() {
+		if (pluginsChangeStamp > 0)
+			return pluginsChangeStamp;
 
 		long start = 0;
 		if (ConfigurationActivator.DEBUG)
 			start = (new Date()).getTime();
 		String[] plugins = getPlugins();
-		pluginsChangeStamp = computeStamp(plugins);
-		pluginsChangeStampIsValid = true;
+		// compute stamp for the features directory
+		long dirStamp = 0;
+		if (PlatformConfiguration.supportsDetection(resolvedURL)) {
+			File root = new File(resolvedURL.getFile().replace('/', File.separatorChar));
+			File pluginsDir = new File(root, PLUGINS);
+			dirStamp = pluginsDir.lastModified();
+		}
+		pluginsChangeStamp = Math.max(dirStamp, computeStamp(plugins));
 		if (ConfigurationActivator.DEBUG) {
 			long end = (new Date()).getTime();
 			Utils.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$
 		}
+		return pluginsChangeStamp;
 	}
 
 	private long computeStamp(String[] targets) {
@@ -300,21 +362,19 @@
 			//        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.
+			// In fact, we should get the last modified from the connection
 			for (int i = 0; i < targets.length; i++)
 				result ^= targets[i].hashCode();
 			Utils.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);
+			File rootFile = new File(resolvedURL.getFile().replace('/', File.separatorChar));
 			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();
+						result = Math.max(result, f.lastModified());
 				}
 			}
 		}
@@ -338,46 +398,166 @@
 		// reset computed values. Will be updated on next access.
 		lastFeaturesChangeStamp = featuresChangeStamp;
 		lastPluginsChangeStamp = pluginsChangeStamp;
-		changeStampIsValid = false;
-		featuresChangeStampIsValid = false;
-		pluginsChangeStampIsValid = false;
-		features = null;
-		plugins = null;
+		featuresChangeStamp = 0;
+		pluginsChangeStamp = 0;
+		changeStamp = 0;
+		featureEntries = null;
+		pluginEntries = null;
 	}
 
-	public void setLastFeaturesChangeStamp(long stamp) {
-		this.lastFeaturesChangeStamp = stamp;
-	}
-	
-	public void setLastPluginsChangeStamp(long stamp) {
-		this.lastPluginsChangeStamp = stamp;
-	}
-	
-	public void invalidateFeaturesChangeStamp() {
-		changeStampIsValid = false;
-		featuresChangeStampIsValid = false;
-		parent.invalidateFeaturesChangeStamp();
-	}
-	
-	public void invalidatePluginsChangeStamp() {
-		changeStampIsValid = false;
-		pluginsChangeStampIsValid = false;
-		parent.invalidatePluginsChangeStamp();
-	}
-	
+//	public void setLastFeaturesChangeStamp(long stamp) {
+//		this.lastFeaturesChangeStamp = stamp;
+//	}
+//	
+//	public void setLastPluginsChangeStamp(long stamp) {
+//		this.lastPluginsChangeStamp = stamp;
+//	}
+//	
+//	public void invalidateFeaturesChangeStamp() {
+//		changeStampIsValid = false;
+//		featuresChangeStampIsValid = false;
+//		parent.invalidateFeaturesChangeStamp();
+//	}
+//	
+//	public void invalidatePluginsChangeStamp() {
+//		changeStampIsValid = false;
+//		pluginsChangeStampIsValid = false;
+//		parent.invalidatePluginsChangeStamp();
+//	}
+//	
 	public void addFeatureEntry(IFeatureEntry feature) {
-		featureEntries.add(feature);
+		if (featureEntries == null)
+			featureEntries = new HashMap();
+		// Make sure we keep the larger version of same feature
+		IFeatureEntry existing = (FeatureEntry)featureEntries.get(feature.getFeatureIdentifier());
+		if (existing != null) {
+			VersionedIdentifier existingVersion = new VersionedIdentifier(existing.getFeatureIdentifier(), existing.getFeatureVersion());
+			VersionedIdentifier newVersion = new VersionedIdentifier(feature.getFeatureIdentifier(), feature.getFeatureVersion());
+			if (existingVersion.compareVersion(newVersion) == VersionedIdentifier.LESS_THAN)
+				featureEntries.put(feature.getFeatureIdentifier(), feature);
+		} else
+			featureEntries.put(feature.getFeatureIdentifier(), feature);
 	}
 	
-	public IFeatureEntry[] getFeatureEntries() {
-		return (IFeatureEntry[])featureEntries.toArray(new IFeatureEntry[featureEntries.size()]);
+	public FeatureEntry[] getFeatureEntries() {
+		if (featureEntries == null)
+			detectFeatures();
+		return (FeatureEntry[])featureEntries.values().toArray(new FeatureEntry[featureEntries.size()]);
 	}
 	
 	public void addPluginEntry(PluginEntry plugin) {
+		if (pluginEntries == null)
+			pluginEntries = new ArrayList();
+		// Note: we could use the latest version of the same plugin, like we do for features, but we let the runtime figure it out
 		pluginEntries.add(plugin);
 	}
 	
 	public PluginEntry[] getPluginEntries() {
+		if (pluginEntries == null)
+			detectPlugins();
 		return (PluginEntry[])pluginEntries.toArray(new PluginEntry[pluginEntries.size()]);
 	}
+	
+	public void loadFromDisk() throws CoreException{
+		detectFeatures();
+		detectPlugins();
+	}
+	
+	/**
+	 * Saves state as xml content in a given parent element
+	 * @param parent
+	 */
+	public Element toXML(Document doc) {
+
+		Element siteElement = doc.createElement(CFG_SITE);
+		
+		if (getURL().toString() != null)
+			siteElement.setAttribute(CFG_URL, getURL().toString());
+//		siteElement.setAttribute(CFG_STAMP, Long.toString(site.getChangeStamp()));
+//		siteElement.setAttribute(CFG_FEATURE_STAMP, Long.toString(site.getFeaturesChangeStamp()));
+//		siteElement.setAttribute(CFG_PLUGIN_STAMP, Long.toString(site.getPluginsChangeStamp()));
+		siteElement.setAttribute(CFG_ENABLED, isEnabled() ? "true" : "false");
+		siteElement.setAttribute(CFG_UPDATEABLE, isUpdateable() ? "true" : "false");
+		if (isExternallyLinkedSite()) 
+			siteElement.setAttribute(CFG_LINK_FILE, getLinkFileName().trim().replace(File.separatorChar, '/')); 
+
+		int type = getSitePolicy().getType();
+		String typeString = CFG_POLICY_TYPE_UNKNOWN;
+		try {
+			typeString = CFG_POLICY_TYPE[type];
+		} catch (IndexOutOfBoundsException e) {
+			// ignore bad attribute ...
+		}
+		siteElement.setAttribute(CFG_POLICY, typeString); 
+		String[] list = getSitePolicy().getList();
+		if (list.length > 0) {
+			StringBuffer sb = new StringBuffer(256);
+			for (int i=0; i<list.length-1; i++) {
+				sb.append(list[i]);
+				sb.append(',');
+			}
+			sb.append(list[list.length-1]);
+			siteElement.setAttribute(CFG_LIST, sb.toString());
+		}
+//		// note: we don't save features inside the site element.
+		
+		// collect feature entries
+//		configElement.setAttribute(CFG_FEATURE_ENTRY_DEFAULT, defaultFeature);
+		FeatureEntry[] feats = getFeatureEntries();
+		for (int i = 0; i < feats.length; i++) {
+			Element featureElement = feats[i].toXML(doc);
+			siteElement.appendChild(featureElement);
+		}
+		
+		return siteElement;
+	}
+	
+	private void validateFeatureEntries() {
+		File root = new File(resolvedURL.getFile().replace('/', File.separatorChar));
+		Iterator iterator = featureEntries.values().iterator();
+		while(iterator.hasNext()) {
+			FeatureEntry feature = (FeatureEntry)iterator.next();
+			// Note: in the future, we can check for absolute url as well.
+			//       For now, feature url is features/org.eclipse.foo/feature.xml
+			File featureXML = new File(root, feature.getURL());
+			if (!featureXML.exists())
+				featureEntries.remove(feature);
+		}
+	}
+	
+	private void validatePluginEntries() {
+		File root = new File(resolvedURL.getFile().replace('/', File.separatorChar));
+		for (int i=0; i<pluginEntries.size(); i++) {
+			PluginEntry plugin = (PluginEntry)pluginEntries.get(i);
+			// Note: in the future, we can check for absolute url as well.
+			//       For now, feature url is plugins/org.eclipse.foo/plugin.xml
+			File pluginXML = new File(root, plugin.getURL());
+			if (!pluginXML.exists())
+				pluginEntries.remove(plugin);
+		}
+	}
+	
+	public boolean isEnabled() {
+		return enabled;
+	}
+	
+	public void setEnabled(boolean enable) {
+		this.enabled = enable;
+	}
+	
+	public FeatureEntry getFeatureEntry(String id) {
+		FeatureEntry[] features = getFeatureEntries();
+		for (int i=0; i<features.length; i++)
+			if (features[i].getFeatureIdentifier().equals(id)) 
+				return features[i];
+		return null;
+	}
+	
+	
+	public boolean unconfigureFeatureEntry(IFeatureEntry feature) {
+		FeatureEntry existingFeature = getFeatureEntry(feature.getFeatureIdentifier());
+		if (existingFeature != null)
+			featureEntries.remove(existingFeature);
+		return existingFeature != null;
+	}
 }
\ No newline at end of file
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SitePolicy.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SitePolicy.java
index 368e612..b192fc4 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SitePolicy.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/SitePolicy.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Utils.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Utils.java
index 5934155..de414a7 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Utils.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/Utils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2003 IBM Corporation and others.
+ * Copyright (c) 2004 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
@@ -15,6 +15,7 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.*;
+import org.eclipse.core.internal.boot.*;
 import org.eclipse.core.internal.boot.PlatformURLBaseConnection;
 import org.eclipse.core.runtime.*;
 
@@ -149,7 +150,28 @@
 	}
 	
 	public static void log(IStatus status) {
-		if (log != null)
-			log.log(status);
+//		if (log != null)
+//			log.log(status);
+		System.out.println(status.getMessage());
+	}
+	
+	/**
+	 * Returns the url as a platform:/ url, if possible, else leaves it unchanged
+	 * @param url
+	 * @return
+	 */
+	public static URL asPlatformURL(URL url) {
+		try {
+			URL platformURL = new URL(PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + "/" + "base" + "/"); //$NON-NLS-1$ //$NON-NLS-2$ // try using platform-relative URL
+			URL resolvedPlatformURL = Platform.asLocalURL(platformURL);
+			String platformURLAsString = resolvedPlatformURL.toExternalForm();
+			String urlAsString = url.toExternalForm();
+			if (urlAsString.startsWith(platformURLAsString))
+				return new URL(platformURL.toExternalForm() + urlAsString.substring(platformURLAsString.length()) );
+			else
+				return url;
+		} catch (Exception e) {
+			return url;
+		}
 	}
 }
diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/VersionedIdentifier.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/VersionedIdentifier.java
index d87a414..1a39ec9 100644
--- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/VersionedIdentifier.java
+++ b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/VersionedIdentifier.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * Copyright (c) 2000, 2004 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