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