1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Anton Kosyakov (Itemis AG) - Bug 438621 - [step filtering] Provide an extension point to enhance methods step filtering.
14  *******************************************************************************/
15 package org.eclipse.debug.core;
16 
17 
18 import java.io.ByteArrayInputStream;
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.nio.charset.StandardCharsets;
23 import java.text.MessageFormat;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.FactoryConfigurationError;
32 import javax.xml.parsers.ParserConfigurationException;
33 import javax.xml.transform.TransformerException;
34 
35 import org.eclipse.core.resources.ISaveContext;
36 import org.eclipse.core.resources.ISaveParticipant;
37 import org.eclipse.core.resources.ResourcesPlugin;
38 import org.eclipse.core.runtime.CoreException;
39 import org.eclipse.core.runtime.IAdaptable;
40 import org.eclipse.core.runtime.IAdapterManager;
41 import org.eclipse.core.runtime.IConfigurationElement;
42 import org.eclipse.core.runtime.IExtensionPoint;
43 import org.eclipse.core.runtime.IProgressMonitor;
44 import org.eclipse.core.runtime.ISafeRunnable;
45 import org.eclipse.core.runtime.IStatus;
46 import org.eclipse.core.runtime.ListenerList;
47 import org.eclipse.core.runtime.Platform;
48 import org.eclipse.core.runtime.PlatformObject;
49 import org.eclipse.core.runtime.Plugin;
50 import org.eclipse.core.runtime.SafeRunner;
51 import org.eclipse.core.runtime.Status;
52 import org.eclipse.core.runtime.jobs.Job;
53 import org.eclipse.debug.core.model.IDebugElement;
54 import org.eclipse.debug.core.model.IDisconnect;
55 import org.eclipse.debug.core.model.IDropToFrame;
56 import org.eclipse.debug.core.model.IProcess;
57 import org.eclipse.debug.core.model.IStep;
58 import org.eclipse.debug.core.model.IStepFilter;
59 import org.eclipse.debug.core.model.IStepFilters;
60 import org.eclipse.debug.core.model.ISuspendResume;
61 import org.eclipse.debug.core.model.ITerminate;
62 import org.eclipse.debug.core.model.IValue;
63 import org.eclipse.debug.core.model.RuntimeProcess;
64 import org.eclipse.debug.internal.core.BreakpointManager;
65 import org.eclipse.debug.internal.core.DebugCoreMessages;
66 import org.eclipse.debug.internal.core.DebugOptions;
67 import org.eclipse.debug.internal.core.ExpressionManager;
68 import org.eclipse.debug.internal.core.IConfigurationElementConstants;
69 import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
70 import org.eclipse.debug.internal.core.LaunchManager;
71 import org.eclipse.debug.internal.core.LogicalStructureManager;
72 import org.eclipse.debug.internal.core.MemoryBlockManager;
73 import org.eclipse.debug.internal.core.Preferences;
74 import org.eclipse.debug.internal.core.StepFilterManager;
75 import org.eclipse.debug.internal.core.commands.CommandAdapterFactory;
76 import org.eclipse.debug.internal.core.groups.GroupMemberChangeListener;
77 import org.eclipse.debug.internal.core.sourcelookup.SourceLookupUtils;
78 import org.eclipse.osgi.service.environment.Constants;
79 import org.osgi.framework.BundleContext;
80 import org.w3c.dom.Document;
81 import org.w3c.dom.Element;
82 import org.xml.sax.SAXException;
83 import org.xml.sax.helpers.DefaultHandler;
84 
85 /**
86  * There is one instance of the debug plug-in available from
87  * <code>DebugPlugin.getDefault()</code>. The debug plug-in provides:
88  * <ul>
89  * <li>access to the breakpoint manager</li>
90  * <li>access to the launch manager</li>
91  * <li>access to the expression manager</li>
92  * <li>access to the registered launcher extensions</li>
93  * <li>access to the memory block manager</li>
94  * <li>debug event notification</li>
95  * <li>status handlers</li>
96  * </ul>
97  * @noinstantiate This class is not intended to be instantiated by clients.
98  * @noextend This class is not intended to be sub-classed by clients.
99  */
100 public class DebugPlugin extends Plugin {
101 
102 	/**
103 	 * Unique identifier constant (value <code>"org.eclipse.debug.core"</code>)
104 	 * for the Debug Core plug-in.
105 	 */
106 	private static final String PI_DEBUG_CORE = "org.eclipse.debug.core"; //$NON-NLS-1$
107 
108 	/**
109 	 * Simple identifier constant (value <code>"launchConfigurationTypes"</code>)
110 	 * for the launch configuration types extension point.
111 	 *
112 	 * @since 2.0
113 	 */
114 	public static final String EXTENSION_POINT_LAUNCH_CONFIGURATION_TYPES= "launchConfigurationTypes"; //$NON-NLS-1$
115 
116 	/**
117 	 * Simple identifier constant (value <code>"launchConfigurationComparators"</code>)
118 	 * for the launch configuration comparators extension point.
119 	 *
120 	 * @since 2.0
121 	 */
122 	public static final String EXTENSION_POINT_LAUNCH_CONFIGURATION_COMPARATORS= "launchConfigurationComparators"; //$NON-NLS-1$
123 
124 	/**
125 	 * Simple identifier constant (value <code>"breakpoints"</code>) for the
126 	 * breakpoints extension point.
127 	 *
128 	 * @since 2.0
129 	 */
130 	public static final String EXTENSION_POINT_BREAKPOINTS= "breakpoints";	 //$NON-NLS-1$
131 
132 	/**
133 	 * Simple identifier constant (value <code>"statusHandlers"</code>) for the
134 	 * status handlers extension point.
135 	 *
136 	 * @since 2.0
137 	 */
138 	public static final String EXTENSION_POINT_STATUS_HANDLERS= "statusHandlers";	 //$NON-NLS-1$
139 
140 	/**
141 	 * Simple identifier constant (value <code>"sourceLocators"</code>) for the
142 	 * source locators extension point.
143 	 *
144 	 * @since 2.0
145 	 */
146 	public static final String EXTENSION_POINT_SOURCE_LOCATORS= "sourceLocators";	 //$NON-NLS-1$
147 
148 	/**
149 	 * Simple identifier constant (value <code>"launchModes"</code>) for the
150 	 * source modes extension point.
151 	 *
152 	 * @since 3.0
153 	 */
154 	public static final String EXTENSION_POINT_LAUNCH_MODES= "launchModes";	 //$NON-NLS-1$
155 
156 	/**
157 	 * Simple identifier constant (value <code>"launchDelegates"</code>) for the
158 	 * launch delegates extension point.
159 	 *
160 	 * @since 3.0
161 	 */
162 	public static final String EXTENSION_POINT_LAUNCH_DELEGATES= "launchDelegates";	 //$NON-NLS-1$
163 
164 	/**
165 	 * Simple identifier constant (value <code>"processFactories"</code>) for the
166 	 * process factories extension point.
167 	 *
168 	 * @since 3.0
169 	 */
170 	public static final String EXTENSION_POINT_PROCESS_FACTORIES = "processFactories"; //$NON-NLS-1$
171 
172 	/**
173 	 * Simple identifier constant (value <code>"logicalStructureTypes"</code>) for the
174 	 * logical structure types extension point.
175 	 *
176 	 * @since 3.0
177 	 */
178 	public static final String EXTENSION_POINT_LOGICAL_STRUCTURE_TYPES = "logicalStructureTypes"; //$NON-NLS-1$
179 
180 	/**
181 	 * Simple identifier constant (value <code>"logicalStructureProviders"</code>) for the
182 	 * logical structure types extension point.
183 	 *
184 	 * @since 3.1
185 	 */
186 	public static final String EXTENSION_POINT_LOGICAL_STRUCTURE_PROVIDERS = "logicalStructureProviders"; //$NON-NLS-1$
187 
188 	/**
189 	 * Simple identifier constant (value <code>"sourceContainerTypes"</code>) for the
190 	 * source container types extension point.
191 	 *
192 	 * @since 3.0
193 	 */
194 	public static final String EXTENSION_POINT_SOURCE_CONTAINER_TYPES = "sourceContainerTypes";	 //$NON-NLS-1$
195 
196 	/**
197 	 * Simple identifier constant (value <code>"sourcePathComputers"</code>) for the
198 	 * source path computers extension point.
199 	 *
200 	 * @since 3.0
201 	 */
202 	public static final String EXTENSION_POINT_SOURCE_PATH_COMPUTERS = "sourcePathComputers"; //$NON-NLS-1$
203 
204 	/**
205 	 * Simple identifier constant for the launch options extension point
206 	 *
207 	 * @since 3.3
208 	 */
209 	public static final String EXTENSION_POINT_LAUNCH_OPTIONS = "launchOptions"; //$NON-NLS-1$
210 
211 	/**
212 	 * Simple identifier constant for the breakpoint import participant extension point
213 	 *
214 	 * @since 3.5
215 	 */
216 	public static final String EXTENSION_POINT_BREAKPOINT_IMPORT_PARTICIPANTS = "breakpointImportParticipants"; //$NON-NLS-1$
217 
218 	/**
219 	 * Simple identifier constant (value <code>"stepFilters"</code>) for the
220 	 * step filters extension point.
221 	 *
222 	 * @since 3.10
223 	 */
224 	public static final String EXTENSION_POINT_STEP_FILTERS = "stepFilters"; //$NON-NLS-1$
225 
226 	/**
227 	 * Status code indicating an unexpected error.
228 	 *
229 	 * @since 3.4
230 	 */
231 	public static final int ERROR = 125;
232 
233 	/**
234 	 * Status code indicating an unexpected internal error.  Internal errors
235 	 * should never be displayed to the user in dialogs or status text.
236 	 * Internal error messages are not translated.
237 	 */
238 	public static final int INTERNAL_ERROR = 120;
239 
240 	/**
241 	 * Status code indicating that the Eclipse runtime does not support
242 	 * launching a program with a working directory. This feature is only
243 	 * available if Eclipse is run on a 1.3 runtime or higher.
244 	 * <p>
245 	 * A status handler may be registered for this error condition,
246 	 * and should return a <code>Boolean</code> indicating whether the program
247 	 * should be re-launched with the default working directory.
248 	 * </p>
249 	 */
250 	public static final int ERR_WORKING_DIRECTORY_NOT_SUPPORTED = 115;
251 
252 	/**
253 	 * The launch configuration attribute that designates the process factory ID
254 	 * for the process factory to be used when creating a new process as a result of launching
255 	 * the launch configuration.
256 	 * @since 3.0
257 	 */
258 	public static final String ATTR_PROCESS_FACTORY_ID = "process_factory_id"; //$NON-NLS-1$
259 
260 	/**
261 	 * The launch attribute that designates whether or not it's associated
262 	 * launch should capture output. Value is a string representing a boolean -
263 	 * <code>true</code> or <code>false</code>. When unspecified, the default
264 	 * value is considered <code>true</code>.
265 	 *
266 	 * @since 3.1
267 	 */
268 	public static final String ATTR_CAPTURE_OUTPUT = PI_DEBUG_CORE + ".capture_output"; //$NON-NLS-1$
269 
270 	/**
271 	 * The launch attribute that stores the time stamp of when a launch configuration was
272 	 * launched. Value is {@link Long#toString(long)} of {@link System#currentTimeMillis()}.
273 	 *
274 	 * @since 3.6
275 	 */
276 	public static final String ATTR_LAUNCH_TIMESTAMP = PI_DEBUG_CORE + ".launch.timestamp";  //$NON-NLS-1$
277 
278 	/**
279 	 * The launch attribute that stores the time stamp of when a launch configuration was
280 	 * launched. Value is {@link Long#toString(long)} of {@link System#currentTimeMillis()}.
281 	 *
282 	 * @since 3.15
283 	 */
284 	public static final String ATTR_TERMINATE_TIMESTAMP = PI_DEBUG_CORE + ".terminate.timestamp"; //$NON-NLS-1$
285 
286 	/**
287 	 * This launch attribute designates the encoding to be used by the console
288 	 * associated with the launch.
289 	 * <p>
290 	 * For release 3.3, the system encoding is used when unspecified. Since 3.4,
291 	 * the inherited encoding is used when unspecified. See {@link ILaunchManager} for a
292 	 * description in <code>getEncoding(ILaunchConfiguration)</code>.
293 	 * </p>
294 	 * <p>
295 	 * Value of this constant is the same as the value of the old
296 	 * <code>IDebugUIConstants.ATTR_CONSOLE_ENCODING</code> constant for backward
297 	 * compatibility.
298 	 * </p>
299 	 * @since 3.3
300 	 */
301 	public static final String ATTR_CONSOLE_ENCODING = "org.eclipse.debug.ui.ATTR_CONSOLE_ENCODING"; //$NON-NLS-1$
302 
303 	/**
304 	 * Launch configuration attribute - a boolean value indicating whether a
305 	 * configuration should be launched with merged error and standard output.
306 	 * Merging output can ensure the process output appears in console in same
307 	 * order as the process produce it. On the other hand the error output can
308 	 * not be colored different from standard output anymore. Default value is
309 	 * <code>false</code>.
310 	 *
311 	 * @since 3.14
312 	 */
313 	public static final String ATTR_MERGE_OUTPUT = PI_DEBUG_CORE + ".ATTR_MERGE_OUTPUT"; //$NON-NLS-1$
314 
315 	/**
316 	 * Boolean preference key (value
317 	 * <code>org.eclipse.debug.core.PREF_DELETE_CONFIGS_ON_PROJECT_DELETE</code>)
318 	 * that controls whether to delete associated configurations when a project
319 	 * is deleted. Default value is <code>false</code>.
320 	 *
321 	 * @since 3.7
322 	 */
323 	public static final String PREF_DELETE_CONFIGS_ON_PROJECT_DELETE = PI_DEBUG_CORE + ".PREF_DELETE_CONFIGS_ON_PROJECT_DELETE"; //$NON-NLS-1$
324 
325 	/**
326 	 * Deleted breakpoint marker attribute (value
327 	 * <code>"org.eclipse.debug.core.breakpointIsDeleted"</code>). The attribute is a
328 	 * <code>boolean</code> corresponding to the deleted state of a breakpoint.
329 	 *
330 	 * @see org.eclipse.core.resources.IMarker#getAttribute(String, boolean)
331 	 * @since 3.7
332 	 */
333 	public static final String ATTR_BREAKPOINT_IS_DELETED= PI_DEBUG_CORE + ".breakpointIsDeleted"; //$NON-NLS-1$
334 
335 	/**
336 	 * Attribute key for the environment used when an {@link IProcess} was run
337 	 * @see IProcess
338 	 * @since 3.8
339 	 */
340 	public static final String ATTR_ENVIRONMENT = PI_DEBUG_CORE + ".ATTR_ENVIRONMENT"; //$NON-NLS-1$
341 
342 	/**
343 	 * Attribute key for the path of the working directory for an {@link IProcess}
344 	 *
345 	 * @see IProcess
346 	 * @since 3.8
347 	 */
348 	public static final String ATTR_WORKING_DIRECTORY = PI_DEBUG_CORE + ".ATTR_WORKING_DIRECTORY"; //$NON-NLS-1$
349 
350 	/**
351 	 * Attribute key for path of the executable that launched an {@link IProcess}
352 	 *
353 	 * @see IProcess
354 	 * @since 3.8
355 	 */
356 	public static final String ATTR_PATH = PI_DEBUG_CORE + ".ATTR_PATH"; //$NON-NLS-1$
357 
358 	/**
359 	 * The singleton debug plug-in instance.
360 	 */
361 	private static DebugPlugin fgDebugPlugin= null;
362 
363 	/**
364 	 * The singleton breakpoint manager.
365 	 */
366 	private BreakpointManager fBreakpointManager;
367 
368 	/**
369 	 * The singleton expression manager.
370 	 */
371 	private ExpressionManager fExpressionManager;
372 
373 	/**
374 	 * The singleton launch manager.
375 	 */
376 	private LaunchManager fLaunchManager;
377 
378 	/**
379 	 * The singleton memory block manager.
380 	 * @since 3.1
381 	 */
382 	private MemoryBlockManager fMemoryBlockManager;
383 
384 	/**
385 	 * The collection of debug event listeners.
386 	 */
387 	private ListenerList<IDebugEventSetListener> fEventListeners = new ListenerList<>();
388 
389 	/**
390 	 * Event filters, or <code>null</code> if none.
391 	 */
392 	private ListenerList<IDebugEventFilter> fEventFilters = new ListenerList<>();
393 
394 	/**
395 	 * Whether this plug-in is in the process of shutting
396 	 * down.
397 	 */
398 	private boolean fShuttingDown= false;
399 
400 	/**
401 	 * Table of status handlers. Keys are {plug-in identifier, status code}
402 	 * pairs, and values are associated <code>IConfigurationElement</code>s.
403 	 */
404 	private HashMap<StatusHandlerKey, IConfigurationElement> fStatusHandlers = null;
405 
406 	/**
407 	 * Map of process factories. Keys are process factory IDs
408 	 * and values are associated <code>IConfigurationElement</code>s.
409 	 * @since 3.0
410 	 */
411 	private HashMap<String, IConfigurationElement> fProcessFactories = null;
412 
413 	/**
414 	 * Mode constants for the event notifier
415 	 */
416 	private static final int NOTIFY_FILTERS = 0;
417 	private static final int NOTIFY_EVENTS = 1;
418 
419 	/**
420 	 * Queue of debug events to fire to listeners and asynchronous runnables to execute
421 	 * in the order received.
422 	 *
423 	 * @since 3.1
424 	 */
425 	private List<Object> fEventQueue = new ArrayList<>();
426 
427 	/**
428 	 * Job to fire events to listeners.
429 	 * @since 3.1
430 	 */
431 	private EventDispatchJob fEventDispatchJob = new EventDispatchJob();
432 
433 	/**
434 	 * Event dispatch job. Processes event queue of debug events and runnables.
435 	 *
436 	 * @since 3.1
437 	 */
438 	class EventDispatchJob extends Job {
439 
440 		EventNotifier fNotifier = new EventNotifier();
441 		AsynchRunner fRunner = new AsynchRunner();
442 
443 		/**
444 		 * Creates a new event dispatch job.
445 		 */
EventDispatchJob()446 		public EventDispatchJob() {
447 			super(DebugCoreMessages.DebugPlugin_1);
448 			setPriority(Job.INTERACTIVE);
449 			setSystem(true);
450 		}
451 
452 		@Override
run(IProgressMonitor monitor)453 		protected IStatus run(IProgressMonitor monitor) {
454 
455 			while (!fEventQueue.isEmpty()) {
456 				Object next = null;
457 				synchronized (fEventQueue) {
458 					if (!fEventQueue.isEmpty()) {
459 						next = fEventQueue.remove(0);
460 					}
461 				}
462 				if (next instanceof Runnable) {
463 					fRunner.async((Runnable) next);
464 				} else if (next != null) {
465 					fNotifier.dispatch((DebugEvent[]) next);
466 				}
467 			}
468 			return Status.OK_STATUS;
469 		}
470 
471 		@Override
shouldRun()472 		public boolean shouldRun() {
473 			return shouldSchedule();
474 		}
475 
476 		@Override
shouldSchedule()477 		public boolean shouldSchedule() {
478 			return !(isShuttingDown() || fEventListeners.isEmpty());
479 		}
480 
481 	}
482 
483 	/**
484 	 * Returns the singleton instance of the debug plug-in.
485 	 *
486 	 * @return the debug plug-in
487 	 */
getDefault()488 	public static DebugPlugin getDefault() {
489 		return fgDebugPlugin;
490 	}
491 
492 	/**
493 	 * Sets the singleton instance of the debug plug-in.
494 	 *
495 	 * @param plugin the debug plug-in, or <code>null</code>
496 	 *  when shutting down
497 	 */
setDefault(DebugPlugin plugin)498 	private static void setDefault(DebugPlugin plugin) {
499 		fgDebugPlugin = plugin;
500 	}
501 
502 	/**
503 	 * Convenience method which returns the unique identifier of this plug-in.
504 	 *
505 	 * @return debug plug-in identifier
506 	 */
getUniqueIdentifier()507 	public static String getUniqueIdentifier() {
508 		return PI_DEBUG_CORE;
509 	}
510 
511 	/**
512 	 * Constructs the debug plug-in.
513 	 * <p>
514 	 * An instance of this plug-in runtime class is automatically created
515 	 * when the facilities provided by this plug-in are required.
516 	 * <b>Clients must never explicitly instantiate a plug-in runtime class.</b>
517 	 * </p>
518 	 */
DebugPlugin()519 	public DebugPlugin() {
520 		super();
521 		setDefault(this);
522 	}
523 
524 	/**
525 	 * Adds the given listener to the collection of registered debug
526 	 * event listeners. Has no effect if an identical listener is already
527 	 * registered.
528 	 *
529 	 * @param listener the listener to add
530 	 * @since 2.0
531 	 */
addDebugEventListener(IDebugEventSetListener listener)532 	public void addDebugEventListener(IDebugEventSetListener listener) {
533 		fEventListeners.add(listener);
534 	}
535 
536 	/**
537 	 * Notifies all registered debug event set listeners of the given
538 	 * debug events. Events which are filtered by a registered debug event
539 	 * filter are not fired.
540 	 *
541 	 * @param events array of debug events to fire
542 	 * @see IDebugEventFilter
543 	 * @see IDebugEventSetListener
544 	 * @since 2.0
545 	 */
fireDebugEventSet(DebugEvent[] events)546 	public void fireDebugEventSet(DebugEvent[] events) {
547 		if (isShuttingDown() || events == null || fEventListeners.isEmpty()) {
548 			return;
549 		}
550 		synchronized (fEventQueue) {
551 			fEventQueue.add(events);
552 		}
553 		fEventDispatchJob.schedule();
554 	}
555 
556 	/**
557 	 * Asynchronously executes the given runnable in a separate
558 	 * thread, after debug event dispatch has completed. If debug
559 	 * events are not currently being dispatched, the runnable is
560 	 * scheduled to run in a separate thread immediately.
561 	 *
562 	 * @param r runnable to execute asynchronously
563 	 * @since 2.1
564 	 */
asyncExec(Runnable r)565 	public void asyncExec(Runnable r) {
566 		synchronized (fEventQueue) {
567 			fEventQueue.add(r);
568 		}
569 		fEventDispatchJob.schedule();
570 	}
571 
572 	/**
573 	 * Returns the breakpoint manager.
574 	 *
575 	 * @return the breakpoint manager
576 	 * @see IBreakpointManager
577 	 */
getBreakpointManager()578 	public synchronized IBreakpointManager getBreakpointManager() {
579 		if (fBreakpointManager == null) {
580 			fBreakpointManager = new BreakpointManager();
581 		}
582 		return fBreakpointManager;
583 	}
584 
585 	/**
586 	 * Returns the launch manager.
587 	 *
588 	 * @return the launch manager
589 	 * @see ILaunchManager
590 	 */
getLaunchManager()591 	public synchronized ILaunchManager getLaunchManager() {
592 		if (fLaunchManager == null) {
593 			fLaunchManager = new LaunchManager();
594 		}
595 		return fLaunchManager;
596 	}
597 
598 	/**
599 	 * Returns the memory block manager.
600 	 * @return the memory block manager.
601 	 * @see IMemoryBlockManager
602 	 * @since 3.1
603 	 */
getMemoryBlockManager()604 	public synchronized IMemoryBlockManager getMemoryBlockManager(){
605 		if (fMemoryBlockManager == null) {
606 			fMemoryBlockManager = new MemoryBlockManager();
607 		}
608 		return fMemoryBlockManager;
609 	}
610 
611 	/**
612 	 * Returns the status handler registered for the given
613 	 * status, or <code>null</code> if none.
614 	 *
615 	 * @param status status for which a status handler has been requested
616 	 * @return the status handler registered for the given
617 	 *  status, or <code>null</code> if none
618 	 * @since 2.0
619 	 */
getStatusHandler(IStatus status)620 	public IStatusHandler getStatusHandler(IStatus status) {
621 		boolean enabled = Platform.getPreferencesService().getBoolean(DebugPlugin.getUniqueIdentifier(), IInternalDebugCoreConstants.PREF_ENABLE_STATUS_HANDLERS, true, null);
622 		if (!enabled) {
623 			return null;
624 		}
625 		StatusHandlerKey key = new StatusHandlerKey(status.getPlugin(), status.getCode());
626 		if (fStatusHandlers == null) {
627 			initializeStatusHandlers();
628 		}
629 		IConfigurationElement config = fStatusHandlers.get(key);
630 		if (config != null) {
631 			try {
632 				Object handler = config.createExecutableExtension(IConfigurationElementConstants.CLASS);
633 				if (handler instanceof IStatusHandler) {
634 					return (IStatusHandler)handler;
635 				}
636 				invalidStatusHandler(null, MessageFormat.format("Registered status handler {0} does not implement required interface IStatusHandler.", new Object[] { config.getDeclaringExtension().getUniqueIdentifier() })); //$NON-NLS-1$
637 			} catch (CoreException e) {
638 				log(e);
639 			}
640 		}
641 		return null;
642 	}
643 
644 	/**
645 	 * Returns the expression manager.
646 	 *
647 	 * @return the expression manager
648 	 * @see IExpressionManager
649 	 * @since 2.0
650 	 */
getExpressionManager()651 	public synchronized IExpressionManager getExpressionManager() {
652 		if (fExpressionManager == null) {
653 			fExpressionManager = new ExpressionManager();
654 		}
655 		return fExpressionManager;
656 	}
657 
658 	/**
659 	 * Removes the given listener from the collection of registered debug
660 	 * event listeners. Has no effect if an identical listener is not already
661 	 * registered.
662 	 *
663 	 * @param listener the listener to remove
664 	 * @since 2.0
665 	 */
removeDebugEventListener(IDebugEventSetListener listener)666 	public void removeDebugEventListener(IDebugEventSetListener listener) {
667 		fEventListeners.remove(listener);
668 	}
669 
670 	@Override
stop(BundleContext context)671 	public void stop(BundleContext context) throws Exception {
672 		try {
673 			setShuttingDown(true);
674 
675 			if (fLaunchManager != null) {
676 				fLaunchManager.shutdown();
677 			}
678 			if (fBreakpointManager != null) {
679 				fBreakpointManager.shutdown();
680 			}
681 			if (fMemoryBlockManager != null)  {
682 				fMemoryBlockManager.shutdown();
683 			}
684 
685 			fEventListeners.clear();
686 			fEventFilters.clear();
687 
688 			SourceLookupUtils.shutdown();
689 			Preferences.savePreferences(DebugPlugin.getUniqueIdentifier());
690 			ResourcesPlugin.getWorkspace().removeSaveParticipant(getUniqueIdentifier());
691 		} finally {
692 			super.stop(context);
693 			setDefault(null);
694 		}
695 	}
696 
697 	@Override
start(BundleContext context)698 	public void start(BundleContext context) throws Exception {
699 		super.start(context);
700 		new DebugOptions(context);
701 		ResourcesPlugin.getWorkspace().addSaveParticipant(getUniqueIdentifier(),
702 				new ISaveParticipant() {
703 					@Override
704 					public void saving(ISaveContext saveContext) throws CoreException {
705 						if (fExpressionManager != null) {
706 							fExpressionManager.storeWatchExpressions();
707 						}
708 						Preferences.savePreferences(DebugPlugin.getUniqueIdentifier());
709 					}
710 					@Override
711 					public void rollback(ISaveContext saveContext) {}
712 					@Override
713 					public void prepareToSave(ISaveContext saveContext) throws CoreException {}
714 					@Override
715 					public void doneSaving(ISaveContext saveContext) {}
716 				});
717 		//command adapters
718 		IAdapterManager manager= Platform.getAdapterManager();
719 		CommandAdapterFactory actionFactory = new CommandAdapterFactory();
720 		manager.registerAdapters(actionFactory, IDisconnect.class);
721 		manager.registerAdapters(actionFactory, IDropToFrame.class);
722 		manager.registerAdapters(actionFactory, IStep.class);
723 		manager.registerAdapters(actionFactory, IStepFilters.class);
724 		manager.registerAdapters(actionFactory, ISuspendResume.class);
725 		manager.registerAdapters(actionFactory, ITerminate.class);
726 		manager.registerAdapters(actionFactory, ILaunch.class);
727 		manager.registerAdapters(actionFactory, IProcess.class);
728 		manager.registerAdapters(actionFactory, IDebugElement.class);
729 
730 		// monitor launch configuration renames for launch groups
731 		getLaunchManager().addLaunchConfigurationListener(new GroupMemberChangeListener());
732 	}
733 
734 	/**
735 	 * Creates and returns a new process representing the given
736 	 * <code>java.lang.Process</code>. A streams proxy is created
737 	 * for the I/O streams in the system process. The process
738 	 * is added to the given launch.
739 	 * <p>
740 	 * If the launch configuration associated with the given launch
741 	 * specifies a process factory, it will be used to instantiate
742 	 * the new process.
743 	 * </p>
744 	 * @param launch the launch the process is contained in
745 	 * @param process the system process to wrap
746 	 * @param label the label assigned to the process
747 	 * @return the process
748 	 * @see IProcess
749 	 * @see IProcessFactory
750 	 */
newProcess(ILaunch launch, Process process, String label)751 	public static IProcess newProcess(ILaunch launch, Process process, String label) {
752 		return newProcess(launch, process, label, null);
753 	}
754 
755 	/**
756 	 * Creates and returns a new process representing the given
757 	 * <code>java.lang.Process</code>. A streams proxy is created
758 	 * for the I/O streams in the system process. The process
759 	 * is added to the given launch, and the process is initialized
760 	 * with the given attribute map.
761 	 * <p>
762 	 * If the launch configuration associated with the given launch
763 	 * specifies a process factory, it will be used to instantiate
764 	 * the new process.
765 	 * </p>
766 	 * @param launch the launch the process is contained in
767 	 * @param process the system process to wrap
768 	 * @param label the label assigned to the process
769 	 * @param attributes initial values for the attribute map
770 	 * @return the process <code>null</code> can be returned if errors occur dealing with the process factory
771 	 * designated to create the process.
772 	 * @see IProcess
773 	 * @see IProcessFactory
774 	 * @since 2.1
775 	 */
newProcess(ILaunch launch, Process process, String label, Map<String, String> attributes)776 	public static IProcess newProcess(ILaunch launch, Process process, String label, Map<String, String> attributes) {
777 		ILaunchConfiguration config= launch.getLaunchConfiguration();
778 		String processFactoryID= null;
779 		if (config != null) {
780 			try {
781 				processFactoryID= config.getAttribute(ATTR_PROCESS_FACTORY_ID, (String)null);
782 			} catch (CoreException e) {
783 			}
784 		}
785 		if (processFactoryID != null) {
786 			DebugPlugin plugin= DebugPlugin.getDefault();
787 			if (plugin.fProcessFactories == null) {
788 				plugin.initializeProcessFactories();
789 			}
790 			IConfigurationElement element= plugin.fProcessFactories.get(processFactoryID);
791 			if (element == null) {
792 				return null;
793 			}
794 			IProcessFactory processFactory= null;
795 			try {
796 				processFactory = (IProcessFactory)element.createExecutableExtension(IConfigurationElementConstants.CLASS);
797 			} catch (CoreException exception) {
798 				log(exception);
799 				return null;
800 			}
801 			return processFactory.newProcess(launch, process, label, attributes);
802 		}
803 		return new RuntimeProcess(launch, process, label, attributes);
804 	}
805 
806 	/**
807 	 * Returns any logical structure types that have been contributed for the given
808 	 * value.
809 	 *
810 	 * @param value the value for which logical structure types have been requested
811 	 * @return logical structure types that have been contributed for the given
812 	 * value, possibly an empty collection
813 	 *
814 	 * @since 3.0
815 	 */
getLogicalStructureTypes(IValue value)816 	public static ILogicalStructureType[] getLogicalStructureTypes(IValue value) {
817 		return LogicalStructureManager.getDefault().getLogicalStructureTypes(value);
818 	}
819 
820 	/**
821 	 * Returns the default logical structure type among the given combination of
822 	 * logical structure types, or <code>null</code> if none. When the given combination
823 	 * of logical structure type is applicable for a value, the default logical structure
824 	 * type is used to display a value.
825 	 *
826 	 * @param types a combination of structures applicable to a value
827 	 * @return the default structure that should be used to display the value
828 	 * or <code>null</code> if none
829 	 *
830 	 * @since 3.1
831 	 */
getDefaultStructureType(ILogicalStructureType[] types)832 	public static ILogicalStructureType getDefaultStructureType(ILogicalStructureType[] types) {
833 		return LogicalStructureManager.getDefault().getSelectedStructureType(types);
834 	}
835 
836 	/**
837 	 * Sets the default logical structure type among the given combination of logical structure
838 	 * types. The logical structure types provided should all be applicable to a single
839 	 * value. Specifying <code>null</code> indicates there is no default logical structure
840 	 * for the given combination of types.
841 	 *
842 	 * @param types a combination of logical structure types applicable to a value
843 	 * @param def the default logical structure among the given combination of types
844 	 * or <code>null</code> if none
845 	 *
846 	 * @since 3.1
847 	 */
setDefaultStructureType(ILogicalStructureType[] types, ILogicalStructureType def)848 	public static void setDefaultStructureType(ILogicalStructureType[] types, ILogicalStructureType def) {
849 		LogicalStructureManager.getDefault().setEnabledType(types, def);
850 	}
851 
852 	/**
853 	 * Convenience method that performs a runtime exec on the given command line
854 	 * in the context of the specified working directory, and returns the
855 	 * resulting process. If the current runtime does not support the
856 	 * specification of a working directory, the status handler for error code
857 	 * <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if the
858 	 * exec should be re-executed without specifying a working directory.
859 	 *
860 	 * @param cmdLine the command line
861 	 * @param workingDirectory the working directory, or <code>null</code>
862 	 * @return the resulting process or <code>null</code> if the exec is
863 	 *  canceled
864 	 * @exception CoreException if the exec fails
865 	 * @see Runtime
866 	 *
867 	 * @since 2.1
868 	 */
exec(String[] cmdLine, File workingDirectory)869 	public static Process exec(String[] cmdLine, File workingDirectory) throws CoreException {
870 		return exec(cmdLine, workingDirectory, null);
871 	}
872 
873 	/**
874 	 * Convenience method that performs a runtime exec on the given command line
875 	 * in the context of the specified working directory, and returns the
876 	 * resulting process. If the current runtime does not support the
877 	 * specification of a working directory, the status handler for error code
878 	 * <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if the
879 	 * exec should be re-executed without specifying a working directory.
880 	 *
881 	 * @param cmdLine the command line
882 	 * @param workingDirectory the working directory, or <code>null</code>
883 	 * @param envp the environment variables set in the process, or <code>null</code>
884 	 * @return the resulting process or <code>null</code> if the exec is
885 	 *  canceled
886 	 * @exception CoreException if the exec fails
887 	 * @see Runtime
888 	 *
889 	 * @since 3.0
890 	 */
exec(String[] cmdLine, File workingDirectory, String[] envp)891 	public static Process exec(String[] cmdLine, File workingDirectory, String[] envp) throws CoreException {
892 		return exec(cmdLine, workingDirectory, envp, false);
893 	}
894 
895 	/**
896 	 * Convenience method that performs a runtime exec on the given command line
897 	 * in the context of the specified working directory, and returns the
898 	 * resulting process. If the current runtime does not support the
899 	 * specification of a working directory, the status handler for error code
900 	 * <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if the
901 	 * exec should be re-executed without specifying a working directory.
902 	 *
903 	 * @param cmdLine the command line
904 	 * @param workingDirectory the working directory, or <code>null</code>
905 	 * @param envp the environment variables set in the process, or
906 	 *            <code>null</code>
907 	 * @param mergeOutput if <code>true</code> the error stream will be merged
908 	 *            with standard output stream and both can be read through the
909 	 *            same output stream
910 	 * @return the resulting process or <code>null</code> if the exec is
911 	 *         canceled
912 	 * @exception CoreException if the exec fails
913 	 * @see Runtime
914 	 *
915 	 * @since 3.14
916 	 */
exec(String[] cmdLine, File workingDirectory, String[] envp, boolean mergeOutput)917 	public static Process exec(String[] cmdLine, File workingDirectory, String[] envp, boolean mergeOutput) throws CoreException {
918 		Process p = null;
919 		try {
920 			// starting with and without merged output could be done with the
921 			// same process builder approach but since the handling of
922 			// environment variables is slightly different between
923 			// ProcessBuilder and Runtime.exec only the new option uses process
924 			// builder to not break existing caller of this method
925 			if (mergeOutput) {
926 				ProcessBuilder pb = new ProcessBuilder(cmdLine);
927 				pb.directory(workingDirectory);
928 				pb.redirectErrorStream(mergeOutput);
929 				if (envp != null) {
930 					Map<String, String> env = pb.environment();
931 					env.clear();
932 					for (String e : envp) {
933 						int index = e.indexOf('=');
934 						if (index != -1) {
935 							env.put(e.substring(0, index), e.substring(index + 1));
936 						}
937 					}
938 				}
939 				p = pb.start();
940 			} else {
941 				if (workingDirectory == null) {
942 					p = Runtime.getRuntime().exec(cmdLine, envp);
943 				} else {
944 					p = Runtime.getRuntime().exec(cmdLine, envp, workingDirectory);
945 				}
946 			}
947 		} catch (IOException e) {
948 			Status status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_0, e);
949 			throw new CoreException(status);
950 		} catch (NoSuchMethodError e) {
951 			//attempting launches on 1.2.* - no ability to set working directory
952 			IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERR_WORKING_DIRECTORY_NOT_SUPPORTED, DebugCoreMessages.DebugPlugin_Eclipse_runtime_does_not_support_working_directory_2, e);
953 			IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status);
954 
955 			if (handler != null) {
956 				Object result = handler.handleStatus(status, null);
957 				if (result instanceof Boolean && ((Boolean) result).booleanValue()) {
958 					p = exec(cmdLine, null);
959 				}
960 			}
961 		}
962 		return p;
963 	}
964 
965 	/**
966 	 * Returns whether this plug-in is in the process of
967 	 * being shutdown.
968 	 *
969 	 * @return whether this plug-in is in the process of
970 	 *  being shutdown
971 	 */
isShuttingDown()972 	private boolean isShuttingDown() {
973 		return fShuttingDown;
974 	}
975 
976 	/**
977 	 * Sets whether this plug-in is in the process of
978 	 * being shutdown.
979 	 *
980 	 * @param value whether this plug-in is in the process of
981 	 *  being shutdown
982 	 */
setShuttingDown(boolean value)983 	private void setShuttingDown(boolean value) {
984 		fShuttingDown = value;
985 	}
986 
987 
988 	/**
989 	 * Adds the given debug event filter to the registered
990 	 * event filters. Has no effect if an identical filter
991 	 * is already registered.
992 	 *
993 	 * @param filter debug event filter
994 	 * @since 2.0
995 	 */
addDebugEventFilter(IDebugEventFilter filter)996 	public void addDebugEventFilter(IDebugEventFilter filter) {
997 		fEventFilters.add(filter);
998 	}
999 
1000 	/**
1001 	 * Removes the given debug event filter from the registered
1002 	 * event filters. Has no effect if an identical filter
1003 	 * is not already registered.
1004 	 *
1005 	 * @param filter debug event filter
1006 	 * @since 2.0
1007 	 */
removeDebugEventFilter(IDebugEventFilter filter)1008 	public void removeDebugEventFilter(IDebugEventFilter filter) {
1009 		fEventFilters.remove(filter);
1010 	}
1011 
1012 	/**
1013 	 * Logs the given message if in debug mode.
1014 	 *
1015 	 * @param message the message to log
1016 	 * @since 2.0
1017 	 */
logDebugMessage(String message)1018 	public static void logDebugMessage(String message) {
1019 		if (getDefault().isDebugging()) {
1020 			// this message is intentionally not externalized, as an exception may
1021 			// be due to the resource bundle itself
1022 			log(new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, MessageFormat.format(DebugCoreMessages.DebugPlugin_2, new Object[] { message }), null));
1023 		}
1024 	}
1025 
1026 	/**
1027 	 * Logs the given message with this plug-in's log and the given
1028 	 * throwable or <code>null</code> if none.
1029 	 * @param message the message to log
1030 	 * @param throwable the exception that occurred or <code>null</code> if none
1031 	 */
logMessage(String message, Throwable throwable)1032 	public static void logMessage(String message, Throwable throwable) {
1033 		log(new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, message, throwable));
1034 	}
1035 
1036 	/**
1037 	 * Logs the specified status with this plug-in's log.
1038 	 *
1039 	 * @param status status to log
1040 	 * @since 2.0
1041 	 */
log(IStatus status)1042 	public static void log(IStatus status) {
1043 		getDefault().getLog().log(status);
1044 	}
1045 
1046 	/**
1047 	 * Logs the specified throwable with this plug-in's log.
1048 	 *
1049 	 * @param t throwable to log
1050 	 * @since 2.0
1051 	 */
log(Throwable t)1052 	public static void log(Throwable t) {
1053 		IStatus status= new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_3, t);
1054 		log(status);
1055 	}
1056 
1057 	/**
1058 	 * Register status handlers.
1059 	 *
1060 	 */
initializeStatusHandlers()1061 	private void initializeStatusHandlers() {
1062 		IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.PI_DEBUG_CORE, EXTENSION_POINT_STATUS_HANDLERS);
1063 		IConfigurationElement[] infos= extensionPoint.getConfigurationElements();
1064 		fStatusHandlers = new HashMap<>(infos.length);
1065 		for (IConfigurationElement configurationElement : infos) {
1066 			String id = configurationElement.getAttribute("plugin"); //$NON-NLS-1$
1067 			String code = configurationElement.getAttribute("code"); //$NON-NLS-1$
1068 
1069 			if (id != null && code != null) {
1070 				try {
1071 					StatusHandlerKey key = new StatusHandlerKey(id, Integer.parseInt(code));
1072 					fStatusHandlers.put(key, configurationElement);
1073 				} catch (NumberFormatException e) {
1074 					// invalid status handler
1075 					invalidStatusHandler(e, configurationElement.getAttribute("id")); //$NON-NLS-1$
1076 				}
1077 			} else {
1078 				// invalid status handler
1079 				invalidStatusHandler(null, configurationElement.getAttribute("id")); //$NON-NLS-1$
1080 			}
1081 		}
1082 	}
1083 
1084 	/**
1085 	 * Register process factories.
1086 	 *
1087 	 */
initializeProcessFactories()1088 	private void initializeProcessFactories() {
1089 		IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.PI_DEBUG_CORE, EXTENSION_POINT_PROCESS_FACTORIES);
1090 		IConfigurationElement[] infos= extensionPoint.getConfigurationElements();
1091 		fProcessFactories = new HashMap<>(infos.length);
1092 		for (IConfigurationElement configurationElement : infos) {
1093 			String id = configurationElement.getAttribute("id"); //$NON-NLS-1$
1094 			String clss = configurationElement.getAttribute("class"); //$NON-NLS-1$
1095 			if (id != null && clss != null) {
1096 				fProcessFactories.put(id, configurationElement);
1097 			} else {
1098 				// invalid process factory
1099 				String badDefiner = configurationElement.getContributor().getName();
1100 				log(new Status(IStatus.ERROR, DebugPlugin.PI_DEBUG_CORE, ERROR, MessageFormat.format(DebugCoreMessages.DebugPlugin_4, new Object[] {
1101 					badDefiner, id }), null));
1102 			}
1103 		}
1104 	}
1105 
invalidStatusHandler(Exception e, String id)1106 	private void invalidStatusHandler(Exception e, String id) {
1107 		log(new Status(IStatus.ERROR, DebugPlugin.PI_DEBUG_CORE, ERROR, MessageFormat.format(DebugCoreMessages.DebugPlugin_5, new Object[] { id }), e));
1108 	}
1109 
1110 	/**
1111 	 * Key for status handler extensions - a plug-in identifier/code pair
1112 	 */
1113 	class StatusHandlerKey {
1114 
1115 		String fPluginId;
1116 		int fCode;
1117 
StatusHandlerKey(String pluginId, int code)1118 		StatusHandlerKey(String pluginId, int code) {
1119 			fPluginId = pluginId;
1120 			fCode = code;
1121 		}
1122 
1123 		@Override
hashCode()1124 		public int hashCode() {
1125 			return fPluginId.hashCode() + fCode;
1126 		}
1127 
1128 		@Override
equals(Object obj)1129 		public boolean equals(Object obj) {
1130 			if (obj instanceof StatusHandlerKey) {
1131 				StatusHandlerKey s = (StatusHandlerKey)obj;
1132 				return fCode == s.fCode && fPluginId.equals(s.fPluginId);
1133 			}
1134 			return false;
1135 		}
1136 	}
1137 
1138 	/**
1139 	 * Executes runnables after event dispatch is complete.
1140 	 *
1141 	 * @since 3.0
1142 	 */
1143 	class AsynchRunner implements ISafeRunnable {
1144 
1145 		private Runnable fRunnable = null;
1146 
async(Runnable runnable)1147 		void async(Runnable runnable) {
1148 			fRunnable = runnable;
1149 			SafeRunner.run(this);
1150 			fRunnable = null;
1151 
1152 		}
1153 
1154 		@Override
handleException(Throwable exception)1155 		public void handleException(Throwable exception) {
1156 			IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_6, exception);
1157 			log(status);
1158 		}
1159 
1160 		@Override
run()1161 		public void run() throws Exception {
1162 			fRunnable.run();
1163 		}
1164 
1165 	}
1166 
1167 	/**
1168 	 * Filters and dispatches events in a safe runnable to handle any
1169 	 * exceptions.
1170 	 */
1171 	class EventNotifier implements ISafeRunnable {
1172 
1173 		private DebugEvent[] fEvents;
1174 		private IDebugEventSetListener fListener;
1175 		private IDebugEventFilter fFilter;
1176 		private int fMode;
1177 
1178 		@Override
handleException(Throwable exception)1179 		public void handleException(Throwable exception) {
1180 			switch (fMode) {
1181 				case NOTIFY_FILTERS:
1182 					IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_7, exception);
1183 					log(status);
1184 					break;
1185 				case NOTIFY_EVENTS:
1186 					status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_8, exception);
1187 					log(status);
1188 					break;
1189 				default:
1190 					break;
1191 			}
1192 		}
1193 
1194 		@Override
run()1195 		public void run() throws Exception {
1196 			switch (fMode) {
1197 				case NOTIFY_FILTERS:
1198 					fEvents = fFilter.filterDebugEvents(fEvents);
1199 					break;
1200 				case NOTIFY_EVENTS:
1201 					fListener.handleDebugEvents(fEvents);
1202 					break;
1203 				default:
1204 					break;
1205 			}
1206 		}
1207 
1208 		/**
1209 		 * Filter and dispatch the given events. If an exception occurs in one
1210 		 * listener, events are still fired to subsequent listeners.
1211 		 *
1212 		 * @param events debug events
1213 		 */
dispatch(DebugEvent[] events)1214 		void dispatch(DebugEvent[] events) {
1215 			fEvents = events;
1216 			if (!fEventFilters.isEmpty()) {
1217 				fMode = NOTIFY_FILTERS;
1218 				for (IDebugEventFilter iDebugEventFilter : fEventFilters) {
1219 					fFilter = iDebugEventFilter;
1220 					SafeRunner.run(this);
1221 					if (fEvents == null || fEvents.length == 0) {
1222 						return;
1223 					}
1224 				}
1225 			}
1226 
1227 			fMode = NOTIFY_EVENTS;
1228 			if (DebugOptions.DEBUG_EVENTS) {
1229 				for (DebugEvent event : fEvents) {
1230 					DebugOptions.trace(event.toString());
1231 				}
1232 			}
1233 			for (IDebugEventSetListener iDebugEventSetListener : fEventListeners) {
1234 				fListener = iDebugEventSetListener;
1235 				SafeRunner.run(this);
1236 			}
1237 			fEvents = null;
1238 			fFilter = null;
1239 			fListener = null;
1240 		}
1241 
1242 	}
1243 
1244 	/**
1245 	 * Creates and returns a new XML document.
1246 	 *
1247 	 * @return a new XML document
1248 	 * @throws CoreException if unable to create a new document
1249 	 * @since 3.0
1250 	 */
newDocument()1251 	public static Document newDocument()throws CoreException {
1252 		try {
1253 			return LaunchManager.getDocument();
1254 		} catch (ParserConfigurationException e) {
1255 			abort("Unable to create new XML document.", e);  //$NON-NLS-1$
1256 		}
1257 		return null;
1258 	}
1259 
1260 	/**
1261 	 * Serializes the given XML document into a string.
1262 	 *
1263 	 * @param document XML document to serialize
1264 	 * @return a string representing the given document
1265 	 * @throws CoreException if unable to serialize the document
1266 	 * @since 3.0
1267 	 */
serializeDocument(Document document)1268 	public static String serializeDocument(Document document) throws CoreException {
1269 		try {
1270 			return LaunchManager.serializeDocument(document);
1271 		} catch (TransformerException e) {
1272 			abort("Unable to serialize XML document.", e);  //$NON-NLS-1$
1273 		} catch (IOException e) {
1274 			abort("Unable to serialize XML document.",e);  //$NON-NLS-1$
1275 		}
1276 		return null;
1277 	}
1278 
1279 	/**
1280 	 * Parses the given string representing an XML document, returning its
1281 	 * root element.
1282 	 *
1283 	 * @param document XML document as a string
1284 	 * @return the document's root element
1285 	 * @throws CoreException if unable to parse the document
1286 	 * @since 3.0
1287 	 */
parseDocument(String document)1288 	public static Element parseDocument(String document) throws CoreException {
1289 		Element root = null;
1290 		InputStream stream = null;
1291 		try{
1292 			DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
1293 			parser.setErrorHandler(new DefaultHandler());
1294 			stream = new ByteArrayInputStream(document.getBytes(StandardCharsets.UTF_8));
1295 			root = parser.parse(stream).getDocumentElement();
1296 		} catch (ParserConfigurationException e) {
1297 			abort("Unable to parse XML document.", e);  //$NON-NLS-1$
1298 		} catch (FactoryConfigurationError e) {
1299 			abort("Unable to parse XML document.", e);  //$NON-NLS-1$
1300 		} catch (SAXException e) {
1301 			abort("Unable to parse XML document.", e);  //$NON-NLS-1$
1302 		} catch (IOException e) {
1303 			abort("Unable to parse XML document.", e);  //$NON-NLS-1$
1304 		} finally {
1305 			try{
1306 				if (stream != null) {
1307 					stream.close();
1308 				}
1309 			} catch(IOException e) {
1310 				abort("Unable to parse XML document.", e);  //$NON-NLS-1$
1311 			}
1312 		}
1313 		return root;
1314 	}
1315 
1316 	/**
1317 	 * Throws an exception with the given message and underlying exception.
1318 	 *
1319 	 * @param message error message
1320 	 * @param exception underlying exception, or <code>null</code>
1321 	 * @throws CoreException if a problem is encountered
1322 	 */
abort(String message, Throwable exception)1323 	private static void abort(String message, Throwable exception) throws CoreException {
1324 		IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.ERROR, message, exception);
1325 		throw new CoreException(status);
1326 	}
1327 
parseArgumentsWindows(String args, boolean split)1328 	private static String[] parseArgumentsWindows(String args, boolean split) {
1329 		// see http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
1330 		List<String> result = new ArrayList<>();
1331 
1332 		final int DEFAULT= 0;
1333 		final int ARG= 1;
1334 		final int IN_DOUBLE_QUOTE= 2;
1335 
1336 		int state= DEFAULT;
1337 		int backslashes= 0;
1338 		StringBuilder buf= new StringBuilder();
1339 		int len= args.length();
1340 		for (int i= 0; i < len; i++) {
1341 			char ch= args.charAt(i);
1342 			if (ch == '\\') {
1343 				backslashes++;
1344 				continue;
1345 			} else if (backslashes != 0) {
1346 				if (ch == '"') {
1347 					for (; backslashes >= 2; backslashes-= 2) {
1348 						buf.append('\\');
1349 						if (split) {
1350 							buf.append('\\');
1351 						}
1352 					}
1353 					if (backslashes == 1) {
1354 						if (state == DEFAULT) {
1355 							state= ARG;
1356 						}
1357 						if (split) {
1358 							buf.append('\\');
1359 						}
1360 						buf.append('"');
1361 						backslashes= 0;
1362 						continue;
1363 					} // else fall through to switch
1364 				} else {
1365 					// false alarm, treat passed backslashes literally...
1366 					if (state == DEFAULT) {
1367 						state= ARG;
1368 					}
1369 					for (; backslashes > 0; backslashes--) {
1370 						buf.append('\\');
1371 					}
1372 					// fall through to switch
1373 				}
1374 			}
1375 			if (Character.isWhitespace(ch)) {
1376 				if (state == DEFAULT) {
1377 					// skip
1378 					continue;
1379 				} else if (state == ARG) {
1380 					state= DEFAULT;
1381 					result.add(buf.toString());
1382 					buf.setLength(0);
1383 					continue;
1384 				}
1385 			}
1386 			switch (state) {
1387 				case DEFAULT:
1388 				case ARG:
1389 					if (ch == '"') {
1390 						state= IN_DOUBLE_QUOTE;
1391 						if (split) {
1392 							buf.append(ch);
1393 						}
1394 					} else {
1395 						state= ARG;
1396 						buf.append(ch);
1397 					}
1398 					break;
1399 
1400 				case IN_DOUBLE_QUOTE:
1401 					if (ch == '"') {
1402 						if (i + 1 < len && args.charAt(i + 1) == '"') {
1403 							/* Undocumented feature in Windows:
1404 							 * Two consecutive double quotes inside a double-quoted argument are interpreted as
1405 							 * a single double quote.
1406 							 */
1407 							buf.append('"');
1408 							i++;
1409 							if (split) {
1410 								buf.append(ch);
1411 							}
1412 						} else if (buf.length() == 0) {
1413 							// empty string on Windows platform. Account for bug in constructor of JDK's java.lang.ProcessImpl.
1414 							result.add("\"\""); //$NON-NLS-1$
1415 							state= DEFAULT;
1416 						} else {
1417 							state= ARG;
1418 							if (split) {
1419 								buf.append(ch);
1420 							}
1421 						}
1422 					} else {
1423 						buf.append(ch);
1424 					}
1425 					break;
1426 
1427 				default:
1428 					throw new IllegalStateException();
1429 			}
1430 		}
1431 		if (buf.length() > 0 || state != DEFAULT) {
1432 			result.add(buf.toString());
1433 		}
1434 
1435 		return result.toArray(new String[result.size()]);
1436 	}
1437 
parseArgumentsImpl(String args, boolean split)1438 	private static String[] parseArgumentsImpl(String args, boolean split) {
1439 		// man sh, see topic QUOTING
1440 		List<String> result = new ArrayList<>();
1441 
1442 		final int DEFAULT= 0;
1443 		final int ARG= 1;
1444 		final int IN_DOUBLE_QUOTE= 2;
1445 		final int IN_SINGLE_QUOTE= 3;
1446 
1447 		int state= DEFAULT;
1448 		StringBuilder buf= new StringBuilder();
1449 		int len= args.length();
1450 		for (int i= 0; i < len; i++) {
1451 			char ch= args.charAt(i);
1452 			if (Character.isWhitespace(ch)) {
1453 				if (state == DEFAULT) {
1454 					// skip
1455 					continue;
1456 				} else if (state == ARG) {
1457 					state= DEFAULT;
1458 					result.add(buf.toString());
1459 					buf.setLength(0);
1460 					continue;
1461 				}
1462 			}
1463 			switch (state) {
1464 				case DEFAULT:
1465 				case ARG:
1466 					if (ch == '"') {
1467 						if (split) {
1468 							buf.append(ch);
1469 						}
1470 						state= IN_DOUBLE_QUOTE;
1471 					} else if (ch == '\'') {
1472 						if (split) {
1473 							buf.append(ch);
1474 						}
1475 						state= IN_SINGLE_QUOTE;
1476 					} else if (ch == '\\' && i + 1 < len) {
1477 						if (split) {
1478 							buf.append(ch);
1479 						}
1480 						state= ARG;
1481 						ch= args.charAt(++i);
1482 						buf.append(ch);
1483 					} else {
1484 						state= ARG;
1485 						buf.append(ch);
1486 					}
1487 					break;
1488 
1489 				case IN_DOUBLE_QUOTE:
1490 					if (ch == '"') {
1491 						if (split) {
1492 							buf.append(ch);
1493 						}
1494 						state= ARG;
1495 					} else if (ch == '\\' && i + 1 < len &&
1496 							(args.charAt(i + 1) == '\\' || args.charAt(i + 1) == '"')) {
1497 						if (split) {
1498 							buf.append(ch);
1499 						}
1500 						ch= args.charAt(++i);
1501 						buf.append(ch);
1502 					} else {
1503 						buf.append(ch);
1504 					}
1505 					break;
1506 
1507 				case IN_SINGLE_QUOTE:
1508 					if (ch == '\'') {
1509 						if (split) {
1510 							buf.append(ch);
1511 						}
1512 						state= ARG;
1513 					} else {
1514 						buf.append(ch);
1515 					}
1516 					break;
1517 
1518 				default:
1519 					throw new IllegalStateException();
1520 			}
1521 		}
1522 		if (buf.length() > 0 || state != DEFAULT) {
1523 			result.add(buf.toString());
1524 		}
1525 
1526 		return result.toArray(new String[result.size()]);
1527 	}
1528 
1529 	/**
1530 	 * Parses the given command line into separate arguments that can be passed
1531 	 * to <code>DebugPlugin.exec(String[], File)</code>. Embedded quotes and
1532 	 * backslashes are interpreted, i.e. the resulting arguments are in the form
1533 	 * that will be passed to an invoked process.
1534 	 * <p>
1535 	 * The reverse operation is {@link #renderArguments(String[], int[])}.
1536 	 * </p>
1537 	 *
1538 	 * @param args command line arguments as a single string
1539 	 * @return individual arguments
1540 	 * @see #renderArguments(String[], int[])
1541 	 * @since 3.1
1542 	 */
parseArguments(String args)1543 	public static String[] parseArguments(String args) {
1544 		if (args == null) {
1545 			return new String[0];
1546 		}
1547 
1548 		if (Constants.OS_WIN32.equals(Platform.getOS())) {
1549 			return parseArgumentsWindows(args, false);
1550 		}
1551 
1552 		return parseArgumentsImpl(args, false);
1553 	}
1554 
1555 	/**
1556 	 * Splits the given command line into separate arguments that can be
1557 	 * concatenated with a space as joiner. Embedded quotes and backslashes are
1558 	 * kept as is (i.e. not interpreted).
1559 	 * <p>
1560 	 * Use this method to avoid e.g. losing quotes around an argument like
1561 	 * <code>"${env_var:A}"</code>, which may later be substituted by a string
1562 	 * that contains spaces.
1563 	 * </p>
1564 	 *
1565 	 * @param args command line arguments as a single string
1566 	 * @return individual arguments in original form
1567 	 * @since 3.10
1568 	 */
splitArguments(String args)1569 	public static String[] splitArguments(String args) {
1570 		if (args == null) {
1571 			return new String[0];
1572 		}
1573 
1574 		if (Constants.OS_WIN32.equals(Platform.getOS())) {
1575 			return parseArgumentsWindows(args, true);
1576 		}
1577 
1578 		return parseArgumentsImpl(args, true);
1579 	}
1580 
1581 	/**
1582 	 * Renders the given array of argument strings into a single command line.
1583 	 * <p>
1584 	 * If an argument contains whitespace, it it quoted. Contained quotes or
1585 	 * backslashes will be escaped.
1586 	 * </p>
1587 	 * <p>
1588 	 * If <code>segments</code> is not <code>null</code>, the array is filled
1589 	 * with the offsets of the start positions of arguments 1 to
1590 	 * <code>arguments.length - 1</code>, as rendered in the resulting string.
1591 	 * </p>
1592 	 *
1593 	 * @param arguments the command line arguments
1594 	 * @param segments an array of size <code>arguments.length - 1</code> or
1595 	 *            <code>null</code>
1596 	 * @return the command line
1597 	 * @see #parseArguments(String)
1598 	 * @since 3.8
1599 	 */
renderArguments(String[] arguments, int[] segments)1600 	public static String renderArguments(String[] arguments, int[] segments) {
1601 		boolean isWin32= Platform.getOS().equals(Constants.OS_WIN32);
1602 		StringBuilder buf = new StringBuilder();
1603 		int count = arguments.length;
1604 		for (int i = 0; i < count; i++) {
1605 			if (i > 0) {
1606 				buf.append(' ');
1607 			}
1608 
1609 			boolean containsSpace = false;
1610 			char[] characters = arguments[i].toCharArray();
1611 			for (char ch : characters) {
1612 				if (ch == ' ' || ch == '\t') {
1613 					containsSpace = true;
1614 					buf.append('"');
1615 					break;
1616 				}
1617 			}
1618 
1619 			int backslashes = 0;
1620 			for (int j = 0; j < characters.length; j++) {
1621 				char ch = characters[j];
1622 				if (ch == '"') {
1623 					if (isWin32) {
1624 						if (j == 0 && characters.length == 2 && characters[1] == '"') {
1625 							// empty string on windows platform, see bug 130767. Bug in constructor of JDK's java.lang.ProcessImpl.
1626 							buf.append("\"\""); //$NON-NLS-1$
1627 							break;
1628 						}
1629 						if (backslashes > 0) {
1630 							// Feature in Windows: need to double-escape backslashes in front of double quote.
1631 							for (; backslashes > 0; backslashes--) {
1632 								buf.append('\\');
1633 							}
1634 						}
1635 					}
1636 					buf.append('\\');
1637 				} else if (ch == '\\') {
1638 					if (isWin32) {
1639 						backslashes++;
1640 					} else {
1641 						buf.append('\\');
1642 					}
1643 				}
1644 				buf.append(ch);
1645 			}
1646 			if (containsSpace) {
1647 				buf.append('"');
1648 			} else if (characters.length == 0) {
1649 				buf.append("\"\""); //$NON-NLS-1$
1650 			}
1651 
1652 			if (segments != null && i < count - 1) {
1653 				segments[i] = buf.length() + 1;
1654 			}
1655 		}
1656 		return buf.toString();
1657 	}
1658 
1659 	/**
1660 	 * Sets whether step filters should be applied to step commands. This
1661 	 * setting is a global option applied to all registered debug targets.
1662 	 *
1663 	 * @param useStepFilters whether step filters should be applied to step
1664 	 *  commands
1665 	 * @since 3.3
1666 	 * @see org.eclipse.debug.core.model.IStepFilters
1667 	 */
setUseStepFilters(boolean useStepFilters)1668 	public static void setUseStepFilters(boolean useStepFilters) {
1669 		getStepFilterManager().setUseStepFilters(useStepFilters);
1670 	}
1671 
1672 	/**
1673 	 * Returns whether step filters are applied to step commands.
1674 	 *
1675 	 * @return whether step filters are applied to step commands
1676 	 * @since 3.3
1677 	 * @see org.eclipse.debug.core.model.IStepFilters
1678 	 * @see org.eclipse.debug.core.commands.IStepFiltersHandler
1679 	 */
isUseStepFilters()1680 	public static boolean isUseStepFilters() {
1681 		return getStepFilterManager().isUseStepFilters();
1682 	}
1683 
1684 	/**
1685 	 * Returns any step filters that have been contributed for the given model
1686 	 * identifier.
1687 	 *
1688 	 * @param modelIdentifier the model identifier
1689 	 * @return step filters that have been contributed for the given model
1690 	 *         identifier, possibly an empty collection
1691 	 * @since 3.10
1692 	 * @see org.eclipse.debug.core.model.IStepFilter
1693 	 */
getStepFilters(String modelIdentifier)1694 	public static IStepFilter[] getStepFilters(String modelIdentifier) {
1695 		return getStepFilterManager().getStepFilters(modelIdentifier);
1696 	}
1697 
1698 	/**
1699 	 * Returns the step filter manager.
1700 	 *
1701 	 * @return step filter manager
1702 	 */
getStepFilterManager()1703 	private static StepFilterManager getStepFilterManager() {
1704 		return ((LaunchManager)getDefault().getLaunchManager()).getStepFilterManager();
1705 	}
1706 
1707 	/**
1708 	 * Returns an adapter of the specified type for the given object or <code>null</code>
1709 	 * if none. The object itself is returned if it is an instance of the specified type.
1710 	 * If the object is adaptable and does not subclass <code>PlatformObject</code>, and
1711 	 * does not provide the specified adapter directly, the platform's adapter manager
1712 	 * is consulted for an adapter.
1713 	 *
1714 	 * @param element element to retrieve adapter for
1715 	 * @param type adapter type
1716 	 * @return adapter or <code>null</code>
1717 	 * @since 3.4
1718 	 */
getAdapter(Object element, Class<?> type)1719 	public static Object getAdapter(Object element, Class<?> type) {
1720 		Object adapter = null;
1721 		if (element != null) {
1722 			if (type.isInstance(element)) {
1723 				return element;
1724 			} else {
1725 				if (element instanceof IAdaptable) {
1726 					adapter = ((IAdaptable)element).getAdapter(type);
1727 				}
1728 				// for objects that don't subclass PlatformObject, check the platform's adapter manager
1729 				if (adapter == null && !(element instanceof PlatformObject)) {
1730 					adapter = Platform.getAdapterManager().getAdapter(element, type);
1731 				}
1732 				// force load the adapter in case it really is available
1733 				if (adapter == null) {
1734 					adapter = Platform.getAdapterManager().loadAdapter(element, type.getName());
1735 				}
1736 			}
1737 		}
1738 		return adapter;
1739 	}
1740 
1741 }
1742 
1743 
1744