blob: 15042404b0bd8fa5652bd6a005f5d269776dec5a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2016 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
*
*******************************************************************************/
package org.eclipse.dltk.launching;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IBuildpathAttribute;
import org.eclipse.dltk.core.IBuildpathContainer;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IScriptModel;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.environment.EnvironmentManager;
import org.eclipse.dltk.core.environment.EnvironmentPathUtils;
import org.eclipse.dltk.core.environment.IEnvironment;
import org.eclipse.dltk.core.environment.IFileHandle;
import org.eclipse.dltk.core.internal.environment.LocalEnvironment;
import org.eclipse.dltk.internal.launching.CompositeId;
import org.eclipse.dltk.internal.launching.DLTKLaunchingPlugin;
import org.eclipse.dltk.internal.launching.DefaultEntryResolver;
import org.eclipse.dltk.internal.launching.DefaultProjectBuildpathEntry;
import org.eclipse.dltk.internal.launching.InterpreterContainerInitializer;
import org.eclipse.dltk.internal.launching.InterpreterDefinitionsContainer;
import org.eclipse.dltk.internal.launching.InterpreterListener;
import org.eclipse.dltk.internal.launching.RuntimeBuildpathEntry;
import org.eclipse.dltk.internal.launching.RuntimeBuildpathEntryResolver;
import org.eclipse.dltk.internal.launching.RuntimeBuildpathProvider;
import org.eclipse.dltk.internal.launching.ScriptSourceLookupUtil;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.service.prefs.BackingStoreException;
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;
import com.ibm.icu.text.MessageFormat;
/**
* The central access point for launching support. This class manages the
* registered interpreters types contributed through the
* <code>"org.eclipse.dltk.launching.interpreterType"</code> extension point. As
* well, this class provides interpreter install change notification, and
* computes buildpaths and source lookup paths for launch configurations.
* <p>
* This class provides static methods only; it is not intended to be
* instantiated or subclassed by clients.
* </p>
*/
public final class ScriptRuntime {
/**
* Classpath container used for a project's InterpreterEnvironment (value
* <code>"org.eclipse.dltk.launching.INTERPRETER_CONTAINER"</code> ). A
* container is resolved in the context of a specific Script project, to one
* or more system libraries contained in a InterpreterEnvironment. The
* container can have zero or two path segments following the container
* name. When no segments follow the container name, the workspace default
* InterpreterEnvironment is used to build a project. Otherwise the segments
* identify a specific InterpreterEnvironment used to build a project:
* <ol>
* <li>Interpreter Install Type Identifier - identifies the type of
* InterpreterEnvironment used to build the project. For example, the
* standard Interpreter.</li>
* <li>Interpreter Install Name - a user defined name that identifies that a
* specific Interpreter of the above kind. For example,
* <code>IBM 1.3.1</code>. This information is shared in a projects
* buildpath file, so teams must agree on InterpreterEnvironment naming
* conventions.</li>
* </ol>
* <p>
* The path may also identify an execution environment as follows:
* <ol>
* <li>Execution environment extension point name (value
* <code>executionEnvironments</code>)</li>
* <li>Identifier of a contributed execution environment</li>
* </ol>
* </p>
*
*/
public static final String INTERPRETER_CONTAINER = DLTKLaunchingPlugin
.getUniqueIdentifier() + ".INTERPRETER_CONTAINER"; //$NON-NLS-1$
/**
* Simple identifier constant (value
* <code>"runtimeBuildpathEntryResolvers"</code>) for the runtime buildpath
* entry resolvers extension point.
*/
public static final String EXTENSION_POINT_RUNTIME_BUILDPATH_ENTRY_RESOLVERS = "runtimeBuildpathEntryResolvers"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"buildpathProviders"</code>) for
* the runtime buildpath providers extension point.
*/
public static final String EXTENSION_POINT_RUNTIME_BUILDPATH_PROVIDERS = "buildpathProviders"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"interpreterInstalls"</code>) for
* the interpreters installs extension point.
*/
public static final String EXTENSION_POINT_INTERPRETER_INSTALLS = "interpreterInstalls"; //$NON-NLS-1$
/**
* A status code indicating that a interpreter could not be resolved for a
* project. When a interpreter cannot be resolved for a project by this
* plug-in's container initializer, an exception is thrown with this status
* code. A status handler may be registered for this status code. The
* <code>source</code> object provided to the status handler is the script
* project for which the path could not be resolved. The status handler must
* return an <code>IInterpreterInstall</code> or <code>null</code>. The
* container resolver will re-set the project's buildpath if required.
*/
public static final int ERR_UNABLE_TO_RESOLVE_INTERPRETER = 160;
/**
* Preference key for launch/connect timeout. Interpreter Runners should
* honor this timeout value when attempting to launch and connect to a
* debugger. The value is an int, indicating a number of milliseconds.
*/
public static final String PREF_CONNECT_TIMEOUT = DLTKLaunchingPlugin
.getUniqueIdentifier() + ".PREF_CONNECT_TIMEOUT"; //$NON-NLS-1$
/**
* Preference key for the String of XML that defines all installed
* Interpreters.
*/
public static final String PREF_INTERPRETER_XML = DLTKLaunchingPlugin
.getUniqueIdentifier() + ".PREF_INTERPRETER_XML"; //$NON-NLS-1$
/**
* Default launch/connect timeout (ms).
*/
public static final int DEF_CONNECT_TIMEOUT = 20000;
/**
* Attribute key for a buildpath attribute referencing a list of shared
* libraries that should appear on the
* <code>-Dinterpreter.library.path</code> system property.
* <p>
* The factory methods <code>newLibraryPathsAttribute(String[])</code> and
* <code>getLibraryPaths(IBuildpathAttribute)</code> should be used to
* encode and decode the attribute value.
* </p>
* <p>
* Each string is used to create an <code>IPath</code> using the constructor
* <code>Path(String)</code>, and may contain <code>IStringVariable</code>
* 's. Variable substitution is performed on the string prior to
* constructing a path from the string. If the resulting <code>IPath</code>
* is a relative path, it is interpreted as relative to the workspace
* location. If the path is absolute, it is interpreted as an absolute path
* in the local file system.
* </p>
*/
public static final String BUILDPATH_ATTR_LIBRARY_PATH_ENTRY = DLTKLaunchingPlugin
.getUniqueIdentifier() + ".CLASSPATH_ATTR_LIBRARY_PATH_ENTRY"; //$NON-NLS-1$
// lock for interpreter initialization
private static Object fgInterpreterLock = new Object();
private static boolean fgInitializingInterpreters = false;
private static IInterpreterInstallType[] fgInterpreterTypes = null;
/**
* This class is deprecated in 5.0 and should not be used outside of DLTK
* core plugins. The affected APIs will be updated, e.g. see
* {@link ScriptRuntime#getDefaultInterpreterInstall(String, IEnvironment)}
*/
@Deprecated
public static class DefaultInterpreterEntry {
private final String nature;
private final String environment;
public DefaultInterpreterEntry(String nature, String environment) {
this.nature = nature;
this.environment = environment;
}
public String getNature() {
return nature;
}
public String getEnvironment() {
return environment;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((environment == null) ? 0 : environment.hashCode());
result = prime * result
+ ((nature == null) ? 0 : nature.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DefaultInterpreterEntry other = (DefaultInterpreterEntry) obj;
if (environment == null) {
if (other.environment != null)
return false;
} else if (!environment.equals(other.environment))
return false;
if (nature == null) {
if (other.nature != null)
return false;
} else if (!nature.equals(other.nature))
return false;
return true;
}
@Override
public String toString() {
return nature + "/" + environment; //$NON-NLS-1$
}
}
/**
* Contain association of default interpreter entries to interprter
* identifiers.
*/
private static Map<DefaultInterpreterEntry, String> fgDefaultInterpreterId = new HashMap<>();
/**
* Contain DefaultInterpreterEntry entry assocications
*/
private static Map<DefaultInterpreterEntry, String> fgDefaultInterpreterConnectorId = new HashMap<>();
/**
* Resolvers keyed by variable name, container id, and runtime buildpath
* entry id.
*/
private static Map<String, IRuntimeBuildpathEntryResolver> fgContainerResolvers = null;
private static Map<String, IRuntimeBuildpathEntryResolver> fgRuntimeBuildpathEntryResolvers = null;
/**
* Path providers keyed by id
*/
private static Map<String, IRuntimeBuildpathProvider> fgPathProviders = null;
/**
* Default buildpath and source path providers.
*/
private static IRuntimeBuildpathProvider fgDefaultBuildpathProvider = new StandardBuildpathProvider();
private static IRuntimeBuildpathProvider fgDefaultSourcepathProvider = new StandardSourcepathProvider();
/**
* Interpreter change listeners
*/
private static ListenerList fgInterpreterListeners = new ListenerList(
ListenerList.IDENTITY);
/**
* Cache of already resolved projects in container entries. Used to avoid
* cycles in project dependencies when resolving buildpath container
* entries. Counters used to know when entering/exiting to clear cache
*/
private static ThreadLocal<List<IScriptProject>> fgProjects = new ThreadLocal<>();
private static ThreadLocal<Integer> fgEntryCount = new ThreadLocal<>();
/**
* Set of IDs of Interpreters contributed via InterpreterInstalls extension
* point.
*/
private static Set<String> fgContributedInterpreters = new HashSet<>();
/**
* This class contains only static methods, and is not intended to be
* instantiated.
*/
private ScriptRuntime() {
}
/**
* Initializes interpreter type extensions.
*/
private static void initializeInterpreterTypeExtensions() {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry()
.getExtensionPoint(DLTKLaunchingPlugin.PLUGIN_ID,
"interpreterInstallTypes"); //$NON-NLS-1$
IConfigurationElement[] configs = extensionPoint
.getConfigurationElements();
MultiStatus status = new MultiStatus(
DLTKLaunchingPlugin.getUniqueIdentifier(), IStatus.OK,
LaunchingMessages.ScriptRuntime_exceptionsOccurred, null);
fgInterpreterTypes = new IInterpreterInstallType[configs.length];
for (int i = 0; i < configs.length; i++) {
try {
IInterpreterInstallType installType = (IInterpreterInstallType) configs[i]
.createExecutableExtension("class"); //$NON-NLS-1$
fgInterpreterTypes[i] = installType;
} catch (CoreException e) {
status.add(e.getStatus());
}
}
if (!status.isOK()) {
// only happens on a CoreException
DLTKLaunchingPlugin.log(status);
// cleanup null entries in fgInterpreterTypes
List<IInterpreterInstallType> temp = new ArrayList<>(
fgInterpreterTypes.length);
for (int i = 0; i < fgInterpreterTypes.length; i++) {
if (fgInterpreterTypes[i] != null) {
temp.add(fgInterpreterTypes[i]);
}
fgInterpreterTypes = temp
.toArray(new IInterpreterInstallType[temp.size()]);
}
}
}
private static String getNatureFromProject(IScriptProject project) {
try {
IDLTKLanguageToolkit toolkit = DLTKLanguageManager
.getLanguageToolkit(project);
if (toolkit != null)
return toolkit.getNatureId();
} catch (Exception e) {
DLTKLaunchingPlugin.log(e);
}
return null;
}
private static String getEnvironmentFromProject(IScriptProject project) {
return EnvironmentManager.getEnvironmentId(project.getProject());
}
/**
* Returns the interpreter assigned to build the given script project. The
* project must exist. The interpreter assigned to a project is determined
* from its build path.
*
* @param project
* the project to retrieve the interpreter from
* @return the interpreter instance that is assigned to build the given
* script project Returns <code>null</code> if no interp. is
* referenced on the project's build path.
* @throws CoreException
* if unable to determine the project's interpreter install
*/
public static IInterpreterInstall getInterpreterInstall(
IScriptProject project) throws CoreException {
// check the buildpath
IInterpreterInstall interpreter = null;
IBuildpathEntry[] buildpath = project.getRawBuildpath();
for (int i = 0; i < buildpath.length; i++) {
IBuildpathEntry entry = buildpath[i];
switch (entry.getEntryKind()) {
case IBuildpathEntry.BPE_CONTAINER:
IRuntimeBuildpathEntryResolver resolver = getContainerResolver(
entry.getPath().segment(0));
if (resolver != null) {
interpreter = resolver.resolveInterpreterInstall(
getNatureFromProject(project),
getEnvironmentFromProject(project), entry);
}
break;
}
if (interpreter != null) {
return interpreter;
}
}
return null;
}
/**
* Returns the interpreter install type with the given unique id.
*
* @param id
* the interpreter install type unique id
* @return The interpreter install type for the given id, or
* <code>null</code> if no interpreter install type with the given
* id is registered.
*/
public static IInterpreterInstallType getInterpreterInstallType(String id) {
IInterpreterInstallType[] installTypes = getInterpreterInstallTypes();
if (installTypes == null) {
return null;
}
for (int i = 0; i < installTypes.length; i++) {
IInterpreterInstallType type = installTypes[i];
if (type != null && type.getId().equals(id)) {
return type;
}
}
return null;
}
/**
* Sets a Interpreter as the system-wide default Interpreter, and notifies
* registered Interpreter install change listeners of the change.
*
* @param interpreter
* The Interpreter to make the default. May be <code>null</code>
* to clear the default.
* @param monitor
* progress monitor or <code>null</code>
*/
public static void setDefaultInterpreterInstall(
IInterpreterInstall interpreter, IProgressMonitor monitor)
throws CoreException {
setDefaultInterpreterInstall(interpreter, monitor, true);
}
/**
* Sets a Interpreter as the nature default Interpreter, and notifies
* registered Interpreter install change listeners of the change.
*
* @param interpreter
* The Interpreter to make the default. May be <code>null</code>
* to clear the default.
* @param monitor
* progress monitor or <code>null</code>
* @param savePreference
* If <code>true</code>, update workbench preferences to reflect
* the new default Interpreter.
*
*/
public static void setDefaultInterpreterInstall(
IInterpreterInstall interpreter, IProgressMonitor monitor,
boolean savePreference) throws CoreException {
final IEnvironment env = interpreter.getEnvironment();
if (env == null) {
return;
}
final String environmentId = env.getId();
IInterpreterInstall previous = null;
String nature = interpreter.getInterpreterInstallType().getNatureId();
DefaultInterpreterEntry defaultInterpreterID = new DefaultInterpreterEntry(
nature, environmentId);
if (fgDefaultInterpreterId.get(defaultInterpreterID) != null) {
previous = getInterpreterFromCompositeId(
fgDefaultInterpreterId.get(defaultInterpreterID));
}
fgDefaultInterpreterId.put(defaultInterpreterID,
getCompositeIdFromInterpreter(interpreter));
if (savePreference) {
saveInterpreterConfiguration();
}
IInterpreterInstall current = null;
if (fgDefaultInterpreterId.get(defaultInterpreterID) != null) {
current = getInterpreterFromCompositeId(
fgDefaultInterpreterId.get(defaultInterpreterID));
}
if (previous != current) {
notifyDefaultInterpreterChanged(previous, current);
}
}
/**
* Return the default interpreter set with
* <code>setDefaultInterpreter()</code> for the specified
* <code>natureId</code> and <code>environment</code> or <code>null</code>.
*/
public static IInterpreterInstall getDefaultInterpreterInstall(
String natureId, IEnvironment environment) {
return getDefaultInterpreterInstall(new DefaultInterpreterEntry(
natureId, environment != null ? environment.getId()
: LocalEnvironment.ENVIRONMENT_ID));
}
/**
* Return the default Interpreter set with
* <code>setDefaultInterpreter()</code>.
*
* @return Returns the default Interpreter.
*/
@Deprecated
public static IInterpreterInstall getDefaultInterpreterInstall(
DefaultInterpreterEntry entry) {
IInterpreterInstall install = getInterpreterFromCompositeId(
getDefaultInterpreterId(entry));
if (install != null && install.getInstallLocation().exists()) {
return install;
}
// if the default interp. goes missing, re-detect
if (install != null) {
// install.getInterpreterInstallType().disposeInterpreterInstall(
// install.getId());
}
if (!hasInterpreterInstalls(entry.getNature(),
entry.getEnvironment())) {
/*
* If there are no any interpreters for the specified nature and
* environment - just return null and avoid re-initialization.
* Generally speaking user should not be forced to configure every
* interpreter for every host.
*/
return null;
}
synchronized (fgInterpreterLock) {
// fgDefaultInterpreterId = null;
fgDefaultInterpreterId.clear();
fgInterpreterTypes = null;
initializeInterpreters();
}
return getInterpreterFromCompositeId(getDefaultInterpreterId(entry));
}
/**
* Tests if there are any interpreters for the specified nature and host.
*
* @param natureId
* @param environmentId
* @return
*/
private static boolean hasInterpreterInstalls(String natureId,
String environmentId) {
if (environmentId == null) {
return false;
}
for (IInterpreterInstallType type : getInterpreterInstallTypes(
natureId)) {
for (IInterpreterInstall install : type.getInterpreterInstalls()) {
if (environmentId.equals(install.getEnvironmentId())) {
return true;
}
}
}
return false;
}
/**
* Returns the list of registered interpreter types. Interpreter types are
* registered via <code>"org.eclipse.dltk.launching.interpreterTypes"</code>
* extension point. Returns an empty list if there are no registered
* interpreter types.
*
* @return the list of registered Interpreter types
*/
public static IInterpreterInstallType[] getInterpreterInstallTypes() {
initializeInterpreters();
return fgInterpreterTypes;
}
/**
* Returns FILTERED list of registered interpreter types. Interpreter types
* are registered via
* <code>"org.eclipse.dltk.launching.interpreterTypes"</code> extension
* point. Returns an empty list if there are no registered interpreter
* types.
*
* @return the list of registered Interpreter types
*/
public static IInterpreterInstallType[] getInterpreterInstallTypes(
String nature) {
List<IInterpreterInstallType> res = new ArrayList<>();
synchronized (fgInterpreterLock) {
initializeInterpreters();
for (int i = 0; i < fgInterpreterTypes.length; i++) {
IInterpreterInstallType t = fgInterpreterTypes[i];
if (t.getNatureId().equals(nature))
res.add(t);
}
}
return res.toArray(new IInterpreterInstallType[res.size()]);
}
public static DefaultInterpreterEntry[] getDefaultInterpreterIDs() {
Set<DefaultInterpreterEntry> set = fgDefaultInterpreterId.keySet();
return set.toArray(new DefaultInterpreterEntry[set.size()]);
}
private static String getDefaultInterpreterId(
DefaultInterpreterEntry entry) {
initializeInterpreters();
return fgDefaultInterpreterId.get(entry);
}
private static String getDefaultInterpreterConnectorId(
DefaultInterpreterEntry entry) {
initializeInterpreters();
return fgDefaultInterpreterConnectorId.get(entry);
}
/**
* Returns a String that uniquely identifies the specified Interpreter
* across all Interpreter types.
*
* @param interpreter
* the instance of IInterpreterInstallType to be identified
*
*
*/
public static String getCompositeIdFromInterpreter(
IInterpreterInstall interpreter) {
if (interpreter == null) {
return null;
}
IInterpreterInstallType interpreterType = interpreter
.getInterpreterInstallType();
String typeID = interpreterType.getId();
CompositeId id = new CompositeId(
new String[] { typeID, interpreter.getId() });
return id.toString();
}
/**
* Return the Interpreter corresponding to the specified composite Id. The
* id uniquely identifies a Interpreter across all Interpreter types.
*
* @param idString
* the composite id that specifies an instance of
* IInterpreterInstall
*
*
*/
public static IInterpreterInstall getInterpreterFromCompositeId(
String idString) {
if (idString == null || idString.length() == 0) {
return null;
}
CompositeId id = CompositeId.fromString(idString);
if (id.getPartCount() == 2) {
IInterpreterInstallType InterpreterType = getInterpreterInstallType(
id.get(0));
if (InterpreterType != null) {
return InterpreterType.findInterpreterInstall(id.get(1));
}
}
return null;
}
/**
* Return the <code>IScriptProject</code> referenced in the specified
* configuration or <code>null</code> if none.
*
* @exception CoreException
* if the referenced Script project does not exist
*
*/
public static IScriptProject getScriptProject(
ILaunchConfiguration configuration) throws CoreException {
String projectName = configuration.getAttribute(
ScriptLaunchConfigurationConstants.ATTR_PROJECT_NAME,
(String) null);
if ((projectName == null) || (projectName.trim().length() < 1)) {
return null;
}
IScriptProject scriptProject = getScriptModel()
.getScriptProject(projectName);
if (scriptProject != null && scriptProject.getProject().exists()
&& !scriptProject.getProject().isOpen()) {
abort(MessageFormat.format(LaunchingMessages.ScriptRuntime_28,
configuration.getName(), projectName),
ScriptLaunchConfigurationConstants.ERR_PROJECT_CLOSED,
null);
}
if ((scriptProject == null) || !scriptProject.exists()) {
abort(MessageFormat.format(
LaunchingMessages.ScriptRuntime_Launch_configuration__0__references_non_existing_project__1___1,
configuration.getName(), projectName),
ScriptLaunchConfigurationConstants.ERR_NOT_A_SCRIPT_PROJECT,
null);
}
return scriptProject;
}
/**
* Convenience method to get the script model.
*/
private static IScriptModel getScriptModel() {
return DLTKCore.create(ResourcesPlugin.getWorkspace().getRoot());
}
/**
* Returns the Interpreter install for the given launch configuration. The
* Interpreter install is determined in the following prioritized way:
* <ol>
* <li>The Interpreter install is explicitly specified on the launch
* configuration via the <code>ATTR_CONTAINER_PATH</code> attribute.</li>
* <li>If no explicit Interpreter install is specified, the Interpreter
* install associated with the launch configuration's project is
* returned.</li>
* <li>If no project is specified, or the project does not specify a custom
* Interpreter install, the workspace default Interpreter install is
* returned.</li>
* </ol>
*
* @param configuration
* launch configuration
* @return interpreter install
* @exception CoreException
* if unable to compute a Interpreter install
*
*/
public static IInterpreterInstall computeInterpreterInstall(
ILaunchConfiguration configuration) throws CoreException {
// get field ATTR_NATURE from launch configuration
String nature = configuration.getAttribute(
ScriptLaunchConfigurationConstants.ATTR_SCRIPT_NATURE,
(String) null);
String containerPath = configuration.getAttribute(
ScriptLaunchConfigurationConstants.ATTR_CONTAINER_PATH,
(String) null);
IScriptProject proj = getScriptProject(configuration);
String environment = getEnvironmentFromProject(proj);
if (containerPath == null) {
if (proj != null) {
IInterpreterInstall install = getInterpreterInstall(proj);
if (install != null) {
return install;
}
if (nature == null) {
IDLTKLanguageToolkit tk = DLTKLanguageManager
.getLanguageToolkit(proj);
nature = tk.getNatureId();
}
}
} else {
IPath interpreterPath = Path.fromPortableString(containerPath);
IBuildpathEntry entry = DLTKCore.newContainerEntry(interpreterPath);
IRuntimeBuildpathEntryResolver2 resolver = getContainerResolver(
interpreterPath.segment(0));
if (resolver != null) {
return resolver.resolveInterpreterInstall(nature, environment,
entry);
} else {
resolver = getContainerResolver(interpreterPath.segment(0));
if (resolver != null) {
return resolver.resolveInterpreterInstall(nature,
environment, entry);
}
}
}
if (nature == null) {
abort(LaunchingMessages.ScriptRuntime_notDefaultInterpreter,
ScriptLaunchConfigurationConstants.ERR_NO_DEFAULT_INTERPRETER_INSTALL,
null);
}
// OR extract project nature from it's name (what about several
// natures?)
DefaultInterpreterEntry entry = new DefaultInterpreterEntry(nature,
environment);
IInterpreterInstall res = getDefaultInterpreterInstall(entry);
if (res == null) {
abort(LaunchingMessages.ScriptRuntime_notDefaultInterpreter,
ScriptLaunchConfigurationConstants.ERR_NO_DEFAULT_INTERPRETER_INSTALL,
null);
}
return res;
}
/**
* Throws a core exception with an internal error status.
*
* @param message
* the status message
* @param exception
* lower level exception associated with the error, or
* <code>null</code> if none
*/
private static CoreException abort(String message, Throwable exception)
throws CoreException {
throw abort(message,
ScriptLaunchConfigurationConstants.ERR_INTERNAL_ERROR,
exception);
}
/**
* Throws a core exception with an internal error status.
*
* @param message
* the status message
* @param code
* status code
* @param exception
* lower level exception associated with the
*
* error, or <code>null</code> if none
*/
private static CoreException abort(String message, int code,
Throwable exception) throws CoreException {
throw new CoreException(new Status(IStatus.ERROR,
DLTKLaunchingPlugin.PLUGIN_ID, code, message, exception));
}
/**
* Saves the Interpreter configuration information to the preferences. This
* includes the following information:
* <ul>
* <li>The list of all defined IInterpreterInstall instances.</li>
* <li>The default Interpreter</li>
* <ul>
* This state will be read again upon first access to Interpreter
* configuration information.
*/
public static void saveInterpreterConfiguration() throws CoreException {
if (fgInterpreterTypes == null) {
// if the Interpreter types have not been instantiated, there can be
// no changes.
return;
}
try {
String xml = getInterpretersAsXML();
getPreferences().put(PREF_INTERPRETER_XML, xml);
savePreferences();
} catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR,
DLTKLaunchingPlugin.getUniqueIdentifier(), IStatus.ERROR,
LaunchingMessages.ScriptRuntime_exceptionsOccurred, e));
} catch (ParserConfigurationException e) {
throw new CoreException(new Status(IStatus.ERROR,
DLTKLaunchingPlugin.getUniqueIdentifier(), IStatus.ERROR,
LaunchingMessages.ScriptRuntime_exceptionsOccurred, e));
} catch (TransformerException e) {
throw new CoreException(new Status(IStatus.ERROR,
DLTKLaunchingPlugin.getUniqueIdentifier(), IStatus.ERROR,
LaunchingMessages.ScriptRuntime_exceptionsOccurred, e));
}
}
private static String getInterpretersAsXML() throws IOException,
ParserConfigurationException, TransformerException {
InterpreterDefinitionsContainer container = new InterpreterDefinitionsContainer();
DefaultInterpreterEntry[] entries = getDefaultInterpreterIDs();
for (int i = 0; i < entries.length; i++) {
String id = getDefaultInterpreterId(entries[i]);
if (id != null)
container.setDefaultInterpreterInstallCompositeID(entries[i],
id);
id = getDefaultInterpreterConnectorId(entries[i]);
if (id != null)
container.setDefaultInterpreterInstallConnectorTypeID(
entries[i], id);
}
IInterpreterInstallType[] InterpreterTypes = getInterpreterInstallTypes();
for (int i = 0; i < InterpreterTypes.length; ++i) {
IInterpreterInstall[] Interpreters = InterpreterTypes[i]
.getInterpreterInstalls();
for (int j = 0; j < Interpreters.length; j++) {
IInterpreterInstall install = Interpreters[j];
container.addInterpreter(install);
}
}
return container.getAsXML();
}
/**
* This method loads installed interpreters based an existing user
* preference or old Interpreter configurations file. The interpreters found
* in the preference or interpreter configurations file are added to the
* given Interpreter definitions container.
*
* Returns whether the user preferences should be set - i.e. if it was not
* already set when initialized.
*/
private static boolean addPersistedInterpreters(
InterpreterDefinitionsContainer interpreterDefs) {
// Try retrieving the interpreter preferences from the preference store
String interpreterXMLString = getPreferences().get(PREF_INTERPRETER_XML,
"");
if (interpreterXMLString.length() == 0) {
// default scope value could be specified in the
// plugin_customization.ini file
interpreterXMLString = DefaultScope.INSTANCE
.getNode(DLTKLaunchingPlugin.PLUGIN_ID)
.get(PREF_INTERPRETER_XML, "");
}
// If the preference was found, load interpreters from it into memory
if (interpreterXMLString.length() > 0) {
try {
Reader inputStream = new StringReader(interpreterXMLString);
InterpreterDefinitionsContainer
.parseXMLIntoContainer(inputStream, interpreterDefs);
return false;
} catch (IOException ioe) {
DLTKLaunchingPlugin.log(ioe);
}
}
return true;
}
/**
* Performs string substitution on the given expression.
*
* @param expression
* @return expression after string substitution
* @throws CoreException
*
*/
private static String substitute(String expression) throws CoreException {
return VariablesPlugin.getDefault().getStringVariableManager()
.performStringSubstitution(expression);
}
/**
* Returns whether the Interpreter install with the specified id was
* contributed via the InterpreterInstalls extension point.
*
* @param id
* Interpreter id
* @return whether the Interpreter install was contributed via extension
* point
*
*/
public static boolean isContributedInterpreterInstall(String id) {
getInterpreterInstallTypes(); // ensure Interpreters are initialized
return fgContributedInterpreters.contains(id);
}
/**
* Evaluates library locations for a IInterpreterInstall. If no library
* locations are set on the install, a default location is evaluated and
* checked if it exists.
*
* @return library locations with paths that exist or are empty
*
*/
public static LibraryLocation[] getLibraryLocations(
IInterpreterInstall interperterInstall) {
return getLibraryLocations(interperterInstall, null);
}
public static LibraryLocation[] getLibraryLocations(
IInterpreterInstall interperterInstall, IProgressMonitor monitor) {
LibraryLocation[] locations = interperterInstall.getLibraryLocations();
if (locations != null) {
return locations;
}
LibraryLocation[] defaultLocations = interperterInstall
.getInterpreterInstallType().getDefaultLibraryLocations(
interperterInstall.getInstallLocation(),
interperterInstall.getEnvironmentVariables(), monitor);
List<LibraryLocation> existingDefaultLocations = new ArrayList<>();
for (int i = 0; i < defaultLocations.length; ++i) {
LibraryLocation location = defaultLocations[i];
IFileHandle file = EnvironmentPathUtils
.getFile(location.getLibraryPath());
if (file.exists()) {
existingDefaultLocations.add(location);
}
}
return existingDefaultLocations
.toArray(new LibraryLocation[existingDefaultLocations.size()]);
}
/**
* Creates and returns a buildpath entry describing the default interpreter
* container entry.
*
* @return a new IBuildpathEntry that describes the default interpreter
* container entry
*
*/
public static IBuildpathEntry getDefaultInterpreterContainerEntry() {
return DLTKCore.newContainerEntry(newDefaultInterpreterContainerPath());
}
/**
* Returns a path for the interpreter buildpath container identifying the
* default interpreter install.
*
* @return buildpath container path
*
*/
public static IPath newDefaultInterpreterContainerPath() {
return new Path(INTERPRETER_CONTAINER);
}
/**
* Returns a path for the interpreter buildpath container identifying the
* specified Interpreter install by type and name.
*
* @param Interpreter
* Interpreter install
* @return buildpath container path
*
*/
public static IPath newInterpreterContainerPath(
IInterpreterInstall interpreter) {
if (interpreter != null) {
String name = interpreter.getName();
String typeId = interpreter.getInterpreterInstallType().getId();
return newInterpreterContainerPath(typeId, name);
}
return null;
}
/**
* Returns a path for the InterpreterEnvironment buildpath container
* identifying the specified Interpreter install by type and name.
*
* @param typeId
* Interpreter install type identifier
* @param name
* Interpreter install name
* @return buildpath container path
*
*/
public static IPath newInterpreterContainerPath(String typeId,
String name) {
if (typeId == null || name == null) {
return null;
}
IPath path = newDefaultInterpreterContainerPath();
path = path.append(typeId);
path = path.append(name.replaceAll("/", "%2F")); //$NON-NLS-1$ //$NON-NLS-2$
return path;
}
/**
* Returns the InterpreterEnvironment referenced by the specified
* InterpreterEnvironment buildpath container path or <code>null</code> if
* none.
*
* @param InterpreterEnvironmentContainerPath
* @return InterpreterEnvironment referenced by the specified
* InterpreterEnvironment buildpath container path or
* <code>null</code>
*
*/
public static IInterpreterInstall getInterpreterInstall(String nature,
String environment, IPath InterpreterEnvironmentContainerPath) {
try {
return InterpreterContainerInitializer.resolveInterpreter(nature,
environment, InterpreterEnvironmentContainerPath);
} catch (CoreException e) {
return null;
}
}
/**
* Returns the <code>IInterpreterInstall</code> represented by the specified
* <code>compositeId</code>.
*
* <p>
* If an interpreter can not be found for the given <code>compositeId</code>
* , the default interpreter for the specified <code>natureId</code> will be
* returned.
*
* If no default interpreter has been configured, <code>null</code> will be
* returned.
* </p>
*
* @param compositeId
* the composite id that specifies an instance of
* IInterpreterInstall
* @param natureId
* nature id
*
* @return IInterpreterInstall instance or <code>null</code> if one can not
* be found.
*/
public static IInterpreterInstall getInterpreterInstall(String compositeId,
String natureId) {
IInterpreterInstall install = getInterpreterFromCompositeId(
compositeId);
if (install == null) {
DefaultInterpreterEntry entry = new DefaultInterpreterEntry(
natureId, LocalEnvironment.ENVIRONMENT_ID);
install = ScriptRuntime.getDefaultInterpreterInstall(entry);
}
return install;
}
/**
* Returns the identifier of the Interpreter install type referenced by the
* given InterpreterEnvironment buildpath container path, or
* <code>null</code> if none.
*
* @param InterpreterEnvironmentContainerPath
* @return Interpreter install type identifier or <code>null</code>
*
*/
public static String getInterpreterInstallTypeId(
IPath InterpreterEnvironmentContainerPath) {
return InterpreterContainerInitializer
.getInterpreterTypeId(InterpreterEnvironmentContainerPath);
}
/**
* Returns the name of the Interpreter install referenced by the given
* InterpreterEnvironment buildpath container path, or <code>null</code> if
* none.
*
* @param InterpreterEnvironmentContainerPath
* @return Interpreter name or <code>null</code>
*
*/
public static String getInterpreterInstallName(
IPath InterpreterEnvironmentContainerPath) {
return InterpreterContainerInitializer
.getInterpreterName(InterpreterEnvironmentContainerPath);
}
/**
* Returns a runtime buildpath entry identifying the InterpreterEnvironment
* to use when launching the specified configuration or <code>null</code> if
* none is specified. The entry returned represents a either abuildpath
* container that resolves to a interpreter.
* <p>
* The entry is resolved as follows:
* <ol>
* <li>If the <code>ATTR_CONTAINER_PATH</code> is present, it is used to
* create a buildpath container referring to a interpreter.</li>
* <li>When such attribute are not specified, a default entry is created
* which refers to the interpreter referenced by the build path of the
* configuration's associated script project. This could be a buildpath
* variable or buildpath container.</li>
* <li>When there is no script project associated with a configuration, the
* workspace default interpreter is used to create a container path.</li>
* </ol>
* </p>
*
* @param configuration
* @return buildpath container path identifying a interpreter or
* <code>null</code>
* @exception org.eclipse.core.runtime.CoreException
* if an exception occurs retrieving attributes from the
* specified launch configuration
*/
public static IRuntimeBuildpathEntry computeInterpreterEntry(
ILaunchConfiguration configuration) throws CoreException {
String containerAttr = configuration.getAttribute(
ScriptLaunchConfigurationConstants.ATTR_CONTAINER_PATH,
(String) null);
IPath containerPath = null;
if (containerAttr == null) {
// default interpreter for the launch configuration
IScriptProject proj = getScriptProject(configuration);
if (proj == null) {
containerPath = newDefaultInterpreterContainerPath();
} else {
return computeInterpreterEntry(proj);
}
} else {
containerPath = Path.fromPortableString(containerAttr);
}
if (containerPath != null) {
return newRuntimeContainerBuildpathEntry(containerPath,
IRuntimeBuildpathEntry.STANDARD_ENTRY);
}
return null;
}
/**
* Returns a runtime buildpath entry identifying the InterpreterEnvironment
* referenced by the specified project, or <code>null</code> if none. The
* entry returned represents a either a buildpath variable or buildpath
* container that resolves to a InterpreterEnvironment.
*
* @param project
* Script project
* @return InterpreterEnvironment runtime buildpath entry or
* <code>null</code>
* @exception org.eclipse.core.runtime.CoreException
* if an exception occurs accessing the project's buildpath
*
*/
public static IRuntimeBuildpathEntry computeInterpreterEntry(
IScriptProject project) throws CoreException {
IBuildpathEntry[] rawBuildpath = project.getRawBuildpath();
IRuntimeBuildpathEntryResolver2 resolver = null;
for (int i = 0; i < rawBuildpath.length; i++) {
IBuildpathEntry entry = rawBuildpath[i];
switch (entry.getEntryKind()) {
case IBuildpathEntry.BPE_CONTAINER:
resolver = getContainerResolver(entry.getPath().segment(0));
if (resolver != null) {
if (resolver.isInterpreterInstallReference(
getNatureFromProject(project),
getEnvironmentFromProject(project), entry)) {
IBuildpathContainer container = DLTKCore
.getBuildpathContainer(entry.getPath(),
project);
if (container != null) {
switch (container.getKind()) {
case IBuildpathContainer.K_APPLICATION:
break;
case IBuildpathContainer.K_DEFAULT_SYSTEM:
return newRuntimeContainerBuildpathEntry(
entry.getPath(),
IRuntimeBuildpathEntry.STANDARD_ENTRY);
case IBuildpathContainer.K_SYSTEM:
return newRuntimeContainerBuildpathEntry(
entry.getPath(),
IRuntimeBuildpathEntry.BOOTSTRAP_ENTRY);
}
}
}
}
break;
}
}
return null;
}
/**
* Returns whether the given runtime buildpath entry refers to a Interpreter
* install.
*
* @param entry
* @return whether the given runtime buildpath entry refers to a Interpreter
* install
*
*/
public static boolean isInterpreterInstallReference(String lang,
String environment, IRuntimeBuildpathEntry entry) {
IBuildpathEntry buildpathEntry = entry.getBuildpathEntry();
if (buildpathEntry != null) {
switch (buildpathEntry.getEntryKind()) {
case IBuildpathEntry.BPE_CONTAINER:
IRuntimeBuildpathEntryResolver2 resolver = getContainerResolver(
buildpathEntry.getPath().segment(0));
if (resolver != null) {
return resolver.isInterpreterInstallReference(lang,
environment, buildpathEntry);
}
break;
}
}
return false;
}
/**
* Adds the given listener to the list of registered Interpreter install
* changed listeners. Has no effect if an identical listener is already
* registered.
*
* @param listener
* the listener to add
*
*/
public static void addInterpreterInstallChangedListener(
IInterpreterInstallChangedListener listener) {
fgInterpreterListeners.add(listener);
}
/**
* Removes the given listener from the list of registered Interpreter
* install changed listeners. Has no effect if an identical listener is not
* already registered.
*
* @param listener
* the listener to remove
*
*/
public static void removeInterpreterInstallChangedListener(
IInterpreterInstallChangedListener listener) {
fgInterpreterListeners.remove(listener);
}
private static void notifyDefaultInterpreterChanged(
IInterpreterInstall previous, IInterpreterInstall current) {
Object[] listeners = fgInterpreterListeners.getListeners();
for (int i = 0; i < listeners.length; i++) {
IInterpreterInstallChangedListener listener = (IInterpreterInstallChangedListener) listeners[i];
listener.defaultInterpreterInstallChanged(previous, current);
}
}
/**
* Notifies all Interpreter install changed listeners of the given property
* change.
*
* @param event
* event describing the change.
*
*/
public static void fireInterpreterChanged(PropertyChangeEvent event) {
Object[] listeners = fgInterpreterListeners.getListeners();
for (int i = 0; i < listeners.length; i++) {
IInterpreterInstallChangedListener listener = (IInterpreterInstallChangedListener) listeners[i];
listener.interpreterChanged(event);
}
}
/**
* Notifies all Interpreter install changed listeners of the Interpreter
* addition
*
* @param Interpreter
* the Interpreter that has been added
*
*/
public static void fireInterpreterAdded(IInterpreterInstall Interpreter) {
if (!fgInitializingInterpreters) {
Object[] listeners = fgInterpreterListeners.getListeners();
for (int i = 0; i < listeners.length; i++) {
IInterpreterInstallChangedListener listener = (IInterpreterInstallChangedListener) listeners[i];
listener.interpreterAdded(Interpreter);
}
}
}
/**
* Notifies all Interpreter install changed listeners of the Interpreter
* removal
*
* @param Interpreter
* the Interpreter that has been removed
*
*/
public static void fireInterpreterRemoved(IInterpreterInstall Interpreter) {
Object[] listeners = fgInterpreterListeners.getListeners();
for (int i = 0; i < listeners.length; i++) {
IInterpreterInstallChangedListener listener = (IInterpreterInstallChangedListener) listeners[i];
listener.interpreterRemoved(Interpreter);
}
}
/**
* Returns the preference store for the launching plug-in.
*
* @return the preference store for the launching plug-in
* @since 5.0 return type changed to {@link IEclipsePreferences}
*/
public static IEclipsePreferences getPreferences() {
return InstanceScope.INSTANCE.getNode(DLTKLaunchingPlugin.PLUGIN_ID);
}
/**
* Saves the preferences for the launching plug-in.
*
*
*/
public static void savePreferences() {
try {
getPreferences().flush();
} catch (BackingStoreException e) {
DLTKLaunchingPlugin.log(e);
}
}
/**
* Registers the given resolver for the specified container.
*
* @param resolver
* runtime buildpath entry resolver
* @param containerIdentifier
* identifier of the buildpath container to register for
*
*/
public static void addContainerResolver(
IRuntimeBuildpathEntryResolver resolver,
String containerIdentifier) {
Map<String, IRuntimeBuildpathEntryResolver> map = getContainerResolvers();
map.put(containerIdentifier, resolver);
}
/**
* Returns all registered container resolvers.
*/
private static Map<String, IRuntimeBuildpathEntryResolver> getContainerResolvers() {
if (fgContainerResolvers == null) {
initializeResolvers();
}
return fgContainerResolvers;
}
private static void initializeResolvers() {
IExtensionPoint point = Platform.getExtensionRegistry()
.getExtensionPoint(DLTKLaunchingPlugin.PLUGIN_ID,
EXTENSION_POINT_RUNTIME_BUILDPATH_ENTRY_RESOLVERS);
IConfigurationElement[] extensions = point.getConfigurationElements();
fgContainerResolvers = new HashMap<>(extensions.length);
fgRuntimeBuildpathEntryResolvers = new HashMap<>(extensions.length);
for (int i = 0; i < extensions.length; i++) {
RuntimeBuildpathEntryResolver res = new RuntimeBuildpathEntryResolver(
extensions[i]);
String container = res.getContainerId();
String entryId = res.getRuntimeBuildpathEntryId();
if (container != null) {
fgContainerResolvers.put(container, res);
}
if (entryId != null) {
fgRuntimeBuildpathEntryResolvers.put(entryId, res);
}
}
}
/**
* Returns all registered buildpath providers.
*/
private static Map<String, IRuntimeBuildpathProvider> getBuildpathProviders() {
if (fgPathProviders == null) {
initializeProviders();
}
return fgPathProviders;
}
private static void initializeProviders() {
IExtensionPoint point = Platform.getExtensionRegistry()
.getExtensionPoint(DLTKLaunchingPlugin.PLUGIN_ID,
EXTENSION_POINT_RUNTIME_BUILDPATH_PROVIDERS);
IConfigurationElement[] extensions = point.getConfigurationElements();
fgPathProviders = new HashMap<>(extensions.length);
for (int i = 0; i < extensions.length; i++) {
RuntimeBuildpathProvider res = new RuntimeBuildpathProvider(
extensions[i]);
fgPathProviders.put(res.getIdentifier(), res);
}
}
/**
* Returns the resolver registered for the given container id, or
* <code>null</code> if none.
*
* @param containerId
* the container to determine the resolver for
* @return the resolver registered for the given container id, or
* <code>null</code> if none
*/
private static IRuntimeBuildpathEntryResolver2 getContainerResolver(
String containerId) {
return (IRuntimeBuildpathEntryResolver2) getContainerResolvers()
.get(containerId);
}
/**
* Returns a collection of paths that should be appended to the given
* project's library path when launched. Entries are searched for on the
* project's build path as extra buildpath attributes. Each entry represents
* an absolute path in the local file system.
*
* @param project
* the project to compute the <code>java.library.path</code> for
* @param requiredProjects
* whether to consider entries in required projects
* @return a collection of paths representing entries that should be
* appended to the given project's <code>java.library.path</code>
* @throws CoreException
* if unable to compute the Script library path
*
* @see org.eclipse.dltk.core.IBuildpathAttribute
* @see ScriptRuntime#BUILDPATH_ATTR_LIBRARY_PATH_ENTRY
*/
public static String[] computeScriptLibraryPath(IScriptProject project,
boolean requiredProjects) throws CoreException {
Set<IScriptProject> visited = new HashSet<>();
List<String> entries = new ArrayList<>();
gatherScriptLibraryPathEntries(project, requiredProjects, visited,
entries);
List<String> resolved = new ArrayList<>(entries.size());
IStringVariableManager manager = VariablesPlugin.getDefault()
.getStringVariableManager();
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (String entry : entries) {
String resolvedEntry = manager.performStringSubstitution(entry);
IPath path = new Path(resolvedEntry);
if (path.isAbsolute()) {
File file = path.toFile();
resolved.add(file.getAbsolutePath());
} else {
IResource resource = root.findMember(path);
if (resource != null) {
IPath location = resource.getLocation();
if (location != null) {
resolved.add(location.toFile().getAbsolutePath());
}
}
}
}
return resolved.toArray(new String[resolved.size()]);
}
/**
* Gathers all Script library entries for the given project and optionally
* its required projects.
*
* @param project
* project to gather entries for
* @param requiredProjects
* whether to consider required projects
* @param visited
* projects already considered
* @param entries
* collection to add library entries to
* @throws CoreException
* if unable to gather buildpath entries
*
*/
private static void gatherScriptLibraryPathEntries(IScriptProject project,
boolean requiredProjects, Set<IScriptProject> visited,
List<String> entries) throws CoreException {
if (visited.contains(project)) {
return;
}
visited.add(project);
IBuildpathEntry[] rawBuildpath = project.getRawBuildpath();
IBuildpathEntry[] required = processScriptLibraryPathEntries(project,
requiredProjects, rawBuildpath, entries);
if (required != null) {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (int i = 0; i < required.length; i++) {
IBuildpathEntry entry = required[i];
String projectName = entry.getPath().segment(0);
IProject p = root.getProject(projectName);
if (p.exists()) {
IScriptProject requiredProject = DLTKCore.create(p);
if (requiredProject != null) {
gatherScriptLibraryPathEntries(requiredProject,
requiredProjects, visited, entries);
}
}
}
}
}
/**
* Adds all library path extra buildpath entry values to the given entries
* collection specified on the given project's buildpath, and returns a
* collection of required projects, or <code>null</code>.
*
* @param project
* project being processed
* @param collectRequired
* whether to collect required projects
* @param buildpathEntries
* the project's raw buildpath
* @param entries
* collection to add script library path entries to
* @return required project buildpath entries or <code>null</code>
* @throws CoreException
*
*/
private static IBuildpathEntry[] processScriptLibraryPathEntries(
IScriptProject project, boolean collectRequired,
IBuildpathEntry[] buildpathEntries, List<String> entries)
throws CoreException {
List<IBuildpathEntry> req = null;
for (int i = 0; i < buildpathEntries.length; i++) {
IBuildpathEntry entry = buildpathEntries[i];
IBuildpathAttribute[] extraAttributes = entry.getExtraAttributes();
for (int j = 0; j < extraAttributes.length; j++) {
String[] paths = getLibraryPaths(extraAttributes[j]);
if (paths != null) {
for (int k = 0; k < paths.length; k++) {
entries.add(paths[k]);
}
}
}
if (entry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER) {
IBuildpathContainer container = DLTKCore
.getBuildpathContainer(entry.getPath(), project);
if (container != null) {
IBuildpathEntry[] requiredProjects = processScriptLibraryPathEntries(
project, collectRequired,
container.getBuildpathEntries(), entries);
if (requiredProjects != null) {
if (req == null) {
req = new ArrayList<>();
}
for (int j = 0; j < requiredProjects.length; j++) {
req.add(requiredProjects[j]);
}
}
}
} else if (collectRequired
&& entry.getEntryKind() == IBuildpathEntry.BPE_PROJECT) {
if (req == null) {
req = new ArrayList<>();
}
req.add(entry);
}
}
if (req != null) {
return req.toArray(new IBuildpathEntry[req.size()]);
}
return null;
}
/**
* Creates a new buildpath attribute referencing a list of shared libraries
* that should appear on the <code>-Djava.library.path</code> system
* property at runtime for an associated {@link IBuildpathEntry}.
* <p>
* The factory methods <code>newLibraryPathsAttribute(String[])</code> and
* <code>getLibraryPaths(IBuildpathAttribute)</code> should be used to
* encode and decode the attribute value.
* </p>
*
* @param paths
* an array of strings representing paths of shared libraries.
* Each string is used to create an <code>IPath</code> using the
* constructor <code>Path(String)</code>, and may contain
* <code>IStringVariable</code>'s. Variable substitution is
* performed on each string before a path is constructed from a
* string.
* @return a buildpath attribute with the name
* <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code> and an value
* encoded to the specified paths.
*
*/
public static IBuildpathAttribute newLibraryPathsAttribute(String[] paths) {
StringBuffer value = new StringBuffer();
for (int i = 0; i < paths.length; i++) {
value.append(paths[i]);
if (i < (paths.length - 1)) {
value.append("|"); //$NON-NLS-1$
}
}
return DLTKCore.newBuildpathAttribute(BUILDPATH_ATTR_LIBRARY_PATH_ENTRY,
value.toString());
}
/**
* Returns an array of strings referencing shared libraries that should
* appear on the <code>-Djava.library.path</code> system property at runtime
* for an associated {@link IBuildpathEntry}, or <code>null</code> if the
* given attribute is not a <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code>.
* Each string is used to create an <code>IPath</code> using the constructor
* <code>Path(String)</code>, and may contain <code>IStringVariable</code>
* 's.
* <p>
* The factory methods <code>newLibraryPathsAttribute(String[])</code> and
* <code>getLibraryPaths(IBuildpathAttribute)</code> should be used to
* encode and decode the attribute value.
* </p>
*
* @param attribute
* a <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code> buildpath
* attribute
* @return an array of strings referencing shared libraries that should
* appear on the <code>-Djava.library.path</code> system property at
* runtime for an associated {@link IBuildpathEntry}, or
* <code>null</code> if the given attribute is not a
* <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code>. Each string is
* used to create an <code>IPath</code> using the constructor
* <code>Path(String)</code>, and may contain
* <code>IStringVariable</code>'s.
*
*/
public static String[] getLibraryPaths(IBuildpathAttribute attribute) {
if (BUILDPATH_ATTR_LIBRARY_PATH_ENTRY.equals(attribute.getName())) {
String value = attribute.getValue();
return value.split("\\|"); //$NON-NLS-1$
}
return null;
}
/**
* Loads contributed interpreter installs
*
*/
private static void addInterpreterExtensions(
InterpreterDefinitionsContainer InterpreterDefs) {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry()
.getExtensionPoint(DLTKLaunchingPlugin.PLUGIN_ID,
ScriptRuntime.EXTENSION_POINT_INTERPRETER_INSTALLS);
IConfigurationElement[] configs = extensionPoint
.getConfigurationElements();
for (int i = 0; i < configs.length; i++) {
IConfigurationElement element = configs[i];
try {
if ("interpreterInstall".equals(element.getName())) { //$NON-NLS-1$
String InterpreterType = element
.getAttribute("interpreterInstallType"); //$NON-NLS-1$
if (InterpreterType == null) {
abort(MessageFormat.format(
"Missing required interpreterInstallType attribute for interpreterInstall contributed by {0}", //$NON-NLS-1$
element.getContributor().getName()), null);
}
String id = element.getAttribute("id"); //$NON-NLS-1$
if (id == null) {
abort(MessageFormat.format(
"Missing required id attribute for interpreterInstall contributed by {0}", //$NON-NLS-1$
element.getContributor().getName()), null);
}
IInterpreterInstallType installType = getInterpreterInstallType(
InterpreterType);
if (installType == null) {
throw abort(MessageFormat.format(
"InterpreterInstall {0} contributed by {1} references undefined Interpreter install type {2}", //$NON-NLS-1$
id, element.getContributor().getName(),
InterpreterType), null);
}
IInterpreterInstall install = installType
.findInterpreterInstall(id);
if (install == null) {
// only load/create if first time we've seen this
// Interpreter install
String name = element.getAttribute("name"); //$NON-NLS-1$
if (name == null) {
abort(MessageFormat.format(
"interpreterInstall {0} contributed by {1} missing required attribute name", //$NON-NLS-1$
id, element.getContributor().getName()),
null);
}
final String home = element.getAttribute("home"); //$NON-NLS-1$
if (home == null) {
abort(MessageFormat.format(
"interpreterInstall {0} contributed by {1} missing required attribute home", //$NON-NLS-1$
id, element.getContributor().getName()),
null);
}
String InterpreterArgs = element
.getAttribute("interpreterArgs"); //$NON-NLS-1$
InterpreterStandin standin = new InterpreterStandin(
installType, id);
standin.setName(name);
// Only local installs can be contributed, so
// use local environment
final IEnvironment localEnv = EnvironmentManager
.getLocalEnvironment();
IFileHandle homeFile = localEnv
.getFile(toPath(element, substitute(home)));
if (homeFile.exists()) {
// adjust for relative path names
homeFile = localEnv.getFile(
new Path(homeFile.getCanonicalPath()));
}
IStatus status = installType.validateInstallLocation(
homeFile, standin.getEnvironmentVariables(),
standin.getLibraryLocations(),
new NullProgressMonitor());
if (!status.isOK()) {
abort(MessageFormat.format(
"Illegal install location {0} for interpreterInstall {1} contributed by {2}: {3}", //$NON-NLS-1$
home, id,
element.getContributor().getName(),
status.getMessage()), null);
}
standin.setInstallLocation(homeFile);
if (InterpreterArgs != null) {
standin.setInterpreterArgs(InterpreterArgs);
}
IConfigurationElement[] libraries = element
.getChildren("library"); //$NON-NLS-1$
LibraryLocation[] locations = null;
if (libraries.length > 0) {
locations = new LibraryLocation[libraries.length];
for (int j = 0; j < libraries.length; j++) {
IConfigurationElement library = libraries[j];
String libPathStr = library
.getAttribute("path"); //$NON-NLS-1$
if (libPathStr == null) {
abort(MessageFormat.format(
"library for interpreterInstall {0} contributed by {1} missing required attribute libPath", //$NON-NLS-1$
id,
element.getContributor().getName()),
null);
}
locations[j] = new LibraryLocation(
homeFile.getFullPath().append(
substitute(libPathStr)));
}
}
standin.setLibraryLocations(locations);
InterpreterDefs.addInterpreter(standin, true);
}
fgContributedInterpreters.add(id);
} else {
abort(MessageFormat.format(
"Illegal element {0} in InterpreterInstalls extension contributed by {1}", //$NON-NLS-1$
element.getName(),
element.getContributor().getName()), null);
}
} catch (CoreException e) {
DLTKLaunchingPlugin.log(e);
}
}
}
private static IPath toPath(IConfigurationElement element, String path)
throws CoreException {
String bundleId = element.getAttribute("bundle");
if (bundleId != null) {
if (bundleId.equals(".")) {
bundleId = element.getContributor().getName();
}
final Bundle bundle = Platform.getBundle(bundleId);
if (bundle != null) {
try {
final File bundleFile = FileLocator.getBundleFile(bundle);
final IPath bundlePath = new Path(
bundleFile.getCanonicalPath());
return bundlePath.append(new Path(path));
} catch (IOException e) {
abort(NLS.bind("Error resolving {0} bundle location",
bundleId), e);
}
}
}
return new Path(path);
}
private static InterpreterStandin[] detectInterpreterInstall(
String natureId, List<IInterpreterInstallType> interpTypes) {
// Try to create a interpreter install using the install location
for (IInterpreterInstallType interpType : interpTypes) {
final IFileHandle[] detectedLocations = interpType
.detectInstallLocations();
if (detectedLocations != null && detectedLocations.length != 0) {
// Make sure the interpreter id is unique
long unique = System.currentTimeMillis();
final InterpreterStandin[] result = new InterpreterStandin[detectedLocations.length];
for (int j = 0; j < detectedLocations.length; ++j) {
while (interpType.findInterpreterInstall(
String.valueOf(unique)) != null) {
unique++;
}
final IFileHandle location = detectedLocations[j];
// Create a standin for the detected interpreter
final InterpreterStandin detected = new InterpreterStandin(
interpType, String.valueOf(unique++));
detected.setInstallLocation(location);
detected.setName(interpType
.generateDetectedInterpreterName(location));
result[j] = detected;
}
return result;
}
}
return null;
}
private static Map<String, List<IInterpreterInstallType>> getInterpreterTypesByNature() {
Map<String, List<IInterpreterInstallType>> result = new HashMap<>();
for (IInterpreterInstallType type : fgInterpreterTypes) {
String natureId = type.getNatureId();
if (!result.containsKey(natureId)) {
result.put(natureId, new ArrayList<IInterpreterInstallType>());
}
result.get(natureId).add(type);
}
return result;
}
private static Map<String, List<IInterpreterInstall>> getValidInterpretersByNature(
List<IInterpreterInstall> validInterpreters) {
Map<String, List<IInterpreterInstall>> result = new HashMap<>();
for (IInterpreterInstall install : validInterpreters) {
String natureId = install.getNatureId();
if (!result.containsKey(natureId)) {
result.put(natureId, new ArrayList<IInterpreterInstall>());
}
result.get(natureId).add(install);
}
return result;
}
/**
* Perform Interpreter type and Interpreter install initialization. Does not
* hold locks while performing change notification.
*
*
*/
private static void initializeInterpreters() {
InterpreterDefinitionsContainer defs = null;
boolean setPref = false;
synchronized (fgInterpreterLock) {
if (fgInterpreterTypes == null) {
try {
fgInitializingInterpreters = true;
// 1. load Interpreter type extensions
initializeInterpreterTypeExtensions();
defs = new InterpreterDefinitionsContainer();
// 2. add persisted Interpreters
setPref = addPersistedInterpreters(defs);
// 3. if there are none, detect interpreters
Map<String, List<IInterpreterInstallType>> typesByNature = getInterpreterTypesByNature();
Map<String, List<IInterpreterInstall>> interpsByNature = getValidInterpretersByNature(
defs.getValidInterpreterList());
for (String natureId : typesByNature.keySet()) {
if (!interpsByNature.containsKey(natureId)
|| interpsByNature.get(natureId).isEmpty()) {
// calling out to detectInterpreterInstall(nature)
// could allow clients to change
// Interpreter settings (i.e. call back into change
// Interpreter settings).
InterpreterListener listener = new InterpreterListener();
addInterpreterInstallChangedListener(listener);
setPref = true;
InterpreterStandin[] interp = detectInterpreterInstall(
natureId, typesByNature.get(natureId));
removeInterpreterInstallChangedListener(listener);
if (!listener.isChanged()) {
if (interp != null) {
for (InterpreterStandin standin : interp) {
defs.addInterpreter(standin);
}
defs.setDefaultInterpreterInstallCompositeID(
new DefaultInterpreterEntry(
natureId,
interp[0]
.getEnvironmentId()),
getCompositeIdFromInterpreter(
interp[0]));
}
} else {
// interpreters were changed - reflect current
// settings
addPersistedInterpreters(defs);
// copy default interpreters
for (Map.Entry<DefaultInterpreterEntry, String> entry : fgDefaultInterpreterId
.entrySet()) {
defs.setDefaultInterpreterInstallCompositeID(
entry.getKey(), entry.getValue());
}
}
}
}
// 4. load contributed Interpreter installs
addInterpreterExtensions(defs);
// 5. verify default interpreters is valid
DefaultInterpreterEntry[] natures = defs
.getInterpreterNatures();
for (int i = 0; i < natures.length; i++) {
String defId = defs
.getDefaultInterpreterInstallCompositeID(
natures[i]);
boolean validDef = false;
if (defId != null) {
for (IInterpreterInstall iterpreter : defs
.getValidInterpreterList()) {
if (getCompositeIdFromInterpreter(iterpreter)
.equals(defId)) {
validDef = true;
break;
}
}
}
if (!validDef) {
// use the first as the default
setPref = true;
List<IInterpreterInstall> list = defs
.getValidInterpreterList(natures[i]);
if (!list.isEmpty()) {
IInterpreterInstall Interpreter = list.get(0);
defs.setDefaultInterpreterInstallCompositeID(
natures[i],
getCompositeIdFromInterpreter(
Interpreter));
}
}
String defInstCID = defs
.getDefaultInterpreterInstallCompositeID(
natures[i]);
fgDefaultInterpreterId.put(natures[i], defInstCID);
String defIntCTypeID = defs
.getDefaultInterpreterInstallConnectorTypeID(
natures[i]);
fgDefaultInterpreterConnectorId.put(natures[i],
defIntCTypeID);
}
// Create the underlying interpreters for each valid
// Interpreter
List<IInterpreterInstall> InterpreterList = defs
.getValidInterpreterList();
Iterator<IInterpreterInstall> InterpreterListIterator = InterpreterList
.iterator();
while (InterpreterListIterator.hasNext()) {
InterpreterStandin InterpreterStandin = (InterpreterStandin) InterpreterListIterator
.next();
InterpreterStandin.convertToRealInterpreter();
}
} finally {
fgInitializingInterpreters = false;
}
}
}
if (defs != null) {
// notify of initial Interpreters for backwards compatibility
IInterpreterInstallType[] installTypes = getInterpreterInstallTypes();
for (int i = 0; i < installTypes.length; i++) {
IInterpreterInstallType type = installTypes[i];
IInterpreterInstall[] installs = type.getInterpreterInstalls();
if (installs != null) {
for (int j = 0; j < installs.length; j++) {
fireInterpreterAdded(installs[j]);
}
}
}
// save settings if required
if (setPref) {
try {
String xml = defs.getAsXML();
getPreferences().put(PREF_INTERPRETER_XML, xml);
} catch (ParserConfigurationException e) {
DLTKLaunchingPlugin.log(e);
} catch (IOException e) {
DLTKLaunchingPlugin.log(e);
} catch (TransformerException e) {
DLTKLaunchingPlugin.log(e);
}
}
}
}
/**
* Returns a new runtime buildpath entry containing the default buildpath
* for the specified script project.
*
* @param project
* project
* @return runtime buildpath entry
*
*/
public static IRuntimeBuildpathEntry newDefaultProjectBuildpathEntry(
IScriptProject project) {
return new DefaultProjectBuildpathEntry(project);
}
/**
* Returns a new runtime buildpath entry for the given project.
*
* @param project
* Script project
* @return runtime buildpath entry
*
*/
public static IRuntimeBuildpathEntry newProjectRuntimeBuildpathEntry(
IScriptProject project) {
IBuildpathEntry cpe = DLTKCore
.newProjectEntry(project.getProject().getFullPath());
return newRuntimeBuildpathEntry(cpe);
}
/**
* Returns a new runtime buildpath entry for the given archive.
*
* @param resource
* archive resource
* @return runtime buildpath entry
*
*/
public static IRuntimeBuildpathEntry newArchiveRuntimeBuildpathEntry(
IResource resource) {
IBuildpathEntry cpe = DLTKCore.newLibraryEntry(resource.getFullPath());
return newRuntimeBuildpathEntry(cpe);
}
/**
* Returns a new runtime buildpath entry for the given archive (possibly
* external).
*
* @param path
* absolute path to an archive
* @return runtime buildpath entry
*
*/
public static IRuntimeBuildpathEntry newArchiveRuntimeBuildpathEntry(
IPath path) {
IBuildpathEntry cpe = DLTKCore.newLibraryEntry(path);
return newRuntimeBuildpathEntry(cpe);
}
/**
* Returns a runtime buildpath entry for the given container path with the
* given buildpath property.
*
* @param path
* container path
* @param buildpathProperty
* the type of entry - one of <code>USER_CLASSES</code>, or
* <code>STANDARD_CLASSES</code>
* @return runtime buildpath entry
* @exception CoreException
* if unable to construct a runtime buildpath entry
*
*/
public static IRuntimeBuildpathEntry newRuntimeContainerBuildpathEntry(
IPath path, int buildpathProperty) throws CoreException {
return newRuntimeContainerBuildpathEntry(path, buildpathProperty, null);
}
/**
* Returns a runtime buildpath entry for the given container path with the
* given buildpath property to be resolved in the context of the given
* Script project.
*
* @param path
* container path
* @param buildpathProperty
* the type of entry - one of <code>USER_CLASSES</code>, or
* <code>STANDARD_CLASSES</code>
* @param project
* Script project context used for resolution, or
* <code>null</code> if to be resolved in the context of the
* launch configuration this entry is referenced in
* @return runtime buildpath entry
* @exception CoreException
* if unable to construct a runtime buildpath entry
*/
public static IRuntimeBuildpathEntry newRuntimeContainerBuildpathEntry(
IPath path, int buildpathProperty, IScriptProject project)
throws CoreException {
IBuildpathEntry cpe = DLTKCore.newContainerEntry(path);
RuntimeBuildpathEntry entry = new RuntimeBuildpathEntry(cpe,
buildpathProperty);
entry.setScriptProject(project);
return entry;
}
/**
* Returns a runtime buildpath entry constructed from the given memento.
*
* @param memento
* a memento for a runtime buildpath entry
* @return runtime buildpath entry
* @exception CoreException
* if unable to construct a runtime buildpath entry
*/
public static IRuntimeBuildpathEntry newRuntimeBuildpathEntry(
String memento) throws CoreException {
try {
Element root = null;
DocumentBuilder parser = DLTKLaunchingPlugin.getParser();
StringReader reader = new StringReader(memento);
InputSource source = new InputSource(reader);
root = parser.parse(source).getDocumentElement();
String id = root.getAttribute("id"); //$NON-NLS-1$
if (id == null || id.length() == 0) {
// assume an old format
return new RuntimeBuildpathEntry(root);
}
// get the extension & create a new one
IRuntimeBuildpathEntry2 entry = DLTKLaunchingPlugin.getDefault()
.newRuntimeBuildpathEntry(id);
NodeList list = root.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
if ("memento".equals(element.getNodeName())) { //$NON-NLS-1$
entry.initializeFrom(element);
}
}
}
return entry;
} catch (SAXException e) {
abort(LaunchingMessages.ScriptRuntime_31, e);
} catch (IOException e) {
abort(LaunchingMessages.ScriptRuntime_32, e);
}
return null;
}
/**
* Returns a runtime buildpath entry that corresponds to the given buildpath
* entry. The buildpath entry may not be of type <code>CPE_SOURCE</code> or
* <code>BPE_CONTAINER</code>.
*
* @param entry
* a buildpath entry
* @return runtime buildpath entry
*/
private static IRuntimeBuildpathEntry newRuntimeBuildpathEntry(
IBuildpathEntry entry) {
return new RuntimeBuildpathEntry(entry);
}
/**
* Computes and returns the default unresolved runtime buildpath for the
* given project.
*
* @return runtime buildpath entries
* @exception CoreException
* if unable to compute the runtime buildpath
* @see IRuntimeBuildpathEntry
*
*/
public static IRuntimeBuildpathEntry[] computeUnresolvedRuntimeBuildpath(
IScriptProject project) throws CoreException {
IBuildpathEntry[] entries = project.getRawBuildpath();
List<IRuntimeBuildpathEntry> buildpathEntries = new ArrayList<>(3);
for (int i = 0; i < entries.length; i++) {
IBuildpathEntry entry = entries[i];
switch (entry.getEntryKind()) {
case IBuildpathEntry.BPE_CONTAINER:
IBuildpathContainer container = DLTKCore
.getBuildpathContainer(entry.getPath(), project);
if (container != null) {
switch (container.getKind()) {
case IBuildpathContainer.K_APPLICATION:
// don't look at application entries
break;
case IBuildpathContainer.K_DEFAULT_SYSTEM:
case IBuildpathContainer.K_SYSTEM:
buildpathEntries.add(newRuntimeContainerBuildpathEntry(
container.getPath(),
IRuntimeBuildpathEntry.STANDARD_ENTRY,
project));
break;
}
}
break;
default:
break;
}
}
buildpathEntries.add(newDefaultProjectBuildpathEntry(project));
return buildpathEntries
.toArray(new IRuntimeBuildpathEntry[buildpathEntries.size()]);
}
/**
* Returns the buildpath provider for the given launch configuration.
*
* @param configuration
* launch configuration
* @return buildpath provider
* @exception CoreException
* if unable to resolve the path provider
*/
public static IRuntimeBuildpathProvider getBuildpathProvider(
ILaunchConfiguration configuration) throws CoreException {
String providerId = configuration.getAttribute(
ScriptLaunchConfigurationConstants.ATTR_BUILDPATH_PROVIDER,
(String) null);
IRuntimeBuildpathProvider provider = null;
if (providerId == null) {
provider = fgDefaultBuildpathProvider;
} else {
provider = getBuildpathProviders().get(providerId);
if (provider == null) {
abort(MessageFormat.format(LaunchingMessages.ScriptRuntime_26,
providerId), null);
}
}
return provider;
}
public static IRuntimeBuildpathProvider getScriptpathProvider(
ILaunchConfiguration configuration) throws CoreException {
String providerId = configuration.getAttribute(
ScriptLaunchConfigurationConstants.ATTR_SOURCEPATH_PROVIDER,
(String) null);
IRuntimeBuildpathProvider provider = null;
if (providerId == null) {
provider = fgDefaultSourcepathProvider;
} else {
provider = getBuildpathProviders().get(providerId);
if (provider == null) {
abort(MessageFormat.format(LaunchingMessages.ScriptRuntime_27,
providerId), null);
}
}
return provider;
}
/**
* Performs default resolution for a container entry. Delegates to the
* Script model.
*/
private static IRuntimeBuildpathEntry[] computeDefaultContainerEntries(
IRuntimeBuildpathEntry entry, ILaunchConfiguration config)
throws CoreException {
IScriptProject project = entry.getScriptProject();
if (project == null) {
project = getScriptProject(config);
}
return computeDefaultContainerEntries(entry, project);
}
/**
* Performs default resolution for a container entry. Delegates to the
* Script model.
*/
private static IRuntimeBuildpathEntry[] computeDefaultContainerEntries(
IRuntimeBuildpathEntry entry, IScriptProject project)
throws CoreException {
if (project == null || entry == null) {
// cannot resolve without entry or project context
return new IRuntimeBuildpathEntry[0];
}
IBuildpathContainer container = DLTKCore
.getBuildpathContainer(entry.getPath(), project);
if (container == null) {
abort(MessageFormat.format(
LaunchingMessages.ScriptRuntime_Could_not_resolve_classpath_container___0__1,
entry.getPath().toString()), null);
// execution will not reach here - exception will be thrown
return null;
}
IBuildpathEntry[] cpes = container.getBuildpathEntries();
int property = -1;
switch (container.getKind()) {
case IBuildpathContainer.K_APPLICATION:
property = IRuntimeBuildpathEntry.USER_ENTRY;
break;
case IBuildpathContainer.K_DEFAULT_SYSTEM:
property = IRuntimeBuildpathEntry.STANDARD_ENTRY;
break;
case IBuildpathContainer.K_SYSTEM:
property = IRuntimeBuildpathEntry.BOOTSTRAP_ENTRY;
break;
}
List<IRuntimeBuildpathEntry> resolved = new ArrayList<>(cpes.length);
List<IScriptProject> projects = fgProjects.get();
Integer count = fgEntryCount.get();
if (projects == null) {
projects = new ArrayList<>();
fgProjects.set(projects);
count = Integer.valueOf(0);
}
int intCount = count.intValue();
intCount++;
fgEntryCount.set(Integer.valueOf(intCount));
try {
for (int i = 0; i < cpes.length; i++) {
IBuildpathEntry cpe = cpes[i];
if (cpe.getEntryKind() == IBuildpathEntry.BPE_PROJECT) {
IProject p = ResourcesPlugin.getWorkspace().getRoot()
.getProject(cpe.getPath().segment(0));
IScriptProject jp = DLTKCore.create(p);
if (!projects.contains(jp)) {
projects.add(jp);
IRuntimeBuildpathEntry buildpath = newDefaultProjectBuildpathEntry(
jp);
IRuntimeBuildpathEntry[] entries = resolveRuntimeBuildpathEntry(
buildpath, jp);
for (int j = 0; j < entries.length; j++) {
IRuntimeBuildpathEntry e = entries[j];
if (!resolved.contains(e)) {
resolved.add(entries[j]);
}
}
}
} else {
IRuntimeBuildpathEntry e = newRuntimeBuildpathEntry(cpe);
if (!resolved.contains(e)) {
resolved.add(e);
}
}
}
} finally {
intCount--;
if (intCount == 0) {
fgProjects.set(null);
fgEntryCount.set(null);
} else {
fgEntryCount.set(Integer.valueOf(intCount));
}
}
// set buildpath property
IRuntimeBuildpathEntry[] result = new IRuntimeBuildpathEntry[resolved
.size()];
for (int i = 0; i < result.length; i++) {
result[i] = resolved.get(i);
result[i].setBuildpathProperty(property);
}
return result;
}
/**
* Returns resolved entries for the given entry in the context of the given
* launch configuration. If the entry is of kind <code>CONTAINER</code>,
* container resolvers are consulted. Otherwise, the given entry is
* returned.
* <p>
* If the given entry is a container, and a resolver is not registered,
* resolved runtime buildpath entries are calculated from the associated
* container buildpath entries, in the context of the project associated
* with the given launch configuration.
* </p>
*
* @param entry
* runtime classpath entry
* @param configuration
* launch configuration
* @return resolved runtime classpath entry
* @exception CoreException
* if unable to resolve
* @see IRuntimeBuildpathEntryResolver
*
*/
public static IRuntimeBuildpathEntry[] resolveRuntimeBuildpathEntry(
IRuntimeBuildpathEntry entry, ILaunchConfiguration configuration)
throws CoreException {
switch (entry.getType()) {
case IRuntimeBuildpathEntry.PROJECT:
// if the project has multiple output locations, they must be
// returned
IResource resource = entry.getResource();
if (resource instanceof IProject) {
IProject p = (IProject) resource;
IScriptProject project = DLTKCore.create(p);
if (project == null || !p.isOpen() || !project.exists()) {
return new IRuntimeBuildpathEntry[0];
}
} else {
abort(MessageFormat.format(
LaunchingMessages.ScriptRuntime_Buildpath_references_non_existant_project___0__3,
entry.getPath().lastSegment()), null);
}
break;
case IRuntimeBuildpathEntry.CONTAINER:
IRuntimeBuildpathEntryResolver resolver = getContainerResolver(
entry.getContainerName());
if (resolver == null) {
return computeDefaultContainerEntries(entry, configuration);
}
return resolver.resolveRuntimeBuildpathEntry(entry, configuration);
case IRuntimeBuildpathEntry.ARCHIVE:
// verify the archive exists
String location = entry.getLocation();
if (location == null) {
abort(MessageFormat.format(
LaunchingMessages.ScriptRuntime_Buildpath_references_non_existant_archive___0__4,
entry.getPath().toString()), null);
}
IFileHandle fileHandle;
IPath path = entry.getPath();
if (EnvironmentPathUtils.isFull(path))
fileHandle = EnvironmentPathUtils.getFile(path);
else
fileHandle = LocalEnvironment.getInstance().getFile(path);
if (fileHandle == null || !fileHandle.exists()) {
abort(MessageFormat.format(
LaunchingMessages.ScriptRuntime_Buildpath_references_non_existant_archive___0__4,
entry.getPath().toString()), null);
}
break;
case IRuntimeBuildpathEntry.OTHER:
resolver = getContributedResolver(
((IRuntimeBuildpathEntry2) entry).getTypeId());
return resolver.resolveRuntimeBuildpathEntry(entry, configuration);
default:
break;
}
return new IRuntimeBuildpathEntry[] { entry };
}
/**
* Returns resolved entries for the given entry in the context of the given
* script project. If the entry is of kind <code>CONTAINER</code>, container
* resolvers are consulted.
* <p>
* If the given entry is a container, and a resolver is not registered,
* resolved runtime classpath entries are calculated from the associated
* container classpath entries, in the context of the given project.
* </p>
*
* @param entry
* runtime classpath entry
* @param project
* Script project context
* @return resolved runtime classpath entry
* @exception CoreException
* if unable to resolve
* @see IRuntimeBuildpathEntryResolver
*
*/
public static IRuntimeBuildpathEntry[] resolveRuntimeBuildpathEntry(
IRuntimeBuildpathEntry entry, IScriptProject project)
throws CoreException {
switch (entry.getType()) {
case IRuntimeBuildpathEntry.CONTAINER:
IRuntimeBuildpathEntryResolver resolver = getContainerResolver(
entry.getContainerName());
if (resolver == null) {
return computeDefaultContainerEntries(entry, project);
}
return resolver.resolveRuntimeBuildpathEntry(entry, project);
case IRuntimeBuildpathEntry.OTHER:
resolver = getContributedResolver(
((IRuntimeBuildpathEntry2) entry).getTypeId());
return resolver.resolveRuntimeBuildpathEntry(entry, project);
default:
break;
}
return new IRuntimeBuildpathEntry[] { entry };
}
/**
* Computes and returns the unresolved build path for the given launch
* configuration. Variable and container entries are unresolved.
*
* @param configuration
* launch configuration
* @return unresolved runtime buildpath entries
* @exception CoreException
* if unable to compute the buildpath
*/
public static IRuntimeBuildpathEntry[] computeUnresolvedRuntimeBuildpath(
ILaunchConfiguration configuration) throws CoreException {
return getBuildpathProvider(configuration)
.computeUnresolvedBuildpath(configuration);
}
/**
* Resolves the given buildpath, returning the resolved buildpath in the
* context of the given launch configuration.
*
* @param entries
* unresolved buildpath
* @param configuration
* launch configuration
* @return resolved runtime buildpath entries
* @exception CoreException
* if unable to compute the buildpath
*/
public static IRuntimeBuildpathEntry[] resolveRuntimeBuildpath(
IRuntimeBuildpathEntry[] entries,
ILaunchConfiguration configuration) throws CoreException {
return getBuildpathProvider(configuration).resolveBuildpath(entries,
configuration);
}
/**
* Returns all registered runtime buildpath entry resolvers.
*/
private static Map<String, IRuntimeBuildpathEntryResolver> getEntryResolvers() {
if (fgRuntimeBuildpathEntryResolvers == null) {
initializeResolvers();
}
return fgRuntimeBuildpathEntryResolvers;
}
/**
* Returns the resolver registered for the given contributed buildpath entry
* type.
*
* @param typeId
* the id of the contributed buildpath entry
* @return the resolver registered for the given buildpath entry
*/
private static IRuntimeBuildpathEntryResolver getContributedResolver(
String typeId) {
IRuntimeBuildpathEntryResolver resolver = getEntryResolvers()
.get(typeId);
if (resolver == null) {
return new DefaultEntryResolver();
}
return resolver;
}
/**
* Computes the default application buildpath entries for the given project.
*
* @param jproject
* The project to compute the buildpath for
* @return The computed buildpath. May be empty, but not null.
* @throws CoreException
* if unable to compute the default buildpath
*/
public static String[] computeDefaultRuntimeClassPath(
IScriptProject jproject) throws CoreException {
IRuntimeBuildpathEntry[] unresolved = computeUnresolvedRuntimeBuildpath(
jproject);
// 1. remove bootpath entries
// 2. resolve & translate to local file system paths
List<String> resolved = new ArrayList<>(unresolved.length);
for (int i = 0; i < unresolved.length; i++) {
IRuntimeBuildpathEntry entry = unresolved[i];
if (entry
.getBuildpathProperty() == IRuntimeBuildpathEntry.USER_ENTRY) {
IRuntimeBuildpathEntry[] entries = resolveRuntimeBuildpathEntry(
entry, jproject);
for (int j = 0; j < entries.length; j++) {
String location = entries[j].getLocation();
if (location != null) {
resolved.add(location);
}
}
}
}
return resolved.toArray(new String[resolved.size()]);
}
public static ISourceContainer[] getSourceContainers(
IRuntimeBuildpathEntry[] resolved) {
return ScriptSourceLookupUtil.translate(resolved);
}
public static IRuntimeBuildpathEntry[] computeUnresolvedSourceBuildpath(
ILaunchConfiguration configuration) throws CoreException {
return getScriptpathProvider(configuration)
.computeUnresolvedBuildpath(configuration);
}
}