blob: 0a9b71229080ff3624cbe230085b57f052046058 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2021 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
* Anton Kosyakov (Itemis AG) - Bug 438621 - [step filtering] Provide an extension point to enhance methods step filtering.
*******************************************************************************/
package org.eclipse.debug.core;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.eclipse.core.resources.ISaveContext;
import org.eclipse.core.resources.ISaveParticipant;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.model.IDebugElement;
import org.eclipse.debug.core.model.IDisconnect;
import org.eclipse.debug.core.model.IDropToFrame;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStep;
import org.eclipse.debug.core.model.IStepFilter;
import org.eclipse.debug.core.model.IStepFilters;
import org.eclipse.debug.core.model.ISuspendResume;
import org.eclipse.debug.core.model.ITerminate;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.RuntimeProcess;
import org.eclipse.debug.internal.core.BreakpointManager;
import org.eclipse.debug.internal.core.DebugCoreMessages;
import org.eclipse.debug.internal.core.DebugOptions;
import org.eclipse.debug.internal.core.ExpressionManager;
import org.eclipse.debug.internal.core.IConfigurationElementConstants;
import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
import org.eclipse.debug.internal.core.LaunchManager;
import org.eclipse.debug.internal.core.LogicalStructureManager;
import org.eclipse.debug.internal.core.MemoryBlockManager;
import org.eclipse.debug.internal.core.Preferences;
import org.eclipse.debug.internal.core.StepFilterManager;
import org.eclipse.debug.internal.core.commands.CommandAdapterFactory;
import org.eclipse.debug.internal.core.groups.GroupMemberChangeListener;
import org.eclipse.debug.internal.core.sourcelookup.SourceLookupUtils;
import org.eclipse.osgi.service.environment.Constants;
import org.osgi.framework.BundleContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* There is one instance of the debug plug-in available from
* <code>DebugPlugin.getDefault()</code>. The debug plug-in provides:
* <ul>
* <li>access to the breakpoint manager</li>
* <li>access to the launch manager</li>
* <li>access to the expression manager</li>
* <li>access to the registered launcher extensions</li>
* <li>access to the memory block manager</li>
* <li>debug event notification</li>
* <li>status handlers</li>
* </ul>
* @noinstantiate This class is not intended to be instantiated by clients.
* @noextend This class is not intended to be sub-classed by clients.
*/
public class DebugPlugin extends Plugin {
/**
* Unique identifier constant (value <code>"org.eclipse.debug.core"</code>)
* for the Debug Core plug-in.
*/
private static final String PI_DEBUG_CORE = "org.eclipse.debug.core"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"launchConfigurationTypes"</code>)
* for the launch configuration types extension point.
*
* @since 2.0
*/
public static final String EXTENSION_POINT_LAUNCH_CONFIGURATION_TYPES= "launchConfigurationTypes"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"launchConfigurationComparators"</code>)
* for the launch configuration comparators extension point.
*
* @since 2.0
*/
public static final String EXTENSION_POINT_LAUNCH_CONFIGURATION_COMPARATORS= "launchConfigurationComparators"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"breakpoints"</code>) for the
* breakpoints extension point.
*
* @since 2.0
*/
public static final String EXTENSION_POINT_BREAKPOINTS= "breakpoints"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"statusHandlers"</code>) for the
* status handlers extension point.
*
* @since 2.0
*/
public static final String EXTENSION_POINT_STATUS_HANDLERS= "statusHandlers"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"sourceLocators"</code>) for the
* source locators extension point.
*
* @since 2.0
*/
public static final String EXTENSION_POINT_SOURCE_LOCATORS= "sourceLocators"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"launchModes"</code>) for the
* source modes extension point.
*
* @since 3.0
*/
public static final String EXTENSION_POINT_LAUNCH_MODES= "launchModes"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"launchDelegates"</code>) for the
* launch delegates extension point.
*
* @since 3.0
*/
public static final String EXTENSION_POINT_LAUNCH_DELEGATES= "launchDelegates"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"processFactories"</code>) for the
* process factories extension point.
*
* @since 3.0
*/
public static final String EXTENSION_POINT_PROCESS_FACTORIES = "processFactories"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"logicalStructureTypes"</code>) for the
* logical structure types extension point.
*
* @since 3.0
*/
public static final String EXTENSION_POINT_LOGICAL_STRUCTURE_TYPES = "logicalStructureTypes"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"logicalStructureProviders"</code>) for the
* logical structure types extension point.
*
* @since 3.1
*/
public static final String EXTENSION_POINT_LOGICAL_STRUCTURE_PROVIDERS = "logicalStructureProviders"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"sourceContainerTypes"</code>) for the
* source container types extension point.
*
* @since 3.0
*/
public static final String EXTENSION_POINT_SOURCE_CONTAINER_TYPES = "sourceContainerTypes"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"sourcePathComputers"</code>) for the
* source path computers extension point.
*
* @since 3.0
*/
public static final String EXTENSION_POINT_SOURCE_PATH_COMPUTERS = "sourcePathComputers"; //$NON-NLS-1$
/**
* Simple identifier constant for the launch options extension point
*
* @since 3.3
*/
public static final String EXTENSION_POINT_LAUNCH_OPTIONS = "launchOptions"; //$NON-NLS-1$
/**
* Simple identifier constant for the breakpoint import participant extension point
*
* @since 3.5
*/
public static final String EXTENSION_POINT_BREAKPOINT_IMPORT_PARTICIPANTS = "breakpointImportParticipants"; //$NON-NLS-1$
/**
* Simple identifier constant (value <code>"stepFilters"</code>) for the
* step filters extension point.
*
* @since 3.10
*/
public static final String EXTENSION_POINT_STEP_FILTERS = "stepFilters"; //$NON-NLS-1$
/**
* Status code indicating an unexpected error.
*
* @since 3.4
*/
public static final int ERROR = 125;
/**
* Status code indicating an unexpected internal error. Internal errors
* should never be displayed to the user in dialogs or status text.
* Internal error messages are not translated.
*/
public static final int INTERNAL_ERROR = 120;
/**
* Status code indicating that the Eclipse runtime does not support
* launching a program with a working directory. This feature is only
* available if Eclipse is run on a 1.3 runtime or higher.
* <p>
* A status handler may be registered for this error condition,
* and should return a <code>Boolean</code> indicating whether the program
* should be re-launched with the default working directory.
* </p>
*/
public static final int ERR_WORKING_DIRECTORY_NOT_SUPPORTED = 115;
/**
* The launch configuration attribute that designates the process factory ID
* for the process factory to be used when creating a new process as a result of launching
* the launch configuration.
* @since 3.0
*/
public static final String ATTR_PROCESS_FACTORY_ID = "process_factory_id"; //$NON-NLS-1$
/**
* The launch attribute that designates whether or not it's associated
* launch should capture output. Value is a string representing a boolean -
* <code>true</code> or <code>false</code>. When unspecified, the default
* value is considered <code>true</code>.
*
* @since 3.1
*/
public static final String ATTR_CAPTURE_OUTPUT = PI_DEBUG_CORE + ".capture_output"; //$NON-NLS-1$
/**
* The launch attribute that stores the time stamp of when a launch configuration was
* launched. Value is {@link Long#toString(long)} of {@link System#currentTimeMillis()}.
*
* @since 3.6
*/
public static final String ATTR_LAUNCH_TIMESTAMP = PI_DEBUG_CORE + ".launch.timestamp"; //$NON-NLS-1$
/**
* The launch attribute that stores the time stamp of when a launch configuration was
* launched. Value is {@link Long#toString(long)} of {@link System#currentTimeMillis()}.
*
* @since 3.15
*/
public static final String ATTR_TERMINATE_TIMESTAMP = PI_DEBUG_CORE + ".terminate.timestamp"; //$NON-NLS-1$
/**
* This launch attribute designates the encoding to be used by the console
* associated with the launch.
* <p>
* For release 3.3, the system encoding is used when unspecified. Since 3.4,
* the inherited encoding is used when unspecified. See {@link ILaunchManager} for a
* description in <code>getEncoding(ILaunchConfiguration)</code>.
* </p>
* <p>
* Value of this constant is the same as the value of the old
* <code>IDebugUIConstants.ATTR_CONSOLE_ENCODING</code> constant for backward
* compatibility.
* </p>
* @since 3.3
*/
public static final String ATTR_CONSOLE_ENCODING = "org.eclipse.debug.ui.ATTR_CONSOLE_ENCODING"; //$NON-NLS-1$
/**
* Launch configuration attribute - a boolean value indicating whether a
* configuration should be launched with merged error and standard output.
* Merging output can ensure the process output appears in console in same
* order as the process produce it. On the other hand the error output can
* not be colored different from standard output anymore. Default value is
* <code>false</code>.
*
* @since 3.14
*/
public static final String ATTR_MERGE_OUTPUT = PI_DEBUG_CORE + ".ATTR_MERGE_OUTPUT"; //$NON-NLS-1$
/**
* Boolean preference key (value
* <code>org.eclipse.debug.core.PREF_DELETE_CONFIGS_ON_PROJECT_DELETE</code>)
* that controls whether to delete associated configurations when a project
* is deleted. Default value is <code>false</code>.
*
* @since 3.7
*/
public static final String PREF_DELETE_CONFIGS_ON_PROJECT_DELETE = PI_DEBUG_CORE + ".PREF_DELETE_CONFIGS_ON_PROJECT_DELETE"; //$NON-NLS-1$
/**
* Deleted breakpoint marker attribute (value
* <code>"org.eclipse.debug.core.breakpointIsDeleted"</code>). The attribute is a
* <code>boolean</code> corresponding to the deleted state of a breakpoint.
*
* @see org.eclipse.core.resources.IMarker#getAttribute(String, boolean)
* @since 3.7
*/
public static final String ATTR_BREAKPOINT_IS_DELETED= PI_DEBUG_CORE + ".breakpointIsDeleted"; //$NON-NLS-1$
/**
* Attribute key for the environment used when an {@link IProcess} was run
* @see IProcess
* @since 3.8
*/
public static final String ATTR_ENVIRONMENT = PI_DEBUG_CORE + ".ATTR_ENVIRONMENT"; //$NON-NLS-1$
/**
* Attribute key for the path of the working directory for an {@link IProcess}
*
* @see IProcess
* @since 3.8
*/
public static final String ATTR_WORKING_DIRECTORY = PI_DEBUG_CORE + ".ATTR_WORKING_DIRECTORY"; //$NON-NLS-1$
/**
* Attribute key for path of the executable that launched an {@link IProcess}
*
* @see IProcess
* @since 3.8
*/
public static final String ATTR_PATH = PI_DEBUG_CORE + ".ATTR_PATH"; //$NON-NLS-1$
/**
* Launch configuration attribute that designates whether or not the
* descendants of the {@link IProcess} associated to a launch of this
* configuration should be terminated if the main-process is terminated. The
* descendants (also called child- or sub-processes) of a operating system
* process are the processes started by that process.
*
* Value is a string representing a boolean - <code>true</code> or
* <code>false</code>. When unspecified, the default value is considered
* <code>true</code>.
*
* @since 3.18
*/
public static final String ATTR_TERMINATE_DESCENDANTS = PI_DEBUG_CORE + ".TERMINATE_DESCENDANTS"; //$NON-NLS-1$
/**
* The singleton debug plug-in instance.
*/
private static DebugPlugin fgDebugPlugin= null;
/**
* The singleton breakpoint manager.
*/
private BreakpointManager fBreakpointManager;
/**
* The singleton expression manager.
*/
private ExpressionManager fExpressionManager;
/**
* The singleton launch manager.
*/
private LaunchManager fLaunchManager;
/**
* The singleton memory block manager.
* @since 3.1
*/
private MemoryBlockManager fMemoryBlockManager;
/**
* The collection of debug event listeners.
*/
private ListenerList<IDebugEventSetListener> fEventListeners = new ListenerList<>();
/**
* Event filters, or <code>null</code> if none.
*/
private ListenerList<IDebugEventFilter> fEventFilters = new ListenerList<>();
/**
* Whether this plug-in is in the process of shutting
* down.
*/
private boolean fShuttingDown= false;
/**
* Table of status handlers. Keys are {plug-in identifier, status code}
* pairs, and values are associated <code>IConfigurationElement</code>s.
*/
private HashMap<StatusHandlerKey, IConfigurationElement> fStatusHandlers = null;
/**
* Map of process factories. Keys are process factory IDs
* and values are associated <code>IConfigurationElement</code>s.
* @since 3.0
*/
private HashMap<String, IConfigurationElement> fProcessFactories = null;
/**
* Mode constants for the event notifier
*/
private static final int NOTIFY_FILTERS = 0;
private static final int NOTIFY_EVENTS = 1;
/**
* Queue of debug events to fire to listeners and asynchronous runnables to execute
* in the order received.
*
* @since 3.1
*/
private List<Object> fEventQueue = new ArrayList<>();
/**
* Job to fire events to listeners.
* @since 3.1
*/
private EventDispatchJob fEventDispatchJob = new EventDispatchJob();
/**
* Event dispatch job. Processes event queue of debug events and runnables.
*
* @since 3.1
*/
class EventDispatchJob extends Job {
EventNotifier fNotifier = new EventNotifier();
AsynchRunner fRunner = new AsynchRunner();
/**
* Creates a new event dispatch job.
*/
public EventDispatchJob() {
super(DebugCoreMessages.DebugPlugin_1);
setPriority(Job.INTERACTIVE);
setSystem(true);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
while (!fEventQueue.isEmpty()) {
Object next = null;
synchronized (fEventQueue) {
if (!fEventQueue.isEmpty()) {
next = fEventQueue.remove(0);
}
}
if (next instanceof Runnable) {
fRunner.async((Runnable) next);
} else if (next != null) {
fNotifier.dispatch((DebugEvent[]) next);
}
}
return Status.OK_STATUS;
}
@Override
public boolean shouldRun() {
return shouldSchedule();
}
@Override
public boolean shouldSchedule() {
return !(isShuttingDown() || fEventListeners.isEmpty());
}
}
/**
* Returns the singleton instance of the debug plug-in.
*
* @return the debug plug-in
*/
public static DebugPlugin getDefault() {
return fgDebugPlugin;
}
/**
* Sets the singleton instance of the debug plug-in.
*
* @param plugin the debug plug-in, or <code>null</code>
* when shutting down
*/
private static void setDefault(DebugPlugin plugin) {
fgDebugPlugin = plugin;
}
/**
* Convenience method which returns the unique identifier of this plug-in.
*
* @return debug plug-in identifier
*/
public static String getUniqueIdentifier() {
return PI_DEBUG_CORE;
}
/**
* Constructs the debug plug-in.
* <p>
* An instance of this plug-in runtime class is automatically created
* when the facilities provided by this plug-in are required.
* <b>Clients must never explicitly instantiate a plug-in runtime class.</b>
* </p>
*/
public DebugPlugin() {
super();
setDefault(this);
}
/**
* Adds the given listener to the collection of registered debug
* event listeners. Has no effect if an identical listener is already
* registered.
*
* @param listener the listener to add
* @since 2.0
*/
public void addDebugEventListener(IDebugEventSetListener listener) {
fEventListeners.add(listener);
}
/**
* Notifies all registered debug event set listeners of the given
* debug events. Events which are filtered by a registered debug event
* filter are not fired.
*
* @param events array of debug events to fire
* @see IDebugEventFilter
* @see IDebugEventSetListener
* @since 2.0
*/
public void fireDebugEventSet(DebugEvent[] events) {
if (isShuttingDown() || events == null || fEventListeners.isEmpty()) {
return;
}
synchronized (fEventQueue) {
fEventQueue.add(events);
}
fEventDispatchJob.schedule();
}
/**
* Asynchronously executes the given runnable in a separate
* thread, after debug event dispatch has completed. If debug
* events are not currently being dispatched, the runnable is
* scheduled to run in a separate thread immediately.
*
* @param r runnable to execute asynchronously
* @since 2.1
*/
public void asyncExec(Runnable r) {
synchronized (fEventQueue) {
fEventQueue.add(r);
}
fEventDispatchJob.schedule();
}
/**
* Returns the breakpoint manager.
*
* @return the breakpoint manager
* @see IBreakpointManager
*/
public synchronized IBreakpointManager getBreakpointManager() {
if (fBreakpointManager == null) {
fBreakpointManager = new BreakpointManager();
}
return fBreakpointManager;
}
/**
* Returns the launch manager.
*
* @return the launch manager
* @see ILaunchManager
*/
public synchronized ILaunchManager getLaunchManager() {
if (fLaunchManager == null) {
fLaunchManager = new LaunchManager();
fLaunchManager.getAllLaunchConfigurations();
// monitor launch configuration renames for launch groups
fLaunchManager.addLaunchConfigurationListener(new GroupMemberChangeListener());
}
return fLaunchManager;
}
/**
* Returns the memory block manager.
* @return the memory block manager.
* @see IMemoryBlockManager
* @since 3.1
*/
public synchronized IMemoryBlockManager getMemoryBlockManager(){
if (fMemoryBlockManager == null) {
fMemoryBlockManager = new MemoryBlockManager();
}
return fMemoryBlockManager;
}
/**
* Returns the status handler registered for the given
* status, or <code>null</code> if none.
*
* @param status status for which a status handler has been requested
* @return the status handler registered for the given
* status, or <code>null</code> if none
* @since 2.0
*/
public IStatusHandler getStatusHandler(IStatus status) {
boolean enabled = Platform.getPreferencesService().getBoolean(DebugPlugin.getUniqueIdentifier(), IInternalDebugCoreConstants.PREF_ENABLE_STATUS_HANDLERS, true, null);
if (!enabled) {
return null;
}
StatusHandlerKey key = new StatusHandlerKey(status.getPlugin(), status.getCode());
if (fStatusHandlers == null) {
initializeStatusHandlers();
}
IConfigurationElement config = fStatusHandlers.get(key);
if (config != null) {
try {
Object handler = config.createExecutableExtension(IConfigurationElementConstants.CLASS);
if (handler instanceof IStatusHandler) {
return (IStatusHandler)handler;
}
invalidStatusHandler(null, MessageFormat.format("Registered status handler {0} does not implement required interface IStatusHandler.", new Object[] { config.getDeclaringExtension().getUniqueIdentifier() })); //$NON-NLS-1$
} catch (CoreException e) {
log(e);
}
}
return null;
}
/**
* Returns the expression manager.
*
* @return the expression manager
* @see IExpressionManager
* @since 2.0
*/
public synchronized IExpressionManager getExpressionManager() {
if (fExpressionManager == null) {
fExpressionManager = new ExpressionManager();
}
return fExpressionManager;
}
/**
* Removes the given listener from the collection of registered debug
* event listeners. Has no effect if an identical listener is not already
* registered.
*
* @param listener the listener to remove
* @since 2.0
*/
public void removeDebugEventListener(IDebugEventSetListener listener) {
fEventListeners.remove(listener);
}
@Override
public void stop(BundleContext context) throws Exception {
try {
setShuttingDown(true);
if (fLaunchManager != null) {
fLaunchManager.shutdown();
}
if (fBreakpointManager != null) {
fBreakpointManager.shutdown();
}
if (fMemoryBlockManager != null) {
fMemoryBlockManager.shutdown();
}
fEventListeners.clear();
fEventFilters.clear();
SourceLookupUtils.shutdown();
Preferences.savePreferences(DebugPlugin.getUniqueIdentifier());
ResourcesPlugin.getWorkspace().removeSaveParticipant(getUniqueIdentifier());
} finally {
super.stop(context);
setDefault(null);
}
}
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
new DebugOptions(context);
ResourcesPlugin.getWorkspace().addSaveParticipant(getUniqueIdentifier(),
new ISaveParticipant() {
@Override
public void saving(ISaveContext saveContext) throws CoreException {
if (fExpressionManager != null) {
fExpressionManager.storeWatchExpressions();
}
Preferences.savePreferences(DebugPlugin.getUniqueIdentifier());
}
@Override
public void rollback(ISaveContext saveContext) {}
@Override
public void prepareToSave(ISaveContext saveContext) throws CoreException {}
@Override
public void doneSaving(ISaveContext saveContext) {}
});
//command adapters
IAdapterManager manager= Platform.getAdapterManager();
CommandAdapterFactory actionFactory = new CommandAdapterFactory();
manager.registerAdapters(actionFactory, IDisconnect.class);
manager.registerAdapters(actionFactory, IDropToFrame.class);
manager.registerAdapters(actionFactory, IStep.class);
manager.registerAdapters(actionFactory, IStepFilters.class);
manager.registerAdapters(actionFactory, ISuspendResume.class);
manager.registerAdapters(actionFactory, ITerminate.class);
manager.registerAdapters(actionFactory, ILaunch.class);
manager.registerAdapters(actionFactory, IProcess.class);
manager.registerAdapters(actionFactory, IDebugElement.class);
}
/**
* Creates and returns a new process representing the given
* <code>java.lang.Process</code>. A streams proxy is created
* for the I/O streams in the system process. The process
* is added to the given launch.
* <p>
* If the launch configuration associated with the given launch
* specifies a process factory, it will be used to instantiate
* the new process.
* </p>
* @param launch the launch the process is contained in
* @param process the system process to wrap
* @param label the label assigned to the process
* @return the process
* @see IProcess
* @see IProcessFactory
*/
public static IProcess newProcess(ILaunch launch, Process process, String label) {
return newProcess(launch, process, label, null);
}
/**
* Creates and returns a new process representing the given
* <code>java.lang.Process</code>. A streams proxy is created
* for the I/O streams in the system process. The process
* is added to the given launch, and the process is initialized
* with the given attribute map.
* <p>
* If the launch configuration associated with the given launch
* specifies a process factory, it will be used to instantiate
* the new process.
* </p>
* @param launch the launch the process is contained in
* @param process the system process to wrap
* @param label the label assigned to the process
* @param attributes initial values for the attribute map
* @return the process <code>null</code> can be returned if errors occur dealing with the process factory
* designated to create the process.
* @see IProcess
* @see IProcessFactory
* @since 2.1
*/
public static IProcess newProcess(ILaunch launch, Process process, String label, Map<String, String> attributes) {
ILaunchConfiguration config= launch.getLaunchConfiguration();
String processFactoryID= null;
if (config != null) {
try {
processFactoryID= config.getAttribute(ATTR_PROCESS_FACTORY_ID, (String)null);
} catch (CoreException e) {
}
}
if (processFactoryID != null) {
DebugPlugin plugin= DebugPlugin.getDefault();
if (plugin.fProcessFactories == null) {
plugin.initializeProcessFactories();
}
IConfigurationElement element= plugin.fProcessFactories.get(processFactoryID);
if (element == null) {
return null;
}
IProcessFactory processFactory= null;
try {
processFactory = (IProcessFactory)element.createExecutableExtension(IConfigurationElementConstants.CLASS);
} catch (CoreException exception) {
log(exception);
return null;
}
return processFactory.newProcess(launch, process, label, attributes);
}
return new RuntimeProcess(launch, process, label, attributes);
}
/**
* Returns any logical structure types that have been contributed for the given
* value.
*
* @param value the value for which logical structure types have been requested
* @return logical structure types that have been contributed for the given
* value, possibly an empty collection
*
* @since 3.0
*/
public static ILogicalStructureType[] getLogicalStructureTypes(IValue value) {
return LogicalStructureManager.getDefault().getLogicalStructureTypes(value);
}
/**
* Returns the default logical structure type among the given combination of
* logical structure types, or <code>null</code> if none. When the given combination
* of logical structure type is applicable for a value, the default logical structure
* type is used to display a value.
*
* @param types a combination of structures applicable to a value
* @return the default structure that should be used to display the value
* or <code>null</code> if none
*
* @since 3.1
*/
public static ILogicalStructureType getDefaultStructureType(ILogicalStructureType[] types) {
return LogicalStructureManager.getDefault().getSelectedStructureType(types);
}
/**
* Sets the default logical structure type among the given combination of logical structure
* types. The logical structure types provided should all be applicable to a single
* value. Specifying <code>null</code> indicates there is no default logical structure
* for the given combination of types.
*
* @param types a combination of logical structure types applicable to a value
* @param def the default logical structure among the given combination of types
* or <code>null</code> if none
*
* @since 3.1
*/
public static void setDefaultStructureType(ILogicalStructureType[] types, ILogicalStructureType def) {
LogicalStructureManager.getDefault().setEnabledType(types, def);
}
/**
* Convenience method that performs a runtime exec on the given command line
* in the context of the specified working directory, and returns the
* resulting process. If the current runtime does not support the
* specification of a working directory, the status handler for error code
* <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if the
* exec should be re-executed without specifying a working directory.
*
* @param cmdLine the command line
* @param workingDirectory the working directory, or <code>null</code>
* @return the resulting process or <code>null</code> if the exec is
* canceled
* @exception CoreException if the exec fails
* @see Runtime
*
* @since 2.1
*/
public static Process exec(String[] cmdLine, File workingDirectory) throws CoreException {
return exec(cmdLine, workingDirectory, null);
}
/**
* Convenience method that performs a runtime exec on the given command line
* in the context of the specified working directory, and returns the
* resulting process. If the current runtime does not support the
* specification of a working directory, the status handler for error code
* <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if the
* exec should be re-executed without specifying a working directory.
*
* @param cmdLine the command line
* @param workingDirectory the working directory, or <code>null</code>
* @param envp the environment variables set in the process, or <code>null</code>
* @return the resulting process or <code>null</code> if the exec is
* canceled
* @exception CoreException if the exec fails
* @see Runtime
*
* @since 3.0
*/
public static Process exec(String[] cmdLine, File workingDirectory, String[] envp) throws CoreException {
return exec(cmdLine, workingDirectory, envp, false);
}
/**
* Convenience method that performs a runtime exec on the given command line
* in the context of the specified working directory, and returns the
* resulting process. If the current runtime does not support the
* specification of a working directory, the status handler for error code
* <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if the
* exec should be re-executed without specifying a working directory.
*
* @param cmdLine the command line
* @param workingDirectory the working directory, or <code>null</code>
* @param envp the environment variables set in the process, or
* <code>null</code>
* @param mergeOutput if <code>true</code> the error stream will be merged
* with standard output stream and both can be read through the
* same output stream
* @return the resulting process or <code>null</code> if the exec is
* canceled
* @exception CoreException if the exec fails
* @see Runtime
*
* @since 3.14
*/
public static Process exec(String[] cmdLine, File workingDirectory, String[] envp, boolean mergeOutput) throws CoreException {
Process p = null;
try {
// starting with and without merged output could be done with the
// same process builder approach but since the handling of
// environment variables is slightly different between
// ProcessBuilder and Runtime.exec only the new option uses process
// builder to not break existing caller of this method
if (mergeOutput) {
ProcessBuilder pb = new ProcessBuilder(cmdLine);
pb.directory(workingDirectory);
pb.redirectErrorStream(mergeOutput);
if (envp != null) {
Map<String, String> env = pb.environment();
env.clear();
for (String e : envp) {
int index = e.indexOf('=');
if (index != -1) {
env.put(e.substring(0, index), e.substring(index + 1));
}
}
}
p = pb.start();
} else if (workingDirectory == null) {
p = Runtime.getRuntime().exec(cmdLine, envp);
} else {
p = Runtime.getRuntime().exec(cmdLine, envp, workingDirectory);
}
} catch (IOException e) {
Status status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_0, e);
throw new CoreException(status);
} catch (NoSuchMethodError e) {
//attempting launches on 1.2.* - no ability to set working directory
IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERR_WORKING_DIRECTORY_NOT_SUPPORTED, DebugCoreMessages.DebugPlugin_Eclipse_runtime_does_not_support_working_directory_2, e);
IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status);
if (handler != null) {
Object result = handler.handleStatus(status, null);
if (result instanceof Boolean && ((Boolean) result).booleanValue()) {
p = exec(cmdLine, null);
}
}
}
return p;
}
/**
* Returns whether this plug-in is in the process of
* being shutdown.
*
* @return whether this plug-in is in the process of
* being shutdown
*/
private boolean isShuttingDown() {
return fShuttingDown;
}
/**
* Sets whether this plug-in is in the process of
* being shutdown.
*
* @param value whether this plug-in is in the process of
* being shutdown
*/
private void setShuttingDown(boolean value) {
fShuttingDown = value;
}
/**
* Adds the given debug event filter to the registered
* event filters. Has no effect if an identical filter
* is already registered.
*
* @param filter debug event filter
* @since 2.0
*/
public void addDebugEventFilter(IDebugEventFilter filter) {
fEventFilters.add(filter);
}
/**
* Removes the given debug event filter from the registered
* event filters. Has no effect if an identical filter
* is not already registered.
*
* @param filter debug event filter
* @since 2.0
*/
public void removeDebugEventFilter(IDebugEventFilter filter) {
fEventFilters.remove(filter);
}
/**
* Logs the given message if in debug mode.
*
* @param message the message to log
* @since 2.0
*/
public static void logDebugMessage(String message) {
if (getDefault().isDebugging()) {
// this message is intentionally not externalized, as an exception may
// be due to the resource bundle itself
log(new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, MessageFormat.format(DebugCoreMessages.DebugPlugin_2, new Object[] { message }), null));
}
}
/**
* Logs the given message with this plug-in's log and the given
* throwable or <code>null</code> if none.
* @param message the message to log
* @param throwable the exception that occurred or <code>null</code> if none
*/
public static void logMessage(String message, Throwable throwable) {
log(new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, message, throwable));
}
/**
* Logs the specified status with this plug-in's log.
*
* @param status status to log
* @since 2.0
*/
public static void log(IStatus status) {
getDefault().getLog().log(status);
}
/**
* Logs the specified throwable with this plug-in's log.
*
* @param t throwable to log
* @since 2.0
*/
public static void log(Throwable t) {
IStatus status= new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_3, t);
log(status);
}
/**
* Register status handlers.
*
*/
private void initializeStatusHandlers() {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.PI_DEBUG_CORE, EXTENSION_POINT_STATUS_HANDLERS);
IConfigurationElement[] infos= extensionPoint.getConfigurationElements();
fStatusHandlers = new HashMap<>(infos.length);
for (IConfigurationElement configurationElement : infos) {
String id = configurationElement.getAttribute("plugin"); //$NON-NLS-1$
String code = configurationElement.getAttribute("code"); //$NON-NLS-1$
if (id != null && code != null) {
try {
StatusHandlerKey key = new StatusHandlerKey(id, Integer.parseInt(code));
fStatusHandlers.put(key, configurationElement);
} catch (NumberFormatException e) {
// invalid status handler
invalidStatusHandler(e, configurationElement.getAttribute("id")); //$NON-NLS-1$
}
} else {
// invalid status handler
invalidStatusHandler(null, configurationElement.getAttribute("id")); //$NON-NLS-1$
}
}
}
/**
* Register process factories.
*
*/
private void initializeProcessFactories() {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.PI_DEBUG_CORE, EXTENSION_POINT_PROCESS_FACTORIES);
IConfigurationElement[] infos= extensionPoint.getConfigurationElements();
fProcessFactories = new HashMap<>(infos.length);
for (IConfigurationElement configurationElement : infos) {
String id = configurationElement.getAttribute("id"); //$NON-NLS-1$
String clss = configurationElement.getAttribute("class"); //$NON-NLS-1$
if (id != null && clss != null) {
fProcessFactories.put(id, configurationElement);
} else {
// invalid process factory
String badDefiner = configurationElement.getContributor().getName();
log(new Status(IStatus.ERROR, DebugPlugin.PI_DEBUG_CORE, ERROR, MessageFormat.format(DebugCoreMessages.DebugPlugin_4, new Object[] {
badDefiner, id }), null));
}
}
}
private void invalidStatusHandler(Exception e, String id) {
log(new Status(IStatus.ERROR, DebugPlugin.PI_DEBUG_CORE, ERROR, MessageFormat.format(DebugCoreMessages.DebugPlugin_5, new Object[] { id }), e));
}
/**
* Key for status handler extensions - a plug-in identifier/code pair
*/
class StatusHandlerKey {
String fPluginId;
int fCode;
StatusHandlerKey(String pluginId, int code) {
fPluginId = pluginId;
fCode = code;
}
@Override
public int hashCode() {
return fPluginId.hashCode() + fCode;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof StatusHandlerKey) {
StatusHandlerKey s = (StatusHandlerKey)obj;
return fCode == s.fCode && fPluginId.equals(s.fPluginId);
}
return false;
}
}
/**
* Executes runnables after event dispatch is complete.
*
* @since 3.0
*/
class AsynchRunner implements ISafeRunnable {
private Runnable fRunnable = null;
void async(Runnable runnable) {
fRunnable = runnable;
SafeRunner.run(this);
fRunnable = null;
}
@Override
public void handleException(Throwable exception) {
IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_6, exception);
log(status);
}
@Override
public void run() throws Exception {
fRunnable.run();
}
}
/**
* Filters and dispatches events in a safe runnable to handle any
* exceptions.
*/
class EventNotifier implements ISafeRunnable {
private DebugEvent[] fEvents;
private IDebugEventSetListener fListener;
private IDebugEventFilter fFilter;
private int fMode;
@Override
public void handleException(Throwable exception) {
switch (fMode) {
case NOTIFY_FILTERS:
IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_7, exception);
log(status);
break;
case NOTIFY_EVENTS:
status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_8, exception);
log(status);
break;
default:
break;
}
}
@Override
public void run() throws Exception {
switch (fMode) {
case NOTIFY_FILTERS:
fEvents = fFilter.filterDebugEvents(fEvents);
break;
case NOTIFY_EVENTS:
fListener.handleDebugEvents(fEvents);
break;
default:
break;
}
}
/**
* Filter and dispatch the given events. If an exception occurs in one
* listener, events are still fired to subsequent listeners.
*
* @param events debug events
*/
void dispatch(DebugEvent[] events) {
fEvents = events;
if (!fEventFilters.isEmpty()) {
fMode = NOTIFY_FILTERS;
for (IDebugEventFilter iDebugEventFilter : fEventFilters) {
fFilter = iDebugEventFilter;
SafeRunner.run(this);
if (fEvents == null || fEvents.length == 0) {
return;
}
}
}
fMode = NOTIFY_EVENTS;
if (DebugOptions.DEBUG_EVENTS) {
for (DebugEvent event : fEvents) {
DebugOptions.trace(event.toString());
}
}
for (IDebugEventSetListener iDebugEventSetListener : fEventListeners) {
fListener = iDebugEventSetListener;
SafeRunner.run(this);
}
fEvents = null;
fFilter = null;
fListener = null;
}
}
/**
* Creates and returns a new XML document.
*
* @return a new XML document
* @throws CoreException if unable to create a new document
* @since 3.0
*/
public static Document newDocument()throws CoreException {
try {
return LaunchManager.getDocument();
} catch (ParserConfigurationException e) {
abort("Unable to create new XML document.", e); //$NON-NLS-1$
}
return null;
}
/**
* Serializes the given XML document into a string.
*
* @param document XML document to serialize
* @return a string representing the given document
* @throws CoreException if unable to serialize the document
* @since 3.0
*/
public static String serializeDocument(Document document) throws CoreException {
try {
return LaunchManager.serializeDocument(document);
} catch (TransformerException e) {
abort("Unable to serialize XML document.", e); //$NON-NLS-1$
} catch (IOException e) {
abort("Unable to serialize XML document.",e); //$NON-NLS-1$
}
return null;
}
/**
* Parses the given string representing an XML document, returning its
* root element.
*
* @param document XML document as a string
* @return the document's root element
* @throws CoreException if unable to parse the document
* @since 3.0
*/
public static Element parseDocument(String document) throws CoreException {
Element root = null;
InputStream stream = null;
try{
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
stream = new ByteArrayInputStream(document.getBytes(StandardCharsets.UTF_8));
root = parser.parse(stream).getDocumentElement();
} catch (ParserConfigurationException e) {
abort("Unable to parse XML document.", e); //$NON-NLS-1$
} catch (FactoryConfigurationError e) {
abort("Unable to parse XML document.", e); //$NON-NLS-1$
} catch (SAXException e) {
abort("Unable to parse XML document.", e); //$NON-NLS-1$
} catch (IOException e) {
abort("Unable to parse XML document.", e); //$NON-NLS-1$
} finally {
try{
if (stream != null) {
stream.close();
}
} catch(IOException e) {
abort("Unable to parse XML document.", e); //$NON-NLS-1$
}
}
return root;
}
/**
* Throws an exception with the given message and underlying exception.
*
* @param message error message
* @param exception underlying exception, or <code>null</code>
* @throws CoreException if a problem is encountered
*/
private static void abort(String message, Throwable exception) throws CoreException {
IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.ERROR, message, exception);
throw new CoreException(status);
}
private static String[] parseArgumentsWindows(String args, boolean split) {
// see http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
List<String> result = new ArrayList<>();
final int DEFAULT= 0;
final int ARG= 1;
final int IN_DOUBLE_QUOTE= 2;
int state= DEFAULT;
int backslashes= 0;
StringBuilder buf= new StringBuilder();
int len= args.length();
for (int i= 0; i < len; i++) {
char ch= args.charAt(i);
if (ch == '\\') {
backslashes++;
continue;
} else if (backslashes != 0) {
if (ch == '"') {
for (; backslashes >= 2; backslashes-= 2) {
buf.append('\\');
if (split) {
buf.append('\\');
}
}
if (backslashes == 1) {
if (state == DEFAULT) {
state= ARG;
}
if (split) {
buf.append('\\');
}
buf.append('"');
backslashes= 0;
continue;
} // else fall through to switch
} else {
// false alarm, treat passed backslashes literally...
if (state == DEFAULT) {
state= ARG;
}
for (; backslashes > 0; backslashes--) {
buf.append('\\');
}
// fall through to switch
}
}
if (Character.isWhitespace(ch)) {
if (state == DEFAULT) {
// skip
continue;
} else if (state == ARG) {
state= DEFAULT;
result.add(buf.toString());
buf.setLength(0);
continue;
}
}
switch (state) {
case DEFAULT:
case ARG:
if (ch == '"') {
state= IN_DOUBLE_QUOTE;
if (split) {
buf.append(ch);
}
} else {
state= ARG;
buf.append(ch);
}
break;
case IN_DOUBLE_QUOTE:
if (ch == '"') {
if (i + 1 < len && args.charAt(i + 1) == '"') {
/* Undocumented feature in Windows:
* Two consecutive double quotes inside a double-quoted argument are interpreted as
* a single double quote.
*/
buf.append('"');
i++;
if (split) {
buf.append(ch);
}
} else if (buf.length() == 0) {
// empty string on Windows platform. Account for bug in constructor of JDK's java.lang.ProcessImpl.
result.add("\"\""); //$NON-NLS-1$
state= DEFAULT;
} else {
state= ARG;
if (split) {
buf.append(ch);
}
}
} else {
buf.append(ch);
}
break;
default:
throw new IllegalStateException();
}
}
if (buf.length() > 0 || state != DEFAULT) {
result.add(buf.toString());
}
return result.toArray(new String[result.size()]);
}
private static String[] parseArgumentsImpl(String args, boolean split) {
// man sh, see topic QUOTING
List<String> result = new ArrayList<>();
final int DEFAULT= 0;
final int ARG= 1;
final int IN_DOUBLE_QUOTE= 2;
final int IN_SINGLE_QUOTE= 3;
int state= DEFAULT;
StringBuilder buf= new StringBuilder();
int len= args.length();
for (int i= 0; i < len; i++) {
char ch= args.charAt(i);
if (Character.isWhitespace(ch)) {
if (state == DEFAULT) {
// skip
continue;
} else if (state == ARG) {
state= DEFAULT;
result.add(buf.toString());
buf.setLength(0);
continue;
}
}
switch (state) {
case DEFAULT:
case ARG:
if (ch == '"') {
if (split) {
buf.append(ch);
}
state= IN_DOUBLE_QUOTE;
} else if (ch == '\'') {
if (split) {
buf.append(ch);
}
state= IN_SINGLE_QUOTE;
} else if (ch == '\\' && i + 1 < len) {
if (split) {
buf.append(ch);
}
state= ARG;
ch= args.charAt(++i);
buf.append(ch);
} else {
state= ARG;
buf.append(ch);
}
break;
case IN_DOUBLE_QUOTE:
if (ch == '"') {
if (split) {
buf.append(ch);
}
state= ARG;
} else if (ch == '\\' && i + 1 < len &&
(args.charAt(i + 1) == '\\' || args.charAt(i + 1) == '"')) {
if (split) {
buf.append(ch);
}
ch= args.charAt(++i);
buf.append(ch);
} else {
buf.append(ch);
}
break;
case IN_SINGLE_QUOTE:
if (ch == '\'') {
if (split) {
buf.append(ch);
}
state= ARG;
} else {
buf.append(ch);
}
break;
default:
throw new IllegalStateException();
}
}
if (buf.length() > 0 || state != DEFAULT) {
result.add(buf.toString());
}
return result.toArray(new String[result.size()]);
}
/**
* Parses the given command line into separate arguments that can be passed
* to <code>DebugPlugin.exec(String[], File)</code>. Embedded quotes and
* backslashes are interpreted, i.e. the resulting arguments are in the form
* that will be passed to an invoked process.
* <p>
* The reverse operation is {@link #renderArguments(String[], int[])}.
* </p>
*
* @param args command line arguments as a single string
* @return individual arguments
* @see #renderArguments(String[], int[])
* @since 3.1
*/
public static String[] parseArguments(String args) {
if (args == null) {
return new String[0];
}
if (Constants.OS_WIN32.equals(Platform.getOS())) {
return parseArgumentsWindows(args, false);
}
return parseArgumentsImpl(args, false);
}
/**
* Splits the given command line into separate arguments that can be
* concatenated with a space as joiner. Embedded quotes and backslashes are
* kept as is (i.e. not interpreted).
* <p>
* Use this method to avoid e.g. losing quotes around an argument like
* <code>"${env_var:A}"</code>, which may later be substituted by a string
* that contains spaces.
* </p>
*
* @param args command line arguments as a single string
* @return individual arguments in original form
* @since 3.10
*/
public static String[] splitArguments(String args) {
if (args == null) {
return new String[0];
}
if (Constants.OS_WIN32.equals(Platform.getOS())) {
return parseArgumentsWindows(args, true);
}
return parseArgumentsImpl(args, true);
}
/**
* Renders the given array of argument strings into a single command line.
* <p>
* If an argument contains whitespace, it it quoted. Contained quotes or
* backslashes will be escaped.
* </p>
* <p>
* If <code>segments</code> is not <code>null</code>, the array is filled
* with the offsets of the start positions of arguments 1 to
* <code>arguments.length - 1</code>, as rendered in the resulting string.
* </p>
*
* @param arguments the command line arguments
* @param segments an array of size <code>arguments.length - 1</code> or
* <code>null</code>
* @return the command line
* @see #parseArguments(String)
* @since 3.8
*/
public static String renderArguments(String[] arguments, int[] segments) {
boolean isWin32= Platform.getOS().equals(Constants.OS_WIN32);
StringBuilder buf = new StringBuilder();
int count = arguments.length;
for (int i = 0; i < count; i++) {
if (i > 0) {
buf.append(' ');
}
boolean containsSpace = false;
char[] characters = arguments[i].toCharArray();
for (char ch : characters) {
if (ch == ' ' || ch == '\t') {
containsSpace = true;
buf.append('"');
break;
}
}
int backslashes = 0;
for (int j = 0; j < characters.length; j++) {
char ch = characters[j];
if (ch == '"') {
if (isWin32) {
if (j == 0 && characters.length == 2 && characters[1] == '"') {
// empty string on windows platform, see bug 130767. Bug in constructor of JDK's java.lang.ProcessImpl.
buf.append("\"\""); //$NON-NLS-1$
break;
}
if (backslashes > 0) {
// Feature in Windows: need to double-escape backslashes in front of double quote.
for (; backslashes > 0; backslashes--) {
buf.append('\\');
}
}
}
buf.append('\\');
} else if (ch == '\\') {
if (isWin32) {
backslashes++;
} else {
buf.append('\\');
}
}
buf.append(ch);
}
if (containsSpace) {
buf.append('"');
} else if (characters.length == 0) {
buf.append("\"\""); //$NON-NLS-1$
}
if (segments != null && i < count - 1) {
segments[i] = buf.length() + 1;
}
}
return buf.toString();
}
/**
* Sets whether step filters should be applied to step commands. This
* setting is a global option applied to all registered debug targets.
*
* @param useStepFilters whether step filters should be applied to step
* commands
* @since 3.3
* @see org.eclipse.debug.core.model.IStepFilters
*/
public static void setUseStepFilters(boolean useStepFilters) {
getStepFilterManager().setUseStepFilters(useStepFilters);
}
/**
* Returns whether step filters are applied to step commands.
*
* @return whether step filters are applied to step commands
* @since 3.3
* @see org.eclipse.debug.core.model.IStepFilters
* @see org.eclipse.debug.core.commands.IStepFiltersHandler
*/
public static boolean isUseStepFilters() {
return getStepFilterManager().isUseStepFilters();
}
/**
* Returns any step filters that have been contributed for the given model
* identifier.
*
* @param modelIdentifier the model identifier
* @return step filters that have been contributed for the given model
* identifier, possibly an empty collection
* @since 3.10
* @see org.eclipse.debug.core.model.IStepFilter
*/
public static IStepFilter[] getStepFilters(String modelIdentifier) {
return getStepFilterManager().getStepFilters(modelIdentifier);
}
/**
* Returns the step filter manager.
*
* @return step filter manager
*/
private static StepFilterManager getStepFilterManager() {
return ((LaunchManager)getDefault().getLaunchManager()).getStepFilterManager();
}
/**
* Returns an adapter of the specified type for the given object or <code>null</code>
* if none. The object itself is returned if it is an instance of the specified type.
* If the object is adaptable and does not subclass <code>PlatformObject</code>, and
* does not provide the specified adapter directly, the platform's adapter manager
* is consulted for an adapter.
*
* @param element element to retrieve adapter for
* @param type adapter type
* @return adapter or <code>null</code>
* @since 3.4
*/
public static Object getAdapter(Object element, Class<?> type) {
Object adapter = null;
if (element != null) {
if (type.isInstance(element)) {
return element;
} else {
if (element instanceof IAdaptable) {
adapter = ((IAdaptable)element).getAdapter(type);
}
// for objects that don't subclass PlatformObject, check the platform's adapter manager
if (adapter == null && !(element instanceof PlatformObject)) {
adapter = Platform.getAdapterManager().getAdapter(element, type);
}
// force load the adapter in case it really is available
if (adapter == null) {
adapter = Platform.getAdapterManager().loadAdapter(element, type.getName());
}
}
}
return adapter;
}
}