blob: 0e9aefdb462e0ad0be45f2a6140676f1d89d3dae [file] [log] [blame]
package org.eclipse.jdt.internal.debug.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.ExceptionRequest;
public class JavaExceptionBreakpoint extends JavaBreakpoint implements IJavaExceptionBreakpoint {
private static final String JAVA_EXCEPTION_BREAKPOINT= "org.eclipse.jdt.debug.javaExceptionBreakpointMarker"; //$NON-NLS-1$
/**
* Exception breakpoint attribute storing the suspend on caught value
* (value <code>"caught"</code>). This attribute is stored as a <code>boolean</code>.
* When this attribute is <code>true</code>, a caught exception of the associated
* type will cause excecution to suspend .
*/
private static final String CAUGHT = "caught"; //$NON-NLS-1$
/**
* Exception breakpoint attribute storing the suspend on uncaught value
* (value <code>"uncaught"</code>). This attribute is stored as a
* <code>boolean</code>. When this attribute is <code>true</code>, an uncaught
* exception of the associated type will cause excecution to suspend. .
*/
public static final String UNCAUGHT = "uncaught"; //$NON-NLS-1$
/**
* Exception breakpoint attribute storing the checked value (value <code>"checked"</code>).
* This attribute is stored as a <code>boolean</code>, indicating whether an
* exception is a checked exception.
*/
private static final String CHECKED = "checked"; //$NON-NLS-1$
// Attribute strings
protected static final String[] fgExceptionBreakpointAttributes= new String[]{CHECKED, TYPE_HANDLE};
public JavaExceptionBreakpoint() {
}
/**
* Creates and returns an exception breakpoint for the
* given (throwable) type. Caught and uncaught specify where the exception
* should cause thread suspensions - that is, in caught and/or uncaught locations.
* Checked indicates if the given exception is a checked exception.
* Note: the breakpoint is not added to the breakpoint manager
* - it is merely created.
*
* @param type the exception for which to create the breakpoint
* @param caught whether to suspend in caught locations
* @param uncaught whether to suspend in uncaught locations
* @param checked whether the exception is a checked exception
* @return a java exception breakpoint
* @exception DebugException if unable to create the associated marker due
* to a lower level exception.
*/
public JavaExceptionBreakpoint(final IType exception, final boolean caught, final boolean uncaught, final boolean checked) throws DebugException {
IWorkspaceRunnable wr= new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
IResource resource= null;
resource= exception.getUnderlyingResource();
if (resource == null) {
resource= exception.getJavaProject().getProject();
}
// create the marker
fMarker= resource.createMarker(JAVA_EXCEPTION_BREAKPOINT);
// configure the standard attributes
setEnabled(true);
// configure caught, uncaught, checked, and the type attributes
setDefaultCaughtAndUncaught();
setTypeAndChecked(exception, checked);
// configure the marker as a Java marker
IMarker marker = ensureMarker();
Map attributes= marker.getAttributes();
JavaCore.addJavaElementMarkerAttributes(attributes, exception);
marker.setAttributes(attributes);
addToBreakpointManager();
}
};
run(wr);
}
/**
* Sets the exception type on which this breakpoint is installed and whether
* or not that exception is a checked exception.
*/
protected void setTypeAndChecked(IType exception, boolean checked) throws CoreException {
String handle = exception.getHandleIdentifier();
Object[] values= new Object[]{new Boolean(checked), handle};
ensureMarker().setAttributes(fgExceptionBreakpointAttributes, values);
}
/**
* Sets the default values for whether this breakpoint will
* suspend execution when the associated exception is thrown
* and caught or not caught..
*/
public void setDefaultCaughtAndUncaught() throws CoreException {
Object[] values= new Object[]{Boolean.TRUE, Boolean.TRUE};
String[] attributes= new String[]{CAUGHT, UNCAUGHT};
ensureMarker().setAttributes(attributes, values);
}
/**
* @see JavaBreakpoint#addToTarget(JDIDebugTarget)
*/
public void addToTarget(JDIDebugTarget target) throws CoreException {
IType exceptionType = getType();
if (exceptionType == null) {
return;
}
String referenceName = getReferenceTypeName();
if (referenceName.equals("")) {
return;
}
// listen to class loads
registerRequest(target.createClassPrepareRequest(referenceName), target);
List classes= target.jdiClassesByName(referenceName);
if (!classes.isEmpty()) {
Iterator iter = classes.iterator();
while (iter.hasNext()) {
ReferenceType exClass = (ReferenceType)iter.next();
createRequest(target, exClass);
}
}
}
/**
* Creates a request in the given target to suspend when the given exception
* type is thrown. The request is returned installed, configured, and enabled
* as appropriate for this breakpoint.
*/
protected EventRequest newRequest(JDIDebugTarget target, ReferenceType type) throws CoreException {
if (!isCaught() && !isUncaught()) {
return null;
}
ExceptionRequest request= null;
try {
request= target.getEventRequestManager().createExceptionRequest(type, isCaught(), isUncaught());
configureRequest(request);
} catch (VMDisconnectedException e) {
if (target.isTerminated() || target.isDisconnected()) {
return null;
}
JDIDebugPlugin.logError(e);
return null;
} catch (RuntimeException e) {
JDIDebugPlugin.logError(e);
return null;
}
return request;
}
/**
* Enable this exception breakpoint.
*
* If the exception breakpoint is not catching caught or uncaught,
* set the default values. If this isn't done, the resulting
* state (enabled with caught and uncaught both disabled)
* is ambiguous.
*/
public void setEnabled(boolean enabled) throws CoreException {
super.setEnabled(enabled);
if (isEnabled()) {
if (!(isCaught() || isUncaught())) {
setDefaultCaughtAndUncaught();
}
}
}
/**
* @see IJavaExceptionBreakpoint#isCaught()
*/
public boolean isCaught() throws CoreException {
return ensureMarker().getAttribute(CAUGHT, false);
}
/**
* @see IJavaExceptionBreakpoint#setCaught(boolean)
*/
public void setCaught(boolean caught) throws CoreException {
if (caught == isCaught()) {
return;
}
ensureMarker().setAttribute(CAUGHT, caught);
if (caught && !isEnabled()) {
setEnabled(true);
} else if (!(caught || isUncaught())) {
setEnabled(false);
}
}
/**
* @see IJavaExceptionBreakpoint#isUncaught()
*/
public boolean isUncaught() throws CoreException {
return ensureMarker().getAttribute(UNCAUGHT, false);
}
/**
* @see IJavaExceptionBreakpoint#setUncaught(boolean)
*/
public void setUncaught(boolean uncaught) throws CoreException {
if (uncaught == isUncaught()) {
return;
}
ensureMarker().setAttribute(UNCAUGHT, uncaught);
if (uncaught && !isEnabled()) {
setEnabled(true);
} else if (!(uncaught || isCaught())) {
setEnabled(false);
}
}
/**
* @see IJavaExceptionBreakpoint#isChecked()
*/
public boolean isChecked() throws CoreException {
return ensureMarker().getAttribute(CHECKED, false);
}
/**
* Update the hit count of an <code>EventRequest</code>. Return a new request with
* the appropriate settings.
*/
protected EventRequest updateHitCount(EventRequest request, JDIDebugTarget target) throws CoreException {
// if the hit count has changed, or the request has expired and is being re-enabled,
// create a new request
if (hasHitCountChanged(request) || (isExpired(request) && isEnabled())) {
request= createUpdatedExceptionRequest(target, (ExceptionRequest)request);
}
return request;
}
/**
* @see JavaBreakpoint#updateRequest(EventRequest, JDIDebugTarget)
*/
protected void updateRequest(EventRequest request, JDIDebugTarget target) throws CoreException {
updateEnabledState(request);
EventRequest newRequest = updateHitCount(request, target);
newRequest= updateCaughtState(newRequest,target);
if (newRequest != null && newRequest != request) {
replaceRequest(target, request, newRequest);
request = newRequest;
}
}
/**
* Return a request that will suspend execution when a caught and/or uncaught
* exception is thrown as is appropriate for the current state of this breakpoint.
*/
protected EventRequest updateCaughtState(EventRequest req, JDIDebugTarget target) throws CoreException {
if(!(req instanceof ExceptionRequest)) {
return req;
}
ExceptionRequest request= (ExceptionRequest) req;
if (request.notifyCaught() != isCaught() || request.notifyUncaught() != isUncaught()) {
request= createUpdatedExceptionRequest(target, (ExceptionRequest)request);
}
return request;
}
/**
* Create a request that reflects the current state of this breakpoint.
* The new request will be installed in the same type as the given
* request.
*/
protected ExceptionRequest createUpdatedExceptionRequest(JDIDebugTarget target, ExceptionRequest request) throws CoreException{
try {
ReferenceType exClass = ((ExceptionRequest)request).exception();
request = (ExceptionRequest) newRequest(target, exClass);
} catch (VMDisconnectedException e) {
if (target.isTerminated() || target.isDisconnected()) {
return request;
}
JDIDebugPlugin.logError(e);
} catch (RuntimeException e) {
JDIDebugPlugin.logError(e);
}
return request;
}
}