| /******************************************************************************* |
| * 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 |
| * |
| * BEA - Daniel R Somerfield - Bug 88939 |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.launching; |
| |
| import java.io.IOException; |
| import java.net.URI; |
| |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.TransformerException; |
| |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| 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.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.dltk.core.BuildpathContainerInitializer; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IBuildpathEntry; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.environment.EnvironmentPathUtils; |
| import org.eclipse.dltk.core.environment.IFileHandle; |
| import org.eclipse.dltk.launching.IRuntimeBuildpathEntry; |
| import org.eclipse.dltk.launching.LaunchingMessages; |
| import org.eclipse.dltk.launching.ScriptLaunchConfigurationConstants; |
| import org.eclipse.osgi.util.NLS; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| |
| /** |
| * An entry on the runtime buildpath that the user can manipulate and share in a |
| * launch configuration. |
| */ |
| public class RuntimeBuildpathEntry implements IRuntimeBuildpathEntry { |
| |
| /** |
| * This entry's type - must be set on creation. |
| */ |
| private int fType = -1; |
| |
| /** |
| * This entry's buildpath property. |
| */ |
| private int fBuildpathProperty = -1; |
| |
| /** |
| * This entry's associated build path entry. |
| */ |
| private IBuildpathEntry fBuildpathEntry = null; |
| |
| /** |
| * Associated Script project, or <code>null</code> |
| */ |
| private IScriptProject fProject = null; |
| |
| /** |
| * The path if the entry was invalid and fBuildpathEntry is null |
| */ |
| private IPath fInvalidPath; |
| |
| /** |
| * Constructs a new runtime buildpath entry based on the (build) buildpath |
| * entry. |
| * |
| * @param entry |
| * the associated buildpath entry |
| */ |
| public RuntimeBuildpathEntry(IBuildpathEntry entry) { |
| switch (entry.getEntryKind()) { |
| case IBuildpathEntry.BPE_PROJECT: |
| setType(PROJECT); |
| break; |
| case IBuildpathEntry.BPE_LIBRARY: |
| setType(ARCHIVE); |
| break; |
| case IBuildpathEntry.BPE_SOURCE: |
| setType(SOURCE); |
| break; |
| default: |
| throw new IllegalArgumentException(NLS.bind( |
| LaunchingMessages.RuntimeBuildpathEntry_Illegal_classpath_entry__0__1, |
| entry.toString())); |
| } |
| setBuildpathEntry(entry); |
| initializeBuildpathProperty(); |
| } |
| |
| /** |
| * Constructs a new container entry in the context of the given project |
| * |
| * @param entry |
| * buildpath entry |
| * @param buildpathProperty |
| * this entry's buildpath property |
| */ |
| public RuntimeBuildpathEntry(IBuildpathEntry entry, int buildpathProperty) { |
| switch (entry.getEntryKind()) { |
| case IBuildpathEntry.BPE_CONTAINER: |
| setType(CONTAINER); |
| break; |
| default: |
| throw new IllegalArgumentException(NLS.bind( |
| LaunchingMessages.RuntimeBuildpathEntry_Illegal_classpath_entry__0__1, |
| entry.toString())); |
| } |
| setBuildpathEntry(entry); |
| setBuildpathProperty(buildpathProperty); |
| } |
| |
| /** |
| * Reconstructs a runtime buildpath entry from the given XML document root |
| * not. |
| * |
| * @param root |
| * a memento root doc element created by this class |
| * @exception CoreException |
| * if unable to restore from the given memento |
| */ |
| public RuntimeBuildpathEntry(Element root) throws CoreException { |
| try { |
| setType(Integer.parseInt(root.getAttribute("type"))); //$NON-NLS-1$ |
| } catch (NumberFormatException e) { |
| abort(LaunchingMessages.RuntimeBuildpathEntry_Unable_to_recover_runtime_class_path_entry_type_2, |
| e); |
| } |
| try { |
| setBuildpathProperty(Integer.parseInt(root.getAttribute("path"))); //$NON-NLS-1$ |
| } catch (NumberFormatException e) { |
| abort(LaunchingMessages.RuntimeBuildpathEntry_Unable_to_recover_runtime_class_path_entry_location_3, |
| e); |
| } |
| |
| String path; |
| |
| switch (getType()) { |
| case PROJECT: |
| String name = root.getAttribute("projectName"); //$NON-NLS-1$ |
| if (isEmpty(name)) { |
| abort(LaunchingMessages.RuntimeBuildpathEntry_Unable_to_recover_runtime_class_path_entry___missing_project_name_4, |
| null); |
| } else { |
| IProject proj = ResourcesPlugin.getWorkspace().getRoot() |
| .getProject(name); |
| setBuildpathEntry(DLTKCore.newProjectEntry(proj.getFullPath())); |
| } |
| break; |
| case ARCHIVE: |
| path = root.getAttribute("externalArchive"); //$NON-NLS-1$ |
| if (isEmpty(path)) { |
| // internal |
| path = root.getAttribute("internalArchive"); //$NON-NLS-1$ |
| if (isEmpty(path)) { |
| abort(LaunchingMessages.RuntimeBuildpathEntry_Unable_to_recover_runtime_class_path_entry___missing_archive_path_5, |
| null); |
| } else { |
| setBuildpathEntry(createLibraryEntry(path)); |
| } |
| } else { |
| // external |
| setBuildpathEntry(createLibraryEntry(path)); |
| } |
| break; |
| case CONTAINER: |
| String var = root.getAttribute("containerPath"); //$NON-NLS-1$ |
| if (isEmpty(var)) { |
| abort(LaunchingMessages.RuntimeBuildpathEntry_Unable_to_recover_runtime_class_path_entry___missing_variable_name_6, |
| null); |
| } else { |
| setBuildpathEntry(DLTKCore.newContainerEntry(new Path(var))); |
| } |
| break; |
| } |
| |
| String name = root.getAttribute("scriptProject"); //$NON-NLS-1$ |
| if (isEmpty(name)) { |
| fProject = null; |
| } else { |
| IProject project2 = ResourcesPlugin.getWorkspace().getRoot() |
| .getProject(name); |
| fProject = DLTKCore.create(project2); |
| } |
| } |
| |
| private IBuildpathEntry createLibraryEntry(String path) { |
| Path p = new Path(path); |
| if (!p.isAbsolute()) { |
| fInvalidPath = p; |
| return null; |
| // abort("There was a problem with path \" " + path + "\": paths |
| // must be absolute.", null); |
| } |
| |
| return DLTKCore.newLibraryEntry(p); |
| } |
| |
| /** |
| * Throws an internal error exception |
| */ |
| protected void abort(String message, Throwable e) throws CoreException { |
| IStatus s = new Status(IStatus.ERROR, |
| DLTKLaunchingPlugin.getUniqueIdentifier(), |
| ScriptLaunchConfigurationConstants.ERR_INTERNAL_ERROR, message, |
| e); |
| throw new CoreException(s); |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#getType() |
| */ |
| @Override |
| public int getType() { |
| return fType; |
| } |
| |
| /** |
| * Sets this entry's type |
| * |
| * @param type |
| * this entry's type |
| */ |
| private void setType(int type) { |
| fType = type; |
| } |
| |
| /** |
| * Sets the buildpath entry associated with this runtime buildpath entry. |
| * Clears the cache of the resolved entry. |
| * |
| * @param entry |
| * the buildpath entry associated with this runtime buildpath |
| * entry |
| */ |
| private void setBuildpathEntry(IBuildpathEntry entry) { |
| fBuildpathEntry = entry; |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#getBuildpathEntry() |
| */ |
| @Override |
| public IBuildpathEntry getBuildpathEntry() { |
| return fBuildpathEntry; |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#getMemento() |
| */ |
| @Override |
| public String getMemento() throws CoreException { |
| |
| Document doc; |
| try { |
| doc = DLTKLaunchingPlugin.getDocument(); |
| } catch (ParserConfigurationException e) { |
| IStatus status = new Status(IStatus.ERROR, |
| DLTKLaunchingPlugin.getUniqueIdentifier(), |
| ScriptLaunchConfigurationConstants.ERR_INTERNAL_ERROR, |
| LaunchingMessages.RuntimeBuildpathEntry_An_exception_occurred_generating_runtime_classpath_memento_8, |
| e); |
| throw new CoreException(status); |
| } |
| Element node = doc.createElement("runtimeBuildpathEntry"); //$NON-NLS-1$ |
| doc.appendChild(node); |
| node.setAttribute("type", Integer.toString(getType())); //$NON-NLS-1$ |
| node.setAttribute("path", Integer.toString(getBuildpathProperty())); //$NON-NLS-1$ |
| switch (getType()) { |
| case PROJECT: |
| node.setAttribute("projectName", getPath().lastSegment()); //$NON-NLS-1$ |
| break; |
| case ARCHIVE: |
| IResource res = getResource(); |
| if (res == null) { |
| node.setAttribute("externalArchive", getPath().toString()); //$NON-NLS-1$ |
| } else { |
| node.setAttribute("internalArchive", //$NON-NLS-1$ |
| res.getFullPath().toString()); |
| } |
| break; |
| case CONTAINER: |
| node.setAttribute("containerPath", getPath().toString()); //$NON-NLS-1$ |
| break; |
| } |
| if (getScriptProject() != null) { |
| node.setAttribute("scriptProject", //$NON-NLS-1$ |
| getScriptProject().getElementName()); |
| } |
| try { |
| return DLTKLaunchingPlugin.serializeDocument(doc); |
| } catch (IOException e) { |
| IStatus status = new Status(IStatus.ERROR, |
| DLTKLaunchingPlugin.getUniqueIdentifier(), |
| ScriptLaunchConfigurationConstants.ERR_INTERNAL_ERROR, |
| LaunchingMessages.RuntimeBuildpathEntry_An_exception_occurred_generating_runtime_classpath_memento_8, |
| e); |
| throw new CoreException(status); |
| } catch (TransformerException e) { |
| IStatus status = new Status(IStatus.ERROR, |
| DLTKLaunchingPlugin.getUniqueIdentifier(), |
| ScriptLaunchConfigurationConstants.ERR_INTERNAL_ERROR, |
| LaunchingMessages.RuntimeBuildpathEntry_An_exception_occurred_generating_runtime_classpath_memento_8, |
| e); |
| throw new CoreException(status); |
| } |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#getPath() |
| */ |
| @Override |
| public IPath getPath() { |
| IBuildpathEntry entry = getBuildpathEntry(); |
| return entry != null ? entry.getPath() : fInvalidPath; |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#getResource() |
| */ |
| @Override |
| public IResource getResource() { |
| switch (getType()) { |
| case CONTAINER: |
| return null; |
| default: |
| return getResource(getPath()); |
| } |
| } |
| |
| /** |
| * Returns the resource in the workspace assciated with the given absolute |
| * path, or <code>null</code> if none. The path may have a device. |
| * |
| * @param path |
| * absolute path, or <code>null</code> |
| * @return resource or <code>null</code> |
| */ |
| protected IResource getResource(IPath path) { |
| if (path != null) { |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| if (path.getDevice() == null) { |
| // search relative to the workspace if no device present |
| return root.findMember(path); |
| } |
| // look for files or folders with the given path |
| IFile[] files = root.findFilesForLocationURI(URIUtil.toURI(path)); |
| if (files.length > 0) { |
| return files[0]; |
| } |
| IContainer[] containers = root |
| .findContainersForLocationURI(URIUtil.toURI(path)); |
| if (containers.length > 0) { |
| return containers[0]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Initlaizes the buildpath property based on this entry's type. |
| */ |
| private void initializeBuildpathProperty() { |
| switch (getType()) { |
| case PROJECT: |
| case ARCHIVE: |
| case SOURCE: |
| setBuildpathProperty(USER_ENTRY); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#setBuildpathProperty(int) |
| */ |
| @Override |
| public void setBuildpathProperty(int location) { |
| fBuildpathProperty = location; |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#setBuildpathProperty(int) |
| */ |
| @Override |
| public int getBuildpathProperty() { |
| return fBuildpathProperty; |
| } |
| |
| /** |
| * @see IRuntimeBuildpathEntry#getLocation() |
| */ |
| @Override |
| public String getLocation() { |
| |
| IPath path = null; |
| switch (getType()) { |
| case PROJECT: |
| IScriptProject pro = (IScriptProject) DLTKCore |
| .create(getResource()); |
| if (pro != null) { |
| path = pro.getPath(); |
| } |
| break; |
| case ARCHIVE: |
| path = getPath(); |
| if (EnvironmentPathUtils.isFull(path)) { |
| IFileHandle file = EnvironmentPathUtils.getFile(path); |
| if (file != null) { |
| path = file.getPath(); |
| } |
| } |
| break; |
| case SOURCE: |
| path = getPath(); |
| break; |
| case CONTAINER: |
| break; |
| } |
| return resolvePath(path).toPortableString(); |
| } |
| |
| @Override |
| public URI getLocationURI() { |
| switch (getType()) { |
| case PROJECT: |
| IResource pro = getResource(); |
| if (pro != null) { |
| return pro.getLocationURI(); |
| } |
| break; |
| case ARCHIVE: { |
| IPath path = getPath(); |
| if (EnvironmentPathUtils.isFull(path)) { |
| IFileHandle file = EnvironmentPathUtils.getFile(path); |
| if (file != null) { |
| return file.toURI(); |
| } |
| } |
| } |
| break; |
| case SOURCE: { |
| IPath path = getPath(); |
| if (EnvironmentPathUtils.isFull(path)) { |
| IFileHandle file = EnvironmentPathUtils.getFile(path); |
| if (file != null) { |
| return file.toURI(); |
| } |
| } else { |
| IResource resource = getResource(path); |
| if (resource != null) { |
| return resource.getLocationURI(); |
| } |
| } |
| } |
| break; |
| case CONTAINER: |
| break; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the OS path for the given absolute or workspace relative path |
| */ |
| protected IPath resolvePath(IPath path) { |
| if (path != null) { |
| IResource res = null; |
| if (EnvironmentPathUtils.isFull(path) == true |
| && EnvironmentPathUtils.getPathEnvironment(path) |
| .isLocal()) { |
| path = EnvironmentPathUtils.getFile(path).getPath(); |
| } |
| if (path.getDevice() == null) { |
| // if there is no device specified, find the resource |
| res = getResource(path); |
| } |
| if (res == null) { |
| return path; |
| } |
| URI locationURI = res.getLocationURI(); |
| if (locationURI != null) { |
| return new Path(locationURI.getPath()); |
| } |
| IPath location = res.getLocation(); |
| if (location != null) { |
| return location; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @see Object#equals(Object) |
| */ |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof IRuntimeBuildpathEntry) { |
| IRuntimeBuildpathEntry r = (IRuntimeBuildpathEntry) obj; |
| if (getType() == r.getType() |
| && getBuildpathProperty() == r.getBuildpathProperty()) { |
| if (getType() == IRuntimeBuildpathEntry.CONTAINER) { |
| String id = getPath().segment(0); |
| BuildpathContainerInitializer initializer = DLTKCore |
| .getBuildpathContainerInitializer(id); |
| IScriptProject scriptProject1 = getScriptProject(); |
| IScriptProject scriptProject2 = r.getScriptProject(); |
| if (initializer == null || scriptProject1 == null |
| || scriptProject2 == null) { |
| // containers are equal if their ID is equal by default |
| return getPath().equals(r.getPath()); |
| } |
| Object comparisonID1 = initializer |
| .getComparisonID(getPath(), scriptProject1); |
| Object comparisonID2 = initializer |
| .getComparisonID(r.getPath(), scriptProject2); |
| return comparisonID1.equals(comparisonID2); |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether the given objects are equal, accounting for null |
| */ |
| protected boolean equal(Object one, Object two) { |
| if (one == null) { |
| return two == null; |
| } |
| return one.equals(two); |
| } |
| |
| /** |
| * @see Object#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| if (getType() == CONTAINER) { |
| return getPath().segment(0).hashCode() + getType(); |
| } |
| return getPath().hashCode() + getType(); |
| } |
| |
| /** |
| * Creates a new underlying buildpath entry for this runtime buildpath entry |
| * with the given paths, due to a change in source attachment. |
| */ |
| protected void updateBuildpathEntry(IPath path) { |
| IBuildpathEntry entry = null; |
| IBuildpathEntry original = getBuildpathEntry(); |
| switch (getType()) { |
| case ARCHIVE: |
| entry = DLTKCore.newLibraryEntry(path, original.getAccessRules(), |
| original.getExtraAttributes(), original.isExported(), |
| original.isExternal()); |
| break; |
| default: |
| return; |
| } |
| setBuildpathEntry(entry); |
| } |
| |
| protected boolean isEmpty(String string) { |
| return string == null || string.length() == 0; |
| } |
| |
| @Override |
| public String toString() { |
| if (fBuildpathEntry != null) { |
| return fBuildpathEntry.toString(); |
| } |
| return super.toString(); |
| |
| } |
| |
| @Override |
| public IScriptProject getScriptProject() { |
| return fProject; |
| } |
| |
| /** |
| * Sets the Script project associated with this buildpath entry. |
| * |
| * @param project |
| * Script project |
| */ |
| public void setScriptProject(IScriptProject project) { |
| fProject = project; |
| } |
| |
| @Override |
| public String getContainerName() { |
| if (getType() == IRuntimeBuildpathEntry.CONTAINER) { |
| return getPath().segment(0); |
| } |
| return null; |
| } |
| } |