blob: 830f7192499e55fbf68d49e72efee8f5c96ca6ff [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 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.dltk.core.environment;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.dltk.compiler.util.Util;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.internal.environment.LocalEnvironment;
import org.eclipse.dltk.internal.core.BuildpathValidation;
import org.eclipse.dltk.internal.core.ExternalScriptProject;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.ScriptProject;
import org.eclipse.dltk.utils.ExecutableOperation;
import org.eclipse.dltk.utils.ExecutionContexts;
import org.eclipse.dltk.utils.LazyExtensionManager;
import org.eclipse.dltk.utils.LazyExtensionManager.Descriptor;
import org.eclipse.osgi.util.NLS;
public final class EnvironmentManager {
private static final QualifiedName PROJECT_ENVIRONMENT = new QualifiedName(
DLTKCore.PLUGIN_ID, "environment"); //$NON-NLS-1$
private static final String ENVIRONMENT_EXTENSION = DLTKCore.PLUGIN_ID
+ ".environment"; //$NON-NLS-1$
private static class EnvironmentProviderManager
extends LazyExtensionManager<IEnvironmentProvider> {
private static class EnvironmentProviderDesc
extends Descriptor<IEnvironmentProvider> {
private String id;
private int priority;
public EnvironmentProviderDesc(EnvironmentProviderManager manager,
IConfigurationElement configurationElement) {
super(manager, configurationElement);
this.priority = parseInt(
configurationElement.getAttribute("priority")); //$NON-NLS-1$
this.id = configurationElement.getAttribute("id"); //$NON-NLS-1$
}
public String getId() {
return id;
}
}
public EnvironmentProviderManager() {
super(ENVIRONMENT_EXTENSION);
}
@Override
protected Descriptor<IEnvironmentProvider> createDescriptor(
IConfigurationElement confElement) {
return new EnvironmentProviderDesc(this, confElement);
}
@Override
protected void initializeDescriptors(
List<Descriptor<IEnvironmentProvider>> descriptors) {
Collections.sort(descriptors, (arg0, arg1) -> {
EnvironmentProviderDesc d1 = (EnvironmentProviderDesc) arg0;
EnvironmentProviderDesc d2 = (EnvironmentProviderDesc) arg1;
return d1.priority - d2.priority;
});
}
}
private static final EnvironmentProviderManager manager = new EnvironmentProviderManager();
private static ListenerList<IEnvironmentChangedListener> listeners = new ListenerList<>();
private static final Map<IProject, IEnvironment> environmentCache = new HashMap<>();
private static IResourceChangeListener resourceListener = event -> {
int eventType = event.getType();
IResource resource = event.getResource();
switch (eventType) {
case IResourceChangeEvent.PRE_DELETE:
if (resource.getType() == IResource.PROJECT) {
synchronized (environmentCache) {
environmentCache.remove(resource);
}
}
return;
}
};
private EnvironmentManager() {
ResourcesPlugin.getWorkspace()
.addResourceChangeListener(resourceListener);
}
/**
* Returns {@link IEnvironmentProvider} with the specified
* <code>providerId</code> or <code>null</code>.
*
* @since 2.0
*/
public static IEnvironmentProvider getEnvironmentProvider(
String providerId) {
if (providerId != null) {
for (Descriptor<IEnvironmentProvider> descriptor : manager
.getDescriptors()) {
final EnvironmentProviderManager.EnvironmentProviderDesc desc = (EnvironmentProviderManager.EnvironmentProviderDesc) descriptor;
if (providerId.equals(desc.getId())) {
return desc.get();
}
}
}
return null;
}
public static IEnvironment getEnvironment(IModelElement element) {
if (element == null) {
return null;
}
IResource res = element.getResource();
if (res != null && res.getType() != IResource.PROJECT) {
URI locationURI = res.getLocationURI();
if (locationURI != null) {
for (IEnvironmentProvider provider : manager) {
waitInitialized(provider);
IEnvironment env = provider.getEnvironment(locationURI);
if (env != null) {
return env;
}
}
}
}
IScriptProject scriptProject = element.getScriptProject();
if (scriptProject == null) {
return null;
}
IProject project = scriptProject.getProject();
if (project == null)
return null;
return getEnvironment(project);
}
public static IEnvironment getEnvironment(IResource element) {
if (element == null) {
return null;
}
if (element.getType() != IResource.PROJECT) {
URI locationURI = element.getLocationURI();
if (locationURI != null) {
for (IEnvironmentProvider provider : manager) {
waitInitialized(provider);
IEnvironment env = provider.getEnvironment(locationURI);
if (env != null) {
return env;
}
}
}
}
IProject project = element.getProject();
if (project == null)
return null;
return getEnvironment(project);
}
public static IEnvironment getEnvironment(IProject project) {
synchronized (environmentCache) {
IEnvironment environment = environmentCache.get(project);
if (environment == null) {
if (!ExternalScriptProject.EXTERNAL_PROJECT_NAME
.equals(project.getName())) {
try {
final String environmentId = project
.getPersistentProperty(PROJECT_ENVIRONMENT);
if (environmentId != null) {
environment = getEnvironmentById(environmentId);
}
} catch (CoreException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
if (environment == null) {
environment = detectEnvironment(project);
}
environmentCache.put(project, environment);
}
return environment;
}
}
/**
* @since 2.0
*/
public static IEnvironment detectEnvironment(IProject project) {
for (IEnvironmentProvider provider : manager) {
waitInitialized(provider);
IEnvironment environment = provider.getProjectEnvironment(project);
if (environment != null) {
return environment;
}
}
return null;
}
public static String getEnvironmentId(IProject project) {
return getEnvironmentId(project, true);
}
public static String getEnvironmentId(IProject project,
boolean detectAutomatically) {
try {
final String environmentId = project
.getPersistentProperty(PROJECT_ENVIRONMENT);
if (environmentId != null) {
verifyEnvironmentCache(project, environmentId);
return environmentId;
}
} catch (CoreException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
if (detectAutomatically) {
final IEnvironment environment = detectEnvironment(project);
return environment != null ? environment.getId() : null;
}
return null;
}
/**
* @param project
* @param environmentId
*/
private static void verifyEnvironmentCache(IProject project,
String environmentId) {
if (environmentId != null) {
synchronized (environmentCache) {
final IEnvironment environment = environmentCache.get(project);
if (environment != null
&& !environmentId.equals(environment.getId())) {
environmentCache.remove(project);
}
}
}
}
public static void setEnvironmentId(IProject project, String environmentId)
throws CoreException {
setEnvironmentId(project, environmentId, true);
}
public static void setEnvironmentId(IProject project, String environmentId,
boolean refresh) throws CoreException {
synchronized (environmentCache) {
environmentCache.remove(project);
}
// TODO check project.getDescription.getLocationURI() scheme ?
project.setPersistentProperty(PROJECT_ENVIRONMENT, environmentId);
if (refresh) {
final IScriptProject scriptProject = DLTKCore.create(project);
if (scriptProject != null) {
DLTKCore.refreshBuildpathContainers(scriptProject);
new BuildpathValidation((ScriptProject) scriptProject)
.validate();
}
}
}
/**
* @since 2.0
*/
public static void refreshBuildpathContainersForMixedProjects(
IProgressMonitor monitor) {
try {
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
final IScriptProject[] projects = ModelManager.getModelManager()
.getModel().getScriptProjects();
subMonitor.worked(10);
subMonitor = subMonitor.newChild(90);
subMonitor.beginTask(Util.EMPTY_STRING, projects.length);
for (int i = 0; i < projects.length; i++) {
final IProject project = projects[i].getProject();
final SubMonitor projectMonitor = subMonitor.newChild(1);
projectMonitor.beginTask(NLS.bind(
Messages.EnvironmentManager_RefreshProjectInterpreter,
project.getName()), 2);
final String environmentId = project
.getPersistentProperty(PROJECT_ENVIRONMENT);
if (environmentId != null) {
verifyEnvironmentCache(project, environmentId);
DLTKCore.refreshBuildpathContainers(projects[i]);
projectMonitor.worked(1);
new BuildpathValidation((ScriptProject) projects[i])
.validate();
projectMonitor.worked(1);
}
}
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
} catch (CoreException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
} finally {
monitor.done();
}
}
public static void setEnvironment(IProject project,
IEnvironment environment) throws CoreException {
setEnvironmentId(project,
environment != null ? environment.getId() : null);
}
public static IEnvironment[] getEnvironments() {
return getEnvironments(true);
}
public static IEnvironment[] getEnvironments(boolean allowWait) {
List<IEnvironment> envList = new ArrayList<>();
for (IEnvironmentProvider provider : manager) {
if (allowWait) {
waitInitialized(provider);
}
envList.addAll(Arrays.asList(provider.getEnvironments()));
}
IEnvironment[] environments = new IEnvironment[envList.size()];
envList.toArray(environments);
return environments;
}
public static boolean isLocal(IEnvironment env) {
return LocalEnvironment.ENVIRONMENT_ID.equals(env.getId());
}
public static IEnvironment getEnvironmentById(String envId) {
for (IEnvironmentProvider provider : manager) {
waitInitialized(provider);
IEnvironment env = provider.getEnvironment(envId);
if (env != null) {
return env;
}
}
return null;
}
public static void addEnvironmentChangedListener(
IEnvironmentChangedListener listener) {
listeners.add(listener);
}
public static void removeEnvironmentChangedListener(
IEnvironmentChangedListener listener) {
listeners.remove(listener);
}
public static void environmentAdded(IEnvironment environment) {
for (IEnvironmentChangedListener listener : listeners) {
listener.environmentAdded(environment);
}
fireEnvirontmentChange();
}
public static void environmentRemoved(IEnvironment environment) {
for (IEnvironmentChangedListener listener : listeners) {
listener.environmentRemoved(environment);
}
fireEnvirontmentChange();
}
public static void environmentChanged(IEnvironment environment) {
for (IEnvironmentChangedListener listener : listeners) {
listener.environmentChanged(environment);
}
fireEnvirontmentChange();
}
public static void fireEnvirontmentChange() {
for (IEnvironmentChangedListener listener : listeners) {
listener.environmentsModified();
}
}
public static IEnvironment getLocalEnvironment() {
return getEnvironmentById(LocalEnvironment.ENVIRONMENT_ID);
}
/**
* Tests if all providers are initialized.
*/
public static boolean isInitialized() {
for (IEnvironmentProvider provider : manager) {
if (!provider.isInitialized()) {
return false;
}
}
return true;
}
private static void waitInitialized(final IEnvironmentProvider provider) {
if (provider.isInitialized()) {
return;
}
ExecutionContexts.getManager()
.executeInBackground(new ExecutableOperation(
Messages.EnvironmentManager_initializingOperationName) {
@Override
public void execute(IProgressMonitor monitor) {
monitor.beginTask(Util.EMPTY_STRING, 1);
monitor.setTaskName(NLS.bind(
Messages.EnvironmentManager_initializingTaskName,
provider.getProviderName()));
provider.waitInitialized();
monitor.worked(1);
monitor.done();
}
});
}
/**
* Waits until all structures are initialized.
*/
public static void waitInitialized() {
waitInitialized((IProgressMonitor) null);
}
public static void waitInitialized(IProgressMonitor monitor) {
if (monitor != null) {
monitor.beginTask(Util.EMPTY_STRING,
manager.getDescriptors().length);
}
for (IEnvironmentProvider provider : manager) {
if (monitor != null) {
monitor.setTaskName(NLS.bind(
Messages.EnvironmentManager_initializingTaskName,
provider.getProviderName()));
}
provider.waitInitialized();
if (monitor != null) {
monitor.worked(1);
}
}
if (monitor != null) {
monitor.done();
}
}
private static class LocationResolverManager
extends LazyExtensionManager<IEnvironmentLocationResolver> {
private static class Desc
extends Descriptor<IEnvironmentLocationResolver> {
private int priority;
public Desc(LocationResolverManager manager,
IConfigurationElement configurationElement) {
super(manager, configurationElement);
this.priority = parseInt(
configurationElement.getAttribute("priority")); //$NON-NLS-1$
}
}
private static final String LOCATION_RESOLVER_EXTENSION = DLTKCore.PLUGIN_ID
+ ".locationResolver"; //$NON-NLS-1$
public LocationResolverManager() {
super(LOCATION_RESOLVER_EXTENSION);
}
@Override
protected Descriptor<IEnvironmentLocationResolver> createDescriptor(
IConfigurationElement confElement) {
return new Desc(this, confElement);
}
@Override
protected void initializeDescriptors(
List<Descriptor<IEnvironmentLocationResolver>> descriptors) {
Collections.sort(descriptors, (arg0, arg1) -> {
Desc d1 = (Desc) arg0;
Desc d2 = (Desc) arg1;
return d1.priority - d2.priority;
});
}
}
private static LocationResolverManager resolverManager = null;
/**
* @since 2.0
*/
public static URI[] resolve(URI location) {
if (resolverManager == null) {
resolverManager = new LocationResolverManager();
}
final List<URI> result = new ArrayList<>();
for (IEnvironmentLocationResolver resolver : resolverManager) {
final URI[] resolved = resolver.resolve(location);
if (resolved.length != 0) {
result.addAll(Arrays.asList(resolved));
}
}
return result.toArray(new URI[result.size()]);
}
}