blob: 50f1a8f848126ac9cdb9562902129e87045b061c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
*******************************************************************************/
package org.eclipse.dltk.internal.launching;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IPath;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IBuildpathAttribute;
import org.eclipse.dltk.core.IBuildpathContainer;
import org.eclipse.dltk.core.IBuildpathContainerExtension;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IBuiltinModuleProvider;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.internal.core.BuildpathEntry;
import org.eclipse.dltk.launching.DLTKInterpreterManager;
import org.eclipse.dltk.launching.IInterpreterContainerExtension;
import org.eclipse.dltk.launching.IInterpreterContainerExtension2;
import org.eclipse.dltk.launching.IInterpreterContainerExtension3;
import org.eclipse.dltk.launching.IInterpreterInstall;
import org.eclipse.dltk.launching.IInterpreterInstallChangedListener;
import org.eclipse.dltk.launching.LaunchingMessages;
import org.eclipse.dltk.launching.LibraryLocation;
import org.eclipse.dltk.launching.PropertyChangeEvent;
import org.eclipse.dltk.launching.ScriptRuntime;
import org.eclipse.osgi.util.NLS;
/**
* Interpreter Container - resolves a buildpath container to an interpreter
*/
public class InterpreterContainer implements IBuildpathContainerExtension {
/**
* Corresponding interpreter
*/
private IInterpreterInstall fInterpreterInstall = null;
/**
* Container path used to resolve to this interpreter
*/
private IPath fPath = null;
private IScriptProject fProject;
private IBuildpathEntry[] fEntries = null;
/**
* Cache of buildpath entries per Interpreter install. Cleared when a
* Interpreter changes.
*/
private static final Map<IInterpreterInstall, IBuildpathEntry[]> fgBuildpathEntries = new HashMap<>(
10);
private static ChangeListener changeListener = null;
private static class ChangeListener
implements IInterpreterInstallChangedListener {
@Override
public void defaultInterpreterInstallChanged(
IInterpreterInstall previous, IInterpreterInstall current) {
}
@Override
public void interpreterChanged(PropertyChangeEvent event) {
if (event.getSource() != null
&& event.getSource() instanceof IInterpreterInstall) {
remove((IInterpreterInstall) event.getSource());
}
}
@Override
public void interpreterAdded(IInterpreterInstall newInterpreter) {
}
@Override
public void interpreterRemoved(IInterpreterInstall removedInterpreter) {
remove(removedInterpreter);
}
private void remove(IInterpreterInstall interpreter) {
synchronized (fgBuildpathEntries) {
fgBuildpathEntries.remove(interpreter);
}
}
}
/**
* Returns the buildpath entries associated with the given interpreter.
*
* @param interpreter
* @return buildpath entries
*/
public static IBuildpathEntry[] getBuildpathEntries(
IInterpreterInstall interpreter) {
IBuildpathEntry[] entries;
synchronized (fgBuildpathEntries) {
if (changeListener == null) {
// add a listener to clear cached value when an interpreter
// changes or is removed
changeListener = new ChangeListener();
ScriptRuntime
.addInterpreterInstallChangedListener(changeListener);
}
entries = fgBuildpathEntries.get(interpreter);
}
if (entries == null) {
// TODO don't call it simultaneously for the same interpreter
entries = computeBuildpathEntries(interpreter);
synchronized (fgBuildpathEntries) {
fgBuildpathEntries.put(interpreter, entries);
}
}
return entries;
}
/**
* Computes the buildpath entries associated with a interpreter - one entry
* per library.
*
* @param interpreter
* @return buildpath entries
*/
private static IBuildpathEntry[] computeBuildpathEntries(
IInterpreterInstall interpreter) {
LibraryLocation[] libs = ScriptRuntime.getLibraryLocations(interpreter);
List<IBuildpathEntry> entries = new ArrayList<>(libs.length);
Set<IPath> rawEntries = new HashSet<>(libs.length);
for (int i = 0; i < libs.length; i++) {
IPath entryPath = libs[i].getLibraryPath();
if (!entryPath.isEmpty()) {
if (rawEntries.contains(entryPath))
continue;
IBuildpathAttribute[] attributes = new IBuildpathAttribute[0];
ArrayList<IPath> excluded = new ArrayList<>(); // paths to
// exclude
for (int j = 0; j < libs.length; j++) {
IPath otherPath = libs[j].getLibraryPath();
if (otherPath.isEmpty())
continue;
// compare, if it contains some another
if (entryPath.isPrefixOf(otherPath)
&& !otherPath.equals(entryPath)) {
IPath pattern = otherPath.setDevice(null)
.removeFirstSegments(entryPath.segmentCount())
.append("*"); //$NON-NLS-1$
if (!excluded.contains(pattern)) {
excluded.add(pattern);
}
}
}
entries.add(DLTKCore.newLibraryEntry(entryPath,
IBuildpathEntry.NO_ACCESS_RULES, attributes,
BuildpathEntry.INCLUDE_ALL,
excluded.toArray(new IPath[excluded.size()]), false,
true));
rawEntries.add(entryPath);
}
}
// Add builtin entry.
{
entries.add(DLTKCore.newBuiltinEntry(
IBuildpathEntry.BUILTIN_EXTERNAL_ENTRY.append(
interpreter.getInstallLocation().toOSString()),
IBuildpathEntry.NO_ACCESS_RULES, new IBuildpathAttribute[0],
BuildpathEntry.INCLUDE_ALL, BuildpathEntry.EXCLUDE_NONE,
false, true));
}
// Preprocess entries using extension
IInterpreterContainerExtension extension = DLTKInterpreterManager
.getInterpreterContainerExtension(interpreter.getNatureId());
if (extension instanceof IInterpreterContainerExtension2) {
((IInterpreterContainerExtension2) extension)
.preProcessEntries(interpreter, entries);
}
return entries.toArray(new IBuildpathEntry[entries.size()]);
}
/**
* Constructs a interpreter buildpath container on the given interpreter
* install
*
* @param interpreter
* Interpreter install - cannot be <code>null</code>
* @param path
* container path used to resolve this interpreter
*/
public InterpreterContainer(IInterpreterInstall interpreter, IPath path,
IScriptProject project) {
fInterpreterInstall = interpreter;
fPath = path;
fProject = project;
}
/**
* @see IBuildpathContainer#getBuildpathEntries(IScriptProject)
*/
@Override
public IBuildpathEntry[] getBuildpathEntries() {
if (fEntries == null) {
fEntries = computeBuildpathEntries();
}
return fEntries;
}
private IBuildpathEntry[] computeBuildpathEntries() {
IBuildpathEntry[] buildpathEntries = getBuildpathEntries(
fInterpreterInstall);
List<IBuildpathEntry> entries = new ArrayList<>(
buildpathEntries.length);
Collections.addAll(entries, buildpathEntries);
// customize entries for this project
IInterpreterContainerExtension extension = DLTKInterpreterManager
.getInterpreterContainerExtension(fProject);
if (extension != null) {
extension.processEntres(fProject, entries);
}
return entries.toArray(new IBuildpathEntry[entries.size()]);
}
/**
* @see IBuildpathContainer#getDescription()
*/
@Override
public String getDescription() {
final IInterpreterContainerExtension extension = DLTKInterpreterManager
.getInterpreterContainerExtension(fProject);
if (extension instanceof IInterpreterContainerExtension3) {
return ((IInterpreterContainerExtension3) extension)
.getDescription(fInterpreterInstall);
} else {
String tag = fInterpreterInstall.getName();
return NLS.bind(
LaunchingMessages.InterpreterEnvironmentContainer_InterpreterEnvironment_System_Library_1,
tag);
}
}
/**
* @see IBuildpathContainer#getKind()
*/
@Override
public int getKind() {
return IBuildpathContainer.K_DEFAULT_SYSTEM;
}
/**
* @see IBuildpathContainer#getPath()
*/
@Override
public IPath getPath() {
return fPath;
}
@Override
public IBuiltinModuleProvider getBuiltinProvider() {
return fInterpreterInstall;
}
}