blob: 0d55fc116ecb1aae5d150ae8245a1a31a6e6cc8f [file] [log] [blame]
/*******************************************************************************
* 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.jdt.internal.debug.core.breakpoints;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
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.debug.core.model.IBreakpoint;
import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.core.StringMatcher;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.model.JDIThread;
import com.sun.jdi.Location;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.ExceptionEvent;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.EventRequestManager;
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>"org.eclipse.jdt.debug.core.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 .
*/
protected static final String CAUGHT = "org.eclipse.jdt.debug.core.caught"; //$NON-NLS-1$
/**
* Exception breakpoint attribute storing the suspend on uncaught value
* (value <code>"org.eclipse.jdt.debug.core.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.
*/
protected static final String UNCAUGHT = "org.eclipse.jdt.debug.core.uncaught"; //$NON-NLS-1$
/**
* Exception breakpoint attribute storing the checked value (value <code>"org.eclipse.jdt.debug.core.checked"</code>).
* This attribute is stored as a <code>boolean</code>, indicating whether an
* exception is a checked exception.
*/
protected static final String CHECKED = "org.eclipse.jdt.debug.core.checked"; //$NON-NLS-1$
/**
* Exception breakpoint attribute storing the String value (value <code>"org.eclipse.jdt.debug.core.filters"</code>).
* This attribute is stored as a <code>String</code>, a comma delimited list
* of class filters. The filters are applied as inclusion or exclusion depending on
* INCLUSIVE_FILTERS.
*/
protected static final String INCLUSION_FILTERS = "org.eclipse.jdt.debug.core.inclusion_filters"; //$NON-NLS-1$
/**
* Exception breakpoint attribute storing the String value (value <code>"org.eclipse.jdt.debug.core.filters"</code>).
* This attribute is stored as a <code>String</code>, a comma delimited list
* of class filters. The filters are applied as inclusion or exclusion depending on
* INCLUSIVE_FILTERS.
*/
protected static final String EXCLUSION_FILTERS = "org.eclipse.jdt.debug.core.exclusion_filters"; //$NON-NLS-1$
/**
* Name of the exception that was actually hit (could be a
* subtype of the type that is being caught).
*/
protected String fExceptionName = null;
/**
* The current set of inclusion class filters.
*/
protected String[] fInclusionClassFilters= null;
/**
* The current set of inclusion class filters.
*/
protected String[] fExclusionClassFilters= null;
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.
* @param resource the resource on which to create the associated
* breakpoint marker
* @param exceptionName the fully qualified name of 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
* @param add whether to add this breakpoint to the breakpoint manager
* @return a Java exception breakpoint
* @exception DebugException if unable to create the associated marker due
* to a lower level exception.
*/
public JavaExceptionBreakpoint(final IResource resource, final String exceptionName, final boolean caught, final boolean uncaught, final boolean checked, final boolean add, final Map attributes) throws DebugException {
IWorkspaceRunnable wr= new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
// create the marker
setMarker(resource.createMarker(JAVA_EXCEPTION_BREAKPOINT));
// add attributes
attributes.put(IBreakpoint.ID, getModelIdentifier());
attributes.put(TYPE_NAME, exceptionName);
attributes.put(ENABLED, Boolean.TRUE);
attributes.put(CAUGHT, new Boolean(caught));
attributes.put(UNCAUGHT, new Boolean(uncaught));
attributes.put(CHECKED, new Boolean(checked));
ensureMarker().setAttributes(attributes);
register(add);
}
};
run(null, wr);
}
/**
* 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;
EventRequestManager manager = target.getEventRequestManager();
if (manager == null) {
target.requestFailed(JDIDebugBreakpointMessages.getString("JavaExceptionBreakpoint.Unable_to_create_breakpoint_request_-_VM_disconnected._1"), null); //$NON-NLS-1$
}
try {
request= manager.createExceptionRequest(type, isCaught(), isUncaught());
configureRequest(request, target);
} catch (VMDisconnectedException e) {
if (target.isAvailable()) {
JDIDebugPlugin.log(e);
}
return null;
} catch (RuntimeException e) {
JDIDebugPlugin.log(e);
return null;
}
return request;
}
/**
* Enable this exception breakpoint.
*
* If the exception breakpoint is not catching caught or uncaught,
* turn both modes on. If this isn't done, the resulting
* state (enabled with caught and uncaught both disabled)
* is ambiguous.
*/
public void setEnabled(boolean enabled) throws CoreException {
if (enabled) {
if (!(isCaught() || isUncaught())) {
setAttributes(new String[] {CAUGHT, UNCAUGHT}, new Object[] {Boolean.TRUE, Boolean.TRUE});
}
}
super.setEnabled(enabled);
}
/**
* Sets the values for whether this breakpoint will
* suspend execution when the associated exception is thrown
* and caught or not caught.
*/
protected void setCaughtAndUncaught(boolean caught, boolean uncaught) throws CoreException {
Object[] values= new Object[]{new Boolean(caught), new Boolean(uncaught)};
String[] attributes= new String[]{CAUGHT, UNCAUGHT};
setAttributes(attributes, values);
}
/**
* @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;
}
setAttribute(CAUGHT, caught);
if (caught && !isEnabled()) {
setEnabled(true);
} else if (!(caught || isUncaught())) {
setEnabled(false);
}
recreate();
}
/**
* @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;
}
setAttribute(UNCAUGHT, uncaught);
if (uncaught && !isEnabled()) {
setEnabled(true);
} else if (!(uncaught || isCaught())) {
setEnabled(false);
}
recreate();
}
/**
* @see IJavaExceptionBreakpoint#isChecked()
*/
public boolean isChecked() throws CoreException {
return ensureMarker().getAttribute(CHECKED, false);
}
/**
* @see JavaBreakpoint#setRequestThreadFilter(EventRequest)
*/
protected void setRequestThreadFilter(EventRequest request, ThreadReference thread) {
((ExceptionRequest)request).addThreadFilter(thread);
}
/**
* @see JavaBreakpoint#handleBreakpointEvent(Event, JDIDebugTarget, JDIThread)
*/
public boolean handleBreakpointEvent(Event event, JDIDebugTarget target, JDIThread thread) {
if (event instanceof ExceptionEvent) {
setExceptionName(((ExceptionEvent)event).exception().type().name());
if (getExclusionClassFilters().length > 1
|| getInclusionClassFilters().length > 1
|| (getExclusionClassFilters().length + getInclusionClassFilters().length) >= 2
|| filtersIncludeDefaultPackage(fInclusionClassFilters)
|| filtersIncludeDefaultPackage(fExclusionClassFilters)) {
Location location = ((ExceptionEvent)event).location();
String typeName = location.declaringType().name();
boolean defaultPackage = typeName.indexOf('.') == -1;
boolean included = true;
String[] filters = getInclusionClassFilters();
if (filters.length > 0) {
included = matchesFilters(filters, typeName, defaultPackage);
}
boolean excluded = false;
filters = getExclusionClassFilters();
if (filters.length > 0) {
excluded = matchesFilters(filters, typeName, defaultPackage);
}
if (included && !excluded) {
return !suspend(thread);
}
return true;
}
return !suspend(thread);
}
return true;
}
protected boolean filtersIncludeDefaultPackage(String[] filters) {
for (int i = 0; i < filters.length; i++) {
if (filters[i].length() == 0 || (filters[i].indexOf('.') == -1)) {
return true;
}
}
return false;
}
/**
* Returns whether the given type is in the given filter set.
*
* @param filters the filter set
* @param typeName fully qualified type name
* @param defaultPackage whether the type name is in the default package
* @return boolean
*/
protected boolean matchesFilters(String[] filters, String typeName, boolean defaultPackage) {
for (int i = 0; i < filters.length; i++) {
String filter = filters[i];
if (defaultPackage && filter.length() == 0) {
return true;
}
StringMatcher matcher = new StringMatcher(filter, false, false);
if (matcher.match(typeName)) {
return true;
}
}
return false;
}
/**
* Sets the name of the exception that was last hit
*
* @param name fully qualified exception name
*/
protected void setExceptionName(String name) {
fExceptionName = name;
}
/**
* @see IJavaExceptionBreakpoint#getExceptionTypeName()
*/
public String getExceptionTypeName() {
return fExceptionName;
}
/**
* @see IJavaExceptionBreakpoint#getFilters()
* @deprecated
*/
public String[] getFilters() {
String[] iFilters= getInclusionFilters();
String[] eFilters= getExclusionFilters();
String[] filters= new String[iFilters.length + eFilters.length];
System.arraycopy(iFilters, 0, filters, 0, iFilters.length);
System.arraycopy(eFilters, 0, filters, iFilters.length, eFilters.length);
return filters;
}
/**
* @see IJavaExceptionBreakpoint#setFilters(String[], boolean)
* @deprecated
*/
public void setFilters(String[] filters, boolean inclusive) throws CoreException {
if (inclusive) {
setInclusionFilters(filters);
} else {
setExclusionFilters(filters);
}
recreate();
}
/**
* Adds the filtering to the exception request
*/
protected void configureRequest(EventRequest eRequest, JDIDebugTarget target) throws CoreException {
String[] iFilters= getInclusionClassFilters();
String[] eFilters= getExclusionClassFilters();
ExceptionRequest request= (ExceptionRequest)eRequest;
if (iFilters.length == 1) {
if (eFilters.length ==0) {
request.addClassFilter(iFilters[0]);
}
} else if (eFilters.length == 1) {
if (iFilters.length == 0) {
request.addClassExclusionFilter(eFilters[0]);
}
}
super.configureRequest(eRequest, target);
}
/**
* Serializes the array of Strings into one comma
* separated String.
* Removes duplicates.
*/
protected String serializeList(String[] list) {
if (list == null) {
return ""; //$NON-NLS-1$
}
Set set= new HashSet(list.length);
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < list.length; i++) {
if (i > 0) {
buffer.append(',');
}
String pattern= list[i];
if (!set.contains(pattern)) {
if (pattern.length() == 0) {
//serialize the default package
pattern= "."; //$NON-NLS-1$
}
buffer.append(pattern);
}
}
return buffer.toString();
}
/**
* Parses the comma separated String into an array of Strings
*/
protected String[] parseList(String listString) {
List list = new ArrayList(10);
StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.equals(".")) { //$NON-NLS-1$
//serialized form for the default package
//@see serializeList(String[])
token= ""; //$NON-NLS-1$
}
list.add(token);
}
return (String[])list.toArray(new String[list.size()]);
}
/**
* @see IJavaExceptionBreakpoint#isInclusiveFiltered()
* @deprecated
*/
public boolean isInclusiveFiltered() throws CoreException {
return ensureMarker().getAttribute(INCLUSION_FILTERS, "").length() > 0; //$NON-NLS-1$
}
protected String[] getInclusionClassFilters() {
if (fInclusionClassFilters == null) {
try {
fInclusionClassFilters= parseList(ensureMarker().getAttribute(INCLUSION_FILTERS, "")); //$NON-NLS-1$
} catch (CoreException ce) {
fInclusionClassFilters= new String[]{};
}
}
return fInclusionClassFilters;
}
protected void setInclusionClassFilters(String[] filters) {
fInclusionClassFilters = filters;
}
protected String[] getExclusionClassFilters() {
if (fExclusionClassFilters == null) {
try {
fExclusionClassFilters= parseList(ensureMarker().getAttribute(EXCLUSION_FILTERS, "")); //$NON-NLS-1$
} catch (CoreException ce) {
fExclusionClassFilters= new String[]{};
}
}
return fExclusionClassFilters;
}
protected void setExclusionClassFilters(String[] filters) {
fExclusionClassFilters = filters;
}
/**
* @see JavaBreakpoint#installableReferenceType(ReferenceType, JDIDebugTarget)
*/
protected boolean installableReferenceType(ReferenceType type, JDIDebugTarget target) throws CoreException {
String installableType= getTypeName();
String queriedType= type.name();
if (installableType == null || queriedType == null) {
return false;
}
if (installableType.equals(queriedType)) {
return queryInstallListeners(target, type);
}
return false;
}
/**
* @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#getExclusionFilters()
*/
public String[] getExclusionFilters() {
return getExclusionClassFilters();
}
/**
* @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#getInclusionFilters()
*/
public String[] getInclusionFilters() {
return getInclusionClassFilters();
}
/**
* @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#setExclusionFilters(String[])
*/
public void setExclusionFilters(String[] filters) throws CoreException {
String serializedFilters= serializeList(filters);
if (serializedFilters.equals(ensureMarker().getAttribute(EXCLUSION_FILTERS, ""))) { //$NON-NLS-1$
//no change
return;
}
setExclusionClassFilters(filters);
setAttribute(EXCLUSION_FILTERS, serializedFilters);
recreate();
}
/**
* @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#setInclusionFilters(String[])
*/
public void setInclusionFilters(String[] filters) throws CoreException {
String serializedFilters= serializeList(filters);
if (serializedFilters.equals(ensureMarker().getAttribute(INCLUSION_FILTERS, ""))) { //$NON-NLS-1$
//no change
return;
}
setInclusionClassFilters(filters);
setAttribute(INCLUSION_FILTERS, serializedFilters);
recreate();
}
/**
* @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#addInstanceFilter(EventRequest, ObjectReference)
*/
protected void addInstanceFilter(EventRequest request, ObjectReference object) {
if (request instanceof ExceptionRequest) {
((ExceptionRequest)request).addInstanceFilter(object);
}
}
}