blob: d53df3be1765de84116ccfef5ec9c132ebf92bfa [file] [log] [blame]
/*******************************************************************************
* 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 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.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.SubProgressMonitor;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
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;
import org.osgi.service.prefs.BackingStoreException;
/**
*
*/
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 IEclipsePreferences.IPreferenceChangeListener listener= new IEclipsePreferences.IPreferenceChangeListener() {
public void preferenceChange(IEclipsePreferences.PreferenceChangeEvent event) {
String key= event.getKey();
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 UserLibrary getUserLibrary(String name) {
return (UserLibrary) 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
IEclipsePreferences instancePreferences = JavaModelManager.getJavaModelManager().getInstancePreferences();
instancePreferences.addPreferenceChangeListener(listener);
// only get variable from preferences not set to their default
try {
String[] propertyNames = instancePreferences.keys();
for (int i = 0; i < propertyNames.length; i++) {
String propertyName = propertyNames[i];
if (propertyName.startsWith(CP_USERLIBRARY_PREFERENCES_PREFIX)) {
try {
String propertyValue = instancePreferences.get(propertyName, null);
if (propertyValue != null)
recreatePersistedUserLibraryEntry(propertyName,propertyValue, false, false);
} catch (JavaModelException e) {
// won't happen: no rebinding
}
}
}
} catch (BackingStoreException e) {
// nothing to do in this case
}
}
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
}
}
IEclipsePreferences instancePreferences = JavaModelManager.getJavaModelManager().getInstancePreferences();
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
}
}
instancePreferences.removePreferenceChangeListener(listener);
try {
instancePreferences.put(containerKey, containerString);
if (save) {
try {
instancePreferences.flush();
} catch (BackingStoreException e) {
// nothing to do in this case
}
}
if (rebind) {
rebindClasspathEntries(name, library==null, monitor);
}
} finally {
instancePreferences.addPreferenceChangeListener(listener);
}
}
private static void rebindClasspathEntries(String name, boolean remove, 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];
if (!remove) {
// Previously, containers array only contained a null value. Then, user library classpath entry was first removed
// and then added a while after when post change delta event on .classpath file was fired...
// Unfortunately, in some cases, this event was fired a little bit too late and missed the refresh of Package Explorer
// (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=61872)
// So now, instanciate a new user library classpath container instead which allow to refresh its content immediately
// as there's no classpath entry removal...
// Note that it works because equals(Object) method is not overridden for UserLibraryClasspathContainer.
// If it was, the update wouldn't happen while setting classpath container
// @see javaCore.setClasspathContainer(IPath, IJavaProject[], IClasspathContainer[], IProgressMonitor)
UserLibraryClasspathContainer container= new UserLibraryClasspathContainer(name);
containers[0] = container;
}
JavaCore.setClasspathContainer(containerPath, affected, containers, monitor);
} else {
if (monitor != null) {
monitor.done();
}
}
}
}