blob: b6db65f99307fcd5bb1ab356a0ebb6de1cf896b8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 Christian Pontesegger and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.modules.platform;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
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.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.ease.IExecutionListener;
import org.eclipse.ease.IScriptEngine;
import org.eclipse.ease.Script;
import org.eclipse.ease.modules.AbstractScriptModule;
import org.eclipse.ease.modules.ScriptParameter;
import org.eclipse.ease.modules.WrapToScript;
import org.eclipse.ease.tools.ResourceTools;
import org.eclipse.ease.tools.RunnableWithResult;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.ui.dialogs.ContainerSelectionDialog;
import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
import org.eclipse.ui.dialogs.IOverwriteQuery;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.texteditor.MarkerUtilities;
import org.eclipse.ui.views.navigator.ResourceComparator;
import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider;
import org.eclipse.ui.wizards.datatransfer.ImportOperation;
/**
* Provides file access for workspace and file system resources. Methods accepting location objects can deal with {@link String}, {@link URI}, {@link IFile} and
* {@link File} instances.
*/
public class ResourcesModule extends AbstractScriptModule implements IExecutionListener {
/** Module identifier. */
public static final String MODULE_ID = "/System/Resources";
/** Access modifier for read mode (1). */
@WrapToScript
public static final int READ = IFileHandle.READ;
/** Access modifier for write mode (2). */
@WrapToScript
public static final int WRITE = IFileHandle.WRITE;
/** Access modifier for append mode (4). */
@WrapToScript
public static final int APPEND = IFileHandle.APPEND;
/** Open file handles managed by this module. */
private final Collection<IFileHandle> fOpenHandles = new HashSet<>();
private static final String LINE_DELIMITER = System.getProperty(Platform.PREF_LINE_SEPARATOR);
/**
* Get the workspace root.
*
* @return workspace root
*/
@WrapToScript
public static IWorkspaceRoot getWorkspace() {
return ResourcesPlugin.getWorkspace().getRoot();
}
/**
* Get a project instance.
*
* @param name
* project name
* @return project or <code>null</code>
*/
@WrapToScript
public static IProject getProject(final String name) {
return getWorkspace().getProject(name);
}
/**
* Get a workspace or file system file. Resolves relative and absolute file locations. Relative files are resolved against the current script file. If
* <i>exists</i> is <code>false</code> this method also returns files that do not exist yet. If <code>true</code> only existing instances are returned.
*
* @scriptExample getFile("workspace://my project/some folder/file.txt") to get the file.txt resource from the workspace
* @scriptExample getFile("project://some folder/file.txt") to get the file.txt resource as a project relative path
*
* @param location
* file location/path to resolve
* @param exists
* whether the resolved file needs to exist
* @return resolved {@link IFile} or {@link File} instance
*/
@WrapToScript
public Object getFile(final String location, @ScriptParameter(defaultValue = "true") final boolean exists) {
final Object resource = ResourceTools.resolve(location, getScriptEngine().getExecutedFile());
if (exists)
return fileExists(resource) ? resource : null;
return resource;
}
/**
* Create a new workspace project. Will create a new project if it not already exists. If creation fails, <code>null</code> is returned.
*
* @param name
* name or project to create
* @return <code>null</code> or project
*/
@WrapToScript
public static IProject createProject(final String name) {
final IProject project = getProject(name);
if (!project.exists()) {
try {
project.create(new NullProgressMonitor());
project.open(new NullProgressMonitor());
} catch (final CoreException e) {
return null;
}
}
return project;
}
/**
* Create a new folder in the workspace or the file system.
*
* @param location
* folder location
* @return {@link IFolder}, {@link File} or <code>null</code> in case of error
* @throws CoreException
* if this method fails. Reasons include:
* <ul>
* <li>This resource already exists in the workspace.</li>
* <li>The workspace contains a resource of a different type at the same path as this resource.</li>
* <li>The parent of this resource does not exist.</li>
* <li>The parent of this resource is a project that is not open.</li>
* <li>The parent contains a resource of a different type at the same path as this resource.</li>
* <li>The parent of this resource is virtual, but this resource is not.</li>
* <li>The name of this resource is not valid (according to <code>IWorkspace.validateName</code>).</li>
* <li>The corresponding location in the local file system is occupied by a file (as opposed to a directory).</li>
* <li>The corresponding location in the local file system is occupied by a folder and <code>force </code> is <code>false</code>.</li>
* <li>Resource changes are disallowed during certain types of resource change event notification. See <code>IResourceChangeEvent</code> for
* more details.</li>
* </ul>
*/
@WrapToScript
public Object createFolder(final Object location) throws CoreException {
Object folder = ResourceTools.resolve(location, getScriptEngine().getExecutedFile());
if (folder instanceof File) {
if (!((File) folder).exists())
if (((File) folder).mkdirs())
return folder;
} else if (folder instanceof IResource) {
// resolve returns IFile instances if a container does not exist, so we need to convert it to a folder
if (folder instanceof IFile)
folder = ((IResource) folder).getProject().getFolder(((IResource) folder).getProjectRelativePath());
if (folder instanceof IFolder) {
if (!((IFolder) folder).exists()) {
((IFolder) folder).create(true, true, new NullProgressMonitor());
return folder;
}
}
}
return null;
}
/**
* Create a new file in the workspace or the file system.
*
* @param location
* file location
* @return {@link IFile}, {@link File} or <code>null</code> in case of error
* @throws Exception
* problems on file access
*/
@WrapToScript
public Object createFile(final Object location) throws Exception {
final IFileHandle handle = getFileHandle(location, IFileHandle.WRITE, false);
return handle.getFile();
}
/**
* Opens a file from the workspace or the file system. If the file does not exist and we open it for writing, the file is created automatically.
*
* @param location
* file location
* @param mode
* one of {@module #READ}, {@module #WRITE}, {@module #APPEND}
* @param autoClose
* automatically close resource when script engine is terminated
*
* @return file handle instance to be used for file modification commands
* @throws Exception
* problems on file access
*/
@WrapToScript
public IFileHandle openFile(final Object location, @ScriptParameter(defaultValue = "1") final int mode,
@ScriptParameter(defaultValue = "true") boolean autoClose) throws Exception {
return getFileHandle(location, mode, autoClose);
}
/**
* Verifies that a specific file exists.
*
* @param location
* file location to verify
* @return <code>true</code> if file exists
*/
@WrapToScript
public boolean fileExists(final Object location) {
final Object resource = ResourceTools.resolve(location, getScriptEngine().getExecutedFile());
return (resource != null) && (ResourceTools.exists(resource)) && (ResourceTools.isFile(resource));
}
/**
* Close a file. Releases system resources bound by an open file.
*
* @param handle
* handle to be closed
*/
@WrapToScript
public void closeFile(final IFileHandle handle) {
fOpenHandles.remove(handle);
handle.close();
}
/**
* Read data from a file. To repeatedly read from a file retrieve a {@link IFileHandle} first using {@module #openFile(String, int)} and use the handle for
* <i>location</i>.
*
* @param location
* file location, file handle or file instance
* @param bytes
* amount of bytes to read (-1 for whole file)
* @return file data or <code>null</code> if EOF is reached
* @throws Exception
* problems on file access
*/
@WrapToScript
public String readFile(final Object location, @ScriptParameter(defaultValue = "-1") final int bytes) throws Exception {
final IFileHandle handle = getFileHandle(location, IFileHandle.READ, false);
if (handle != null) {
final String result = handle.read(bytes);
if (!(location instanceof IFileHandle))
handle.close();
return result;
}
throw new IOException("File \"" + location + "\" not found");
}
/**
* Copies a file from location to targetLocation.
*
* @param sourceLocation
* file location, file handle or file instance for the source object
* @param targetLocation
* file location, file handle or file instance for the target object
* @throws Exception
* problems on file access
*/
@WrapToScript
public void copyFile(final Object sourceLocation, final Object targetLocation) throws Exception {
final IFileHandle inputFile = getFileHandle(sourceLocation, -1, false);
final IFileHandle targetFile = getFileHandle(targetLocation, -1, false);
if (targetFile != null) {
Files.copy(inputFile.getPath(), targetFile.getPath(), StandardCopyOption.REPLACE_EXISTING);
}
if (targetFile.getFile() instanceof IResource) {
((IResource) targetFile.getFile()).getParent().refreshLocal(1, null);
}
}
/**
* Delete a file from the workspace or local file system.
*
* @param source
* file to be deleted
* @throws CoreException
* on deletion errors
*/
@WrapToScript
public void deleteFile(final Object source) throws CoreException {
final Object file = ResourceTools.resolve(source, getScriptEngine().getExecutedFile());
if (ResourceTools.isFile(file)) {
if (file instanceof IFile)
((IFile) file).delete(true, new NullProgressMonitor());
else if (file instanceof File)
((File) file).delete();
}
}
/**
* Delete a folder from the workspace or local file system.
*
* @param source
* folder to be deleted
* @throws CoreException
* on deletion errors
*/
@WrapToScript
public void deleteFolder(final Object source) throws CoreException {
final Object folder = ResourceTools.resolve(source, getScriptEngine().getExecutedFile());
if (ResourceTools.isFolder(folder)) {
if (folder instanceof IFolder)
((IFolder) folder).delete(true, new NullProgressMonitor());
else if (folder instanceof File)
((File) folder).delete();
}
}
/**
* Delete a project from the workspace.
*
* @param source
* project to be deleted
* @throws CoreException
* on deletion errors
*/
@WrapToScript
public void deleteProject(final Object source) throws CoreException {
final Object project = ResourceTools.resolve(source, getScriptEngine().getExecutedFile());
if (project instanceof IProject)
((IProject) project).delete(true, new NullProgressMonitor());
else if (source != null) {
final IProject localProject = getProject(source.toString());
if (localProject != null)
localProject.delete(true, new NullProgressMonitor());
}
}
/**
* Read a single line from a file. To repeatedly read from a file retrieve a {@link IFileHandle} first using {@module #openFile(String, int)} and use the
* handle for <i>location</i>.
*
* @param location
* file location, file handle or file instance
* @return line of text or <code>null</code> if EOF is reached
* @throws Exception
* problems on file access
*/
@WrapToScript
public String readLine(final Object location) throws Exception {
final IFileHandle handle = getFileHandle(location, IFileHandle.READ, false);
if (handle != null) {
final String result = handle.readLine();
if (!(location instanceof IFileHandle))
handle.close();
return result;
}
throw new IOException("File \"" + location + "\" not found");
}
/**
* Write data to a file. When not using an {@link IFileHandle}, previous file content will be overridden. Files that do not exist yet will be automatically
* created. After the write operation the file remains open. It needs to be closed explicitly using the {@module #closeFile(IFileHandle)} command
*
* @param location
* file location
* @param data
* data to be written
* @param mode
* write mode ({@module #WRITE}/{@module #APPEND})
* @param autoClose
* automatically close resource when script engine is terminated
* @return file handle to continue write operations
* @throws Exception
* problems on file access
*/
@WrapToScript
public IFileHandle writeFile(final Object location, final Object data, @ScriptParameter(defaultValue = "2") final int mode,
@ScriptParameter(defaultValue = "true") boolean autoClose) throws Exception {
final IFileHandle handle = getFileHandle(location, mode, autoClose);
if (handle != null) {
if (data instanceof byte[])
handle.write((byte[]) data);
else if (data != null)
handle.write(data.toString());
} else
throw new IOException("Could not access resource: " + location);
return handle;
}
/**
* Write a line of data to a file. When not using an {@link IFileHandle}, previous file content will be overridden. Files that do not exist yet will be
* automatically created.
*
* @param location
* file location
* @param data
* data to be written
* @param mode
* write mode ({@module #WRITE}/{@module #APPEND})
* @param autoClose
* automatically close resource when script engine is terminated
* @return file handle to continue write operations
* @throws Exception
* problems on file access
*/
@WrapToScript
public IFileHandle writeLine(final Object location, final String data, @ScriptParameter(defaultValue = "2") final int mode,
@ScriptParameter(defaultValue = "true") boolean autoClose) throws Exception {
final IFileHandle handle = getFileHandle(location, mode, autoClose);
if (handle != null)
handle.write(data + LINE_DELIMITER);
else
throw new IOException("Could not access resource: " + location);
return handle;
}
private IFileHandle getFileHandle(final Object location, final int mode, boolean autoClose) throws Exception {
IFileHandle handle = null;
if (location instanceof IFileHandle)
handle = (IFileHandle) location;
else if (location instanceof File)
handle = new FilesystemHandle((File) location, mode);
else if (location instanceof IFile)
handle = new ResourceHandle((IFile) location, mode);
else if (location != null)
handle = getFileHandle(ResourceTools.resolve(location, getScriptEngine().getExecutedFile()), mode, autoClose);
if ((handle != null) && (!handle.exists())) {
// create file if it does not exist yet
handle.createFile(true);
}
if ((handle != null) && (autoClose)) {
fOpenHandles.add(handle);
// we need to manage a handle, register script engine listener
getScriptEngine().addExecutionListener(this);
}
return handle;
}
/**
* Opens a file dialog. Depending on the <i>rootFolder</i> a workspace dialog or a file system dialog will be used. If the folder cannot be located, the
* workspace root folder is used by default. When type is set to {@module #WRITE} or {@module #APPEND} a save dialog will be shown instead of the default
* open dialog.
*
* @param rootFolder
* root folder path to use
* @param type
* dialog type to use ({@module #WRITE}/ {@module #APPEND} for save dialog, other for open dialog)
* @param title
* dialog title
* @param message
* dialog message
* @return full path to selected file
*/
@WrapToScript
public String showFileSelectionDialog(@ScriptParameter(defaultValue = ScriptParameter.NULL) final Object rootFolder,
@ScriptParameter(defaultValue = "0") final int type, @ScriptParameter(defaultValue = ScriptParameter.NULL) final String title,
@ScriptParameter(defaultValue = ScriptParameter.NULL) final String message) {
Object root = ResourceTools.resolve(rootFolder, getScriptEngine().getExecutedFile());
if (rootFolder == null)
root = getWorkspace();
if (root instanceof File) {
// file system
final int mode;
switch (type) {
case WRITE:
// fall through
case APPEND:
mode = SWT.SAVE;
break;
default:
mode = SWT.OPEN;
break;
}
final File dialogRoot = (File) root;
final RunnableWithResult<String> runnable = new RunnableWithResult<String>() {
@Override
public String runWithTry() throws Throwable {
final FileDialog dialog = new FileDialog(Display.getDefault().getActiveShell(), mode);
if (title != null)
dialog.setText(title);
dialog.setFilterPath(dialogRoot.getAbsolutePath());
return dialog.open();
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
} else if (root instanceof IContainer) {
// workspace
final IContainer dialogRoot = (IContainer) root;
final RunnableWithResult<String> runnable = new RunnableWithResult<String>() {
@Override
public String runWithTry() throws Throwable {
if ((type == WRITE) || (type == APPEND)) {
// open a save as dialog
final SaveAsDialog dialog = new SaveAsDialog(Display.getDefault().getActiveShell());
// set default filename if a subfolder is selected
if (!dialogRoot.equals(getWorkspace()))
dialog.setOriginalFile(dialogRoot.getFile(new Path("newFile")));
dialog.setTitle(title);
dialog.setMessage(message);
if (dialog.open() == Window.OK)
return "workspace:/" + dialog.getResult().toPortableString();
} else {
// open a select file dialog
final ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(Display.getDefault().getActiveShell(),
new WorkbenchLabelProvider(), new WorkbenchContentProvider());
dialog.setTitle(title);
dialog.setMessage(message);
dialog.setInput(dialogRoot);
dialog.setComparator(new ResourceComparator(ResourceComparator.NAME));
if (dialog.open() == Window.OK)
return "workspace:/" + ((IResource) dialog.getFirstResult()).getFullPath().toPortableString();
}
return null;
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
return null;
}
/**
* Opens a dialog box which allows the user to select a container (project or folder). Workspace paths will always display the workspace as root object.
*
* @param rootFolder
* root folder to display: for workspace paths this will set the default selection
* @param title
* dialog title
* @param message
* dialog message
*
* @return path to selected folder
*/
@WrapToScript
public String showFolderSelectionDialog(@ScriptParameter(defaultValue = ScriptParameter.NULL) final Object rootFolder,
@ScriptParameter(defaultValue = ScriptParameter.NULL) final String title,
@ScriptParameter(defaultValue = ScriptParameter.NULL) final String message) {
Object root = ResourceTools.resolve(rootFolder, getScriptEngine().getExecutedFile());
if (rootFolder == null)
root = getWorkspace();
if (root instanceof File) {
final File dialogRoot = (File) root;
final RunnableWithResult<String> runnable = new RunnableWithResult<String>() {
@Override
public String runWithTry() throws Throwable {
final DirectoryDialog dialog = new DirectoryDialog(Display.getCurrent().getActiveShell());
if (title != null)
dialog.setText(title);
if (message != null)
dialog.setMessage(message);
dialog.setFilterPath(dialogRoot.getAbsolutePath());
return dialog.open();
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
} else if (root instanceof IContainer) {
// workspace
final IContainer dialogRoot = (IContainer) root;
final RunnableWithResult<String> runnable = new RunnableWithResult<String>() {
@Override
public String runWithTry() throws Throwable {
final ContainerSelectionDialog dialog = new ContainerSelectionDialog(Display.getDefault().getActiveShell(), dialogRoot, true, message);
dialog.setTitle(title);
if (dialog.open() == Window.OK)
return "workspace:/" + ((IPath) dialog.getResult()[0]).toPortableString();
return null;
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
return null;
}
/**
* Return files matching a certain pattern.
*
* @param pattern
* search pattern: use * and ? as wildcards. If the pattern starts with '^' then a regular expression can be used.
* @param rootFolder
* root folder to start your search from. <code>null</code> for workspace root
* @param recursive
* searches subfolders when set to <code>true</code>
* @return An array of all matching files
*/
@WrapToScript
public Object[] findFiles(final String pattern, @ScriptParameter(defaultValue = ScriptParameter.NULL) final Object rootFolder,
@ScriptParameter(defaultValue = "true") final boolean recursive) {
// evaluate expression to look for
Pattern regExp;
if (!pattern.startsWith("^"))
regExp = Pattern.compile(pattern.replace("*", ".*").replace('?', '.'));
else
regExp = Pattern.compile(pattern);
final List<Object> result = new ArrayList<>();
// locate root folder to start with
Object root = ResourceTools.resolve(rootFolder, getScriptEngine().getExecutedFile());
if (root == null)
root = getWorkspace();
if (root instanceof IContainer) {
// search in workspace
final Collection<IContainer> toVisit = new HashSet<>();
toVisit.add((IContainer) root);
do {
final IContainer container = toVisit.iterator().next();
toVisit.remove(container);
try {
for (final IResource child : container.members()) {
if (child instanceof IFile) {
if (regExp.matcher(child.getName()).matches())
result.add(child);
} else if ((recursive) && (child instanceof IContainer))
toVisit.add((IContainer) child);
}
} catch (final CoreException e) {
// cannot parse container, skip and continue with next one
}
} while (!toVisit.isEmpty());
} else if (root instanceof File) {
// search in file system
final Collection<File> toVisit = new HashSet<>();
toVisit.add((File) root);
do {
final File container = toVisit.iterator().next();
toVisit.remove(container);
for (final File child : container.listFiles()) {
if (child.isFile()) {
if (regExp.matcher(child.getName()).matches())
result.add(child);
} else if ((recursive) && (child.isDirectory()))
toVisit.add(child);
}
} while (!toVisit.isEmpty());
}
return result.toArray(new Object[result.size()]);
}
/**
* Links a project into the current workspace. Does not copy resources to the workspace.
*
* @param location
* location of project (needs to contain <i>.project</i> file)
* @return linked project, throws otherwise
* @throws CoreException
* when project description cannot be loaded from .project file
* @throws IOException
* when project location cannot be read/linked
*/
@WrapToScript
public IProject linkProject(final Object location) throws CoreException, IOException {
final Object resolvedLocation = ResourceTools.resolve(location, getScriptEngine().getExecutedFile());
if (resolvedLocation instanceof IContainer) {
throw new IOException("The folder to link is already part of the workspace: " + location);
} else if (resolvedLocation instanceof File) {
if (((File) resolvedLocation).isDirectory()) {
final IProjectDescription description = getProjectDescription((File) resolvedLocation);
final IProject project = getProject(description.getName());
project.create(description, null);
project.open(null);
return project;
} else
throw new IOException("location is not a directory");
} else {
throw new IOException("Could not resolve file location \"" + location + "\"");
}
}
private IProjectDescription getProjectDescription(File rootPath) throws CoreException {
final Path projectPath = new Path(rootPath.getAbsoluteFile() + File.separator + ".project");
return ResourcesPlugin.getWorkspace().loadProjectDescription(projectPath);
}
/**
* Imports a project into the current workspace. The project needs to be available on the file system and needs to be unpacked. Zipped projects cannot be
* imported.
*
* @param location
* location of project (needs to contain <i>.project</i> file)
* @return project instance, throws otherwise
* @throws InvocationTargetException
* on errors during the import
* @throws InterruptedException
* when import task got interrupted
* @throws IOException
* when project cannot be located on disk
* @throws CoreException
* when project description cannot be loaded from .project file
*/
@WrapToScript
public IProject importProject(final Object location) throws InvocationTargetException, InterruptedException, IOException, CoreException {
final Object resolvedFile = ResourceTools.resolve(location, getScriptEngine().getExecutedFile());
if (resolvedFile instanceof File) {
if (((File) resolvedFile).isDirectory()) {
final IProjectDescription projectDescription = getProjectDescription((File) resolvedFile);
final IProject project = createProject(projectDescription.getName());
final ImportOperation importOperation = new ImportOperation(project.getFullPath(), resolvedFile, FileSystemStructureProvider.INSTANCE,
file -> IOverwriteQuery.ALL);
importOperation.setCreateContainerStructure(false);
importOperation.run(new NullProgressMonitor());
return project;
} else
throw new IOException("location is not a directory");
} else
throw new IOException("Could not resolve file location \"" + location + "\"");
}
/**
* Refresh a given resource and all its child elements.
*
* @param resource
* {@link IFile}, {@link IFolder}, {@link IProject} or workspace root to update
* @throws CoreException
* if this method fails. Reasons include:
* <ul>
* <li>Resource changes are disallowed during certain types of resource change event notification. See {@link IResourceChangeEvent} for more
* details.</li>
* </ul>
* @scriptExample refreshResource(getProject("my project")) to update the project and all its subfolders
*/
@WrapToScript
public void refreshResource(final Object resource) throws CoreException {
final Object resolvedResource = ResourceTools.resolve(resource);
if (resolvedResource instanceof IResource)
((IResource) resolvedResource).refreshLocal(IResource.DEPTH_INFINITE, null);
}
/**
* Read from an InputStream into a string. Consumes an {@link InputStream} and stores all available data in a string. Usually a stream is only readable
* once.
*
* @param input
* input stream to read from
* @return string content of stream.
* @throws IOException
* on read error on the stream
*/
@WrapToScript
public String readStream(final InputStream input) throws IOException {
return ResourceTools.toString(input);
}
/**
* Create a problem marker on a file resource.
*
* @param severity
* one of <i>error</i>/<i>warning</i>/<i>info</i>
* @param location
* file resource to create marker for
* @param lineNumber
* line number to set marker on
* @param message
* message to be added to the marker
* @param type
* marker type to create, needs to match an existing type
* @param permanent
* <code>true</code> for permanent markers, <code>false</code> for temporary markers
* @throws CoreException
* when marker cannot be created
*/
@WrapToScript
public void createProblemMarker(final String severity, final Object location, final int lineNumber, final String message,
@ScriptParameter(defaultValue = "org.eclipse.core.resources.problemmarker") final String type,
@ScriptParameter(defaultValue = "true") final boolean permanent) throws CoreException {
final Object file = ResourceTools.resolve(location, getScriptEngine().getExecutedFile());
if (file instanceof IFile) {
int intSeverity = IMarker.SEVERITY_INFO;
if ("error".equals(severity))
intSeverity = IMarker.SEVERITY_ERROR;
if ("warning".equals(severity))
intSeverity = IMarker.SEVERITY_WARNING;
final HashMap<String, Object> attributes = new HashMap<>();
attributes.put(IMarker.LINE_NUMBER, lineNumber);
attributes.put(IMarker.SEVERITY, intSeverity);
attributes.put(IMarker.MESSAGE, message);
attributes.put(IMarker.TRANSIENT, !permanent);
MarkerUtilities.createMarker((IFile) file, attributes, type);
}
}
@Override
public void notify(IScriptEngine engine, Script script, int status) {
if (status == IExecutionListener.ENGINE_END) {
// close all managed file handles
for (final IFileHandle handle : fOpenHandles)
handle.close();
fOpenHandles.clear();
}
}
/**
* Create or update a zip file with given resources.
*
* @param sourceLocation
* source folder/file to zip
* @param zipLocation
* zip file to use
* @return zip file reference
* @throws IOException
* when zip file cannot be created
*/
@WrapToScript
public Object zip(Object sourceLocation, Object zipLocation) throws IOException {
// resolve source
Object sourceFile = ResourceTools.resolve(sourceLocation, getScriptEngine().getExecutedFile());
if (sourceFile instanceof IResource)
sourceFile = ((IResource) sourceFile).getLocation().toFile();
if (!(sourceFile instanceof File))
throw new IOException("Cannot resolve source: " + sourceLocation);
final java.nio.file.Path sourceFilePath = ((File) sourceFile).toPath();
// resolve target
Object zipFile = ResourceTools.resolve(zipLocation, getScriptEngine().getExecutedFile());
if (zipFile instanceof IFile)
zipFile = ((IFile) zipFile).getLocation().toFile();
if (!(zipFile instanceof File))
throw new IOException("Cannot resolve target: " + zipLocation);
final java.nio.file.Path zipFilePath = ((File) zipFile).toPath();
// create zip file
final URI uri = URI.create("jar:file:" + zipFilePath.toUri().getPath());
final Map<String, String> env = new HashMap<>();
env.put("create", ((File) zipFile).exists() ? Boolean.FALSE.toString() : Boolean.TRUE.toString());
try (FileSystem fileSystem = FileSystems.newFileSystem(uri, env)) {
final java.nio.file.Path root = fileSystem.getPath("/");
if (Files.isDirectory(((File) sourceFile).toPath())) {
// source is a directory
Files.walkFileTree(((File) sourceFile).toPath(), new SimpleFileVisitor<java.nio.file.Path>() {
@Override
public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs) throws IOException {
final java.nio.file.Path relativePath = sourceFilePath.relativize(file);
final java.nio.file.Path zipEntry = fileSystem.getPath(root.toString(), relativePath.toString());
Files.copy(file, zipEntry, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException {
final java.nio.file.Path relativePath = sourceFilePath.relativize(dir);
final java.nio.file.Path zipDirectory = fileSystem.getPath(root.toString(), relativePath.toString());
if (Files.notExists(zipDirectory))
Files.createDirectories(zipDirectory);
return FileVisitResult.CONTINUE;
}
});
} else {
// source is a simple file
if (Files.notExists(root))
Files.createDirectories(root);
final java.nio.file.Path destinationPath = fileSystem.getPath(root.toString(), sourceFilePath.getFileName().toString());
Files.copy(sourceFilePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
}
}
// refresh workspace
zipFile = ResourceTools.resolve(zipLocation, getScriptEngine().getExecutedFile());
if (zipFile instanceof IFile) {
try {
((IFile) zipFile).getParent().refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
} catch (final CoreException e) {
// ignore any refresh problems
}
}
return zipFile;
}
/**
* Unzip a zip file to the provided target location.
*
* @param zipLocation
* zip file to unzip
* @param targetLocation
* folder to unzip to
* @return target location reference
* @throws IOException
* when zip file cannot be unzipped
*/
@WrapToScript
public Object unzip(Object zipLocation, Object targetLocation) throws IOException {
// resolve zip
Object zipFile = ResourceTools.resolve(zipLocation, getScriptEngine().getExecutedFile());
if (zipFile instanceof IResource)
zipFile = ((IResource) zipFile).getLocation().toFile();
if (!(zipFile instanceof File))
throw new IOException("Cannot resolve source: " + zipLocation);
final java.nio.file.Path zipFilePath = ((File) zipFile).toPath();
// resolve target directory
Object targetDirectory = ResourceTools.resolve(targetLocation, getScriptEngine().getExecutedFile());
if (targetDirectory instanceof IResource)
targetDirectory = ((IResource) targetDirectory).getLocation().toFile();
if (!(targetDirectory instanceof File))
throw new IOException("Cannot resolve target: " + targetLocation);
if (!((File) targetDirectory).exists())
Files.createDirectories(((File) targetDirectory).toPath());
final java.nio.file.Path targetDirectoryPath = ((File) targetDirectory).toPath();
// create zip file
final URI uri = URI.create("jar:file:" + zipFilePath.toUri().getPath());
final Map<String, String> env = new HashMap<>();
env.put("create", Boolean.FALSE.toString());
try (FileSystem fileSystem = FileSystems.newFileSystem(uri, env)) {
final java.nio.file.Path root = fileSystem.getPath("/");
// walk the zip file tree and copy files to the destination
Files.walkFileTree(root, new SimpleFileVisitor<java.nio.file.Path>() {
@Override
public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs) throws IOException {
final java.nio.file.Path targetFile = java.nio.file.Paths.get(targetDirectoryPath.toString(), file.toString());
Files.copy(file, targetFile, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException {
final java.nio.file.Path targetDirectory = java.nio.file.Paths.get(targetDirectoryPath.toString(), dir.toString());
if (Files.notExists(targetDirectory))
Files.createDirectory(targetDirectory);
return FileVisitResult.CONTINUE;
}
});
}
// refresh workspace
targetDirectory = ResourceTools.resolve(targetLocation, getScriptEngine().getExecutedFile());
if (targetDirectory instanceof IContainer) {
try {
((IContainer) targetDirectory).getParent().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
} catch (final CoreException e) {
// ignore any refresh problems
}
}
return targetDirectory;
}
}