package com.aptana.ruby.internal.debug.core.breakpoints; import java.text.MessageFormat; import java.util.Map; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IBreakpoint; import com.aptana.core.util.StringUtil; import com.aptana.ruby.debug.core.IRubyLineBreakpoint; import com.aptana.ruby.debug.core.RubyDebugModel; public class RubyLineBreakpoint extends RubyBreakpoint implements IRubyLineBreakpoint { // TODO Move this constant to some public interface... public static final String RUBY_LINE_BREAKPOINT = "com.aptana.ruby.debug.core.rubyLineBreakpointMarker"; //$NON-NLS-1$ /** * Breakpoint attribute storing a breakpoint's conditional expression (value * <code>"com.aptana.ruby.debug.core.condition"</code>). This attribute is stored as a <code>String</code>. */ protected static final String CONDITION = "com.aptana.ruby.debug.core.condition"; //$NON-NLS-1$ /** * Breakpoint attribute storing a breakpoint's condition enabled state (value * <code>"com.aptana.ruby.debug.core.conditionEnabled"</code>). This attribute is stored as an <code>boolean</code>. */ protected static final String CONDITION_ENABLED = "com.aptana.ruby.debug.core.conditionEnabled"; //$NON-NLS-1$ /** * Breakpoint attribute storing a breakpoint's condition suspend policy (value * <code>" com.aptana.ruby.debug.core.conditionSuspendOnTrue" * </code>). This attribute is stored as an <code>boolean</code>. */ protected static final String CONDITION_SUSPEND_ON_TRUE = "com.aptana.ruby.debug.core.conditionSuspendOnTrue"; //$NON-NLS-1$ public static final String EXTERNAL_FILENAME = "externalFileName"; //$NON-NLS-1$ private int index = -1; // index of breakpoint on ruby debugger side public RubyLineBreakpoint() { } /** * @param typeName * @see RDtDebugModel#createLineBreakpoint(IResource, String, int, int, int, int, boolean, Map) */ public RubyLineBreakpoint(IResource resource, IPath fileName, String typeName, int lineNumber, int charStart, int charEnd, int hitCount, boolean add, Map<String, Object> attributes) throws DebugException { this(resource, fileName, typeName, lineNumber, charStart, charEnd, hitCount, add, attributes, RUBY_LINE_BREAKPOINT); } protected RubyLineBreakpoint(final IResource resource, final IPath fileName, final String typeName, final int lineNumber, final int charStart, final int charEnd, final int hitCount, final boolean add, final Map<String, Object> attributes, final String markerType) throws DebugException { IWorkspaceRunnable wr = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { // create the marker setMarker(resource.createMarker(RUBY_LINE_BREAKPOINT)); String msgFilename = resource.getFullPath().toString(); if (resource.equals(ResourcesPlugin.getWorkspace().getRoot())) { attributes.put(EXTERNAL_FILENAME, fileName.toPortableString()); msgFilename = fileName.toOSString(); } // add attributes attributes.put( IMarker.MESSAGE, MessageFormat.format(Messages.RubyLineBreakpoint_Message, msgFilename, Integer.toString(lineNumber))); addLineBreakpointAttributes(attributes, getModelIdentifier(), true, lineNumber, charStart, charEnd); addTypeNameAndHitCount(attributes, typeName, hitCount); // set attributes // attributes.put(SUSPEND_POLICY, new Integer(getDefaultSuspendPolicy())); ensureMarker().setAttributes(attributes); // add to breakpoint manager if requested register(add); } }; run(getMarkerRule(resource), wr); } /** * Adds the standard attributes of a line breakpoint to the given attribute map. The standard attributes are: * <ol> * <li>IBreakpoint.ID</li> * <li>IBreakpoint.ENABLED</li> * <li>IMarker.LINE_NUMBER</li> * <li>IMarker.CHAR_START</li> * <li>IMarker.CHAR_END</li> * </ol> */ public void addLineBreakpointAttributes(Map<String, Object> attributes, String modelIdentifier, boolean enabled, int lineNumber, int charStart, int charEnd) { attributes.put(IBreakpoint.ID, modelIdentifier); attributes.put(IBreakpoint.ENABLED, Boolean.valueOf(enabled)); attributes.put(IMarker.LINE_NUMBER, new Integer(lineNumber)); attributes.put(IMarker.CHAR_START, new Integer(charStart)); attributes.put(IMarker.CHAR_END, new Integer(charEnd)); } /** * Adds type name and hit count attributes to the given map. If <code>hitCount > 0</code>, adds the * <code>HIT_COUNT</code> attribute to 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 addTypeNameAndHitCount(Map<String, Object> attributes, String typeName, int hitCount) { attributes.put(TYPE_NAME, typeName); if (hitCount > 0) { attributes.put(HIT_COUNT, new Integer(hitCount)); attributes.put(EXPIRED, Boolean.FALSE); } } // FIXME Return URI? public IPath getFilePath() throws CoreException { IResource resource = ensureMarker().getResource(); if (resource.equals(ResourcesPlugin.getWorkspace().getRoot())) { return Path.fromPortableString(ensureMarker().getAttribute(EXTERNAL_FILENAME, StringUtil.EMPTY)); } return resource.getProjectRelativePath(); } public IPath getLocation() throws CoreException { IResource resource = ensureMarker().getResource(); if (resource.equals(ResourcesPlugin.getWorkspace().getRoot())) { return Path.fromPortableString(ensureMarker().getAttribute(EXTERNAL_FILENAME, StringUtil.EMPTY)); } return resource.getLocation(); } public int getLineNumber() throws CoreException { return ensureMarker().getAttribute(IMarker.LINE_NUMBER, -1); } public int getCharStart() throws CoreException { return ensureMarker().getAttribute(IMarker.CHAR_START, -1); } public int getCharEnd() throws CoreException { return ensureMarker().getAttribute(IMarker.CHAR_END, -1); } /** * Returns the type of marker associated with this type of breakpoints */ public static String getMarkerType() { return RUBY_LINE_BREAKPOINT; } public String getModelIdentifier() { return RubyDebugModel.getModelIdentifier(); } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getCondition() throws CoreException { return ensureMarker().getAttribute(CONDITION, null); } public boolean isConditionEnabled() throws CoreException { return ensureMarker().getAttribute(CONDITION_ENABLED, false); } public boolean isConditionSuspendOnTrue() throws DebugException { return ensureMarker().getAttribute(CONDITION_SUSPEND_ON_TRUE, true); } public void setCondition(String condition) throws CoreException { if (condition != null && condition.trim().length() == 0) { condition = null; } setAttributes(new String[] { CONDITION }, new Object[] { condition }); recreate(); } public void setConditionEnabled(boolean conditionEnabled) throws CoreException { setAttributes(new String[] { CONDITION_ENABLED }, new Object[] { Boolean.valueOf(conditionEnabled) }); recreate(); } public void setConditionSuspendOnTrue(boolean suspendOnTrue) throws CoreException { if (isConditionSuspendOnTrue() != suspendOnTrue) { setAttributes(new String[] { CONDITION_SUSPEND_ON_TRUE }, new Object[] { Boolean.valueOf(suspendOnTrue) }); recreate(); } } public boolean supportsCondition() { return true; } }