package org.rubypeople.rdt.internal.debug.ui; import java.util.Map; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.rubypeople.rdt.core.IMember; import org.rubypeople.rdt.core.IMethod; import org.rubypeople.rdt.core.IRubyElement; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.IType; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.debug.core.IRubyBreakpoint; import org.rubypeople.rdt.debug.core.IRubyLineBreakpoint; import org.rubypeople.rdt.debug.core.IRubyMethodBreakpoint; public class BreakpointUtils { /** * Marker attribute storing the handle id of the Ruby element associated with a Ruby breakpoint */ private static final String HANDLE_ID = RdtDebugUiPlugin.getUniqueIdentifier() + ".RUBY_ELEMENT_HANDLE_ID"; //$NON-NLS-1$ /** * Marker attribute used to denote a run to line breakpoint */ private static final String RUN_TO_LINE = RdtDebugUiPlugin.getUniqueIdentifier() + ".run_to_line"; //$NON-NLS-1$ /** * Marker attribute used to denote the start of the region within a Ruby member that the breakpoint is located * within. */ private static final String MEMBER_START = RdtDebugUiPlugin.getUniqueIdentifier() + ".member_start"; //$NON-NLS-1$ /** * Marker attribute used to denote the end of the region within a Ruby member that the breakpoint is located within. */ private static final String MEMBER_END = RdtDebugUiPlugin.getUniqueIdentifier() + ".member_end"; //$NON-NLS-1$ /** * Adds attributes to the given attribute map: * <ul> * <li>Ruby element handle id</li> * <li>Attributes defined by <code>RubyCore</code></li> * </ul> * * @param attributes * the attribute map to use * @param element * the Ruby element associated with the breakpoint * @exception CoreException * if an exception occurs configuring the marker */ public static void addRubyBreakpointAttributes(Map attributes, IRubyElement element) { String handleId = element.getHandleIdentifier(); attributes.put(HANDLE_ID, handleId); // RubyCore.addRubyElementMarkerAttributes(attributes, element); } /** * Adds attributes to the given attribute map: * <ul> * <li>Ruby element handle id</li> * <li>Member start position</li> * <li>Member end position</li> * <li>Attributes defined by <code>RubyCore</code></li> * </ul> * * @param attributes * the attribute map to use * @param element * the Ruby element associated with the breakpoint * @param memberStart * the start position of the Ruby member that the breakpoint is positioned within * @param memberEnd * the end position of the Ruby member that the breakpoint is positioned within * @exception CoreException * if an exception occurs configuring the marker */ public static void addRubyBreakpointAttributesWithMemberDetails(Map attributes, IRubyElement element, int memberStart, int memberEnd) { addRubyBreakpointAttributes(attributes, element); attributes.put(MEMBER_START, new Integer(memberStart)); attributes.put(MEMBER_END, new Integer(memberEnd)); } /** * 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(IRubyElement member) { if (member instanceof IMember) { IRubyScript script = ((IMember)member).getRubyScript(); if (script != null && script.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 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(IRubyLineBreakpoint breakpoint) throws CoreException { if (breakpoint instanceof IRubyMethodBreakpoint) { return getMethod((IRubyMethodBreakpoint) breakpoint); } // if (breakpoint instanceof IRubyWatchpoint) // { // return getField((IRubyWatchpoint) 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; } /** * Returns the method associated with the method entry breakpoint. * * @param breakpoint * Ruby method entry breakpoint * @return method */ public static IMethod getMethod(IRubyMethodBreakpoint breakpoint) { String handle = breakpoint.getMarker().getAttribute(HANDLE_ID, null); if (handle != null) { IRubyElement je = RubyCore.create(handle); if (je != null) { if (je instanceof IMethod) { return (IMethod) je; } } } return null; } /** * Returns the type that the given Ruby breakpoint refers to * * @param breakpoint * Ruby breakpoint * @return the type the breakpoint is associated with */ public static IType getType(IRubyBreakpoint breakpoint) { String handle = breakpoint.getMarker().getAttribute(HANDLE_ID, null); if (handle != null) { IRubyElement je = RubyCore.create(handle); if (je != null) { if (je instanceof IType) { return (IType) je; } if (je instanceof IMember) { return ((IMember) je).getDeclaringType(); } } } return null; } /** * Searches the given source range of the container for a member that is not the same as the given type. */ protected static IMember binSearch(IType type, int start, int end) throws RubyModelException { IRubyElement 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 Ruby element if it is an <code>IMember</code>, otherwise <code>null</code>. * * @param element * Ruby element * @return the given element if it is a type member, otherwise <code>null</code> */ private static IMember asMember(IRubyElement element) { if (element instanceof IMember) { return (IMember) element; } return null; } /** * Returns the element at the given position in the given type */ protected static IRubyElement getElementAt(IType type, int pos) throws RubyModelException { return type.getRubyScript().getElementAt(pos); } }