blob: 82e1ce50a9ef579b057236bcfc774bcafa9685bb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.debug.ui;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.debug.core.IJavaBreakpoint;
import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
import org.eclipse.jdt.debug.core.IJavaMethodBreakpoint;
import org.eclipse.jdt.debug.core.IJavaWatchpoint;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.SimpleMarkerAnnotation;
/**
* Utility class for Java breakpoints
*/
public class BreakpointUtils {
/**
* Marker attribute storing the handle id of the
* Java element associated with a Java breakpoint
*/
private static final String HANDLE_ID = JDIDebugUIPlugin.getUniqueIdentifier() + ".JAVA_ELEMENT_HANDLE_ID"; //$NON-NLS-1$
/**
* Marker attribute used to denote a run to line breakpoint
*/
private static final String RUN_TO_LINE = JDIDebugUIPlugin.getUniqueIdentifier() + ".run_to_line"; //$NON-NLS-1$
/**
* Marker attribute used to denote the start of the region within a Java
* member that the breakpoint is located within.
*/
private static final String MEMBER_START = JDIDebugUIPlugin.getUniqueIdentifier() + ".member_start"; //$NON-NLS-1$
/**
* Marker attribute used to denote the end of the region within a Java
* member that the breakpoint is located within.
*/
private static final String MEMBER_END = JDIDebugUIPlugin.getUniqueIdentifier() + ".member_end"; //$NON-NLS-1$
/**
* Returns the resource on which a breakpoint marker should
* be created for the given member. The resource returned is the
* associated file, or workspace root in the case of a binary in
* an external archive.
*
* @param member member in which a breakpoint is being created
* @return resource the resource on which a breakpoint marker
* should be created
*/
public static IResource getBreakpointResource(IMember member) {
ICompilationUnit cu = member.getCompilationUnit();
if (cu != null && cu.isWorkingCopy()) {
member = (IMember)member.getPrimaryElement();
}
IResource res = member.getResource();
if (res == null) {
res = ResourcesPlugin.getWorkspace().getRoot();
}
else if(!res.getProject().exists()) {
res = ResourcesPlugin.getWorkspace().getRoot();
}
return res;
}
/**
* Returns the type that the given Java breakpoint refers to
*
* @param breakpoint Java breakpoint
* @return the type the breakpoint is associated with
*/
public static IType getType(IJavaBreakpoint breakpoint) {
String handle = breakpoint.getMarker().getAttribute(HANDLE_ID, null);
if (handle != null) {
IJavaElement je = JavaCore.create(handle);
if (je != null) {
if (je instanceof IType) {
return (IType)je;
}
if (je instanceof IMember) {
return ((IMember)je).getDeclaringType();
}
}
}
return null;
}
/**
* Returns the member associated with the line number of
* the given breakpoint.
*
* @param breakpoint Java line breakpoint
* @return member at the given line number in the type
* associated with the breakpoint
* @exception CoreException if an exception occurs accessing
* the breakpoint
*/
public static IMember getMember(IJavaLineBreakpoint breakpoint) throws CoreException {
if (breakpoint instanceof IJavaMethodBreakpoint) {
return getMethod((IJavaMethodBreakpoint)breakpoint);
}
if (breakpoint instanceof IJavaWatchpoint) {
return getField((IJavaWatchpoint)breakpoint);
}
int start = breakpoint.getCharStart();
int end = breakpoint.getCharEnd();
IType type = getType(breakpoint);
if (start == -1 && end == -1) {
start= breakpoint.getMarker().getAttribute(MEMBER_START, -1);
end= breakpoint.getMarker().getAttribute(MEMBER_END, -1);
}
IMember member = null;
if ((type != null && type.exists()) && (end >= start) && (start >= 0)) {
member= binSearch(type, start, end);
}
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.
* @param type the {@link IType}
* @param start the starting position
* @param end the ending position
* @return the {@link IMember} from the given start-end range
* @throws JavaModelException if there is a problem with the backing Java model
*/
protected static IMember binSearch(IType type, int start, int end) throws JavaModelException {
IJavaElement je = getElementAt(type, start);
if (je != null && !je.equals(type)) {
return asMember(je);
}
if (end > start) {
je = getElementAt(type, end);
if (je != null && !je.equals(type)) {
return asMember(je);
}
int mid = ((end - start) / 2) + start;
if (mid > start) {
je = binSearch(type, start + 1, mid);
if (je == null) {
je = binSearch(type, mid + 1, end - 1);
}
return asMember(je);
}
}
return null;
}
/**
* Returns the given Java element if it is an
* <code>IMember</code>, otherwise <code>null</code>.
*
* @param element Java element
* @return the given element if it is a type member,
* otherwise <code>null</code>
*/
private static IMember asMember(IJavaElement element) {
if (element instanceof IMember) {
return (IMember)element;
}
return null;
}
/**
* Returns the element at the given position in the given type
* @param type the {@link IType}
* @param pos the position
* @return the {@link IJavaElement} at the given position
* @throws JavaModelException if there is a problem with the backing Java model
*/
protected static IJavaElement getElementAt(IType type, int pos) throws JavaModelException {
if (type.isBinary()) {
return type.getClassFile().getElementAt(pos);
}
return type.getCompilationUnit().getElementAt(pos);
}
/**
* Adds attributes to the given attribute map:<ul>
* <li>Java element handle id</li>
* <li>Attributes defined by <code>JavaCore</code></li>
* </ul>
*
* @param attributes the attribute map to use
* @param element the Java element associated with the breakpoint
*/
public static void addJavaBreakpointAttributes(Map<String, Object> attributes, IJavaElement element) {
String handleId = element.getHandleIdentifier();
attributes.put(HANDLE_ID, handleId);
JavaCore.addJavaElementMarkerAttributes(attributes, element);
}
/**
* Adds attributes to the given attribute map:<ul>
* <li>Java element handle id</li>
* <li>Member start position</li>
* <li>Member end position</li>
* <li>Attributes defined by <code>JavaCore</code></li>
* </ul>
*
* @param attributes the attribute map to use
* @param element the Java element associated with the breakpoint
* @param memberStart the start position of the Java member that the breakpoint is positioned within
* @param memberEnd the end position of the Java member that the breakpoint is positioned within
*/
public static void addJavaBreakpointAttributesWithMemberDetails(Map<String, Object> attributes, IJavaElement element, int memberStart, int memberEnd) {
addJavaBreakpointAttributes(attributes, element);
attributes.put(MEMBER_START, Integer.valueOf(memberStart));
attributes.put(MEMBER_END, Integer.valueOf(memberEnd));
}
/**
* Adds attributes to the given attribute map to make the
* breakpoint a run-to-line breakpoint:<ul>
* <li>PERSISTED = false</li>
* <li>RUN_TO_LINE = true</li>
* </ul>
*
* @param attributes the attribute map to use
*/
public static void addRunToLineAttributes(Map<String, Object> attributes) {
attributes.put(IBreakpoint.PERSISTED, Boolean.FALSE);
attributes.put(RUN_TO_LINE, Boolean.TRUE);
}
/**
* Returns the method associated with the method entry
* breakpoint.
*
* @param breakpoint Java method entry breakpoint
* @return method
*/
public static IMethod getMethod(IJavaMethodBreakpoint breakpoint) {
String handle = breakpoint.getMarker().getAttribute(HANDLE_ID, null);
if (handle != null) {
IJavaElement je = JavaCore.create(handle);
if (je != null) {
if (je instanceof IMethod) {
return (IMethod)je;
}
}
}
return null;
}
/**
* Returns the field associated with the watchpoint.
*
* @param breakpoint Java watchpoint
* @return field
*/
public static IField getField(IJavaWatchpoint breakpoint) {
String handle = breakpoint.getMarker().getAttribute(HANDLE_ID, null);
if (handle != null) {
IJavaElement je = JavaCore.create(handle);
if (je != null) {
if (je instanceof IField) {
return (IField)je;
}
}
}
return null;
}
/**
* Returns whether the given breakpoint is a run to line
* breakpoint
*
* @param breakpoint line breakpoint
* @return whether the given breakpoint is a run to line
* breakpoint
*/
public static boolean isRunToLineBreakpoint(IJavaLineBreakpoint breakpoint) {
return breakpoint.getMarker().getAttribute(RUN_TO_LINE, false);
}
/**
* Returns whether the given breakpoint is a compilation
* problem breakpoint or uncaught exception breakpoint
*
* @param breakpoint breakpoint
* @return whether the given breakpoint is a compilation error breakpoint or
* uncaught exception breakpoint
*/
public static boolean isProblemBreakpoint(IBreakpoint breakpoint) {
return breakpoint == JavaDebugOptionsManager.getDefault().getSuspendOnCompilationErrorBreakpoint() ||
breakpoint == JavaDebugOptionsManager.getDefault().getSuspendOnUncaughtExceptionBreakpoint();
}
/**
* Resolves the {@link IBreakpoint} from the given editor and ruler information. Returns <code>null</code>
* if no breakpoint exists or the operation fails.
*
* @param editor the editor
* @param info the current ruler information
* @return the {@link IBreakpoint} from the current editor position or <code>null</code>
* @since 3.8
*/
public static IBreakpoint getBreakpointFromEditor(ITextEditor editor, IVerticalRulerInfo info) {
IAnnotationModel annotationModel = editor.getDocumentProvider().getAnnotationModel(editor.getEditorInput());
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
if (annotationModel != null) {
Iterator<Annotation> iterator = annotationModel.getAnnotationIterator();
while (iterator.hasNext()) {
Object object = iterator.next();
if (object instanceof SimpleMarkerAnnotation) {
SimpleMarkerAnnotation markerAnnotation = (SimpleMarkerAnnotation) object;
IMarker marker = markerAnnotation.getMarker();
try {
if (marker.isSubtypeOf(IBreakpoint.BREAKPOINT_MARKER)) {
Position position = annotationModel.getPosition(markerAnnotation);
int line = document.getLineOfOffset(position.getOffset());
if (line == info.getLineOfLastMouseButtonActivity()) {
IBreakpoint breakpoint = DebugPlugin.getDefault().getBreakpointManager().getBreakpoint(marker);
if (breakpoint != null) {
return breakpoint;
}
}
}
} catch (CoreException e) {
} catch (BadLocationException e) {
}
}
}
}
return null;
}
}