User Library Container API
diff --git a/model/org/eclipse/jdt/core/ClasspathContainerInitializer.java b/model/org/eclipse/jdt/core/ClasspathContainerInitializer.java
index 83a3b1f..8b9e833 100644
--- a/model/org/eclipse/jdt/core/ClasspathContainerInitializer.java
+++ b/model/org/eclipse/jdt/core/ClasspathContainerInitializer.java
@@ -147,5 +147,25 @@
     	// By default, a container path is the only available description
     	return containerPath.makeRelative().toString();
     }
+
+	/**
+	 * Returns an object which identifies a container for comparison purpose. This allows
+	 * to eliminate redundant containers when accumulating classpath entries (e.g. 
+	 * runtime classpath computation). When requesting a container comparison ID, one
+	 * should ensure using its corresponding container initializer. Indeed, a random container
+	 * initializer cannot be held responsible for determining comparison IDs for arbitrary 
+	 * containers.
+	 * <p>
+	 * @param containerPath the path of the container which is being checked
+	 * @param project the project for which the container is to being checked
+	 * @return returns an Object identifying the container for comparison
+	 * @since 3.0
+	 */
+	public Object getComparisonID(IPath containerPath, IJavaProject project) {
+
+		// By default, containers are identical if they have the same containerPath,
+		// but this may be refined by other container initializer implementations.
+		return containerPath; 
+	}
 }
 
diff --git a/model/org/eclipse/jdt/core/JavaCore.java b/model/org/eclipse/jdt/core/JavaCore.java
index 15145fb..2b6a7c3 100644
--- a/model/org/eclipse/jdt/core/JavaCore.java
+++ b/model/org/eclipse/jdt/core/JavaCore.java
@@ -123,6 +123,11 @@
 	protected static final String ATT_HANDLE_ID =
 		"org.eclipse.jdt.internal.core.JavaModelManager.handleId" ; //$NON-NLS-1$
 
+	/**
+	 * Name of the User Library Container id.
+	 */
+	public static final String USER_LIBRARY_CONTAINER_ID= "org.eclipse.jdt.USER_LIBRARY"; //$NON-NLS-1$
+	
 	// *************** Possible IDs for configurable options. ********************
 
 	/**
@@ -2337,6 +2342,15 @@
 	}
 	
 	/**
+	 * Returns the names of all defined user libraries. The corresponding classpath container path
+	 * is the name appended to the USER_LIBRARY_CONTAINER_ID.  
+	 * @return Return an array containing the names of all known user defined.
+	 */
+	public static String[] getUserLibraryNames() {
+		return UserLibraryManager.getUserLibraryNames();
+	}
+
+	/**
 	 * Returns the working copies that have the given owner. 
 	 * Only compilation units in working copy mode are returned.
 	 * If the owner is <code>null</code>, primary working copies are returned.
@@ -3582,7 +3596,7 @@
 		// persist options
 		getPlugin().savePluginPreferences();
 	}
-	
+
 	/**
 	 * Shutdown the JavaCore plug-in.
 	 * <p>
diff --git a/model/org/eclipse/jdt/internal/core/UserLibrary.java b/model/org/eclipse/jdt/internal/core/UserLibrary.java
new file mode 100644
index 0000000..2a4b10f
--- /dev/null
+++ b/model/org/eclipse/jdt/internal/core/UserLibrary.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.internal.core.util.Util;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Internal model element to represent a user library and code to serialize / deserialize.
+ */
+public class UserLibrary {
+
+	private static final String CURRENT_VERSION= "1"; //$NON-NLS-1$
+
+	private static final String TAG_VERSION= "version"; //$NON-NLS-1$
+	private static final String TAG_USERLIBRARY= "userlibrary"; //$NON-NLS-1$
+	private static final String TAG_SOURCEATTACHMENT= "sourceattachment"; //$NON-NLS-1$
+	private static final String TAG_SOURCEATTACHMENTROOT= "sourceattachmentroot"; //$NON-NLS-1$
+	private static final String TAG_PATH= "path"; //$NON-NLS-1$
+	private static final String TAG_ARCHIVE= "archive"; //$NON-NLS-1$
+	private static final String TAG_SYSTEMLIBRARY= "systemlibrary"; //$NON-NLS-1$
+	
+	private boolean isSystemLibrary;
+	private IClasspathEntry[] entries;
+
+	public UserLibrary(IClasspathEntry[] entries, boolean isSystemLibrary) {
+		Assert.isNotNull(entries);
+		this.entries= entries;
+		this.isSystemLibrary= isSystemLibrary;
+	}
+	
+	public IClasspathEntry[] getEntries() {
+		return this.entries;
+	}
+	
+	public boolean isSystemLibrary() {
+		return this.isSystemLibrary;
+	}
+	
+	/* (non-Javadoc)
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	public boolean equals(Object obj) {
+		if (obj != null && obj.getClass() == getClass()) {
+			UserLibrary other= (UserLibrary) obj;
+			if (this.entries.length == other.entries.length && this.isSystemLibrary == other.isSystemLibrary) {
+				for (int i= 0; i < this.entries.length; i++) {
+					if (!this.entries[i].equals(other.entries[i])) {
+						return false;
+					}
+				}
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/* (non-Javadoc)
+	 * @see java.lang.Object#hashCode()
+	 */
+	public int hashCode() {
+		int hashCode= 0;
+		if (this.isSystemLibrary) {
+			hashCode++;
+		}
+		for (int i= 0; i < this.entries.length; i++) {
+			hashCode= hashCode * 17 + this.entries.hashCode();
+		}
+		return hashCode;
+	}
+	
+	/* package */  String serialize() throws IOException {
+		ByteArrayOutputStream s = new ByteArrayOutputStream();
+		OutputStreamWriter writer = new OutputStreamWriter(s, "UTF8"); //$NON-NLS-1$
+		XMLWriter xmlWriter = new XMLWriter(writer);
+		
+		HashMap library = new HashMap();
+		library.put(TAG_VERSION, String.valueOf(CURRENT_VERSION));
+		library.put(TAG_SYSTEMLIBRARY, String.valueOf(this.isSystemLibrary));
+		xmlWriter.printTag(TAG_USERLIBRARY, library, true, true, false);
+		
+		for (int i = 0; i < this.entries.length; ++i) {
+			IClasspathEntry curr= this.entries[i];
+			
+			HashMap archive = new HashMap();
+			archive.put(TAG_PATH, curr.getPath().toString());
+			IPath sourceAttach= curr.getSourceAttachmentPath();
+			if (sourceAttach != null)
+				archive.put(TAG_SOURCEATTACHMENT, sourceAttach);
+			IPath sourceAttachRoot= curr.getSourceAttachmentRootPath();
+			if (sourceAttachRoot != null)
+				archive.put(TAG_SOURCEATTACHMENTROOT, sourceAttachRoot);				
+			xmlWriter.printTag(TAG_ARCHIVE, archive, true, true, true);
+		}	
+		xmlWriter.endTag(TAG_USERLIBRARY, true);
+		writer.flush();
+		writer.close();
+		return s.toString("UTF8");//$NON-NLS-1$
+	}
+	
+	/* package */ static UserLibrary createFromString(Reader reader) throws IOException {
+		Element cpElement;
+		try {
+			DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+			cpElement = parser.parse(new InputSource(reader)).getDocumentElement();
+		} catch (SAXException e) {
+			throw new IOException(Util.bind("file.badFormat")); //$NON-NLS-1$
+		} catch (ParserConfigurationException e) {
+			throw new IOException(Util.bind("file.badFormat")); //$NON-NLS-1$
+		} finally {
+			reader.close();
+		}
+		
+		if (!cpElement.getNodeName().equalsIgnoreCase(TAG_USERLIBRARY)) { //$NON-NLS-1$
+			throw new IOException(Util.bind("file.badFormat")); //$NON-NLS-1$
+		}
+		// String version= cpElement.getAttribute(TAG_VERSION);
+		// in case we update the format: add code to read older versions
+		
+		boolean isSystem= Boolean.valueOf(cpElement.getAttribute(TAG_SYSTEMLIBRARY)).booleanValue();
+		
+		NodeList list= cpElement.getChildNodes();
+		int length = list.getLength();
+		
+		ArrayList res= new ArrayList(length);
+		for (int i = 0; i < length; ++i) {
+			Node node = list.item(i);
+			
+			if (node.getNodeType() == Node.ELEMENT_NODE) {
+				Element element= (Element) node;
+				if (element.getNodeName().equals(TAG_ARCHIVE)) {
+					String path = element.getAttribute(TAG_PATH);
+					IPath sourceAttach= element.hasAttribute(TAG_SOURCEATTACHMENT) ? new Path(element.getAttribute(TAG_SOURCEATTACHMENT)) : null;
+					IPath sourceAttachRoot= element.hasAttribute(TAG_SOURCEATTACHMENTROOT) ? new Path(element.getAttribute(TAG_SOURCEATTACHMENTROOT)) : null;
+					res.add(JavaCore.newLibraryEntry(new Path(path), sourceAttach, sourceAttachRoot));
+				}
+			}
+		}
+		
+		IClasspathEntry[] entries= (IClasspathEntry[]) res.toArray(new IClasspathEntry[res.size()]);
+		
+		return new UserLibrary(entries, isSystem);
+	}
+}
diff --git a/model/org/eclipse/jdt/internal/core/UserLibraryClasspathContainer.java b/model/org/eclipse/jdt/internal/core/UserLibraryClasspathContainer.java
new file mode 100644
index 0000000..338f095
--- /dev/null
+++ b/model/org/eclipse/jdt/internal/core/UserLibraryClasspathContainer.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+
+/**
+ *
+ */
+public class UserLibraryClasspathContainer implements IClasspathContainer {
+	
+	private String name;
+	
+	public UserLibraryClasspathContainer(String libName) {
+		this.name= libName;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.IClasspathContainer#getClasspathEntries()
+	 */
+	public IClasspathEntry[] getClasspathEntries() {
+		IClasspathContainer library= UserLibraryManager.getUserLibrary(this.name);
+		if (library != null) {
+			return library.getClasspathEntries();
+		}
+		return new IClasspathEntry[0];
+		
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.IClasspathContainer#getDescription()
+	 */
+	public String getDescription() {
+		return this.name;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.IClasspathContainer#getKind()
+	 */
+	public int getKind() {
+		IClasspathContainer library= UserLibraryManager.getUserLibrary(this.name);
+		if (library != null) {
+			return library.getKind();
+		}
+		return K_APPLICATION;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.IClasspathContainer#getPath()
+	 */
+	public IPath getPath() {
+		return new Path(JavaCore.USER_LIBRARY_CONTAINER_ID).append(this.name);
+	}
+}
diff --git a/model/org/eclipse/jdt/internal/core/UserLibraryClasspathContainerInitializer.java b/model/org/eclipse/jdt/internal/core/UserLibraryClasspathContainerInitializer.java
new file mode 100644
index 0000000..5d72cae
--- /dev/null
+++ b/model/org/eclipse/jdt/internal/core/UserLibraryClasspathContainerInitializer.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+
+/**
+ *
+ */
+public class UserLibraryClasspathContainerInitializer extends ClasspathContainerInitializer {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#initialize(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject)
+	 */
+	public void initialize(IPath containerPath, IJavaProject project) throws CoreException {
+		if (isUserLibraryContainer(containerPath)) {
+			String userLibName= containerPath.segment(1);
+						
+			IClasspathContainer library = UserLibraryManager.getUserLibrary(userLibName);
+			if (library != null) {
+				JavaCore.setClasspathContainer(containerPath, new IJavaProject[] { project }, 	new IClasspathContainer[] { library }, null);
+			}
+		}
+	}
+	
+	private boolean isUserLibraryContainer(IPath path) {
+		return path != null && path.segmentCount() == 2 && JavaCore.USER_LIBRARY_CONTAINER_ID.equals(path.segment(0));
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#canUpdateClasspathContainer(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject)
+	 */
+	public boolean canUpdateClasspathContainer(IPath containerPath, IJavaProject project) {
+		return isUserLibraryContainer(containerPath);
+	}
+
+	/**
+	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#requestClasspathContainerUpdate(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject, org.eclipse.jdt.core.IClasspathContainer)
+	 */
+	public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion) throws CoreException {
+		if (isUserLibraryContainer(containerPath)) {
+			String name= containerPath.segment(1);
+			if (containerSuggestion != null) {
+				UserLibrary library= new UserLibrary(containerSuggestion.getClasspathEntries(), containerSuggestion.getKind() == IClasspathContainer.K_SYSTEM);
+				UserLibraryManager.setUserLibrary(name, library, null); // should use a real progress monitor
+			} else {
+				UserLibraryManager.setUserLibrary(name, null, null); // should use a real progress monitor
+			}
+		}
+	}
+
+	/**
+	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#getDescription(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject)
+	 */
+	public String getDescription(IPath containerPath, IJavaProject project) {
+		if (isUserLibraryContainer(containerPath)) {
+			return containerPath.segment(1);
+		}
+		return super.getDescription(containerPath, project);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#getComparisonID(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject)
+	 */
+	public Object getComparisonID(IPath containerPath, IJavaProject project) {
+		return containerPath.segment(0);
+	}
+}
diff --git a/model/org/eclipse/jdt/internal/core/UserLibraryManager.java b/model/org/eclipse/jdt/internal/core/UserLibraryManager.java
new file mode 100644
index 0000000..31f9b97
--- /dev/null
+++ b/model/org/eclipse/jdt/internal/core/UserLibraryManager.java
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+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.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.core.util.Util;
+
+/**
+ *
+ */
+public class UserLibraryManager {
+	
+	public final static String CP_USERLIBRARY_PREFERENCES_PREFIX = JavaCore.PLUGIN_ID+".userLibrary."; //$NON-NLS-1$
+	public final static String CP_ENTRY_IGNORE = "##<cp entry ignore>##"; //$NON-NLS-1$
+
+	private static Map userLibraries;
+	private static final boolean logProblems= false;
+	private static IPropertyChangeListener listener= new IPropertyChangeListener() {
+
+		public void propertyChange(PropertyChangeEvent event) {
+			String key= event.getProperty();
+			if (key.startsWith(CP_USERLIBRARY_PREFERENCES_PREFIX)) {
+				try {
+					recreatePersistedUserLibraryEntry(key, (String) event.getNewValue(), false, true);
+				} catch (JavaModelException e) {
+					if (logProblems) {
+						Util.log(e, "Exception while rebinding user library '"+ key.substring(CP_USERLIBRARY_PREFERENCES_PREFIX.length()) +"'."); //$NON-NLS-1$ //$NON-NLS-2$
+					}
+					
+				}
+			}
+		}
+		
+	};
+	
+	private UserLibraryManager() {
+		// do not instantiate
+	}
+		
+	/**
+	 * Returns the names of all defined user libraries. The corresponding classpath container path
+	 * is the name appended to the CONTAINER_ID.  
+	 * @return Return an array containing the names of all known user defined.
+	 */
+	public static String[] getUserLibraryNames() {
+		Set set= getLibraryMap().keySet();
+		return (String[]) set.toArray(new String[set.size()]);
+	}
+	
+	/**
+	 * Gets the library for a given name or <code>null</code> if no such library exists.
+	 * @param name The name of the library
+	 * @return The library registered for the given name or <code>null</code>.
+	 */
+	public static IClasspathContainer getUserLibrary(String name) {
+		return (IClasspathContainer) getLibraryMap().get(name);
+	}
+
+	/**
+	 * Registers user libraries for given names. If a library for the given name already exists, its value will be updated.
+	 * This call will also rebind all related classpath container. 
+	 * @param newNames The names to register the libraries for
+	 * @param newLibs The libraries to register
+	 * @param monitor A progress monitor used when rebinding the classpath containers
+	 * @throws JavaModelException
+	 */
+	public static void setUserLibraries(String[] newNames, UserLibrary[] newLibs, IProgressMonitor monitor) throws JavaModelException {
+		Assert.isTrue(newNames.length == newLibs.length, "names and libraries should have the same length"); //$NON-NLS-1$
+		
+		if (monitor == null) {
+			monitor= new NullProgressMonitor();
+		}
+		
+		monitor.beginTask("Configure user libraries...", newNames.length);	//$NON-NLS-1$
+		try {
+			int last= newNames.length - 1;
+			for (int i= 0; i < newLibs.length; i++) {
+				internalSetUserLibrary(newNames[i], newLibs[i], i == last, true, new SubProgressMonitor(monitor, 1));
+			}
+		} finally {
+			monitor.done();
+		}
+	}
+	
+	/**
+	 * Registers a user library for a given name. If a library for the given name already exists, its value will be updated.
+	 * This call will also rebind all related classpath container. 
+	 * @param name The name to register the library for
+	 * @param library The library to register
+	 * @param monitor A progress monitor used when rebinding the classpath containers
+	 * @throws JavaModelException
+	 */
+	public static void setUserLibrary(String name, UserLibrary library, IProgressMonitor monitor) throws JavaModelException {
+		internalSetUserLibrary(name, library, true, true, monitor);
+	}
+	
+	static Map getLibraryMap() {
+		if (userLibraries == null) {
+			userLibraries= new HashMap();
+			// load variables and containers from preferences into cache
+			Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
+			preferences.addPropertyChangeListener(listener);
+
+			// only get variable from preferences not set to their default
+			String[] propertyNames = preferences.propertyNames();
+			for (int i = 0; i < propertyNames.length; i++) {
+				String propertyName = propertyNames[i];
+				if (propertyName.startsWith(CP_USERLIBRARY_PREFERENCES_PREFIX)) {
+					try {
+						recreatePersistedUserLibraryEntry(propertyName, preferences.getString(propertyName), false, false);
+					} catch (JavaModelException e) {
+						// won't happen: no rebinding
+					}
+				}
+			}
+		}
+		return userLibraries;
+	}
+	
+	static void recreatePersistedUserLibraryEntry(String propertyName, String savedString, boolean save, boolean rebind) throws JavaModelException {
+		String libName= propertyName.substring(CP_USERLIBRARY_PREFERENCES_PREFIX.length());
+		if (savedString == null || savedString.equals(CP_ENTRY_IGNORE)) {
+			internalSetUserLibrary(libName, null, save, rebind, null);
+		} else {
+			try {
+				StringReader reader = new StringReader(savedString);
+				UserLibrary library= UserLibrary.createFromString(reader);
+				internalSetUserLibrary(libName, library, save, rebind, null);
+			} catch (IOException e) {
+				if (logProblems) {
+					Util.log(e, "Exception while retrieving user library '"+ propertyName +"', library will be removed."); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+				internalSetUserLibrary(libName, null, save, rebind, null);
+			}
+		}
+	}
+
+
+
+	static void internalSetUserLibrary(String name, UserLibrary library, boolean save, boolean rebind, IProgressMonitor monitor) throws JavaModelException {
+		if (library == null) {
+			Object previous= getLibraryMap().remove(name);
+			if (previous == null) {
+				return; // no change
+			}
+		} else {
+			Object previous= getLibraryMap().put(name, library);
+			if (library.equals(previous)) {
+				return; // no change
+			}
+		}
+		
+		Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
+		String containerKey = CP_USERLIBRARY_PREFERENCES_PREFIX+name;
+		String containerString = CP_ENTRY_IGNORE;
+		if (library != null) {
+			try {
+				containerString= library.serialize();
+			} catch (IOException e) {
+				// could not encode entry: leave it as CP_ENTRY_IGNORE
+			}
+		}
+		preferences.removePropertyChangeListener(listener);
+		try {
+			preferences.setDefault(containerKey, CP_ENTRY_IGNORE); // use this default to get rid of removed ones
+			preferences.setValue(containerKey, containerString);
+			if (save) {
+				JavaCore.getPlugin().savePluginPreferences();
+			}
+			if (rebind) {
+				rebindClasspathEntries(name, monitor);
+			}
+			
+		} finally {
+			preferences.addPropertyChangeListener(listener);
+		}
+	}
+
+	private static void rebindClasspathEntries(String name, IProgressMonitor monitor) throws JavaModelException {
+		IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
+		IJavaProject[] projects= JavaCore.create(root).getJavaProjects();
+		IPath containerPath= new Path(JavaCore.USER_LIBRARY_CONTAINER_ID).append(name);
+		
+		ArrayList affectedProjects= new ArrayList();
+		
+		for (int i= 0; i < projects.length; i++) {
+			IJavaProject project= projects[i];
+			IClasspathEntry[] entries= project.getRawClasspath();
+			for (int k= 0; k < entries.length; k++) {
+				IClasspathEntry curr= entries[k];
+				if (curr.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
+					if (containerPath.equals(curr.getPath())) {
+						affectedProjects.add(project);
+						break;
+					}				
+				}
+			}
+		}
+		if (!affectedProjects.isEmpty()) {
+			IJavaProject[] affected= (IJavaProject[]) affectedProjects.toArray(new IJavaProject[affectedProjects.size()]);
+			IClasspathContainer[] containers= new IClasspathContainer[affected.length];
+			
+			JavaCore.setClasspathContainer(containerPath, affected, containers, monitor);
+		} else {
+			if (monitor != null) {
+				monitor.done();
+			}
+		}
+	}
+
+
+	
+}