*** empty log message ***
diff --git a/bundles/org.eclipse.core.boot/src/org/eclipse/core/internal/boot/PlatformConfiguration.java b/bundles/org.eclipse.core.boot/src/org/eclipse/core/internal/boot/PlatformConfiguration.java
index c945cb2..6423c6c 100644
--- a/bundles/org.eclipse.core.boot/src/org/eclipse/core/internal/boot/PlatformConfiguration.java
+++ b/bundles/org.eclipse.core.boot/src/org/eclipse/core/internal/boot/PlatformConfiguration.java
@@ -820,11 +820,15 @@
 		}

 	}

 	

+	URL getPluginPath(String pluginId) {

+		return getPluginPath(pluginId, null);

+	}	

+		

 	// This method is currently public because it is called by InternalPlatform.

 	// However, it is NOT intended as API

 	// FIXME: restructure the code so that InternalBootLoader passes the

 	// required information to InternalPlatform

-	public URL getPluginPath(String pluginId) {

+	public URL getPluginPath(String pluginId, String versionId) {

 		// return the plugin path element for the specified plugin. This method

 		// is used during boot processing to obtain "kernel" plugins whose

 		// class loaders must be created prior to the plugin registry being 

diff --git a/bundles/org.eclipse.core.runtime/plugin.xml b/bundles/org.eclipse.core.runtime/plugin.xml
index 71fa3f5..c37a3e9 100644
--- a/bundles/org.eclipse.core.runtime/plugin.xml
+++ b/bundles/org.eclipse.core.runtime/plugin.xml
@@ -2,7 +2,7 @@
 <plugin

   name="%pluginName"

   id="org.eclipse.core.runtime"

-  version="2.0.4"

+  version="2.0.5"

   provider-name="Object Technology International, Inc.">

 

   <runtime>

diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginDescriptor.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginDescriptor.java
index 03fb0fd..ac29c94 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginDescriptor.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginDescriptor.java
Binary files differ
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginRegistry.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginRegistry.java
index f844a43..fcb46bd 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginRegistry.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/PluginRegistry.java
@@ -21,10 +21,10 @@
 	// lifecycle events

 	private static final int STARTUP = 0;

 	private static final int SHUTDOWN = 1;

-  public PluginRegistry()

-  {

+

+public PluginRegistry() {

 	super();

-  }  

+}  

 /**

  * Iterate over the plug-ins in this registry.  Plug-ins are visited in dependent order.  That is, 

  * a plug-in, A, which requires another plug-in, B, is visited before its dependents (i.e., A is 

diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheReader.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheReader.java
index 60342d8..c01b4db 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheReader.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheReader.java
@@ -8,10 +8,17 @@
 import org.eclipse.core.runtime.*;

 import org.eclipse.core.runtime.model.*;

 import org.eclipse.core.internal.plugins.*;

+import org.eclipse.core.internal.runtime.InternalPlatform;

 import org.eclipse.core.internal.runtime.Policy;

 import org.eclipse.core.boot.BootLoader;

+import org.eclipse.core.internal.boot.LaunchInfo;

+

 import java.io.*;

+import java.net.URL;

 import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.Map;

 

 public class RegistryCacheReader {

 

@@ -90,8 +97,11 @@
 	public static final byte VALUE_LABEL = 43;

 	public static final byte VERSION_LABEL = 44;

 	

+	public static final byte REGISTRY_LAST_MOD_START = 56;

+	public static final byte REGISTRY_LAST_MOD_END = 57;

+	

 	// So it's easier to add a new label ...

-	public static final byte LARGEST_LABEL = 55;

+	public static final byte LARGEST_LABEL = 57;

 	

 public RegistryCacheReader(Factory factory) {

 	super();

@@ -107,6 +117,9 @@
 	return (objectTable.size() - 1);

 

 }

+private void debug(String msg) {

+	System.out.println("RegistryCacheReader: " + msg);

+}

 public static String decipherLabel(byte labelValue) {

 	switch (labelValue) {

 		case REGISTRY_LABEL:

@@ -201,6 +214,28 @@
 			return "<configElementIndex>";

 		case REGISTRY_INDEX_LABEL:

 			return "<registryIndex>";

+		case FRAGMENT_END_LABEL:

+			return "<fragmentEnd>";

+		case FRAGMENT_INDEX_LABEL:

+			return "<fragmentIndex>";

+		case FRAGMENT_LABEL:

+			return "<fragment>";

+		case FRAGMENT_PLUGIN_LABEL:

+			return "<fragmentPlugin>";

+		case FRAGMENT_PLUGIN_MATCH_LABEL:

+			return "<fragmentPluginMatch>";

+		case FRAGMENT_PLUGIN_VERSION_LABEL:

+			return "<fragmentPluginVersion>";

+		case REGISTRY_LAST_MOD_END:

+			return "<registryLastModifiedListEnd>";

+		case REGISTRY_LAST_MOD_START:

+			return "<registryLastModifiedListStart>";

+		case REQUIRES_OPTIONAL_LABEL:

+			return "<requiresOptional>";

+		case SOURCE_LABEL:

+			return "<source>";

+		case TYPE_LABEL:

+			return "<type>";

 	}

 

 	return "<unknown label>";

@@ -898,12 +933,14 @@
 	}

 	return requires;

 }

-public PluginRegistryModel readPluginRegistry(DataInputStream in) {

+public PluginRegistryModel readPluginRegistry(DataInputStream in, URL[] pluginPath, boolean debugFlag) {

 	if (cacheReadProblems == null) {

 		cacheReadProblems = new MultiStatus(Platform.PI_RUNTIME, Platform.PARSE_PROBLEM, Policy.bind("meta.registryCacheReadProblems"), null);

 	}

 

 	if (!interpretHeaderInformation(in)) {

+		if (debugFlag)

+			debug ("Cache header information out of date - ignoring cache");

 		return null;

 	}

 	PluginRegistryModel cachedRegistry = cacheFactory.createPluginRegistry();

@@ -921,6 +958,14 @@
 				break;

 			}

 			switch (inByte) {

+				case REGISTRY_LAST_MOD_START :

+					if (!readAndCheckLastModified((PluginRegistry)cachedRegistry, in, pluginPath, debugFlag)) {

+						// something has changed and we should discard

+						// the cache file

+						done = true;

+						cachedRegistry = null;

+					}

+					break;

 				case READONLY_LABEL :

 					if (in.readBoolean()) {

 						setReadOnlyFlag = true;

@@ -959,6 +1004,9 @@
 		cacheReadProblems.add(new Status(IStatus.WARNING, Platform.PI_RUNTIME, Platform.PARSE_PROBLEM, Policy.bind ("meta.regCacheIOException", decipherLabel(REGISTRY_LABEL)), ioe));

 		return null;

 	}

+	if (cachedRegistry == null)

+		return null;

+		

 	if (setReadOnlyFlag) {

 		// If we are finished reading this registry, we don't need to worry

 		// about setting the read-only flag on other objects we might wish

@@ -974,4 +1022,99 @@
 		return cachedRegistry;

 	}

 }

+private String[] getPathMembers(URL path) {

+	String[] list = null;

+	String protocol = path.getProtocol();

+	if (protocol.equals("file") || (InternalPlatform.inVAJ() && protocol.equals("valoader"))) {

+		list = (new File(path.getFile())).list();

+	} else {

+		// XXX: attempt to read URL and see if we got html dir page

+	}

+	return list == null ? new String[0] : list;

+}

+private boolean readAndCheckLastModified (PluginRegistry cachedRegistry, DataInputStream in, URL[] pluginPath, boolean debugFlag){

+	try {

+		int numEntries = in.readInt();

+		for (int i = 0; i < numEntries; i++) {

+			String fileName = in.readUTF();

+			long lastMod = in.readLong();

+			InternalPlatform.addLastModifiedTime(fileName, lastMod);

+		}

+	} catch (IOException ioe) {

+		cacheReadProblems.add(new Status(IStatus.WARNING, Platform.PI_RUNTIME, Platform.PARSE_PROBLEM, Policy.bind ("meta.regCacheIOException", decipherLabel(REGISTRY_LABEL)), ioe));

+		return false;

+	}

+	

+	Map onDiskModTimes = null;

+	if (pluginPath != null) {

+		onDiskModTimes = new HashMap(30);	

+		for (int i = 0; i < pluginPath.length; i++) {

+			String pluginString = pluginPath[i].getFile();

+			if (pluginString.endsWith("/")) {

+				// directory entry - search for plugins

+				String[] members = getPathMembers(pluginPath[i]);

+				for (int j = 0; j < members.length; j++) {

+					onDiskModTimes.put(new String(pluginString + members[j] + "/plugin.xml"), new Long(0L));

+					onDiskModTimes.put(new String(pluginString + members[j] + "/fragment.xml"), new Long(0L));

+				}

+			} else {

+				onDiskModTimes.put(new String(pluginString), new Long(0L));

+			}

+		}

+	}

+	

+	Map regIndex = InternalPlatform.getRegIndex();

+	

+	// Get the simple (i.e. null) cases out of the way quickly

+	if ((regIndex == null) && (onDiskModTimes == null))

+		return true;

+		

+	if (((regIndex == null) && (onDiskModTimes != null)) ||

+		((regIndex != null) && (onDiskModTimes == null)))

+		return false;

+	

+	for (Iterator keys = regIndex.keySet().iterator(); keys.hasNext();) {

+		String cacheKey = (String)keys.next();

+		File diskFile = new File (cacheKey);

+		if (!onDiskModTimes.containsKey(cacheKey)) {

+			// This entry exists in the cache but is no longer in

+			// the plugin path - discard the cache

+			if (debugFlag)

+				debug (cacheKey + " exists in cache but not part of plugin path.");

+			return false;

+		}

+		if (!diskFile.exists()) {

+			// This entry is in the cache but no longer on disk

+			if (debugFlag)

+				debug (cacheKey + " exists in cache but not on disk");

+			return false;

+		}

+		long cacheLastMod = ((Long)regIndex.get(cacheKey)).longValue();

+		long diskLastMod = diskFile.lastModified();

+		if (diskLastMod != cacheLastMod) {

+			// something has changed

+			if (debugFlag)

+				debug (cacheKey + " last modified time different than on disk");

+			return false;

+		}

+		onDiskModTimes.remove(cacheKey);

+	}

+	

+	// We've iterated through all the elements in the cache and

+	// they all exist with the same last modified times as before.

+	// Now check to see if there is anything else on disk that needs

+	// to be added.

+	for (Iterator keys = onDiskModTimes.keySet().iterator(); keys.hasNext();) {

+		String diskKey = (String)keys.next();

+		File diskFile = new File(diskKey);

+		if (diskFile.exists()) {

+			// This one isn't in the cache

+			if (debugFlag)

+				debug (diskKey + " exists on disk but not in the cache");

+			return false;

+		}

+	}

+			

+	return true;

+}

 }

diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheWriter.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheWriter.java
index 0be6d20..2e1a9bf 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheWriter.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryCacheWriter.java
@@ -6,15 +6,18 @@
  */

 

 import org.eclipse.core.boot.BootLoader;

+import org.eclipse.core.internal.boot.LaunchInfo;

+import org.eclipse.core.internal.runtime.InternalPlatform;

 import org.eclipse.core.internal.runtime.Policy;

 import org.eclipse.core.runtime.IStatus;

 import org.eclipse.core.runtime.MultiStatus;

 import org.eclipse.core.runtime.Platform;

 import org.eclipse.core.runtime.Status;

 import org.eclipse.core.runtime.model.*;

+

 import java.io.DataOutputStream;

 import java.io.IOException;

-import java.util.ArrayList;

+import java.util.*;

 

 public class RegistryCacheWriter {

 	// See RegistryCacheReader for constants commonly used here too.

@@ -592,6 +595,20 @@
 		// add this object to the object table first

 		addToObjectTable(registry);

 		writeHeaderInformation(out);

+		// Now write out all the last modified times

+		Map regIndex = InternalPlatform.getRegIndex();

+		if (regIndex != null) {

+			writeLabel(RegistryCacheReader.REGISTRY_LAST_MOD_START, out);

+			out.writeInt(regIndex.keySet().size());

+			for (Iterator list = regIndex.keySet().iterator(); list.hasNext();) {

+				String fileName = (String)list.next();

+				long lastMod = ((Long)regIndex.get(fileName)).longValue();

+				out.writeUTF(fileName);

+				out.writeLong(lastMod);

+			}

+			writeLabel(RegistryCacheReader.REGISTRY_LAST_MOD_END, out);

+		}

+	

 		String outString = null;

 

 		writeLabel(RegistryCacheReader.REGISTRY_LABEL, out);

diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryLoader.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryLoader.java
index 37469b2..b5f9a02 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryLoader.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/plugins/RegistryLoader.java
@@ -5,13 +5,14 @@
  * All Rights Reserved.

  */

 

-import org.eclipse.core.runtime.*;

-import org.eclipse.core.runtime.model.*;

+import java.io.*;

+import java.net.MalformedURLException;

+import java.net.URL;

+

 import org.eclipse.core.internal.runtime.InternalPlatform;

 import org.eclipse.core.internal.runtime.Policy;

-import java.io.*;

-import java.net.URL;

-import java.net.MalformedURLException;

+import org.eclipse.core.runtime.*;

+import org.eclipse.core.runtime.model.*;

 import org.xml.sax.InputSource;

 import org.xml.sax.SAXParseException;

 

@@ -42,6 +43,14 @@
 	}

 	return list == null ? new String[0] : list;

 }

+/**

+ * Reports an error and returns true.

+ */

+private boolean parseProblem(String message) {

+	factory.error(new Status(

+		IStatus.WARNING, Platform.PI_RUNTIME, Platform.PARSE_PROBLEM, message, null));

+	return true;

+}

 private PluginRegistryModel parseRegistry(URL[] pluginPath) {

 	long startTick = System.currentTimeMillis();

 	PluginRegistryModel result = processManifestFiles(pluginPath);

@@ -91,39 +100,54 @@
 		// directory entry - search for plugins

 		String[] members = getPathMembers(location);

 		for (int j = 0; j < members.length; j++) {

+			boolean found = false;

 			try {

-				boolean found = processPluginPathFile(registry, new URL(location, members[j] + "/plugin.xml"));

+				found = processPluginPathFile(registry, new URL(location, members[j] + "/plugin.xml"));

 				if (!found)

 					found = processPluginPathFile(registry, new URL(location, members[j] + "/fragment.xml"));

 			} catch (MalformedURLException e) {

 			}

 			if (debug)

-				debug("Processed - " + members[j]);

+				debug(found ? "Processed - " : "Processed (not found) - " + members[j]);

 		}

 	} else {

 		// specific file entry - load the given file

 		boolean found = processPluginPathFile(registry, location);

 		if (debug)

-			debug("Processed - " + location);

+			debug(found ? "Processed - " : "Processed (not found) - " + location);

 	}

 }

+/**

+ * @return true if a file was found at the given location, and false otherwise.

+ */

 private boolean processPluginPathFile(PluginRegistryModel registry, URL location) {

 	PluginModel entry = processManifestFile(location);

 	if (entry == null)

 		return false;

-

+	if (entry instanceof PluginDescriptorModel) {

+		if (entry.getId() == null || entry.getVersion() == null) {

+			return parseProblem(Policy.bind("parse.nullPluginIdentifier", location.toString()));

+		}

+		//skip duplicate entries

+		if (registry.getPlugin(entry.getId(), entry.getVersion()) != null) {

+			return parseProblem(Policy.bind("parse.duplicatePlugin", entry.getId()));

+		}

+		registry.addPlugin((PluginDescriptorModel) entry);

+	} else {

+		if (entry.getId() == null || entry.getVersion() == null) {

+			return parseProblem(Policy.bind("parse.nullFragmentIdentifier", location.toString()));

+		}

+		if (entry instanceof PluginFragmentModel) {

+			registry.addFragment((PluginFragmentModel) entry);

+		} else {

+			return parseProblem(Policy.bind("parse.unknownEntry", location.toString()));

+		}

+	}

 	String url = location.toString();

 	url = url.substring(0, 1 + url.lastIndexOf('/'));

-	if (entry instanceof PluginDescriptorModel)

-		registry.addPlugin((PluginDescriptorModel) entry);

-	else

-		if (entry instanceof PluginFragmentModel)

-			registry.addFragment((PluginFragmentModel) entry);

-		else

-			// XXX log some kind of error or throw an exception here

-			return false;

 	entry.setRegistry(registry);

 	entry.setLocation(url);

+	InternalPlatform.addLastModifiedTime(location.getFile(), new File(location.getFile()).lastModified());

 	return true;

 }

 }

diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java
index ece3ea7..62c35f9 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java
@@ -24,6 +24,12 @@
 public final class InternalPlatform {

 	private static IAdapterManager adapterManager;

 	private static PluginRegistry registry;

+	// registry index - used to store last modified times for

+	// registry caching

+	// ASSUMPTION:  Only the plugin registry in 'registry' above

+	// will be cached

+	private static Map regIndex = null;

+	

 	private static Set logListeners = new HashSet(5);

 	private static Map logs = new HashMap(5);

 	private static PlatformLogListener platformLog = null;

@@ -41,7 +47,7 @@
 	private static String password = "";

 	private static boolean inDevelopmentMode = false;

 	private static boolean splashDown = false;

-	private static boolean cacheRegistry = false;

+	private static boolean cacheRegistry = true;

 

 	private static File lockFile = null;

 	private static RandomAccessFile lockRAF = null;

@@ -55,6 +61,7 @@
 

 	// execution options

 	private static final String OPTION_DEBUG = Platform.PI_RUNTIME + "/debug";

+	private static final String OPTION_DEBUG_SYSTEM_CONTEXT = Platform.PI_RUNTIME + "/debug/context";

 	private static final String OPTION_DEBUG_PLUGINS = Platform.PI_RUNTIME + "/registry/debug";

 	private static final String OPTION_DEBUG_PLUGINS_DUMP = Platform.PI_RUNTIME + "/registry/debug/dump";

 

@@ -64,10 +71,11 @@
 	private static final String PASSWORD = "-password";

 	private static final String DEV = "-dev";

 	private static final String ENDSPLASH = "-endsplash";

-	private static final String REGISTRYCACHE = "-registrycache";

+	private static final String NOREGISTRYCACHE = "-noregistrycache";

 

 	// debug support:  set in loadOptions()

 	public static boolean DEBUG = false;

+	public static boolean DEBUG_CONTEXT = false;

 	public static boolean DEBUG_PLUGINS = false;

 	public static String DEBUG_PLUGINS_DUMP = "";

 

@@ -210,7 +218,7 @@
 

 	try {

 		PlatformConfiguration config = InternalBootLoader.getCurrentPlatformConfiguration();

-		URL url = config.getPluginPath(PI_XML);

+		URL url = config.getPluginPath(PI_XML, XML_VERSION);

 		if (url == null)

 			url = new URL(BootLoader.getInstallURL(), XML_LOCATION);

 		descriptor.setLocation(url.toExternalForm());

@@ -561,6 +569,9 @@
 	// can't register url handlers until after the plugin registry is loaded

 	PlatformURLPluginHandlerFactory.startup();

 	activateDefaultPlugins();

+	if (DEBUG_CONTEXT)

+		System.out.println("OS: " + BootLoader.getOS() + " WS: " + BootLoader.getWS() + 

+			" NL: " + BootLoader.getNL() + " ARCH: " + BootLoader.getOSArch());

 	// can't install the log or log problems until after the platform has been initialized.

 	platformLog = new PlatformLogListener();

 	addLogListener(platformLog);

@@ -612,6 +623,7 @@
 		options.put(key, ((String) options.get(key)).trim());

 	}

 	DEBUG = getBooleanOption(OPTION_DEBUG, false);

+	DEBUG_CONTEXT = getBooleanOption(OPTION_DEBUG_SYSTEM_CONTEXT, false);

 	DEBUG_PLUGINS = getBooleanOption(OPTION_DEBUG_PLUGINS, false);

 	DEBUG_PLUGINS_DUMP = getDebugOption(OPTION_DEBUG_PLUGINS_DUMP);

 	InternalBootLoader.setupOptions();

@@ -628,13 +640,16 @@
 	IPath tempPath = getMetaArea().getBackupFilePathFor(path);

 	DataInputStream input = null;

 	registry = null;

+	// augment the plugin path with any additional platform entries

+	// (eg. user scripts)

+	URL[] augmentedPluginPath = getAugmentedPluginPath(pluginPath);

 	if (path.toFile().exists() && cacheRegistry) {

 		try {

 			input = new DataInputStream(new BufferedInputStream(new SafeFileInputStream(path.toOSString(), tempPath.toOSString())));

 			try {

 				long start = System.currentTimeMillis();

 				RegistryCacheReader cacheReader = new RegistryCacheReader(factory);

-				registry = (PluginRegistry)cacheReader.readPluginRegistry(input);

+				registry = (PluginRegistry)cacheReader.readPluginRegistry(input, augmentedPluginPath, DEBUG && DEBUG_PLUGINS);

 				if (DEBUG)

 					System.out.println("Read registry cache: " + (System.currentTimeMillis() - start) + "ms");

 			} finally {

@@ -646,7 +661,6 @@
 		}

 	}

 	if (registry == null) {

-		URL[] augmentedPluginPath = getAugmentedPluginPath(pluginPath);	// augment the plugin path with any additional platform entries	(eg. user scripts)

 		long start = System.currentTimeMillis();

 		registry = (PluginRegistry) parsePlugins(augmentedPluginPath, factory, DEBUG && DEBUG_PLUGINS);

 		IStatus resolveStatus = registry.resolve(true, true);

@@ -722,9 +736,9 @@
 			found = true;

 		}

 

-		// look for the registry cache flag

-		if (args[i].equalsIgnoreCase(REGISTRYCACHE)) {

-			cacheRegistry = true;

+		// look for the no registry cache flag

+		if (args[i].equalsIgnoreCase(NOREGISTRYCACHE)) {

+			cacheRegistry = false;

 			found = true;

 		}

 

@@ -826,4 +840,12 @@
 	if (keyringFile == null)

 		keyringFile = metaArea.getLocation().append(PlatformMetaArea.F_KEYRING).toOSString();

 }

+public static void addLastModifiedTime (String pathKey, long lastModTime) {

+	if (regIndex == null)

+		regIndex = new HashMap(30);

+	regIndex.put(pathKey, new Long(lastModTime));

+}

+public static Map getRegIndex() {

+	return regIndex;

+}

 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/messages.properties b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/messages.properties
index 09e5b49..1717658 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/messages.properties
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/messages.properties
@@ -41,6 +41,10 @@
 parse.unsatisfiedOptPrereq = Optional prerequisite constraint from {0} to {1} ignored.

 parse.prereqOptLoop = Optional prerequisite from {0} to {1} produced loop.  Prerequisite ignored.

 parse.unknownLibraryType = Unknown library type {0} for library {1}.

+parse.duplicatePlugin= Two plugins found with the same id: {0}. Ignoring duplicate.

+parse.unknownEntry=Unknown element parsed by plugin registry: {0}.

+parse.nullPluginIdentifier=Plugin not loaded due to missing id or version number: {0}.

+parse.nullFragmentIdentifier=Fragment not loaded due to missing id or version number: {0}.

 

 ### metadata

 meta.exceptionParsingLog = An exception occurred while parsing the log file: {0}

diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/model/PluginRegistryModel.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/model/PluginRegistryModel.java
index f8fee5e..db601be 100644
--- a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/model/PluginRegistryModel.java
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/runtime/model/PluginRegistryModel.java
@@ -45,6 +45,14 @@
 		list[0] = fragment;

 		fragments.put(key, list);

 	} else {

+		//XXX should we check for duplicates?  See javadoc comment.

+//		for (int i = 0; i < list.length; i++) {

+//			if (list[i].getVersion().equals(fragment.getVersion())) {

+//				//replace with new plugin

+//				list[i] = fragment;

+//				return;

+//			}

+//		}

 		PluginFragmentModel[] newList = new PluginFragmentModel[list.length + 1];

 		System.arraycopy(list, 0, newList, 0, list.length);

 		newList[list.length] = fragment;

@@ -67,6 +75,14 @@
 		pluginList[0] = plugin;

 		plugins.put(key, pluginList);

 	} else {

+		//check for duplicates

+		for (int i = 0; i < pluginList.length; i++) {

+			if (pluginList[i].getVersion().equals(plugin.getVersion())) {

+				//replace with new plugin

+				pluginList[i] = plugin;

+				return;

+			}

+		}

 		PluginDescriptorModel[] newPluginList = new PluginDescriptorModel[pluginList.length + 1];

 		System.arraycopy(pluginList, 0, newPluginList, 0, pluginList.length);

 		newPluginList[pluginList.length] = plugin;