| /******************************************************************************* |
| * 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, new Integer(memberStart)); |
| attributes.put(MEMBER_END, new Integer(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; |
| } |
| } |