/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Martin Oberhuber (Wind River) - [325557] Perspective "none" is not persisted *******************************************************************************/ package org.eclipse.debug.internal.ui.launchConfigurations; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.eclipse.core.commands.contexts.Context; import org.eclipse.core.resources.ISaveContext; import org.eclipse.core.resources.ISaveParticipant; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchDelegate; import org.eclipse.debug.core.ILaunchListener; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.internal.core.IConfigurationElementConstants; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.core.LaunchManager; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; import org.eclipse.debug.internal.ui.viewers.AsynchronousSchedulingRuleFactory; import org.eclipse.debug.internal.ui.views.ViewContextManager; import org.eclipse.debug.internal.ui.views.ViewContextService; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.contexts.ISuspendTrigger; import org.eclipse.debug.ui.contexts.ISuspendTriggerListener; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IPerspectiveDescriptor; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.contexts.IContextActivation; import org.eclipse.ui.contexts.IContextService; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.ibm.icu.text.MessageFormat; /** * The perspective manager manages the 'perspective' settings * defined by launch configurations. Specifically it: * * * Since 3.3 the Perspectives Manager is an ISaveParticipant, allowing it to participate in * workspace persistence life-cycles. * * @see IDebugUIContants.ATTR_RUN_PERSPECTIVE * @see IDebugUIContants.ATTR_DEBUG_PERSPECTIVE * @see ISaveParticipant * @see ISuspendTriggerListener * @see ILaunchListener * @see org.eclipse.debug.internal.ui.preferences.LaunchPerspectivePreferencePage * @see DebugUIPlugin */ public class PerspectiveManager implements ILaunchListener, ISuspendTriggerListener, ISaveParticipant { /** * Describes exactly one perspective context, which is composed of an ILaunchCOnfigurationType, and set of modes * and an ILaunchDelegate. Perspective ids are then cached for a context based on mode set. * * @since 3.3 */ class PerspectiveContext { private ILaunchConfigurationType fType = null; private ILaunchDelegate fDelegate = null; private Map, String> fPerspectives = null; /** * Constructor * @param type * @param delegate * @param modes */ public PerspectiveContext(ILaunchConfigurationType type, ILaunchDelegate delegate, Set modes) { fType = type; fDelegate = delegate; fPerspectives = new HashMap<>(); fPerspectives.put(modes, null); } /** * We can specially compare two cases: *
    *
  1. a launch object
  2. *
  3. an object array of the form [IlaunchConfigurationType, ILaunchDelegate, Set]
  4. *
* @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object object) { ILaunchDelegate delegate = null; ILaunchConfigurationType type = null; if(object instanceof ILaunch) { try { ILaunch launch = (ILaunch) object; type = launch.getLaunchConfiguration().getType(); delegate = resolveLaunchDelegate(launch); } catch (CoreException e) {return false;} } else if(object instanceof PerspectiveContext) { PerspectiveContext context = (PerspectiveContext) object; type = context.getLaunchConfigurationType(); delegate = context.getLaunchDelegate(); } if(fType != null && type != null && fType.getIdentifier().equals(type.getIdentifier())) { if(fDelegate == null) { return delegate == null; } else { return fDelegate.equals(delegate); } } return super.equals(object); } @Override public int hashCode() { if (fType != null) { int hash = fType.getIdentifier().hashCode(); if (fDelegate != null) { hash += fDelegate.hashCode(); } return hash; } return super.hashCode(); } public ILaunchConfigurationType getLaunchConfigurationType() {return fType;} public ILaunchDelegate getLaunchDelegate() {return fDelegate;} public Map, String> getPersepctiveMap() { return fPerspectives; } /** * Creates a new mapping of the specified perspective id to the specified mode set. * If a mapping for the modeset already exists it is over-written. * @param modes the set of modes * @param pid the id of the perspective */ public void setPerspective(Set modes, String pid) { if(fPerspectives == null) { fPerspectives = new HashMap<>(); } fPerspectives.put(modes, pid); } /** * Returns the perspective id associated with the given mode set * @param modes the set of mode * @return the perspective id associated with the given mode set, or * null, if there isn't one */ public String getPerspective(Set modes) { if(fPerspectives != null) { return fPerspectives.get(modes); } return null; } } /** * Use a customized UI job so that nested jobs with a scheduling rule are * not prevented from running. * See bug 377593 */ private abstract class MyUIJob extends Job { public MyUIJob(String name) { super(name); setSystem(true); setPriority(Job.INTERACTIVE); setRule(AsynchronousSchedulingRuleFactory.getDefault().newSerialPerObjectRule(this)); } @Override protected IStatus run(final IProgressMonitor monitor) { if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } Display asyncDisplay = DebugUIPlugin.getStandardDisplay(); if (asyncDisplay == null || asyncDisplay.isDisposed()) { return Status.CANCEL_STATUS; } asyncDisplay.asyncExec(new Runnable() { @Override public void run() { IStatus result = null; Throwable throwable = null; try { if (monitor.isCanceled()) { result = Status.CANCEL_STATUS; } else { result = runInUIThread(monitor); } } catch(Throwable t){ throwable = t; } finally { if (result == null) { result = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.ERROR, LaunchConfigurationsMessages.PerspectiveManager_Error_1, throwable); } done(result); } } }); return Job.ASYNC_FINISH; } public abstract IStatus runInUIThread(IProgressMonitor monitor); } /** * A listing of PerspectiveContexts * * @since 3.3 */ private Set fPerspectiveContexts = null; /** * id for the 'delegate' attribute * * @since 3.3 */ public static final String ATTR_DELEGATE_ID = "delegate"; //$NON-NLS-1$ /** * Flag used to indicate that the user is already being prompted to * switch perspectives. This flag allows us to not open multiple * prompts at the same time. */ private boolean fPrompting; /** * Maps each launch to its perspective context activation. These * are disabled when a launch terminates. */ private Map fLaunchToContextActivations = new HashMap<>(); /** * Called by the debug ui plug-in on startup. * The perspective manager starts listening for * launches to be registered. */ public void startup() { DebugUIPlugin.getDefault().addSaveParticipant(this); DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); initPerspectives(); } /** * Called by the debug ui plug-in on shutdown. * The perspective manager de-registers as a * launch listener. */ public void shutdown() { DebugUIPlugin.getDefault().removeSaveParticipant(this); DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this); } /** * If there are no launches, remove the Suspend Trigger Listener * * @see ILaunchListener#launchRemoved(ILaunch) */ @Override public synchronized void launchRemoved(final ILaunch launch) { ISuspendTrigger trigger = launch.getAdapter(ISuspendTrigger.class); if (trigger != null) { trigger.removeSuspendTriggerListener(this); } Runnable r= new Runnable() { @Override public void run() { IContextActivation[] activations = fLaunchToContextActivations.remove(launch); if (activations != null) { for (int i = 0; i < activations.length; i++) { IContextActivation activation = activations[i]; activation.getContextService().deactivateContext(activation); } } } }; async(r); } /** * Do nothing. * * @see ILaunchListener#launchChanged(ILaunch) */ @Override public void launchChanged(ILaunch launch) {} /** * Switch to the perspective specified by the * launch configuration. * * @see ILaunchListener#launchAdded(ILaunch) */ @Override public synchronized void launchAdded(ILaunch launch) { ISuspendTrigger trigger = launch.getAdapter(ISuspendTrigger.class); if (trigger != null) { trigger.addSuspendTriggerListener(this); } String perspectiveId = null; // check event filters try { perspectiveId = getPerspectiveId(launch); } catch (CoreException e) { String name = DebugUIPlugin.getModelPresentation().getText(launch); switchFailed(e, name); } // don't switch if a private config ILaunchConfiguration configuration = launch.getLaunchConfiguration(); if (configuration != null) { if (!LaunchConfigurationManager.isVisible(configuration)) { perspectiveId = null; } } final String id = perspectiveId; // switch Job switchJob = new MyUIJob("Perspective Switch Job") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { IWorkbenchWindow window = getWindowForPerspective(id); if (id != null && window != null && shouldSwitchPerspective(window, id, IInternalDebugUIConstants.PREF_SWITCH_TO_PERSPECTIVE)) { switchToPerspective(window, id); } return Status.OK_STATUS; } }; switchJob.setSystem(true); switchJob.setPriority(Job.INTERACTIVE); //switchJob.setRule(AsynchronousSchedulingRuleFactory.getDefault().newSerialPerObjectRule(this)); switchJob.schedule(); } /** * Switches to the specified perspective * * @param id perspective identifier */ protected void switchToPerspective(IWorkbenchWindow window, String id) { try { // don't loose the focus dialog if there is one Shell dialog = getModalDialogOpen(window.getShell()); window.getWorkbench().showPerspective(id, window); if (dialog != null) { dialog.setFocus(); } } catch (WorkbenchException e) { DebugUIPlugin.errorDialog(DebugUIPlugin.getShell(), LaunchConfigurationsMessages.PerspectiveManager_Error_1, MessageFormat.format(LaunchConfigurationsMessages.PerspectiveManager_Unable_to_switch_to_perspective___0__2, new Object[] { id }), e); } } /** * Utility method to submit an asynchronous runnable to the UI */ protected void async(Runnable r) { Display d = DebugUIPlugin.getStandardDisplay(); if (d != null && !d.isDisposed()) { d.asyncExec(r); } } /** * Utility method to submit a synchronous runnable to the UI */ protected void sync(Runnable r) { Display d = DebugUIPlugin.getStandardDisplay(); if (d != null && !d.isDisposed()) { d.syncExec(r); } } /** * Reports failure to switch perspectives to the user * * @param status exception status describing failure * @param launchName the name of the launch that the * failure is associated with */ protected void switchFailed(final Throwable t, final String launchName) { sync(new Runnable() { @Override public void run() { DebugUIPlugin.errorDialog(DebugUIPlugin.getShell(), LaunchConfigurationsMessages.PerspectiveManager_Error_1, MessageFormat.format(LaunchConfigurationsMessages.PerspectiveManager_Unable_to_switch_perpsectives_as_specified_by_launch___0__4, new Object[] { launchName }), t); }}); } /** * A breakpoint has been hit. Carry out perspective switching * as appropriate for the given debug event. * * @param event the suspend event */ private void handleBreakpointHit(final ILaunch launch) { String perspectiveId = null; try { perspectiveId = getPerspectiveId(launch); } catch (CoreException e) {DebugUIPlugin.log(e);} // if no perspective specified, always switch to debug // perspective // this has to be done in an async, such that the workbench // window can be accessed final String targetId = perspectiveId; Job switchJob = new MyUIJob("Perspective Switch Job") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { IWorkbenchWindow window = null; if (targetId != null) { // get the window to open the perspective in window = getWindowForPerspective(targetId); if (window == null) { return Status.OK_STATUS; } // switch the perspective if user preference is set if (shouldSwitchPerspective(window, targetId, IInternalDebugUIConstants.PREF_SWITCH_PERSPECTIVE_ON_SUSPEND)) { switchToPerspective(window, targetId); window = getWindowForPerspective(targetId); if (window == null) { return Status.OK_STATUS; } } // make sure the shell is active Shell shell= window.getShell(); if (shell != null) { if (DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugUIConstants.PREF_ACTIVATE_WORKBENCH)) { Shell dialog = getModalDialogOpen(shell); if (shell.getMinimized()) { shell.setMinimized(false); if (dialog != null) { dialog.setFocus(); } } // If a model dialog is open on the shell, don't activate it if (dialog == null) { shell.forceActive(); } } } // Activate a context for the launch Object ca = fLaunchToContextActivations.get(launch); if (ca == null) { ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); if (launchConfiguration != null) { try { String type = launchConfiguration.getType().getIdentifier(); ViewContextService service = ViewContextManager.getDefault().getService(window); if (service != null) { IContextService contextServce = PlatformUI.getWorkbench().getAdapter(IContextService.class); String[] ids = service.getEnabledPerspectives(); IContextActivation[] activations = new IContextActivation[ids.length]; for (int i = 0; i < ids.length; i++) { // Include the word '.internal.' so the context is filtered from the key binding pref page (Bug 144019) also see ViewContextService.contextActivated() Context context = contextServce.getContext(type + ".internal." + ids[i]); //$NON-NLS-1$ if (!context.isDefined()) { context.define(context.getId(), null, null); } IContextActivation activation = contextServce.activateContext(context.getId()); activations[i] = activation; } fLaunchToContextActivations.put(launch, activations); } } catch (CoreException e) { DebugUIPlugin.log(e); } } } } if (window != null && DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IInternalDebugUIConstants.PREF_ACTIVATE_DEBUG_VIEW)) { ViewContextService service = ViewContextManager.getDefault().getService(window); service.showViewQuiet(IDebugUIConstants.ID_DEBUG_VIEW); } return Status.OK_STATUS; } }; switchJob.setSystem(true); switchJob.setPriority(Job.INTERACTIVE); switchJob.setRule(AsynchronousSchedulingRuleFactory.getDefault().newSerialPerObjectRule(this)); switchJob.schedule(); } /** * Returns a modal dialog currently open on the given shell or null if none. * * @param shell shell to check * @return a modal dialog currently open on the given shell or null if none */ private Shell getModalDialogOpen(Shell shell) { Shell[] shells = shell.getShells(); for (int i = 0; i < shells.length; i++) { Shell dialog = shells[i]; if ((dialog.getStyle() & (SWT.APPLICATION_MODAL | SWT.PRIMARY_MODAL | SWT.SYSTEM_MODAL)) > 0) { return dialog; } } return null; } /** * Returns the workbench window in which the given perspective * should be shown. First, check the current window to see if it * is already showing the perspective. Then check any other windows. * * @param perspectiveId the perspective identifier * @return which window the given perspective should be shown in * or null if there are no windows available */ private IWorkbenchWindow getWindowForPerspective(String perspectiveId) { IWorkbenchWindow window = DebugUIPlugin.getActiveWorkbenchWindow(); if (isWindowShowingPerspective(window, perspectiveId)) { return window; } IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows(); for (int i = 0; i < windows.length; i++) { window = windows[i]; if (isWindowShowingPerspective(window, perspectiveId)) { return window; } } window = DebugUIPlugin.getActiveWorkbenchWindow(); if (window != null) { return window; } if (windows.length > 0) { return windows[0]; } return null; } /** * Returns if the specified window is showing the perspective denoted by the specified id * @param window the window to query * @param perspectiveId the perspective to ask about * @return true if the specified window is showing the perspective, false otherwise */ private boolean isWindowShowingPerspective(IWorkbenchWindow window, String perspectiveId) { if (window != null) { IWorkbenchPage page = window.getActivePage(); if (page != null) { IPerspectiveDescriptor perspectiveDescriptor = page.getPerspective(); if (perspectiveDescriptor != null && perspectiveDescriptor.getId().equals(perspectiveId)) { return true; } } } return false; } /** * Returns whether or not the user wishes to switch to the specified * perspective when a launch occurs. * * @param perspectiveName the name of the perspective that will be presented * to the user for confirmation if they've asked to be prompted about * perspective switching * @param message a message to be presented to the user. This message is expected to * contain a slot for the perspective name to be inserted ("{0}"). * @param preferenceKey the preference key of the perspective switching preference * @return whether or not the user wishes to switch to the specified perspective * automatically */ private boolean shouldSwitchPerspective(IWorkbenchWindow window, String perspectiveId, String preferenceKey) { if (isCurrentPerspective(window, perspectiveId)) { return false; } String perspectiveName = getPerspectiveLabel(perspectiveId); if (perspectiveName == null) { return false; } String perspectiveDesc = getPerspectiveDescription(perspectiveId); Object[] args; if (perspectiveDesc != null) { args = new String[] { perspectiveName , perspectiveDesc }; } else { args = new String[] { perspectiveName }; } String switchPerspective = DebugUIPlugin.getDefault().getPreferenceStore().getString(preferenceKey); if (MessageDialogWithToggle.ALWAYS.equals(switchPerspective)) { return true; } else if (MessageDialogWithToggle.NEVER.equals(switchPerspective)) { return false; } Shell shell= window.getShell(); if (shell == null || fPrompting) { return false; } fPrompting= true; // Activate the shell if necessary so the prompt is visible Shell modal = getModalDialogOpen(shell); if (shell.getMinimized()) { shell.setMinimized(false); if (modal != null) { modal.setFocus(); } } if (DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugUIConstants.PREF_ACTIVATE_WORKBENCH)) { if (modal == null) { shell.forceActive(); } } String message = IInternalDebugCoreConstants.EMPTY_STRING; if(IInternalDebugUIConstants.PREF_SWITCH_PERSPECTIVE_ON_SUSPEND.equals(preferenceKey)) { if (getPerspectiveDescription(perspectiveId) != null) { message = LaunchConfigurationsMessages.PerspectiveManager_suspend_description; } else { message = LaunchConfigurationsMessages.PerspectiveManager_13; } } else if(IInternalDebugUIConstants.PREF_SWITCH_TO_PERSPECTIVE.equals(preferenceKey)) { if (getPerspectiveDescription(perspectiveId) != null) { message = LaunchConfigurationsMessages.PerspectiveManager_launch_description; } else { message = LaunchConfigurationsMessages.PerspectiveManager_15; } } LinkedHashMap buttonLabelToId = new LinkedHashMap<>(); buttonLabelToId.put(LaunchConfigurationsMessages.PerspectiveManager_switch, IDialogConstants.YES_ID); buttonLabelToId.put(IDialogConstants.NO_LABEL, IDialogConstants.NO_ID); MessageDialogWithToggle dialog = MessageDialogWithToggle.open(MessageDialog.QUESTION, shell, LaunchConfigurationsMessages.PerspectiveManager_12, MessageFormat.format(message, args), null, false, DebugUIPlugin.getDefault().getPreferenceStore(), preferenceKey, SWT.NONE, buttonLabelToId); boolean answer = (dialog.getReturnCode() == IDialogConstants.YES_ID); synchronized (this) { fPrompting= false; notifyAll(); } if (isCurrentPerspective(window, perspectiveId)) { answer = false; } return answer; } /** * Returns whether the given perspective identifier matches the * identifier of the current perspective. * * @param perspectiveId the identifier * @return whether the given perspective identifier matches the * identifier of the current perspective */ protected boolean isCurrentPerspective(IWorkbenchWindow window, String perspectiveId) { boolean isCurrent= false; if (window != null) { IWorkbenchPage page = window.getActivePage(); if (page != null) { IPerspectiveDescriptor perspectiveDescriptor = page.getPerspective(); if (perspectiveDescriptor != null) { isCurrent= perspectiveId.equals(perspectiveDescriptor.getId()); } } } return isCurrent; } /** * Returns the label of the perspective with the given identifier or * null if no such perspective exists. * * @param perspectiveId the identifier * @return the label of the perspective with the given identifier or * null if no such perspective exists */ protected String getPerspectiveLabel(String perspectiveId) { IPerspectiveDescriptor newPerspective = PlatformUI.getWorkbench().getPerspectiveRegistry().findPerspectiveWithId(perspectiveId); if (newPerspective == null) { return null; } return newPerspective.getLabel(); } /** * Returns the label of the perspective with the given identifier or * null if no such perspective exists. * * @param perspectiveId the identifier * @return the label of the perspective with the given identifier or * null if no such perspective exists */ protected String getPerspectiveDescription(String perspectiveId) { IPerspectiveDescriptor newPerspective = PlatformUI.getWorkbench().getPerspectiveRegistry().findPerspectiveWithId(perspectiveId); if (newPerspective == null) { return null; } return newPerspective.getDescription(); } /** * Returns the perspective associated with the * given launch, or null if none. * * @param launch a launch * @return the perspective associated with the launch, * or null * @exception CoreException if unable to retrieve a required * launch configuration attribute */ protected String getPerspectiveId(ILaunch launch) throws CoreException { if (launch == null) { return null; } ILaunchConfiguration config = launch.getLaunchConfiguration(); if (config == null) { return null; } Set modes = launch.getLaunchConfiguration().getModes(); modes.add(launch.getLaunchMode()); String perspectiveId = getLaunchPerspective(config.getType(), modes, resolveLaunchDelegate(launch)); if (perspectiveId != null && perspectiveId.equals(IDebugUIConstants.PERSPECTIVE_NONE)) { perspectiveId = null; } return perspectiveId; } /** * Returns the id of the perspective associated with the given type and set of modes. Passing null for * the launch delegate results in the default perspective id being returned (if there is one). * @param type the type we are launching * @param modes the set of modes the type was launched with * @param delegate the delegate performing the launch for this type and modeset * @return the id of the perspective for the given launch configuration type, modeset and launch delegate * * @since 3.3 */ public String getLaunchPerspective(ILaunchConfigurationType type, Set modes, ILaunchDelegate delegate) { String id = null; PerspectiveContext context = findContext(new PerspectiveContext(type, delegate, modes)); if(context == null || (context != null && !context.getPersepctiveMap().containsKey(modes))) { //try with a null delegate, denoting the perspective for the type context = findContext(new PerspectiveContext(type, null, modes)); if(context == null || (context != null && !context.getPersepctiveMap().containsKey(modes))) { //last resort, try the default perspective return getDefaultLaunchPerspective(type, delegate, modes); } } if(context != null) { id = context.getPerspective(modes); } return id; } /** * Returns the perspective to switch to when a configuration of the given type * is launched in the given mode, or null if no switch should take * place. *

* This method is equivalent to calling getLaunchPerspective(ILaunchConfigurationType type, Set modes, ILaunchDelegate delegate), * with the 'mode' parameter comprising a single element set and passing null as the launch delegate. *

* @param type launch configuration type * @param mode launch mode identifier * @return perspective identifier or null * @since 3.0 */ public String getLaunchPerspective(ILaunchConfigurationType type, String mode) { HashSet modes = new HashSet<>(); modes.add(mode); return getLaunchPerspective(type, modes, null); } /** * Sets the perspective to switch to when a configuration of the given type * is launched in the given mode. PERSPECTIVE_NONE indicates no * perspective switch should take place. PERSPECTIVE_DEFAULT indicates * a default perspective switch should take place, as defined by the associated * launch tab group extension. *

* Calling this method is equivalent to calling setLaunchPerspective(ILaunchConfigurationType type, Set modes, ILaunchDelegate delegate, String perspectiveid), * with the parameter 'mode' used in the set modes, and null passed as the delegate *

* @param type launch configuration type * @param mode launch mode identifier * @param perspective identifier, PERSPECTIVE_NONE, or * PERSPECTIVE_DEFAULT * @since 3.0 */ public void setLaunchPerspective(ILaunchConfigurationType type, String mode, String perspective) { HashSet modes = new HashSet<>(); modes.add(mode); setLaunchPerspective(type, modes, null, perspective); } /** * Sets the perspective that should be switched to when a configuration of the given type is launched with the * specified modes set by the given launch delegate. *

* Passing null as a launch delegate will set the default perspective switch for that type and modeset, where * PERSPECTIVE_NONE indicates no perspective switch should take place. *

* @param type the type to set a perspective context for * @param modes the set of modes * @param delegate the delegate, or null if the default perspective should be used * * @since 3.3 */ public void setLaunchPerspective(ILaunchConfigurationType type, Set modes, ILaunchDelegate delegate, String perspectiveid) { PerspectiveContext context = new PerspectiveContext(type, delegate, modes); String id = null; if(!IDebugUIConstants.PERSPECTIVE_NONE.equals(perspectiveid)) { if(IDebugUIConstants.PERSPECTIVE_DEFAULT.equals(perspectiveid)) { id = getDefaultLaunchPerspective(type, delegate, modes); } else { id = perspectiveid; } } PerspectiveContext item = findContext(context); if(item != null) { item.setPerspective(modes, id); } else { context.setPerspective(modes, id); item = context; } fPerspectiveContexts.add(item); } /** * Searches the listing of perspective contexts to see if the specified one already exists * @param context the context to compare * @return the matching PerspectiveContext or null if none * * @since 3.3 */ private PerspectiveContext findContext(PerspectiveContext context) { for (PerspectiveContext c : fPerspectiveContexts) { if (context.equals(c)) { return c; } } return null; } /** * Generates XML for the user specified perspective settings. * * @return XML * @exception IOException if unable to generate the XML * @exception TransformerException if unable to generate the XML * @exception ParserConfigurationException if unable to generate the XML */ private String generatePerspectiveXML() throws ParserConfigurationException, CoreException { Document doc = DebugUIPlugin.getDocument(); Element root = doc.createElement(IConfigurationElementConstants.LAUNCH_PERSPECTIVES); doc.appendChild(root); Map, String> modesets = null; Element element = null; String id = null; ILaunchConfigurationType type = null; ILaunchDelegate delegate = null; for (PerspectiveContext context : fPerspectiveContexts) { modesets = context.getPersepctiveMap(); type = context.getLaunchConfigurationType(); delegate = context.getLaunchDelegate(); for (Set modes : modesets.keySet()) { id = context.getPerspective(modes); String defaultId = getDefaultLaunchPerspective(type, delegate, modes); if(id == null && defaultId != null) { //bug 325557: Override of a default perspective id = IDebugUIConstants.PERSPECTIVE_NONE; } if(id != null && !id.equals(defaultId)) { element = doc.createElement(IConfigurationElementConstants.LAUNCH_PERSPECTIVE); element.setAttribute(IConfigurationElementConstants.MODE, createModesetString(modes)); if(delegate != null) { element.setAttribute(ATTR_DELEGATE_ID, delegate.getId()); } element.setAttribute(IConfigurationElementConstants.CONFIGURATION_TYPES, type.getIdentifier()); element.setAttribute(IConfigurationElementConstants.PERSPECTIVE, id); root.appendChild(element); } } } return DebugPlugin.serializeDocument(doc); } /** * Returns the default perspective to switch to when a configuration of the given type is launched by the specified * launch delegate in the given mode set, or null if none * @param type the type * @param delegate the associated delegate, or null to specify that the default perspective id for that given type and mode set should be returned * @param modes the set of modes this applies to * @return the default perspective id for the given type, delegate and mode set combination, or null if none * * @since 3.3 */ public String getDefaultLaunchPerspective(ILaunchConfigurationType type, ILaunchDelegate delegate, Set modes) { String id = null; if(delegate != null) { id = delegate.getPerspectiveId(modes); } if(id == null) { LaunchConfigurationTabGroupExtension extension = LaunchConfigurationPresentationManager.getDefault().getExtension(type.getIdentifier(), modes); if (extension != null) { id = extension.getPerspective(modes); if (id == null) { if (modes.contains(ILaunchManager.DEBUG_MODE)) { id = IDebugUIConstants.ID_DEBUG_PERSPECTIVE; } } } } return id; } /** * Resolves the ILaunchDelegate from the given ILaunch * @param launch the launch * @return * @throws CoreException */ private ILaunchDelegate resolveLaunchDelegate(ILaunch launch) throws CoreException { Set modes = launch.getLaunchConfiguration().getModes(); modes.add(launch.getLaunchMode()); ILaunchConfigurationType type = launch.getLaunchConfiguration().getType(); ILaunchDelegate[] delegates = LaunchConfigurationManager.filterLaunchDelegates(type, modes); ILaunchDelegate delegate = null; if(delegates.length == 1) { delegate = delegates[0]; } else if(delegates.length > 1) { delegate = launch.getLaunchConfiguration().getPreferredDelegate(modes); if(delegate == null) { delegate = type.getPreferredDelegate(modes); } } return delegate; } /** * Initialize the preference set with settings from user preferences */ private void initPerspectives() { if(fPerspectiveContexts == null) { fPerspectiveContexts = new HashSet<>(); String xml = DebugUIPlugin.getDefault().getPreferenceStore().getString(IInternalDebugUIConstants.PREF_LAUNCH_PERSPECTIVES); if (xml != null && xml.length() > 0) { try { Element root = DebugPlugin.parseDocument(xml); NodeList list = root.getChildNodes(); LaunchManager lm = (LaunchManager) DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType lctype = null; ILaunchDelegate ldelegate = null; Set modes = null; Node node = null; Element element = null; for (int i = 0; i < list.getLength(); ++i) { node = list.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { element = (Element) node; String nodeName = element.getNodeName(); if (nodeName.equalsIgnoreCase(IConfigurationElementConstants.LAUNCH_PERSPECTIVE)) { String type = element.getAttribute(IConfigurationElementConstants.CONFIGURATION_TYPES); String mode = element.getAttribute(IConfigurationElementConstants.MODE); String perspective = element.getAttribute(IConfigurationElementConstants.PERSPECTIVE); String delegate = element.getAttribute(ATTR_DELEGATE_ID); lctype = lm.getLaunchConfigurationType(type); ldelegate = lm.getLaunchDelegate(delegate); modes = parseModes(mode); if(lctype != null && !modes.isEmpty() && !IInternalDebugCoreConstants.EMPTY_STRING.equals(perspective)) { setLaunchPerspective(lctype, modes, ldelegate, perspective); } } } } } catch (CoreException e) {DebugUIPlugin.log(e);} } } } /** * Parses a string argument into a set of modes * @param modes the string to parse * @return a set of modes parsed from the specified string of the empty set, never null * * @since 3.3 */ private Set parseModes(String modes) { HashSet modeset = new HashSet<>(); String[] ms = modes.split(","); //$NON-NLS-1$ for(int i = 0; i < ms.length; i++) { modeset.add(ms[i].trim()); } return modeset; } /** * Creates a standard comma separated list of the modes from the specified * set * * @param modes the set to write to string * @return the mode set as a string */ private String createModesetString(Set modes) { String str = IInternalDebugCoreConstants.EMPTY_STRING; if(modes != null) { for (Iterator iter = modes.iterator(); iter.hasNext();) { str += iter.next(); if(iter.hasNext()) { str += ","; //$NON-NLS-1$ } } } return str; } /** * Schedules the given job after perspective switching is complete, or * immediately if a perspective switch is not in progress. * * @param job job to run after perspective switching */ public void schedulePostSwitch(Job job) { job.setRule(AsynchronousSchedulingRuleFactory.getDefault().newSerialPerObjectRule(this)); job.schedule(); } /* (non-Javadoc) * @see org.eclipse.debug.ui.contexts.ISuspendTriggerListener#suspended(org.eclipse.debug.core.ILaunch, java.lang.Object) */ @Override public void suspended(ILaunch launch, Object context) { handleBreakpointHit(launch); } /** * @see org.eclipse.core.resources.ISaveParticipant#doneSaving(org.eclipse.core.resources.ISaveContext) */ @Override public void doneSaving(ISaveContext context) {} /** * @see org.eclipse.core.resources.ISaveParticipant#prepareToSave(org.eclipse.core.resources.ISaveContext) */ @Override public void prepareToSave(ISaveContext context) throws CoreException {} /** * @see org.eclipse.core.resources.ISaveParticipant#rollback(org.eclipse.core.resources.ISaveContext) */ @Override public void rollback(ISaveContext context) {} /** * @see org.eclipse.core.resources.ISaveParticipant#saving(org.eclipse.core.resources.ISaveContext) */ @Override public void saving(ISaveContext context) throws CoreException { try { DebugUIPlugin.getDefault().getPreferenceStore().putValue(IInternalDebugUIConstants.PREF_LAUNCH_PERSPECTIVES, generatePerspectiveXML()); } catch (ParserConfigurationException e) { throw new CoreException(DebugUIPlugin.newErrorStatus("Exception occurred while generating launch perspectives preference XML", e)); //$NON-NLS-1$ } } }