blob: ae92c728fd849998b6a1b7c95231f5bd6a36a325 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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
* Pawel Piech - Bug 82003: The IDisconnect implementation by Launch module is too restrictive.
*******************************************************************************/
package org.eclipse.debug.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IDisconnect;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.internal.core.DebugCoreMessages;
import org.eclipse.debug.internal.core.LaunchManager;
/**
* A launch is the result of launching a debug session
* and/or one or more system processes. This class provides
* a public implementation of <code>ILaunch</code> for client
* use.
* <p>
* Clients may instantiate this class. Clients may subclass this class.
* </p>
* @see ILaunch
* @see ILaunchManager
*/
public class Launch extends PlatformObject implements ILaunch, IDisconnect, ILaunchListener, ILaunchConfigurationListener, IDebugEventSetListener {
/**
* The debug targets associated with this
* launch (the primary target is the first one
* in this collection), or empty if
* there are no debug targets.
*/
private List fTargets= new ArrayList();
/**
* The configuration that was launched, or null.
*/
private ILaunchConfiguration fConfiguration= null;
/**
* The system processes associated with
* this launch, or empty if none.
*/
private List fProcesses= new ArrayList();
/**
* The source locator to use in the debug session
* or <code>null</code> if not supported.
*/
private ISourceLocator fLocator= null;
/**
* The mode this launch was launched in.
*/
private String fMode;
/**
* Table of client defined attributes
*/
private HashMap fAttributes;
/**
* Flag indiating that change notification should
* be suppressed. <code>true</code> until this
* launch has been initialzied.
*/
private boolean fSuppressChange = true;
/**
* Constructs a launch with the specified attributes.
*
* @param launchConfiguration the configuration that was launched
* @param mode the mode of this launch - run or debug (constants
* defined by <code>ILaunchManager</code>)
* @param locator the source locator to use for this debug session, or
* <code>null</code> if not supported
*/
public Launch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) {
setLaunchConfiguration(launchConfiguration);
setSourceLocator(locator);
setLaunchMode(mode);
fSuppressChange = false;
}
/**
* Registers listeners for launches, launch configurations
* and debug events.
*/
private void addListeners() {
getLaunchManager().addLaunchListener(this);
getLaunchManager().addLaunchConfigurationListener(this);
DebugPlugin.getDefault().addDebugEventListener(this);
}
/**
* Remvoes the registered listeners.
*/
private void removeListeners() {
getLaunchManager().removeLaunchListener(this);
getLaunchManager().removeLaunchConfigurationListener(this);
DebugPlugin.getDefault().removeDebugEventListener(this);
}
/**
* @see org.eclipse.debug.core.model.ITerminate#canTerminate()
*/
public boolean canTerminate() {
List processes = getProcesses0();
for (int i = 0; i < processes.size(); i++) {
IProcess process = (IProcess)processes.get(i);
if (process.canTerminate()) {
return true;
}
}
List targets = getDebugTargets0();
for (int i = 0; i < targets.size(); i++) {
IDebugTarget target = (IDebugTarget)targets.get(i);
if (target.canTerminate() || target.canDisconnect()) {
return true;
}
}
return false;
}
/**
* @see ILaunch#getChildren()
*/
public Object[] getChildren() {
ArrayList children = new ArrayList(getDebugTargets0());
children.addAll(getProcesses0());
return children.toArray();
}
/**
* @see ILaunch#getDebugTarget()
*/
public IDebugTarget getDebugTarget() {
if (!getDebugTargets0().isEmpty()) {
return (IDebugTarget)getDebugTargets0().get(0);
}
return null;
}
/**
* Sets the configuration that was launched
*
* @param configuration the configuration that was launched
*/
private void setLaunchConfiguration(ILaunchConfiguration configuration) {
fConfiguration = configuration;
}
/**
* @see ILaunch#getProcesses()
*/
public IProcess[] getProcesses() {
return (IProcess[])getProcesses0().toArray(new IProcess[getProcesses0().size()]);
}
/**
* Returns the processes associated with this
* launch, in its internal form - a list.
*
* @return list of processes
*/
protected List getProcesses0() {
return fProcesses;
}
/**
* @see ILaunch#getSourceLocator()
*/
public ISourceLocator getSourceLocator() {
return fLocator;
}
/**
* @see ILaunch#setSourceLocator(ISourceLocator)
*/
public void setSourceLocator(ISourceLocator sourceLocator) {
fLocator = sourceLocator;
}
/**
* @see org.eclipse.debug.core.model.ITerminate#isTerminated()
*/
public boolean isTerminated() {
if (getProcesses0().isEmpty() && getDebugTargets0().isEmpty()) {
return false;
}
Iterator processes = getProcesses0().iterator();
while (processes.hasNext()) {
IProcess process = (IProcess)processes.next();
if (!process.isTerminated()) {
return false;
}
}
Iterator targets = getDebugTargets0().iterator();
while (targets.hasNext()) {
IDebugTarget target = (IDebugTarget)targets.next();
if (!(target.isTerminated() || target.isDisconnected())) {
return false;
}
}
return true;
}
/**
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
public void terminate() throws DebugException {
MultiStatus status=
new MultiStatus(DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, DebugCoreMessages.Launch_terminate_failed, null); //$NON-NLS-1$
// terminate the system processes
IProcess[] processes = getProcesses();
for (int i = 0; i < processes.length; i++) {
IProcess process = processes[i];
if (process.canTerminate()) {
try {
process.terminate();
} catch (DebugException e) {
status.merge(e.getStatus());
}
}
}
// terminate or disconnect debug target if it is still alive
IDebugTarget[] targets = getDebugTargets();
for (int i = 0; i < targets.length; i++) {
IDebugTarget target= targets[i];
if (target != null) {
if (target.canTerminate()) {
try {
target.terminate();
} catch (DebugException e) {
status.merge(e.getStatus());
}
} else {
if (target.canDisconnect()) {
try {
target.disconnect();
} catch (DebugException de) {
status.merge(de.getStatus());
}
}
}
}
}
if (status.isOK()) {
return;
}
IStatus[] children= status.getChildren();
if (children.length == 1) {
throw new DebugException(children[0]);
}
throw new DebugException(status);
}
/**
* @see ILaunch#getLaunchMode()
*/
public String getLaunchMode() {
return fMode;
}
/**
* Sets the mode in which this launch was
* launched.
*
* @param mode the mode in which this launch
* was launched - one of the constants defined
* by <code>ILaunchManager</code>.
*/
private void setLaunchMode(String mode) {
fMode = mode;
}
/**
* @see ILaunch#getLaunchConfiguration()
*/
public ILaunchConfiguration getLaunchConfiguration() {
return fConfiguration;
}
/**
* @see ILaunch#setAttribute(String, String)
*/
public void setAttribute(String key, String value) {
if (fAttributes == null) {
fAttributes = new HashMap(5);
}
fAttributes.put(key, value);
}
/**
* @see ILaunch#getAttribute(String)
*/
public String getAttribute(String key) {
if (fAttributes == null) {
return null;
}
return (String)fAttributes.get(key);
}
/**
* @see ILaunch#getDebugTargets()
*/
public IDebugTarget[] getDebugTargets() {
return (IDebugTarget[])fTargets.toArray(new IDebugTarget[fTargets.size()]);
}
/**
* Returns the debug targets associated with this
* launch, in its internal form - a list
*
* @return list of debug targets
*/
protected List getDebugTargets0() {
return fTargets;
}
/**
* @see ILaunch#addDebugTarget(IDebugTarget)
*/
public void addDebugTarget(IDebugTarget target) {
if (target != null) {
if (!getDebugTargets0().contains(target)) {
addListeners();
getDebugTargets0().add(target);
fireChanged();
}
}
}
/**
* @see ILaunch#removeDebugTarget(IDebugTarget)
*/
public void removeDebugTarget(IDebugTarget target) {
if (target != null) {
if (getDebugTargets0().remove(target)) {
fireChanged();
}
}
}
/**
* @see ILaunch#addProcess(IProcess)
*/
public void addProcess(IProcess process) {
if (process != null) {
if (!getProcesses0().contains(process)) {
addListeners();
getProcesses0().add(process);
fireChanged();
}
}
}
/**
* @see ILaunch#removeProcess(IProcess)
*/
public void removeProcess(IProcess process) {
if (process != null) {
if (getProcesses0().remove(process)) {
fireChanged();
}
}
}
/**
* Adds the given processes to this launch.
*
* @param processes processes to add
*/
protected void addProcesses(IProcess[] processes) {
if (processes != null) {
for (int i = 0; i < processes.length; i++) {
addProcess(processes[i]);
fireChanged();
}
}
}
/**
* Notifies listeners that this launch has changed.
* Has no effect of this launch has not yet been
* properly created/initialized.
*/
protected void fireChanged() {
if (!fSuppressChange) {
((LaunchManager)getLaunchManager()).fireUpdate(this, LaunchManager.CHANGED);
((LaunchManager)getLaunchManager()).fireUpdate(new ILaunch[] {this}, LaunchManager.CHANGED);
}
}
/**
* Notifies listeners that this launch has terminated.
* Has no effect of this launch has not yet been
* properly created/initialized.
*/
protected void fireTerminate() {
if (!fSuppressChange) {
((LaunchManager)getLaunchManager()).fireUpdate(this, LaunchManager.TERMINATE);
((LaunchManager)getLaunchManager()).fireUpdate(new ILaunch[] {this}, LaunchManager.TERMINATE);
}
removeListeners();
}
/**
* @see ILaunch#hasChildren()
*/
public boolean hasChildren() {
return getProcesses0().size() > 0 || (getDebugTargets0().size() > 0);
}
/**
* Returns whether any processes or targets can be disconnected.
* Ones that are already terminated or disconnected are ignored.
*
* @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
*/
public boolean canDisconnect() {
List processes = getProcesses0();
for (int i = 0; i < processes.size(); i++) {
if (processes.get(i) instanceof IDisconnect) {
IDisconnect process = (IDisconnect)processes.get(i);
if (process.canDisconnect()) {
return true;
}
}
}
List targets = getDebugTargets0();
for (int i = 0; i < targets.size(); i++) {
if ( ((IDebugTarget)targets.get(i)).canDisconnect() ) {
return true;
}
}
return false;
}
/**
* @see org.eclipse.debug.core.model.IDisconnect#disconnect()
*/
public void disconnect() throws DebugException {
List processes = getProcesses0();
for (int i = 0; i < processes.size(); i++) {
if (processes.get(i) instanceof IDisconnect) {
IDisconnect disconnect = (IDisconnect)processes.get(i);
if (disconnect.canDisconnect()) {
disconnect.disconnect();
}
}
}
List targets = getDebugTargets0();
for (int i = 0; i < targets.size(); i++) {
IDebugTarget debugTarget = (IDebugTarget)targets.get(i);
if (debugTarget.canDisconnect()) {
debugTarget.disconnect();
}
}
}
/**
* Returns whether all of the contained targets and processes are
* disconnected. Processes that don't support disconnecting are not
* counted.
*
* @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
*/
public boolean isDisconnected() {
List processes = getProcesses0();
for (int i = 0; i < processes.size(); i++) {
if (processes.get(i) instanceof IDisconnect) {
IDisconnect process = (IDisconnect)processes.get(i);
if (!process.isDisconnected()) {
return false;
}
}
}
List targets = getDebugTargets0();
for (int i = 0; i < targets.size(); i++) {
if ( !((IDebugTarget)targets.get(i)).isDisconnected() ) {
return false;
}
}
// only return true if there are processes or targets that are disconnected
return hasChildren();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.ILaunchListener#launchRemoved(org.eclipse.debug.core.ILaunch)
*/
public void launchRemoved(ILaunch launch) {
if (this.equals(launch)) {
removeListeners();
}
}
/**
* Returns the launch manager.
*
* @return the launch manager.
*/
protected ILaunchManager getLaunchManager() {
return DebugPlugin.getDefault().getLaunchManager();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.ILaunchListener#launchAdded(org.eclipse.debug.core.ILaunch)
*/
public void launchAdded(ILaunch launch) {
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.ILaunchListener#launchChanged(org.eclipse.debug.core.ILaunch)
*/
public void launchChanged(ILaunch launch) {
}
/* (non-Javadoc)
*
* If the launch configuration this launch is associated with is
* moved, update the underlying handle to the new location.
*
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationAdded(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void launchConfigurationAdded(ILaunchConfiguration configuration) {
ILaunchConfiguration from = getLaunchManager().getMovedFrom(configuration);
if (from != null && from.equals(getLaunchConfiguration())) {
setLaunchConfiguration(configuration);
fireChanged();
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationChanged(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
}
/* (non-Javadoc)
*
* Update the launch configuration associated with this launch if the
* underlying configuration is deleted.
*
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationRemoved(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void launchConfigurationRemoved(ILaunchConfiguration configuration) {
if (configuration.equals(getLaunchConfiguration())) {
if (getLaunchManager().getMovedTo(configuration) == null) {
setLaunchConfiguration(null);
fireChanged();
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
*/
public void handleDebugEvents(DebugEvent[] events) {
for (int i = 0; i < events.length; i++) {
DebugEvent event = events[i];
if (event.getKind() == DebugEvent.TERMINATE) {
Object object = event.getSource();
ILaunch launch = null;
if (object instanceof IProcess) {
launch = ((IProcess)object).getLaunch();
} else if (object instanceof IDebugTarget) {
launch = ((IDebugTarget)object).getLaunch();
}
if (this.equals(launch)) {
if (isTerminated()) {
fireTerminate();
}
}
}
}
}
}