| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.debug.internal.ui.launchConfigurations; |
| |
| |
| import java.io.IOException; |
| import java.io.StringReader; |
| import java.text.MessageFormat; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.apache.xerces.dom.DocumentImpl; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IDebugEventSetListener; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchConfigurationType; |
| import org.eclipse.debug.core.ILaunchListener; |
| import org.eclipse.debug.core.ILaunchManager; |
| import org.eclipse.debug.core.model.IDebugElement; |
| import org.eclipse.debug.core.model.IProcess; |
| import org.eclipse.debug.internal.ui.DebugUIPlugin; |
| import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; |
| import org.eclipse.debug.ui.DebugUITools; |
| import org.eclipse.debug.ui.IDebugUIConstants; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IViewPart; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.WorkbenchException; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * The perspective manager manages the 'perspective' settings |
| * defined by launch configurations. Specifically it: <ul> |
| * <li>changes perspectives as launches are registered</li> |
| * <li>change perspective when a thread suspends</li> |
| * </ul> |
| * |
| * @see IDebugUIContants.ATTR_RUN_PERSPECTIVE |
| * @see IDebugUIContants.ATTR_DEBUG_PERSPECTIVE |
| */ |
| public class PerspectiveManager implements ILaunchListener, IDebugEventSetListener { |
| |
| /** |
| * Table of config types to tables of user specified perspective settings (mode ids |
| * to perspective ids). |
| */ |
| private Map fPreferenceMap; |
| |
| // XML tags |
| private static final String ELEMENT_PERSPECTIVES = "launchPerspectives"; //$NON-NLS-1$ |
| private static final String ELEMENT_PERSPECTIVE = "launchPerspective"; //$NON-NLS-1$ |
| private static final String ATTR_TYPE_ID = "configurationType"; //$NON-NLS-1$ |
| private static final String ATTR_MODE_ID = "mode"; //$NON-NLS-1$ |
| private static final String ATTR_PERSPECTIVE_ID = "perspective"; //$NON-NLS-1$ |
| |
| /** |
| * Called by the debug ui plug-in on startup. |
| * The perspective manager starts listening for |
| * launches to be registered. |
| */ |
| public void startup() { |
| DebugPlugin plugin = DebugPlugin.getDefault(); |
| plugin.getLaunchManager().addLaunchListener(this); |
| plugin.addDebugEventListener(this); |
| } |
| |
| /** |
| * Called by the debug ui plug-in on shutdown. |
| * The perspective manager de-registers as a |
| * launch listener. |
| */ |
| public void shutdown() { |
| DebugPlugin plugin = DebugPlugin.getDefault(); |
| plugin.getLaunchManager().removeLaunchListener(this); |
| plugin.removeDebugEventListener(this); |
| } |
| |
| /** |
| * Do nothing. |
| * |
| * @see ILaunchListener#launchRemoved(ILaunch) |
| */ |
| public void launchRemoved(ILaunch launch) { |
| } |
| |
| /** |
| * Do nothing. |
| * |
| * @see ILaunchListener#launchChanged(ILaunch) |
| */ |
| public void launchChanged(ILaunch launch) { |
| } |
| |
| /** |
| * Switch to the perspective specified by the |
| * launch configuration. |
| * |
| * @see ILaunchListener#launchAdded(ILaunch) |
| */ |
| public void launchAdded(ILaunch launch) { |
| 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; |
| } |
| } |
| |
| // switch |
| if (perspectiveId != null) { |
| switchToPerspective(perspectiveId); |
| } |
| } |
| |
| |
| /** |
| * Switches to the specified perspective |
| * |
| * @param id perspective identifier |
| */ |
| protected void switchToPerspective(final String id) { |
| async(new Runnable() { |
| public void run() { |
| IWorkbenchWindow window = DebugUIPlugin.getActiveWorkbenchWindow(); |
| if (window != null) { |
| try { |
| window.getWorkbench().showPerspective(id, window); |
| } catch (WorkbenchException e) { |
| DebugUIPlugin.errorDialog(DebugUIPlugin.getShell(), |
| LaunchConfigurationsMessages.getString("PerspectiveManager.Error_1"), //$NON-NLS-1$ |
| MessageFormat.format(LaunchConfigurationsMessages.getString("PerspectiveManager.Unable_to_switch_to_perspective__{0}_2"), new String[]{id}), //$NON-NLS-1$ |
| e); |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Utility method to submit an asnychronous 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() { |
| public void run() { |
| DebugUIPlugin.errorDialog(DebugUIPlugin.getShell(), LaunchConfigurationsMessages.getString("PerspectiveManager.Error_1"), //$NON-NLS-1$ |
| MessageFormat.format(LaunchConfigurationsMessages.getString("PerspectiveManager.Unable_to_switch_perpsectives_as_specified_by_launch__{0}_4"), new String[] {launchName}), //$NON-NLS-1$ |
| t); |
| }}); |
| } |
| |
| /** |
| * On a SUSPEND event, show the debug view. If no debug view is open, |
| * switch to the perspective specified by the launcher. |
| * |
| * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[]) |
| */ |
| public void handleDebugEvents(DebugEvent[] events) { |
| // open the debugger if this is a suspend event and the debug view is not yet open |
| // and the preferences are set to switch |
| for (int i = 0; i < events.length; i++) { |
| DebugEvent event = events[i]; |
| if (event.getKind() == DebugEvent.SUSPEND && (event.getDetail() == DebugEvent.BREAKPOINT || event.getDetail() == DebugEvent.STEP_END)) { |
| // apply event filters |
| ILaunch launch = null; |
| Object source = event.getSource(); |
| if (source instanceof IDebugElement) { |
| launch = ((IDebugElement)source).getLaunch(); |
| } else if (source instanceof IProcess) { |
| launch = ((IProcess)source).getLaunch(); |
| } |
| 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 asynch, such that the workbench |
| // window can be accessed |
| final String id = perspectiveId; |
| Runnable r = new Runnable() { |
| public void run() { |
| String targetId = id; |
| IWorkbenchWindow window = DebugUIPlugin.getActiveWorkbenchWindow(); |
| if (window == null) { |
| return; |
| } |
| if (targetId == null) { |
| IWorkbenchPage page = window.getActivePage(); |
| if (page != null) { |
| IViewPart part = page.findView(IDebugUIConstants.ID_DEBUG_VIEW); |
| if (part == null) { |
| targetId = IDebugUIConstants.ID_DEBUG_PERSPECTIVE; |
| } |
| } |
| } |
| if (targetId != null) { |
| // re-open the window if minimized |
| Shell shell= window.getShell(); |
| if (shell != null) { |
| if (shell.getMinimized()) { |
| shell.setMinimized(false); |
| } |
| if (DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugUIConstants.PREF_ACTIVATE_WORKBENCH)) { |
| shell.forceActive(); |
| } |
| } |
| switchToPerspective(targetId); |
| } |
| } |
| }; |
| async(r); |
| } |
| } |
| } |
| |
| /** |
| * Returns the perspective associated with the |
| * given launch, or <code>null</code> if none. |
| * |
| * @param launch a launch |
| * @return the perspective associated with the launch, |
| * or <code>null</code> |
| * @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; |
| } |
| String perspectiveId = null; |
| perspectiveId = DebugUITools.getLaunchPerspective(config.getType(), launch.getLaunchMode()); |
| if (perspectiveId != null && perspectiveId.equals(IDebugUIConstants.PERSPECTIVE_NONE)) { |
| perspectiveId = null; |
| } |
| return perspectiveId; |
| } |
| |
| /** |
| * Returns the perspective to switch to when a configuration of the given type |
| * is launched in the given mode, or <code>null</code> if no switch should take |
| * place. |
| * |
| * @param type launch configuration type |
| * @param mode launch mode identifier |
| * @return perspective identifier or <code>null</code> |
| * @since 3.0 |
| */ |
| public String getLaunchPerspective(ILaunchConfigurationType type, String mode) { |
| String id = getUserSpecifiedLaunchPerspective(type, mode); |
| if (id == null) { |
| // get the default |
| id = getDefaultLaunchPerspective(type, mode); |
| } else if (id.equals(IDebugUIConstants.PERSPECTIVE_NONE)) { |
| // translate NONE to null |
| id = null; |
| } |
| return id; |
| } |
| |
| /** |
| * Sets the perspective to switch to when a configuration of the given type |
| * is launched in the given mode. <code>PERSPECTIVE_NONE</code> indicates no |
| * perspective switch should take place. <code>PERSPECTIVE_DEFAULT</code> indicates |
| * a default perspective switch should take place, as defined by the associated |
| * launch tab group extension. Saves plug-in preferences. |
| * |
| * @param type launch configuration type |
| * @param mode launch mode identifier |
| * @param perspective identifier, <code>PERSPECTIVE_NONE</code>, or |
| * <code>PERSPECTIVE_DEFAULT</code> |
| * @since 3.0 |
| */ |
| public void setLaunchPerspective(ILaunchConfigurationType type, String mode, String perspective) { |
| internalSetLaunchPerspective(type.getIdentifier(), mode, perspective); |
| // update preference |
| String xml; |
| try { |
| xml = generatePerspectiveXML(); |
| DebugUIPlugin.getDefault().getPreferenceStore().putValue(IInternalDebugUIConstants.PREF_LAUNCH_PERSPECTIVES, xml); |
| DebugUIPlugin.getDefault().savePluginPreferences(); |
| } catch (IOException e) { |
| DebugUIPlugin.log(DebugUIPlugin.newErrorStatus(LaunchConfigurationsMessages.getString("PerspectiveManager.9"), e)); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Sets the perspective to switch to when a configuration of the given type |
| * is launched in the given mode. <code>PERSPECTIVE_NONE</code> indicates no |
| * perspective switch should take place. <code>PERSPECTIVE_DEFAULT</code> indicates |
| * a default perspective switch should take place, as defined by the associated |
| * launch tab group extension. |
| * |
| * @param type launch configuration type identifier |
| * @param mode launch mode identifier |
| * @param perspective identifier, <code>PERSPECTIVE_NONE</code>, or |
| * <code>PERSPECTIVE_DEFAULT</code> |
| * @since 3.0 |
| */ |
| private void internalSetLaunchPerspective(String type, String mode, String perspective) { |
| Map modeMap = (Map)fPreferenceMap.get(type); |
| if (modeMap == null) { |
| modeMap = new HashMap(); |
| fPreferenceMap.put(type, modeMap); |
| } |
| if (perspective.equals(IDebugUIConstants.PERSPECTIVE_DEFAULT)) { |
| // remove user preference setting |
| modeMap.remove(mode); |
| } else { |
| // override default setting |
| modeMap.put(mode, perspective); |
| } |
| } |
| |
| /** |
| * Generates XML for the user specified perspective settings. |
| * |
| * @return XML |
| * @exception CoreException if unable to generate the XML |
| */ |
| private String generatePerspectiveXML() throws IOException { |
| |
| Document doc = new DocumentImpl(); |
| Element configRootElement = doc.createElement(ELEMENT_PERSPECTIVES); |
| doc.appendChild(configRootElement); |
| |
| Iterator configTypes = fPreferenceMap.keySet().iterator(); |
| while (configTypes.hasNext()) { |
| String type = (String)configTypes.next(); |
| Map modeMap = (Map)fPreferenceMap.get(type); |
| if (modeMap != null && !modeMap.isEmpty()) { |
| Iterator modes = modeMap.keySet().iterator(); |
| while (modes.hasNext()) { |
| String mode = (String)modes.next(); |
| String perspective = (String)modeMap.get(mode); |
| Element element = doc.createElement(ELEMENT_PERSPECTIVE); |
| element.setAttribute(ATTR_TYPE_ID, type); |
| element.setAttribute(ATTR_MODE_ID, mode); |
| element.setAttribute(ATTR_PERSPECTIVE_ID, perspective); |
| configRootElement.appendChild(element); |
| } |
| } |
| } |
| |
| return DebugUIPlugin.serializeDocument(doc); |
| } |
| |
| /** |
| * Returns the default perspective to switch to when a configuration of the given |
| * type is lanuched in the given mode, or <code>null</code> if none. |
| * |
| * @param type launch configuration type |
| * @param mode launch mode |
| * @return perspective identifier, or <code>null</code> |
| */ |
| protected String getDefaultLaunchPerspective(ILaunchConfigurationType type, String mode) { |
| LaunchConfigurationTabGroupExtension extension = LaunchConfigurationPresentationManager.getDefault().getExtension(type.getIdentifier(), mode); |
| if (extension != null) { |
| String id = extension.getPerspective(mode); |
| if (id == null) { |
| // revert to hard coded default (for backwards compatibility) |
| // since nothing is specified in XML |
| if (mode.equals(ILaunchManager.DEBUG_MODE)) { |
| return IDebugUIConstants.ID_DEBUG_PERSPECTIVE; |
| } |
| } else { |
| return id; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the user specified perspective to switch to when a configuration of the |
| * given type is lanuched in the given mode, or <code>null</code> if unspecified. |
| * Returns <code>PERSPECTIVE_NONE</code> to indicate no switch |
| * |
| * @param type launch configuration type |
| * @param mode launch mode |
| * @return perspective identifier, <code>PERSPECTIVE_NONE</code>, or <code>null</code> |
| */ |
| protected String getUserSpecifiedLaunchPerspective(ILaunchConfigurationType type, String mode) { |
| String id = null; |
| if (fPreferenceMap == null) { |
| initPerspectives(); |
| } |
| Map modeMap = (Map)fPreferenceMap.get(type.getIdentifier()); |
| if (modeMap != null) { |
| id = (String)modeMap.get(mode); |
| } |
| return id; |
| } |
| |
| /** |
| * Initialize the preference map with settings from user preference |
| */ |
| private void initPerspectives() { |
| fPreferenceMap = new HashMap(); |
| String xml = DebugUIPlugin.getDefault().getPreferenceStore().getString(IInternalDebugUIConstants.PREF_LAUNCH_PERSPECTIVES); |
| if (xml != null && xml.length() > 0) { |
| try { |
| Element root = null; |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| StringReader reader = new StringReader(xml); |
| InputSource source = new InputSource(reader); |
| root = parser.parse(source).getDocumentElement(); |
| |
| NodeList list = root.getChildNodes(); |
| int length = list.getLength(); |
| for (int i = 0; i < length; ++i) { |
| Node node = list.item(i); |
| short nt = node.getNodeType(); |
| if (nt == Node.ELEMENT_NODE) { |
| Element element = (Element) node; |
| String nodeName = element.getNodeName(); |
| if (nodeName.equalsIgnoreCase(ELEMENT_PERSPECTIVE)) { |
| String type = element.getAttribute(ATTR_TYPE_ID); |
| String mode = element.getAttribute(ATTR_MODE_ID); |
| String perpsective = element.getAttribute(ATTR_PERSPECTIVE_ID); |
| internalSetLaunchPerspective(type, mode, perpsective); |
| } |
| } |
| } |
| } catch (ParserConfigurationException e) { |
| DebugUIPlugin.log(e); |
| } catch (SAXException e) { |
| DebugUIPlugin.log(e); |
| } catch (IOException e) { |
| DebugUIPlugin.log(e); |
| } |
| } |
| |
| } |
| |
| } |