blob: 6a6d49b4838165c6e9620cfc4cef406cec0a56da [file] [log] [blame]
package org.eclipse.jdt.internal.debug.core;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.IDebugConstants;
import org.eclipse.debug.core.model.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.debug.core.*;
import com.sun.jdi.*;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.LocatableEvent;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.EventRequest;
public class LineBreakpoint extends JavaBreakpoint implements IJavaEvaluationListener {
// Thread label String keys
private static final String LINE_BREAKPOINT_SYS= THREAD_LABEL + "line_breakpoint_sys";
private static final String LINE_BREAKPOINT_USR= THREAD_LABEL + "line_breakpoint_usr";
// Marker label String keys
private static final String LINE= "line";
private static final String HITCOUNT= "hitCount";
static String fMarkerType= IJavaDebugConstants.JAVA_LINE_BREAKPOINT;
protected Event fEvent= null;
/**
* Sets of attributes used to configure a line breakpoint
*/
protected static final String[] fgTypeAndHitCountAttributes= new String[]{IJavaDebugConstants.TYPE_HANDLE, IJavaDebugConstants.HIT_COUNT, IJavaDebugConstants.EXPIRED};
protected static final String[] fgLineBreakpointAttributes= new String[]{IDebugConstants.MODEL_IDENTIFIER, IDebugConstants.ENABLED, IMarker.LINE_NUMBER, IMarker.CHAR_START, IMarker.CHAR_END};
public LineBreakpoint(){
}
public LineBreakpoint(IMarker marker) {
super(marker);
}
public LineBreakpoint(IType type, int lineNumber, int charStart, int charEnd, int hitCount) throws DebugException {
this(type, lineNumber, charStart, charEnd, hitCount, fMarkerType);
}
public LineBreakpoint(final IType type, final int lineNumber, final int charStart, final int charEnd, final int hitCount, final String markerType) throws DebugException {
IWorkspaceRunnable wr= new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
IResource resource= getResource(type);
// create the marker
fMarker= resource.createMarker(markerType);
setLineBreakpointAttributes(getPluginIdentifier(), true, lineNumber, charStart, charEnd);
// configure the hit count and type handle
setTypeAndHitCount(type, hitCount);
// configure the marker as a Java marker
Map attributes= getAttributes();
JavaCore.addJavaElementMarkerAttributes(attributes, type);
setAttributes(attributes);
}
};
run(wr);
}
/**
* Get the resource associated with the given type. This is
* used to set the breakpoint's resource during initialization.
*/
protected IResource getResource(IType type) {
IResource resource= null;
try {
resource= type.getUnderlyingResource();
if (resource == null) {
resource= type.getJavaProject().getProject();
}
} catch (JavaModelException jme) {
logError(jme);
}
return resource;
}
/**
* @see JavaBreakpoint#installIn(JDIDebugTarget)
*/
public void addToTarget(JDIDebugTarget target) {
fTarget= target;
String topLevelName= getTopLevelTypeName();
if (topLevelName == null) {
// internalError(ERROR_BREAKPOINT_NO_TYPE);
return;
}
// look for the top-level class - if it is loaded, inner classes may also be loaded
List classes= target.jdiClassesByName(topLevelName);
if (classes == null || classes.isEmpty()) {
// defer
target.defer(this, topLevelName);
} else {
// try to install
ReferenceType type= (ReferenceType) classes.get(0);
if (!installLineBreakpoint(target, type)) {
// install did not succeed - could be an inner type not yet loaded
target.defer(this, topLevelName);
}
}
}
/**
* Installs a line breakpoint in the given type, returning whether successful.
*/
protected boolean installLineBreakpoint(JDIDebugTarget target, ReferenceType type) {
Location location= null;
int lineNumber= getLineNumber();
location= determineLocation(lineNumber, type);
if (location == null) {
// could be an inner type not yet loaded, or line information not available
return false;
}
if (createLineBreakpointRequest(location, target) != null) {
// update the install attibute on the breakpoint
if (!target.inHCR()) {
try {
incrementInstallCount();
} catch (CoreException e) {
logError(e);
}
}
return true;
} else {
return false;
}
}
/**
* Creates, installs, and returns a line breakpoint request at
* the given location for the given breakpoint.
*/
protected BreakpointRequest createLineBreakpointRequest(Location location, JDIDebugTarget target) {
BreakpointRequest request = null;
try {
request= target.getEventRequestManager().createBreakpointRequest(location);
configureRequest(request);
} catch (VMDisconnectedException e) {
target.uninstallBreakpoint(this);
return null;
} catch (RuntimeException e) {
target.uninstallBreakpoint(this);
logError(e);
return null;
}
request.setEnabled(isEnabled());
target.installBreakpoint(this, request);
return request;
}
/**
* Returns a location for the line number in the given type, or any of its
* nested types. Returns <code>null</code> if a location cannot be determined.
*/
protected Location determineLocation(int lineNumber, ReferenceType type) {
List locations= null;
try {
locations= type.locationsOfLine(lineNumber);
} catch (AbsentInformationException e) {
return null;
} catch (NativeMethodException e) {
return null;
} catch (InvalidLineNumberException e) {
//possible in a nested type, fall through and traverse nested types
} catch (VMDisconnectedException e) {
return null;
} catch (ClassNotPreparedException e) {
// could be a nested type that is not yet loaded
return null;
} catch (RuntimeException e) {
// not able to retrieve line info
logError(e);
return null;
}
if (locations != null && locations.size() > 0) {
return (Location) locations.get(0);
} else {
Iterator nestedTypes= null;
try {
nestedTypes= type.nestedTypes().iterator();
} catch (RuntimeException e) {
// not able to retrieve line info
logError(e);
return null;
}
while (nestedTypes.hasNext()) {
ReferenceType nestedType= (ReferenceType) nestedTypes.next();
Location innerLocation= determineLocation(lineNumber, nestedType);
if (innerLocation != null) {
return innerLocation;
}
}
}
return null;
}
public void changeForTarget(JDIDebugTarget target) {
BreakpointRequest request= (BreakpointRequest) target.getRequest(this);
if (request != null) {
// already installed - could be a change in the enabled state or hit count
//may result in a new request being generated
request= updateHitCount(request, target);
if (request != null) {
updateEnabledState(request);
target.installBreakpoint(this, request);
}
}
}
/**
* Update the hit count of an <code>EventRequest</code>. Return a new request with
* the appropriate settings.
*/
protected BreakpointRequest updateHitCount(BreakpointRequest request, JDIDebugTarget target) {
// 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) && this.isEnabled())) {
try {
// delete old request
//on JDK you cannot delete (disable) an event request that has hit its count filter
if (!isExpired(request)) {
target.getEventRequestManager().deleteEventRequest(request); // disable & remove
}
Location location = ((BreakpointRequest) request).location();
request = createLineBreakpointRequest(location, target);
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
logError(e);
}
}
return request;
}
/**
* Returns whether the hitCount of this breakpoint is equal to the hitCount of
* the associated request.
*/
protected boolean hasHitCountChanged(EventRequest request) {
int hitCount= getHitCount();
Integer requestCount= (Integer) request.getProperty(IJavaDebugConstants.HIT_COUNT);
int oldCount = -1;
if (requestCount != null) {
oldCount = requestCount.intValue();
}
return hitCount != oldCount;
}
/**
* Configure a breakpoint request with common properties:
* <ul>
* <li><code>IDebugConstants.BREAKPOINT_MARKER</code></li>
* <li><code>IJavaDebugConstants.HIT_COUNT</code></li>
* <li><code>IJavaDebugConstants.EXPIRED</code></li>
* </ul>
* and sets the suspend policy of the request to suspend the event thread.
*/
protected void configureRequest(EventRequest request) {
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
request.putProperty(IDebugConstants.BREAKPOINT, this);
int hitCount= getHitCount();
if (hitCount > 0) {
request.addCountFilter(hitCount);
request.putProperty(IJavaDebugConstants.HIT_COUNT, new Integer(hitCount));
request.putProperty(IJavaDebugConstants.EXPIRED, Boolean.FALSE);
}
}
/**
* Enable a request and increment the install count of the associated breakpoint.
*/
protected void completeConfiguration(EventRequest request) {
// Important: Enable only after request has been configured
try {
request.setEnabled(isEnabled());
incrementInstallCount();
} catch (CoreException e) {
logError(e);
}
}
/**
* @see JavaBreakpoint#handleEvent(Event)
*/
public void handleEvent(Event event, JDIDebugTarget target) {
if (!(event instanceof LocatableEvent)) {
return;
}
fEvent= event;
ThreadReference threadRef= ((LocatableEvent)fEvent).thread();
JDIThread thread= target.findThread(threadRef);
thread.handleSuspendForBreakpointQuiet(this);
evaluateCondition(getCondition());
}
/**
* Evaluate the given condition in the context of this breakpoint's
* last known target.
*/
public void evaluateCondition(String conditionString) {
if (conditionString.equals("")) {
evaluationComplete(null);
}
IStackFrame stackFrame= getContextFromTarget(fTarget);
IJavaStackFrame adapter= (IJavaStackFrame) stackFrame.getAdapter(IJavaStackFrame.class);
IJavaElement javaElement= getJavaElement(stackFrame);
if (javaElement == null) {
return;
}
IJavaProject project = javaElement.getJavaProject();
try {
adapter.evaluate(conditionString, this, project);
} catch (DebugException e) {
logError(e);
}
}
/**
* @see IJavaEvaluationListener
*/
public void evaluationComplete(IJavaEvaluationResult result) {
IValue value= null;
ThreadReference threadRef= ((LocatableEvent)fEvent).thread();
JDIThread thread= fTarget.findThread(threadRef);
if (result != null) {
value= result.getValue();
}
if (result == null || result.hasProblems() || value == null || !trueCondition(value)){
fTarget.resume(threadRef);
}
else {
thread.notifyOfSuspendForBreakpoint(this);
expireHitCount((LocatableEvent)fEvent);
}
}
/**
* Returns whether the given value represents the boolean value
* "true".
*/
private boolean trueCondition(IValue value) {
try {
if (value.getValueString() == "true") {
return true;
}
} catch (DebugException de) {
}
return false;
}
protected IJavaElement getJavaElement(IStackFrame stackFrame) {
// Get the corresponding element.
ISourceLocator locator= stackFrame.getSourceLocator();
if (locator == null)
return null;
Object sourceElement = locator.getSourceElement(stackFrame);
if (sourceElement instanceof IType)
return (IType) sourceElement;
return null;
}
/**
* Resolves a stack frame context from the model
*/
protected IStackFrame getContextFromTarget(IDebugTarget dt) {
if (!dt.isTerminated()) {
try {
IDebugElement[] threads= dt.getChildren();
for (int i= 0; i < threads.length; i++) {
IThread thread= (IThread)threads[i];
if (thread.isSuspended()) {
return thread.getTopStackFrame();
}
}
} catch(DebugException x) {
logError(x);
}
}
return null;
}
/**
* Called when a breakpoint event is encountered
*/
public void expireHitCount(LocatableEvent event) {
EventRequest request= (EventRequest)event.request();
Integer requestCount= (Integer) request.getProperty(IJavaDebugConstants.HIT_COUNT);
if (requestCount != null) {
try {
request.putProperty(IJavaDebugConstants.EXPIRED, Boolean.TRUE);
this.disable();
// make a note that we auto-disabled this breakpoint.
setExpired(true);
} catch (CoreException ce) {
logError(ce);
}
}
}
/**
* @see JavaBreakpoint#removeFromTarget(JDIDebugTarget)
*/
public void removeFromTarget(JDIDebugTarget target) {
BreakpointRequest request= (BreakpointRequest) target.getRequest(this);
if (request == null) {
//deferred breakpoint
if (!this.exists()) {
//resource no longer exists
return;
}
String name= getTopLevelTypeName();
if (name == null) {
// internalError(ERROR_BREAKPOINT_NO_TYPE);
return;
}
List breakpoints= (List) target.getDeferredBreakpointsByClass(name);
if (breakpoints == null) {
return;
}
breakpoints.remove(this);
if (breakpoints.isEmpty()) {
target.removeDeferredBreakpointByClass(name);
}
} else {
//installed breakpoint
try {
// cannot delete an expired request
if (!isExpired(request)) {
target.getEventRequestManager().deleteEventRequest(request); // disable & remove
}
} catch (VMDisconnectedException e) {
return;
} catch (RuntimeException e) {
logError(e);
}
}
}
/**
* Returns the <code>CONDITION</code> attribute of the given breakpoint
* or "" if the attribute is not set.
*/
public String getCondition() {
return "grah() == 3";
}
/**
* Returns the <code>HIT_COUNT</code> attribute of the given breakpoint
* or -1 if the attribute is not set.
*/
public int getHitCount() {
return getAttribute(IJavaDebugConstants.HIT_COUNT, -1);
}
/**
* Sets the <code>HIT_COUNT</code> attribute of the given breakpoint,
* and resets the <code>EXPIRED</code> attribute to false (since, if
* the hit count is changed, the breakpoint should no longer be expired).
*/
public void setHitCount(int count) throws CoreException {
setAttributes(new String[]{IJavaDebugConstants.HIT_COUNT, IJavaDebugConstants.EXPIRED},
new Object[]{new Integer(count), Boolean.FALSE});
}
/**
* @see JavaBreakpoint#isSupportedBy(VirtualMachine)
*/
public boolean isSupportedBy(VirtualMachine vm) {
return true;
}
/**
* Returns whether the given breakpoint has expired.
*/
public boolean isExpired() {
return getBooleanAttribute( IJavaDebugConstants.EXPIRED);
}
/**
* Sets the <code>EXPIRED</code> attribute of the given breakpoint.
*/
public void setExpired(boolean expired) throws CoreException {
setBooleanAttribute(IJavaDebugConstants.EXPIRED, expired);
}
/**
* Returns the method the given breakpoint is installed in
* or <code>null</code> if a method cannot be resolved.
*/
public IMethod getMethod() {
try {
String handle = getMethodHandleIdentifier();
if (handle != null) {
return (IMethod)JavaCore.create(handle);
}
} catch (CoreException e) {
logError(e);
}
return null;
}
/**
* Set standard attributes of a line breakpoint
*/
public void setLineBreakpointAttributes(String modelIdentifier, boolean enabled, int lineNumber, int charStart, int charEnd) throws CoreException {
Object[] values= new Object[]{getPluginIdentifier(), new Boolean(true), new Integer(lineNumber), new Integer(charStart), new Integer(charEnd)};
setAttributes(fgLineBreakpointAttributes, values);
}
/**
* Sets the <code>TYPE_HANDLE</code> attribute of the given breakpoint, associated
* with the given IType.
*
* If <code>hitCount > 0</code>, sets the <code>HIT_COUNT</code> attribute of the given breakpoint,
* and resets the <code>EXPIRED</code> attribute to false (since, if
* the hit count is changed, the breakpoint should no longer be expired).
*/
public void setTypeAndHitCount(IType type, int hitCount) throws CoreException {
if (hitCount == 0) {
setType(type);
return;
}
String handle = type.getHandleIdentifier();
Object[] values= new Object[]{handle, new Integer(hitCount), Boolean.FALSE};
setAttributes(fgTypeAndHitCountAttributes, values);
}
/**
* Returns the <code>METHOD_HANDLE</code> attribute of the given breakpoint.
*/
public String getMethodHandleIdentifier() throws CoreException {
return (String)getAttribute(IJavaDebugConstants.METHOD_HANDLE);
}
/**
* Returns the smallest determinable <code>IMember</code> the given breakpoint is installed in.
*/
public IMember getMember() {
int start = getCharStart();
int end = getCharEnd();
IType type = getInstalledType();
IMember member = null;
if (type != null && end >= start && start >= 0) {
try {
if (type.isBinary()) {
member= binSearch(type.getClassFile(), type, start, end);
} else {
member= binSearch(type.getCompilationUnit(), type, start, end);
}
} catch (CoreException ce) {
logError(ce);
}
}
if (member == null) {
member= type;
}
return member;
}
/**
* Searches the given source range of the container for a member that is
* not the same as the given type.
*/
protected IMember binSearch(IClassFile container, IType type, int start, int end) throws JavaModelException {
IJavaElement je = container.getElementAt(start);
if (je != null && !je.equals(type)) {
return (IMember)je;
}
if (end > start) {
je = container.getElementAt(end);
if (je != null && !je.equals(type)) {
return (IMember)je;
}
int mid = ((end - start) / 2) + start;
if (mid > start) {
je = binSearch(container, type, start + 1, mid);
if (je == null) {
je = binSearch(container, type, mid + 1, end - 1);
}
return (IMember)je;
}
}
return null;
}
/**
* Searches the given source range of the container for a member that is
* not the same as the given type.
*/
protected IMember binSearch(ICompilationUnit container, IType type, int start, int end) throws JavaModelException {
IJavaElement je = container.getElementAt(start);
if (je != null && !je.equals(type)) {
return (IMember)je;
}
if (end > start) {
je = container.getElementAt(end);
if (je != null && !je.equals(type)) {
return (IMember)je;
}
int mid = ((end - start) / 2) + start;
if (mid > start) {
je = binSearch(container, type, start + 1, mid);
if (je == null) {
je = binSearch(container, type, mid + 1, end - 1);
}
return (IMember)je;
}
}
return null;
}
/**
* @see JavaBreakpoint
*/
public String getFormattedThreadText(String threadName, String typeName, boolean systemThread) {
int lineNumber= getAttribute(IMarker.LINE_NUMBER, -1);
if (lineNumber > -1) {
if (systemThread) {
return getFormattedString(LINE_BREAKPOINT_SYS, new String[] {threadName, String.valueOf(lineNumber), typeName});
} else {
return getFormattedString(LINE_BREAKPOINT_USR, new String[] {threadName, String.valueOf(lineNumber), typeName});
}
}
return "";
}
public String getMarkerText(boolean showQualified, String memberString) {
IType type= getInstalledType();
if (type != null) {
StringBuffer label= new StringBuffer();
if (showQualified) {
label.append(type.getFullyQualifiedName());
} else {
label.append(type.getElementName());
}
int lineNumber= getLineNumber();
if (lineNumber > 0) {
label.append(" [");
label.append(DebugJavaUtils.getResourceString(MARKER_LABEL + LINE));
label.append(' ');
label.append(lineNumber);
label.append(']');
}
int hitCount= getHitCount();
if (hitCount > 0) {
label.append(" [");
label.append(DebugJavaUtils.getResourceString(MARKER_LABEL + HITCOUNT));
label.append(' ');
label.append(hitCount);
label.append(']');
}
if (memberString != null) {
label.append(" - ");
label.append(memberString);
}
return label.toString();
}
return "";
}
}