[469037] RuntimeClasspathProviderDelegate does not update the classpath
container in certain cases
diff --git a/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java
index bdae750..91bc0d0 100644
--- a/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java
+++ b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java
@@ -1,390 +1,406 @@
-/*******************************************************************************
- * Copyright (c) 2003, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     IBM Corporation - Initial API and implementation
- *******************************************************************************/
-package org.eclipse.jst.server.core;
-
-import java.io.File;
-import java.util.*;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.jdt.core.IClasspathAttribute;
-import org.eclipse.jdt.core.IClasspathContainer;
-import org.eclipse.jdt.core.IClasspathEntry;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jst.server.core.internal.IMemento;
-import org.eclipse.jst.server.core.internal.JavaServerPlugin;
-import org.eclipse.jst.server.core.internal.RuntimeClasspathContainer;
-import org.eclipse.jst.server.core.internal.Trace;
-import org.eclipse.jst.server.core.internal.XMLMemento;
-import org.eclipse.wst.server.core.IRuntime;
-/**
- * A runtime classpath provider provides the classpath for a Java server runtime.
- * This provider is scoped by runtime type and may provide the classpath for multiple
- * runtime instances.
- * <p>
- * This abstract class is intended to be extended only by clients
- * to extend the <code>runtimeClasspathProviders</code> extension point.
- * </p>
- * <p>
- * <b>Provisional API:</b> This class/interface is part of an interim API that is still under development and expected to 
- * change significantly before reaching stability. It is being made available at this early stage to solicit feedback 
- * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken 
- * (repeatedly) as the API evolves.
- * </p>
- * 
- * @plannedfor 3.0
- */
-public abstract class RuntimeClasspathProviderDelegate {
-	protected class SourceAttachmentUpdate {
-		String runtimeId;
-		IPath entry;
-		IPath sourceAttachmentPath;
-		IPath sourceAttachmentRootPath;
-		IClasspathAttribute[] attributes;
-	}
-
-	private volatile List<SourceAttachmentUpdate> sourceAttachments;
-
-	private String extensionId;
-
-	private Map<String, IPath> runtimePathMap = Collections.synchronizedMap(new HashMap<String, IPath>());
-
-	private Map<String, Integer> previousClasspath = Collections.synchronizedMap(new HashMap<String, Integer>());
-
-	public RuntimeClasspathProviderDelegate() {
-		// default constructor
-	}
-
-	/**
-	 * Initializes this classpath provider with its life-long id.
-	 * <p>
-	 * This method is called by the framework.
-	 * Clients should never call this method.
-	 * </p>
-	 * @param id the extension id
-	 */
-	public final void initialize(String id) {
-		extensionId = id;
-	}
-
-	/**
-	 * Resolves (creates the classpath entries for) the classpath container with
-	 * the given runtime and the given classpath container id (returned from
-	 * getClasspathEntryIds()). If the classpath container cannot be resolved
-	 * (for instance, if the runtime does not exist), return null.
-	 * 
-	 * @param runtime the runtime to resolve the container for
-	 * @return an array of classpath entries for the container, or null if the
-	 *   container could not be resolved
-	 * @deprecated use resolveClasspathContainer(IProject, IRuntime) instead
-	 */
-	public IClasspathEntry[] resolveClasspathContainer(IRuntime runtime) {
-		return null;
-	}
-
-	/**
-	 * Resolves (creates the classpath entries for) the classpath container with
-	 * the given runtime and the given classpath container id (returned from
-	 * getClasspathEntryIds()). If the classpath container cannot be resolved
-	 * (for instance, if the runtime does not exist), return null.
-	 * 
-	 * @param project the project to resolve
-	 * @param runtime the runtime to resolve the container for
-	 * @return an array of classpath entries for the container, or null if the
-	 *   container could not be resolved
-	 */
-	public IClasspathEntry[] resolveClasspathContainer(IProject project, IRuntime runtime) {
-		return null;
-	}
-
-	/**
-	 * Resolve the classpath container.
-	 * 
-	 * @param runtime a runtime
-	 * @return a possibly empty array of classpath entries
-	 * @deprecated should use resolveClasspathContainerImpl(IProject, IRuntime) instead
-	 */
-	public IClasspathEntry[] resolveClasspathContainerImpl(IRuntime runtime) {
-		return resolveClasspathContainerImpl(null, runtime);
-	}
-
-	/**
-	 * Resolve the classpath container.
-	 * 
-	 * @param project a project
-	 * @param runtime a runtime
-	 * @return a possibly empty array of classpath entries
-	 */
-	public IClasspathEntry[] resolveClasspathContainerImpl(IProject project, IRuntime runtime) {
-		if (runtime == null)
-			return new IClasspathEntry[0];
-		runtimePathMap.put(runtime.getId(), runtime.getLocation());
-		IClasspathEntry[] entries = resolveClasspathContainer(project, runtime);
-		if (entries == null)
-			entries = resolveClasspathContainer(runtime);
-		
-		if (entries == null)
-			entries = new IClasspathEntry[0];
-		
-		synchronized (this) {
-			if (sourceAttachments == null)
-				load();
-		}
-		List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;
-
-		if (srcAttachments != null) {
-			int size = entries.length;
-			int size2 = srcAttachments.size();
-			for (int i = 0; i < size; i++) {
-				for (int j = 0; j < size2; j++) {
-					SourceAttachmentUpdate sau = srcAttachments.get(j);
-					if (sau.runtimeId.equals(runtime.getId()) && sau.entry.equals(entries[i].getPath())) {
-						IClasspathAttribute[] consolidatedClasspathAttributes = consolidateClasspathAttributes(sau.attributes, entries[i].getExtraAttributes());
-						entries[i] = JavaCore.newLibraryEntry(entries[i].getPath(), sau.sourceAttachmentPath, sau.sourceAttachmentRootPath, entries[i].getAccessRules(), consolidatedClasspathAttributes, false);
-						break;
-					}
-				}
-			}
-		}
-		
-		String key = project.getName() + "/" + runtime.getId();
-		if (!previousClasspath.containsKey(key))
-			previousClasspath.put(key, new Integer(entries.length));
-		else {
-			Integer previousEntries = previousClasspath.get(key);
-			
-			if ((previousEntries == null) || (previousEntries.intValue() != entries.length)) {
-				if (Trace.FINEST) {
-					Trace.trace(Trace.STRING_FINEST, "Classpath update: " + key + " " + entries);
-				}
-				previousClasspath.put(key, new Integer(entries.length));
-				
-				IPath path = new Path(RuntimeClasspathContainer.SERVER_CONTAINER);
-				path = path.append(extensionId).append(runtime.getId());
-				try {
-					IJavaProject javaProject = JavaCore.create(project);
-					JavaCore.setClasspathContainer(path, new IJavaProject[] { javaProject },
-							new IClasspathContainer[] { null }, new NullProgressMonitor());
-				} catch (Exception e) {
-					if (Trace.WARNING) {
-						Trace.trace(Trace.STRING_WARNING, "Error updating classpath", e);
-					}
-				}
-			}
-		}
-		
-		return entries;
-	}
-
-	/*
-	 * Returns true if there are any changes in the runtime since the last time that the
-	 * classpath was resolved which may affect the classpath, and false otherwise. This
-	 * method is used to check projects when a runtime changes and automatically rebuild
-	 * them if necessary.
-	 * 
-	 * @param runtime a runtime
-	 * @return <code>true</code> if the classpath may change due to a change in the runtime,
-	 *    and <code>false</code> if there are no changes
-	 */
-	public boolean hasRuntimeClasspathChanged(IRuntime runtime) {
-		try {
-			IPath path = runtimePathMap.get(runtime.getId());
-			return (path != null && !path.equals(runtime.getLocation()));
-		} catch (Exception e) {
-			// ignore
-		}
-		return false;
-	}
-
-	private static void addJarFiles(File dir, List<IClasspathEntry> list, boolean includeSubdirectories) {
-		int depth = 0;
-		if (includeSubdirectories)
-			depth = 2;
-		addJarFiles(dir, list, depth);
-	}
-
-	private static void addJarFiles(File dir, List<IClasspathEntry> list, int depth) {
-		if (dir == null)
-			throw new IllegalArgumentException();
-		
-		File[] files = dir.listFiles();
-		if (files != null) {
-			for (File file : files) {
-				if (file.isDirectory() && depth > 0) {
-					addJarFiles(file, list, depth - 1);
-				} else if (file.getAbsolutePath().endsWith(".jar") || file.getAbsolutePath().endsWith(".zip")) {
-					IPath path = new Path(file.getAbsolutePath());
-					list.add(JavaCore.newLibraryEntry(path, null, null));
-				}
-			}
-		}
-	}
-
-	/**
-	 * Add library entries to the given list for every jar file found in the
-	 * given directory. Optionally search subdirectories as well.
-	 * 
-	 * @param list a list
-	 * @param dir a directory
-	 * @param includeSubdirectories <code>true</code> to include subdirectories, and
-	 *    <code>false</code> otherwise
-	 */
-	protected static void addLibraryEntries(List<IClasspathEntry> list, File dir, boolean includeSubdirectories) {
-		if (dir == null)
-			throw new IllegalArgumentException();
-		addJarFiles(dir, list, includeSubdirectories);
-	}
-
-	/**
-	 * Request that the classpath container for the given runtime and id be updated
-	 * with the given classpath container entries.
-	 * 
-	 * @param runtime a runtime
-	 * @param entries an array of classpath entries
-	 */
-	public void requestClasspathContainerUpdate(IRuntime runtime, IClasspathEntry[] entries) {
-		// default behaviour is to save the source path entries
-		if (runtime == null || entries == null)
-			return;
-		
-		// find the source attachments
-		List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();
-		
-		for (IClasspathEntry entry : entries) {
-			if (entry.getSourceAttachmentPath() != null || (entry.getExtraAttributes() != null && entry.getExtraAttributes().length > 0)) {
-				SourceAttachmentUpdate sau = new SourceAttachmentUpdate();
-				sau.runtimeId = runtime.getId();
-				sau.entry = entry.getPath();
-				sau.sourceAttachmentPath = entry.getSourceAttachmentPath();
-				sau.sourceAttachmentRootPath = entry.getSourceAttachmentRootPath();
-				sau.attributes = entry.getExtraAttributes();
-				srcAttachments.add(sau);
-			}
-		}
-		sourceAttachments = srcAttachments;
-		save();
-	}
-
-	/**
-	 * Load source attachment info.
-	 */
-	private void load() {
-		List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();
-		
-		String id = extensionId;
-		String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();
-		if (!(new File(filename)).exists())
-			return;
-		
-		try {
-			IMemento memento = XMLMemento.loadMemento(filename);
-			
-			IMemento[] children = memento.getChildren("source-attachment");
-			for (IMemento child : children) {
-				try {
-					SourceAttachmentUpdate sau = new SourceAttachmentUpdate();
-					sau.runtimeId = child.getString("runtime-id");
-					String temp = child.getString("entry");
-					if (temp != null)
-						sau.entry = new Path(temp);
-					temp = child.getString("source-attachment-path");
-					if (temp != null)
-						sau.sourceAttachmentPath = new Path(temp);
-					temp = child.getString("source-attachment-root-path");
-					if (temp != null)
-						sau.sourceAttachmentRootPath = new Path(temp);
-					IMemento[] attrChildren = child.getChildren("attribute");
-					if (attrChildren != null) {
-						int size2 = attrChildren.length;
-						sau.attributes = new IClasspathAttribute[size2];
-						for (int j = 0; j < size2; j++) {
-							String name = attrChildren[j].getString("name");
-							String value = attrChildren[j].getString("value");
-							sau.attributes[j] = JavaCore.newClasspathAttribute(name, value);
-						}
-					}
-					srcAttachments.add(sau);
-				} catch (Exception e) {
-					if (Trace.WARNING) {
-						Trace.trace(Trace.STRING_WARNING, "Could not load source attachment: " + e);
-					}
-				}
-			}
-		} catch (Exception e) {
-			if (Trace.WARNING) {
-				Trace.trace(Trace.STRING_WARNING, "Could not load source path info", e);
-			}
-		}
-		sourceAttachments = srcAttachments;
-	}
-
-	/**
-	 * Save source attachment info.
-	 */
-	private synchronized void save() {
-		List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;
-		if (srcAttachments == null)
-			return;
-		String id = extensionId;
-		String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();
-		try {
-			XMLMemento memento = XMLMemento.createWriteRoot("classpath");
-
-			Iterator iterator = srcAttachments.iterator();
-			while (iterator.hasNext()) {
-				SourceAttachmentUpdate sau = (SourceAttachmentUpdate) iterator.next();
-				IMemento child = memento.createChild("source-attachment");
-				child.putString("runtime-id", sau.runtimeId);
-				if (sau.entry != null)
-					child.putString("entry", sau.entry.toPortableString());
-				if (sau.sourceAttachmentPath != null)
-					child.putString("source-attachment-path", sau.sourceAttachmentPath.toPortableString());
-				if (sau.sourceAttachmentRootPath != null)
-					child.putString("source-attachment-root-path", sau.sourceAttachmentRootPath.toPortableString());
-				if (sau.attributes != null) {
-					for (IClasspathAttribute attr : sau.attributes) {
-						IMemento attrChild = child.createChild("attribute");
-						attrChild.putString("name", attr.getName());
-						attrChild.putString("value", attr.getValue());
-					}
-				}
-			}
-			
-			memento.saveToFile(filename);
-		} catch (Exception e) {
-			if (Trace.SEVERE) {
-				Trace.trace(Trace.STRING_SEVERE, "Error saving source path info", e);
-			}
-		}
-	}
-	
-	public IClasspathAttribute[] consolidateClasspathAttributes(IClasspathAttribute[] sourceAttachmentAttributes, IClasspathAttribute[] classpathEntryAttributes) {
-		List classpathAttributeList = new ArrayList();
-		classpathAttributeList.addAll(Arrays.asList(sourceAttachmentAttributes));
-		for (int i = 0; i < classpathEntryAttributes.length; i++) {
-			boolean attributeCollision = false;
-			for (int j = 0; j < sourceAttachmentAttributes.length; j++) {
-				String name = classpathEntryAttributes[i].getName();
-				if(name != null && name.equals(sourceAttachmentAttributes[j].getName())) {
-					attributeCollision = true;
-					break;
-				}
-			}
-			if(!attributeCollision) {
-				classpathAttributeList.add(classpathEntryAttributes[i]);
-			}
-		}
-		return (IClasspathAttribute[]) classpathAttributeList.toArray(new IClasspathAttribute[classpathAttributeList.size()]);
-	}
+/*******************************************************************************

+ * Copyright (c) 2003, 2011 IBM Corporation and others.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * which accompanies this distribution, and is available at

+ * http://www.eclipse.org/legal/epl-v10.html

+ *

+ * Contributors:

+ *     IBM Corporation - Initial API and implementation

+ *******************************************************************************/

+package org.eclipse.jst.server.core;

+

+import java.io.File;

+import java.util.*;

+

+import org.eclipse.core.resources.IProject;

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

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

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

+import org.eclipse.jdt.core.IClasspathAttribute;

+import org.eclipse.jdt.core.IClasspathContainer;

+import org.eclipse.jdt.core.IClasspathEntry;

+import org.eclipse.jdt.core.IJavaProject;

+import org.eclipse.jdt.core.JavaCore;

+import org.eclipse.jst.server.core.internal.IMemento;

+import org.eclipse.jst.server.core.internal.JavaServerPlugin;

+import org.eclipse.jst.server.core.internal.RuntimeClasspathContainer;

+import org.eclipse.jst.server.core.internal.Trace;

+import org.eclipse.jst.server.core.internal.XMLMemento;

+import org.eclipse.wst.server.core.IRuntime;

+/**

+ * A runtime classpath provider provides the classpath for a Java server runtime.

+ * This provider is scoped by runtime type and may provide the classpath for multiple

+ * runtime instances.

+ * <p>

+ * This abstract class is intended to be extended only by clients

+ * to extend the <code>runtimeClasspathProviders</code> extension point.

+ * </p>

+ * <p>

+ * <b>Provisional API:</b> This class/interface is part of an interim API that is still under development and expected to 

+ * change significantly before reaching stability. It is being made available at this early stage to solicit feedback 

+ * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken 

+ * (repeatedly) as the API evolves.

+ * </p>

+ * 

+ * @plannedfor 3.0

+ */

+public abstract class RuntimeClasspathProviderDelegate {

+	protected class SourceAttachmentUpdate {

+		String runtimeId;

+		IPath entry;

+		IPath sourceAttachmentPath;

+		IPath sourceAttachmentRootPath;

+		IClasspathAttribute[] attributes;

+	}

+

+	private volatile List<SourceAttachmentUpdate> sourceAttachments;

+

+	private String extensionId;

+

+	private Map<String, IPath> runtimePathMap = Collections.synchronizedMap(new HashMap<String, IPath>());

+

+	private Map<String, IClasspathEntry[]> previousClasspath = Collections.synchronizedMap(new HashMap<String, IClasspathEntry[]>());

+

+	public RuntimeClasspathProviderDelegate() {

+		// default constructor

+	}

+

+	/**

+	 * Initializes this classpath provider with its life-long id.

+	 * <p>

+	 * This method is called by the framework.

+	 * Clients should never call this method.

+	 * </p>

+	 * @param id the extension id

+	 */

+	public final void initialize(String id) {

+		extensionId = id;

+	}

+

+	/**

+	 * Resolves (creates the classpath entries for) the classpath container with

+	 * the given runtime and the given classpath container id (returned from

+	 * getClasspathEntryIds()). If the classpath container cannot be resolved

+	 * (for instance, if the runtime does not exist), return null.

+	 * 

+	 * @param runtime the runtime to resolve the container for

+	 * @return an array of classpath entries for the container, or null if the

+	 *   container could not be resolved

+	 * @deprecated use resolveClasspathContainer(IProject, IRuntime) instead

+	 */

+	public IClasspathEntry[] resolveClasspathContainer(IRuntime runtime) {

+		return null;

+	}

+

+	/**

+	 * Resolves (creates the classpath entries for) the classpath container with

+	 * the given runtime and the given classpath container id (returned from

+	 * getClasspathEntryIds()). If the classpath container cannot be resolved

+	 * (for instance, if the runtime does not exist), return null.

+	 * 

+	 * @param project the project to resolve

+	 * @param runtime the runtime to resolve the container for

+	 * @return an array of classpath entries for the container, or null if the

+	 *   container could not be resolved

+	 */

+	public IClasspathEntry[] resolveClasspathContainer(IProject project, IRuntime runtime) {

+		return null;

+	}

+

+	/**

+	 * Resolve the classpath container.

+	 * 

+	 * @param runtime a runtime

+	 * @return a possibly empty array of classpath entries

+	 * @deprecated should use resolveClasspathContainerImpl(IProject, IRuntime) instead

+	 */

+	public IClasspathEntry[] resolveClasspathContainerImpl(IRuntime runtime) {

+		return resolveClasspathContainerImpl(null, runtime);

+	}

+

+	/**

+	 * Resolve the classpath container.

+	 * 

+	 * @param project a project

+	 * @param runtime a runtime

+	 * @return a possibly empty array of classpath entries

+	 */

+	public IClasspathEntry[] resolveClasspathContainerImpl(IProject project, IRuntime runtime) {

+		if (runtime == null)

+			return new IClasspathEntry[0];

+		runtimePathMap.put(runtime.getId(), runtime.getLocation());

+		IClasspathEntry[] entries = resolveClasspathContainer(project, runtime);

+		if (entries == null)

+			entries = resolveClasspathContainer(runtime);

+		

+		if (entries == null)

+			entries = new IClasspathEntry[0];

+		

+		synchronized (this) {

+			if (sourceAttachments == null)

+				load();

+		}

+		List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;

+

+		if (srcAttachments != null) {

+			int size = entries.length;

+			int size2 = srcAttachments.size();

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

+				for (int j = 0; j < size2; j++) {

+					SourceAttachmentUpdate sau = srcAttachments.get(j);

+					if (sau.runtimeId.equals(runtime.getId()) && sau.entry.equals(entries[i].getPath())) {

+						IClasspathAttribute[] consolidatedClasspathAttributes = consolidateClasspathAttributes(sau.attributes, entries[i].getExtraAttributes());

+						entries[i] = JavaCore.newLibraryEntry(entries[i].getPath(), sau.sourceAttachmentPath, sau.sourceAttachmentRootPath, entries[i].getAccessRules(), consolidatedClasspathAttributes, false);

+						break;

+					}

+				}

+			}

+		}

+		

+		String key = project.getName() + "/" + runtime.getId();

+		if (!previousClasspath.containsKey(key))

+			previousClasspath.put(key, entries);

+		else {

+			IClasspathEntry[] previousClasspathEntries = previousClasspath.get(key);

+			

+			if (previousClasspathEntries == null 

+					|| previousClasspathEntries.length != entries.length 

+					|| entriesChanged(previousClasspathEntries,entries)) {

+				if (Trace.FINEST) {

+					Trace.trace(Trace.STRING_FINEST, "Classpath update: " + key + " " + entries);

+				}

+				previousClasspath.put(key, entries);

+				

+				IPath path = new Path(RuntimeClasspathContainer.SERVER_CONTAINER);

+				path = path.append(extensionId).append(runtime.getId());

+				try {

+					IJavaProject javaProject = JavaCore.create(project);

+					JavaCore.setClasspathContainer(path, new IJavaProject[] { javaProject },

+							new IClasspathContainer[] { null }, new NullProgressMonitor());

+				} catch (Exception e) {

+					if (Trace.WARNING) {

+						Trace.trace(Trace.STRING_WARNING, "Error updating classpath", e);

+					}

+				}

+			}

+		}

+		

+		return entries;

+	}

+

+	private boolean entriesChanged(IClasspathEntry[] previousEntries, IClasspathEntry[] entries) {

+		if (previousEntries.length != entries.length) {

+			return true;

+		}

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

+			if ((previousEntries[i] == null && entries[i] != null)

+					|| (previousEntries[i].getPath() == null && entries[i].getPath() != null)

+					|| !previousEntries[i].getPath().equals(entries[i].getPath())) {

+				return true;

+			}

+		}

+		return false;

+	}

+

+	/*

+	 * Returns true if there are any changes in the runtime since the last time that the

+	 * classpath was resolved which may affect the classpath, and false otherwise. This

+	 * method is used to check projects when a runtime changes and automatically rebuild

+	 * them if necessary.

+	 * 

+	 * @param runtime a runtime

+	 * @return <code>true</code> if the classpath may change due to a change in the runtime,

+	 *    and <code>false</code> if there are no changes

+	 */

+	public boolean hasRuntimeClasspathChanged(IRuntime runtime) {

+		try {

+			IPath path = runtimePathMap.get(runtime.getId());

+			return (path != null && !path.equals(runtime.getLocation()));

+		} catch (Exception e) {

+			// ignore

+		}

+		return false;

+	}

+

+	private static void addJarFiles(File dir, List<IClasspathEntry> list, boolean includeSubdirectories) {

+		int depth = 0;

+		if (includeSubdirectories)

+			depth = 2;

+		addJarFiles(dir, list, depth);

+	}

+

+	private static void addJarFiles(File dir, List<IClasspathEntry> list, int depth) {

+		if (dir == null)

+			throw new IllegalArgumentException();

+		

+		File[] files = dir.listFiles();

+		if (files != null) {

+			for (File file : files) {

+				if (file.isDirectory() && depth > 0) {

+					addJarFiles(file, list, depth - 1);

+				} else if (file.getAbsolutePath().endsWith(".jar") || file.getAbsolutePath().endsWith(".zip")) {

+					IPath path = new Path(file.getAbsolutePath());

+					list.add(JavaCore.newLibraryEntry(path, null, null));

+				}

+			}

+		}

+	}

+

+	/**

+	 * Add library entries to the given list for every jar file found in the

+	 * given directory. Optionally search subdirectories as well.

+	 * 

+	 * @param list a list

+	 * @param dir a directory

+	 * @param includeSubdirectories <code>true</code> to include subdirectories, and

+	 *    <code>false</code> otherwise

+	 */

+	protected static void addLibraryEntries(List<IClasspathEntry> list, File dir, boolean includeSubdirectories) {

+		if (dir == null)

+			throw new IllegalArgumentException();

+		addJarFiles(dir, list, includeSubdirectories);

+	}

+

+	/**

+	 * Request that the classpath container for the given runtime and id be updated

+	 * with the given classpath container entries.

+	 * 

+	 * @param runtime a runtime

+	 * @param entries an array of classpath entries

+	 */

+	public void requestClasspathContainerUpdate(IRuntime runtime, IClasspathEntry[] entries) {

+		// default behaviour is to save the source path entries

+		if (runtime == null || entries == null)

+			return;

+		

+		// find the source attachments

+		List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();

+		

+		for (IClasspathEntry entry : entries) {

+			if (entry.getSourceAttachmentPath() != null || (entry.getExtraAttributes() != null && entry.getExtraAttributes().length > 0)) {

+				SourceAttachmentUpdate sau = new SourceAttachmentUpdate();

+				sau.runtimeId = runtime.getId();

+				sau.entry = entry.getPath();

+				sau.sourceAttachmentPath = entry.getSourceAttachmentPath();

+				sau.sourceAttachmentRootPath = entry.getSourceAttachmentRootPath();

+				sau.attributes = entry.getExtraAttributes();

+				srcAttachments.add(sau);

+			}

+		}

+		sourceAttachments = srcAttachments;

+		save();

+	}

+

+	/**

+	 * Load source attachment info.

+	 */

+	private void load() {

+		List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();

+		

+		String id = extensionId;

+		String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();

+		if (!(new File(filename)).exists())

+			return;

+		

+		try {

+			IMemento memento = XMLMemento.loadMemento(filename);

+			

+			IMemento[] children = memento.getChildren("source-attachment");

+			for (IMemento child : children) {

+				try {

+					SourceAttachmentUpdate sau = new SourceAttachmentUpdate();

+					sau.runtimeId = child.getString("runtime-id");

+					String temp = child.getString("entry");

+					if (temp != null)

+						sau.entry = new Path(temp);

+					temp = child.getString("source-attachment-path");

+					if (temp != null)

+						sau.sourceAttachmentPath = new Path(temp);

+					temp = child.getString("source-attachment-root-path");

+					if (temp != null)

+						sau.sourceAttachmentRootPath = new Path(temp);

+					IMemento[] attrChildren = child.getChildren("attribute");

+					if (attrChildren != null) {

+						int size2 = attrChildren.length;

+						sau.attributes = new IClasspathAttribute[size2];

+						for (int j = 0; j < size2; j++) {

+							String name = attrChildren[j].getString("name");

+							String value = attrChildren[j].getString("value");

+							sau.attributes[j] = JavaCore.newClasspathAttribute(name, value);

+						}

+					}

+					srcAttachments.add(sau);

+				} catch (Exception e) {

+					if (Trace.WARNING) {

+						Trace.trace(Trace.STRING_WARNING, "Could not load source attachment: " + e);

+					}

+				}

+			}

+		} catch (Exception e) {

+			if (Trace.WARNING) {

+				Trace.trace(Trace.STRING_WARNING, "Could not load source path info", e);

+			}

+		}

+		sourceAttachments = srcAttachments;

+	}

+

+	/**

+	 * Save source attachment info.

+	 */

+	private synchronized void save() {

+		List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;

+		if (srcAttachments == null)

+			return;

+		String id = extensionId;

+		String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();

+		try {

+			XMLMemento memento = XMLMemento.createWriteRoot("classpath");

+

+			Iterator iterator = srcAttachments.iterator();

+			while (iterator.hasNext()) {

+				SourceAttachmentUpdate sau = (SourceAttachmentUpdate) iterator.next();

+				IMemento child = memento.createChild("source-attachment");

+				child.putString("runtime-id", sau.runtimeId);

+				if (sau.entry != null)

+					child.putString("entry", sau.entry.toPortableString());

+				if (sau.sourceAttachmentPath != null)

+					child.putString("source-attachment-path", sau.sourceAttachmentPath.toPortableString());

+				if (sau.sourceAttachmentRootPath != null)

+					child.putString("source-attachment-root-path", sau.sourceAttachmentRootPath.toPortableString());

+				if (sau.attributes != null) {

+					for (IClasspathAttribute attr : sau.attributes) {

+						IMemento attrChild = child.createChild("attribute");

+						attrChild.putString("name", attr.getName());

+						attrChild.putString("value", attr.getValue());

+					}

+				}

+			}

+			

+			memento.saveToFile(filename);

+		} catch (Exception e) {

+			if (Trace.SEVERE) {

+				Trace.trace(Trace.STRING_SEVERE, "Error saving source path info", e);

+			}

+		}

+	}

+	

+	public IClasspathAttribute[] consolidateClasspathAttributes(IClasspathAttribute[] sourceAttachmentAttributes, IClasspathAttribute[] classpathEntryAttributes) {

+		List classpathAttributeList = new ArrayList();

+		classpathAttributeList.addAll(Arrays.asList(sourceAttachmentAttributes));

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

+			boolean attributeCollision = false;

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

+				String name = classpathEntryAttributes[i].getName();

+				if(name != null && name.equals(sourceAttachmentAttributes[j].getName())) {

+					attributeCollision = true;

+					break;

+				}

+			}

+			if(!attributeCollision) {

+				classpathAttributeList.add(classpathEntryAttributes[i]);

+			}

+		}

+		return (IClasspathAttribute[]) classpathAttributeList.toArray(new IClasspathAttribute[classpathAttributeList.size()]);

+	}

 }
\ No newline at end of file