/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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.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; import com.ibm.icu.text.MessageFormat; /** * 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$ /** * 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$ /** * 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$ /** * 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<Object>(); /** * 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); } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @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; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#shouldRun() */ @Override public boolean shouldRun() { return shouldSchedule(); } /* (non-Javadoc) * @see org.eclipse.core.internal.jobs.InternalJob#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(); } 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); } /* (non-Javadoc) * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ @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); } } /* (non-Javadoc) * @see org.eclipse.core.runtime.Plugin#start(org.osgi.framework.BundleContext) */ @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); // monitor launch configuration renames for launch groups getLaunchManager().addLaunchConfigurationListener(new GroupMemberChangeListener()); } /** * 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 { Process p= null; try { 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<StatusHandlerKey, IConfigurationElement>(infos.length); for (int i= 0; i < infos.length; i++) { IConfigurationElement configurationElement = infos[i]; 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<String, IConfigurationElement>(infos.length); for (int i= 0; i < infos.length; i++) { IConfigurationElement configurationElement = infos[i]; 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= infos[i].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; } /* (non-Javadoc) * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) */ @Override public void handleException(Throwable exception) { IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_6, exception); log(status); } /* (non-Javadoc) * @see org.eclipse.core.runtime.ISafeRunnable#run() */ @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; /** * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) */ @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; } } /** * @see org.eclipse.core.runtime.ISafeRunnable#run() */ @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 (int i = 0; i < fEvents.length; i++) { DebugOptions.trace(fEvents[i].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("UTF8")); //$NON-NLS-1$ 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<String>(); final int DEFAULT= 0; final int ARG= 1; final int IN_DOUBLE_QUOTE= 2; int state= DEFAULT; int backslashes= 0; StringBuffer buf= new StringBuffer(); 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<String>(); final int DEFAULT= 0; final int ARG= 1; final int IN_DOUBLE_QUOTE= 2; final int IN_SINGLE_QUOTE= 3; int state= DEFAULT; StringBuffer buf= new StringBuffer(); 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); StringBuffer buf = new StringBuffer(); 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 (int j = 0; j < characters.length; j++) { char ch = characters[j]; 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; } }