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