blob: 7762564aa390a305427c2e2c701adaea5575a94d [file] [log] [blame]
package org.eclipse.jdt.internal.debug.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.IDebugConstants;
import org.eclipse.debug.core.IDebugStatusConstants;
import org.eclipse.debug.core.model.Breakpoint;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.debug.core.IJavaBreakpoint;
import org.eclipse.jdt.debug.core.JDIDebugModel;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.LocatableEvent;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.MethodEntryRequest;
public abstract class JavaBreakpoint extends Breakpoint implements IJavaBreakpoint, IJDIEventListener {
/**
* Breakpoint attribute storing the expired value (value <code>"expired"</code>).
* This attribute is stored as a <code>boolean</code>. Once a hit count has
* been reached, a breakpoint is considered to be "expired".
*/
protected static final String EXPIRED = "expired"; //$NON-NLS-1$
/**
* Breakpoint attribute storing a breakpoint's hit count value
* (value <code>"hitCount"</code>). This attribute is stored as an
* <code>int</code>.
*/
protected static final String HIT_COUNT = "hitCount"; //$NON-NLS-1$
/**
* Breakpoint attribute storing the number of debug targets a
* breakpoint is installed in (value <code>"installCount"</code>).
* This attribute is a <code>int</code>.
*/
protected static final String INSTALL_COUNT = "installCount"; //$NON-NLS-1$
/**
* Breakpoint attribute storing the handle identifier of the Java element
* corresponding to the type in which a breakpoint is contained
* (value <code>"typeHandle"</code>). This attribute is a <code>String</code>.
*/
protected static final String TYPE_HANDLE = "typeHandle"; //$NON-NLS-1$
protected HashMap fRequestsByTarget;
/**
* Propery identifier for a breakpoint object on an event request
*/
public static final String JAVA_BREAKPOINT_PROPERTY = "org.eclipse.jdt.debug.breakpoint"; //$NON-NLS-1$
/**
* JavaBreakpoint attributes
*/
protected static final String[] fgExpiredEnabledAttributes= new String[]{EXPIRED, IDebugConstants.ENABLED};
public JavaBreakpoint() {
fRequestsByTarget = new HashMap(2);
}
public String getModelIdentifier() {
return JDIDebugModel.getPluginIdentifier();
}
public void setMarker(IMarker marker) throws CoreException {
super.setMarker(marker);
configureAtStartup();
}
protected IMarker ensureMarker() throws DebugException {
IMarker m = getMarker();
if (m == null) {
throw new DebugException(new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), IDebugStatusConstants.REQUEST_FAILED,
JDIDebugModelMessages.getString("JavaBreakpoint.no_asscoiated_marker"),null)); //$NON-NLS-1$
}
return m;
}
protected void registerRequest(JDIDebugTarget target, EventRequest request) throws CoreException {
if (request == null) {
return;
}
List reqs = getRequests(target);
if (reqs == null) {
reqs = new ArrayList(1);
fRequestsByTarget.put(target, reqs);
}
reqs.add(request);
target.addJDIEventListener(this, request);
// update the install attibute on the breakpoint
if (!(request instanceof ClassPrepareRequest)) {
incrementInstallCount();
}
}
protected List getRequests(JDIDebugTarget target) {
return (List)fRequestsByTarget.get(target);
}
protected void deregisterRequest(EventRequest request, JDIDebugTarget target) throws CoreException {
target.removeJDIEventListener(this, request);
if (!(request instanceof ClassPrepareRequest)) {
decrementInstallCount();
}
}
protected void replaceRequest(JDIDebugTarget target, EventRequest oldRequest, EventRequest newRequest) {
List list = getRequests(target);
if (list == null) {
// error
return;
}
list.remove(oldRequest);
list.add(newRequest);
target.removeJDIEventListener(this, oldRequest);
target.addJDIEventListener(this, newRequest);
}
/**
* @see IJavaBreakpoint#handleEvent(Event)
*/
public boolean handleEvent(Event event, JDIDebugTarget target) {
if (event instanceof ClassPrepareEvent) {
// create a new request
ClassPrepareEvent cpe = (ClassPrepareEvent)event;
try {
createRequest(target, cpe.referenceType());
} catch (CoreException e) {
JDIDebugPlugin.logError(e);
}
return true;
} else {
ThreadReference threadRef= ((LocatableEvent)event).thread();
JDIThread thread= target.findThread(threadRef);
if (thread == null) {
return true;
} else {
thread.handleSuspendForBreakpoint(this);
expireHitCount(event);
return false;
}
}
}
/**
* Called when a breakpoint event is encountered
*/
public void expireHitCount(Event event) {
EventRequest request= event.request();
Integer requestCount= (Integer) request.getProperty(HIT_COUNT);
if (requestCount != null) {
try {
request.putProperty(EXPIRED, Boolean.TRUE);
setEnabled(false);
// make a note that we auto-disabled this breakpoint.
setExpired(true);
} catch (CoreException ce) {
JDIDebugPlugin.logError(ce);
}
}
}
protected abstract void createRequest(JDIDebugTarget target, ReferenceType type) throws CoreException;
protected abstract void addToTarget(JDIDebugTarget target) throws CoreException;
protected void changeForTarget(JDIDebugTarget target) throws CoreException {
List requests = getRequests(target);
if (requests != null) {
Iterator iter = requests.iterator();
while (iter.hasNext()) {
EventRequest req = (EventRequest)iter.next();
if (!(req instanceof ClassPrepareRequest)) {
updateRequest(req, target);
}
}
}
}
protected void updateRequest(EventRequest request, JDIDebugTarget target) throws CoreException {
updateEnabledState(request);
EventRequest newRequest = updateHitCount(request, target);
if (newRequest != request) {
replaceRequest(target, request, newRequest);
request = newRequest;
}
}
protected abstract EventRequest updateHitCount(EventRequest request, JDIDebugTarget target) throws CoreException;
/**
* Returns whether the hitCount of this breakpoint is equal to the hitCount of
* the associated request.
*/
protected boolean hasHitCountChanged(EventRequest request) throws CoreException {
int hitCount= getHitCount();
Integer requestCount= (Integer) request.getProperty(HIT_COUNT);
int oldCount = -1;
if (requestCount != null) {
oldCount = requestCount.intValue();
}
return hitCount != oldCount;
}
protected void fork(final IWorkspaceRunnable wRunnable) {
Runnable runnable= new Runnable() {
public void run() {
try {
ResourcesPlugin.getWorkspace().run(wRunnable, null);
} catch (CoreException ce) {
JDIDebugPlugin.logError(ce);
}
}
};
new Thread(runnable).start();
}
/**
* An exception breakpoint has been removed
*/
protected void removeFromTarget(JDIDebugTarget target) throws CoreException {
List requests = getRequests(target);
if (requests == null) {
// error
return;
}
Iterator iter = requests.iterator();
while (iter.hasNext()) {
EventRequest req = (EventRequest)iter.next();
try {
// cannot delete an expired request
if (!isExpired(req)) {
target.getEventRequestManager().deleteEventRequest(req); // disable & remove
}
} catch (VMDisconnectedException e) {
if (target.isDisconnected() || target.isTerminated()) {
return;
}
JDIDebugPlugin.logError(e);
} catch (RuntimeException e) {
JDIDebugPlugin.logError(e);
}
deregisterRequest(req, target);
}
}
/**
* Update the enabled state of the given request, which is associated
* with this breakpoint. Set the enabled state of the request
* to the enabled state of this breakpoint.
*/
protected void updateEnabledState(EventRequest request) throws CoreException {
boolean enabled = isEnabled();
if (request.isEnabled() != enabled) {
// change the enabled state
try {
// if the request has expired, and is not a method entry request, do not disable.
// BreakpointRequests that have expired cannot be deleted. However method entry
// requests that are expired can be deleted (since we simulate the hit count)
if (request instanceof MethodEntryRequest || !isExpired(request)) {
request.setEnabled(enabled);
}
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
JDIDebugPlugin.logError(e);
}
}
}
/**
* Returns whether this breakpoint has expired.
*/
public boolean isExpired() throws CoreException {
return ensureMarker().getAttribute(EXPIRED, false);
}
/**
* Returns whether the given request is expired
*/
protected boolean isExpired(EventRequest request) {
Boolean requestExpired= (Boolean) request.getProperty(EXPIRED);
if (requestExpired == null) {
return false;
}
return requestExpired.booleanValue();
}
/**
* @see IJavaBreakpoint#isInstalled()
*/
public boolean isInstalled() throws CoreException {
return ensureMarker().getAttribute(INSTALL_COUNT, 0) > 0;
}
/**
* Increments the install count on this breakpoint
*/
public void incrementInstallCount() throws CoreException {
int count = getInstallCount();
ensureMarker().setAttribute(INSTALL_COUNT, count + 1);
}
/**
* Returns the <code>INSTALL_COUNT</code> attribute of this breakpoint
* or 0 if the attribute is not set.
*/
public int getInstallCount() throws CoreException {
return ensureMarker().getAttribute(INSTALL_COUNT, 0);
}
/**
* @see IJavaBreakpoint
*/
public void decrementInstallCount() throws CoreException {
int count= getInstallCount();
if (count > 0) {
ensureMarker().setAttribute(INSTALL_COUNT, count - 1);
}
if (count == 1) {
if (isExpired()) {
// if breakpoint was auto-disabled, re-enable it
ensureMarker().setAttributes(fgExpiredEnabledAttributes,
new Object[]{Boolean.FALSE, Boolean.TRUE});
}
}
}
/**
* Sets the <code>TYPE_HANDLE</code> attribute of the given breakpoint, associated
* with the given IType.
*/
public void setType(IType type) throws CoreException {
String handle = type.getHandleIdentifier();
setTypeHandleIdentifier(handle);
}
/**
* Sets the <code>TYPE_HANDLE</code> attribute of the given breakpoint.
*/
public void setTypeHandleIdentifier(String identifier) throws CoreException {
ensureMarker().setAttribute(TYPE_HANDLE, identifier);
}
/**
* @see IJavaBreakpoint#getType()
*/
public IType getType() throws CoreException {
String handle = getTypeHandleIdentifier();
if (handle != null) {
return (IType)JavaCore.create(handle);
}
return null;
}
/**
* Returns the <code>TYPE_HANDLE</code> attribute of the given breakpoint.
*/
public String getTypeHandleIdentifier() throws CoreException {
return (String) ensureMarker().getAttribute(TYPE_HANDLE);
}
/**
* Returns the top-level type name associated with the type
* the given breakpoint is associated with, or <code>null</code>.
*/
public String getTopLevelTypeName() throws CoreException {
IType type = getType();
if (type != null) {
while (type.getDeclaringType() != null) {
type = type.getDeclaringType();
}
return type.getFullyQualifiedName();
}
return null;
}
/**
* Returns the identifier for this JDI debug model plug-in
*
* @return plugin identifier
*/
public static String getPluginIdentifier() {
return JDIDebugPlugin.getDefault().getDescriptor().getUniqueIdentifier();
}
protected void run(IWorkspaceRunnable wr) throws DebugException {
try {
ResourcesPlugin.getWorkspace().run(wr, null);
} catch (CoreException e) {
throw new DebugException(e.getStatus());
}
}
/**
* Resets the install count attribute on this breakpoint's marker
* to "0". Resets the expired attribute on all breakpoint markers to <code>false</code>.
* Resets the enabled attribute on the breakpoint marker to <code>true</code>.
* If a workbench crashes, the attributes could have been persisted
* in an incorrect state.
*/
private void configureAtStartup() throws CoreException {
List attributes= new ArrayList(3);
List values= new ArrayList(3);
if (isInstalled()) {
attributes.add(INSTALL_COUNT);
values.add(new Integer(0));
}
if (isExpired()) {
// if breakpoint was auto-disabled, re-enable it
attributes.add(EXPIRED);
values.add(Boolean.FALSE);
attributes.add(IDebugConstants.ENABLED);
values.add(Boolean.TRUE);
}
if (!attributes.isEmpty()) {
String[] strAttributes= new String[attributes.size()];
ensureMarker().setAttributes((String[])attributes.toArray(strAttributes), values.toArray());
}
}
/**
* Add this breakpoint to the breakpoint manager
*/
protected void addToBreakpointManager() throws DebugException {
getBreakpointManager().addBreakpoint(this);
}
/**
* Returns the breakpoint manager
*/
protected IBreakpointManager getBreakpointManager() {
return DebugPlugin.getDefault().getBreakpointManager();
}
/**
* @see IJavaBreakpoint#getHitCount()
*/
public int getHitCount() throws CoreException {
return ensureMarker().getAttribute(HIT_COUNT, -1);
}
/**
* @see IJavaBreakpoint#setHitCount(int)
*/
public void setHitCount(int count) throws CoreException {
if (!isEnabled() && count > -1) {
ensureMarker().setAttributes(new String []{HIT_COUNT, EXPIRED, IDebugConstants.ENABLED},
new Object[]{new Integer(count), Boolean.FALSE, Boolean.TRUE});
} else {
ensureMarker().setAttributes(new String[]{HIT_COUNT, EXPIRED},
new Object[]{new Integer(count), Boolean.FALSE});
}
}
/**
* Sets the <code>EXPIRED</code> attribute of the given breakpoint.
*/
public void setExpired(boolean expired) throws CoreException {
ensureMarker().setAttribute(EXPIRED, expired);
}
}