blob: 3a2ddc1adec802949f9d8d51774048f4f1c2089d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.debug.core;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.internal.core.DebugCoreMessages;
import org.eclipse.debug.internal.core.IMementoConstants;
import org.eclipse.debug.internal.core.ResourceFactory;
import org.eclipse.debug.internal.core.XMLMemento;
/**
* Utilities for launch configurations that persist, restore, and refresh
* collections of resources.
*
* @since 3.6
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public class RefreshUtil {
/**
* String attribute identifying a scope of resources that should be
* refreshed - for example, after an external tool is run. The value is either
* a resource memento constant by this class, a resource memento created
* via {@link RefreshUtil#toMemento(IResource[])}, <code>null</code>, indicating no
* refresh.
*/
public static final String ATTR_REFRESH_SCOPE = DebugPlugin.getUniqueIdentifier() + ".ATTR_REFRESH_SCOPE"; //$NON-NLS-1$
/**
* Boolean attribute indicating if a refresh scope is recursive. Default
* value is <code>true</code>. When a refresh is recursive, resources are
* refreshed to an infinite depth, otherwise they are refreshed to a depth
* of one.
*/
public static final String ATTR_REFRESH_RECURSIVE = DebugPlugin.getUniqueIdentifier() + ".ATTR_REFRESH_RECURSIVE"; //$NON-NLS-1$
/**
* Resource memento referring to the selected resource's project.
* Only works when the debug user interface is running.
*
* @see #toResources(String)
*/
public static final String MEMENTO_SELECTED_PROJECT = "${project}"; //$NON-NLS-1$
/**
* Resource memento referring to the selected resource's container.
* Only works when the debug user interface is running.
*
* @see #toResources(String)
*/
public static final String MEMENTO_SELECTED_CONTAINER = "${container}"; //$NON-NLS-1$
/**
* Resource memento referring to the selected resource.
* Only works when the debug user interface is running.
*
* @see #toResources(String)
*/
public static final String MEMENTO_SELECTED_RESOURCE = "${resource}"; //$NON-NLS-1$
/**
* Resource memento referring to the workspace root.
*
* @see #toResources(String)
*/
public static final String MEMENTO_WORKSPACE = "${workspace}"; //$NON-NLS-1$
/**
* Indicates no working set has been selected (for backwards compatibility).
* The new format uses an empty working set
*/
private static final String NO_WORKING_SET = "NONE"; //$NON-NLS-1$
/**
* Refreshes the resources as specified by the given launch configuration.
*
* @param resources
* resources to refresh
* @param depth one of {@link IResource#DEPTH_INFINITE}, {@link IResource#DEPTH_ONE},
* or {@link IResource#DEPTH_ZERO}
* @param monitor
* progress monitor which may be <code>null</code>
* @throws CoreException
* if an exception occurs while refreshing resources
*/
public static void refreshResources(IResource[] resources, int depth, IProgressMonitor monitor) throws CoreException {
if (resources == null || resources.length == 0) {
return;
}
SubMonitor lmonitor = SubMonitor.convert(monitor, DebugCoreMessages.RefreshingResources, resources.length);
if (lmonitor.isCanceled()) {
return;
}
MultiStatus status = new MultiStatus(DebugPlugin.getUniqueIdentifier(), 0, DebugCoreMessages.RefreshingResourcesError, null);
try {
for (IResource resource : resources) {
if (lmonitor.isCanceled()) {
break;
}
if (resource != null && resource.isAccessible()) {
try {
resource.refreshLocal(depth, null);
} catch (CoreException e) {
status.merge(e.getStatus());
}
}
lmonitor.worked(1);
}
} finally {
lmonitor.done();
}
if (!status.isOK()) {
throw new CoreException(status);
}
}
/**
* Returns a collection of resources referred to by the specified
* memento generated via {@link #toMemento(IResource[])}.
*
* @param memento
* resource memento generated by this manager
* @return collection of resources referred to by the memento
* @throws CoreException
* if unable to resolve a set of resources
*/
public static IResource[] toResources(String memento) throws CoreException {
if (memento.startsWith("${resource:")) { //$NON-NLS-1$
// This is an old format that is replaced with 'working_set'
String pathString = memento.substring(11, memento.length() - 1);
Path path = new Path(pathString);
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (resource == null) {
throw new CoreException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), IStatus.ERROR, MessageFormat.format(DebugCoreMessages.RefreshUtil_1, pathString), null));
}
return new IResource[] { resource };
} else if (memento.startsWith("${working_set:")) { //$NON-NLS-1$
String ws = memento.substring(14, memento.length() - 1);
return getResources(ws);
} else if (memento.equals(MEMENTO_WORKSPACE)) {
return new IResource[] { ResourcesPlugin.getWorkspace().getRoot() };
} else {
// result the selected resource for backwards compatibility
IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager();
IResource resource = null;
try {
String pathString = manager.performStringSubstitution("${selected_resource_path}"); //$NON-NLS-1$
resource = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(pathString));
} catch (CoreException e) {
// unable to resolve a resource
}
if (resource == null) {
// empty selection
return new IResource[]{};
} else {
if (memento.equals(MEMENTO_SELECTED_RESOURCE)) {
return new IResource[] { resource };
} else if (memento.equals(MEMENTO_SELECTED_CONTAINER)) {
return new IResource[] {resource.getParent()};
} else if (memento.equals(MEMENTO_SELECTED_PROJECT)) {
return new IResource[] {resource.getProject()};
}
}
}
throw new CoreException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), MessageFormat.format(DebugCoreMessages.RefreshUtil_0, memento)));
}
/**
* Returns a memento for a collection of resources that can be restored
* via {@link #toResources(String)}.
*
* @param resources resources to create a memento for
* @return memento for the given resources
*/
public static String toMemento(IResource[] resources) {
XMLMemento memento = XMLMemento.createWriteRoot("resources"); //$NON-NLS-1$
for (IResource resource : resources) {
final XMLMemento itemMemento = memento.createChild(IMementoConstants.MEMENTO_ITEM);
ResourceFactory.saveState(itemMemento, resource);
}
StringWriter writer = new StringWriter();
try {
memento.save(writer);
} catch (IOException e) {
DebugPlugin.log(e);
}
StringBuilder buf = new StringBuilder();
buf.append("${working_set:"); //$NON-NLS-1$
buf.append(writer.toString());
buf.append("}"); //$NON-NLS-1$
return buf.toString();
}
/**
* Restores a collection of resources from a working set memento, for backwards
* compatibility.
*
* @param wsMemento working set memento
* @return resource collection, possibly empty
*/
private static IResource[] getResources(String wsMemento) {
if (NO_WORKING_SET.equals(wsMemento)) {
return null;
}
List<IAdaptable> resourcesList = new ArrayList<>();
StringReader reader = new StringReader(wsMemento);
XMLMemento memento = null;
try {
memento = XMLMemento.createReadRoot(reader);
} catch (Exception e) {
DebugPlugin.log(e);
return null;
}
XMLMemento[] mementos = memento.getChildren(IMementoConstants.MEMENTO_ITEM);
for (XMLMemento m : mementos) {
resourcesList.add(ResourceFactory.createElement(m));
}
return resourcesList.toArray(new IResource[resourcesList.size()]);
}
/**
* Returns whether the refresh scope specified by the given launch
* configuration is recursive.
*
* @param configuration the {@link ILaunchConfiguration}
* @return whether the refresh scope is recursive
* @throws CoreException
* if unable to access the associated attribute
*/
public static boolean isRefreshRecursive(ILaunchConfiguration configuration) throws CoreException {
return configuration.getAttribute(ATTR_REFRESH_RECURSIVE, true);
}
/**
* Refreshes the resources as specified by the given launch configuration via its
* {@link RefreshUtil#ATTR_REFRESH_SCOPE} and {@link #ATTR_REFRESH_RECURSIVE} attributes.
*
* @param configuration launch configuration
* @param monitor progress monitor which may be <code>null</code>
* @throws CoreException
* if an exception occurs while refreshing resources or accessing launch
* configuration attributes
*/
public static void refreshResources(ILaunchConfiguration configuration, IProgressMonitor monitor) throws CoreException {
String scope = configuration.getAttribute(ATTR_REFRESH_SCOPE, (String) null);
if (scope != null) {
IResource[] resources = toResources(scope);
if (resources != null && resources.length > 0) {
int depth = IResource.DEPTH_ONE;
if (isRefreshRecursive(configuration)) {
depth = IResource.DEPTH_INFINITE;
}
refreshResources(resources, depth, monitor);
}
}
}
}