package org.eclipse.dltk.internal.debug.core.model; import java.util.Map; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.dltk.debug.core.DLTKDebugPlugin; import org.eclipse.dltk.debug.core.model.IScriptExceptionBreakpoint; public class ScriptExceptionBreakpoint extends AbstractScriptBreakpoint implements IScriptExceptionBreakpoint { private static final String SCRIPT_EXCEPTION_BREAKPOINT = "org.eclipse.dltk.debug.scriptExceptionBreakpointMarker"; //$NON-NLS-1$ /** * Breakpoint attribute storing the fully qualified name of the type this * breakpoint is located in. (value * <code>"org.eclipse.jdt.debug.core.typeName"</code>). This attribute is a * <code>String</code>. */ protected static final String TYPE_NAME = DLTKDebugPlugin.PLUGIN_ID + ".typeName"; //$NON-NLS-1$ /** * Exception breakpoint attribute storing the suspend on caught value (value * <code>"org.eclipse.dltk.debug.core.caught"</code>). This attribute is * stored as a <code>boolean</code>. When this attribute is * <code>true</code>, a caught exception of the associated type will cause * excecution to suspend . */ protected static final String CAUGHT = DLTKDebugPlugin.PLUGIN_ID + ".caught"; //$NON-NLS-1$ /** * Exception breakpoint attribute storing the suspend on uncaught value * (value <code>"org.eclipse.dltk.debug.core.uncaught"</code>). This * attribute is stored as a <code>boolean</code>. When this attribute is * <code>true</code>, an uncaught exception of the associated type will * cause excecution to suspend. */ protected static final String UNCAUGHT = DLTKDebugPlugin.PLUGIN_ID + ".uncaught"; //$NON-NLS-1$ /** * Allows the user to specify whether we should suspend if subclasses of the * specified exception are thrown/caught */ protected static final String SUSPEND_ON_SUBCLASSES = DLTKDebugPlugin.PLUGIN_ID + ".suspend_on_subclasses"; //$NON-NLS-1$ /** * Name of the exception that was actually hit (could be a subtype of the * type that is being caught). */ protected String fExceptionName = null; public ScriptExceptionBreakpoint() { } /** * Creates and returns an exception breakpoint for the given type. Caught * and uncaught specify where the exception should cause thread suspensions * - that is, in caught and/or uncaught locations. Checked indicates if the * given exception is a checked exception. * * @param resource * the resource on which to create the associated breakpoint * marker * @param exceptionName * the fully qualified name of the exception for which to create * the breakpoint * @param caught * whether to suspend in caught locations * @param uncaught * whether to suspend in uncaught locations * @param checked * whether the exception is a checked exception * @param add * whether to add this breakpoint to the breakpoint manager * @return a Java exception breakpoint * @exception DebugException * if unable to create the associated marker due to a lower * level exception. */ public ScriptExceptionBreakpoint(final String debugModelId, final IResource resource, final String exceptionName, final boolean caught, final boolean uncaught, final boolean add, final Map attributes) throws DebugException { IWorkspaceRunnable wr = monitor -> { // create the marker setMarker(resource.createMarker(SCRIPT_EXCEPTION_BREAKPOINT)); // add general breakpoint attributes addScriptBreakpointAttributes(attributes, debugModelId, true); // add exception breakpoint attributes attributes.put(TYPE_NAME, exceptionName); attributes.put(CAUGHT, Boolean.valueOf(caught)); attributes.put(UNCAUGHT, Boolean.valueOf(uncaught)); ensureMarker().setAttributes(attributes); register(add); }; run(getMarkerRule(resource), wr); } /** * Enable this exception breakpoint. * * If the exception breakpoint is not catching caught or uncaught, turn both * modes on. If this isn't done, the resulting state (enabled with caught * and uncaught both disabled) is ambiguous. */ @Override public void setEnabled(boolean enabled) throws CoreException { if (enabled) { if (!(isCaught() || isUncaught())) { setAttributes(new String[] { CAUGHT, UNCAUGHT }, new Object[] { Boolean.TRUE, Boolean.TRUE }); } } super.setEnabled(enabled); } @Override public boolean isCaught() throws CoreException { return ensureMarker().getAttribute(CAUGHT, false); } @Override public void setCaught(boolean caught) throws CoreException { if (caught == isCaught()) { return; } setAttribute(CAUGHT, caught); if (caught && !isEnabled()) { setEnabled(true); } else if (!(caught || isUncaught())) { setEnabled(false); } } @Override public void setSuspendOnSubclasses(boolean suspend) throws CoreException { if (suspend != isSuspendOnSubclasses()) { setAttribute(SUSPEND_ON_SUBCLASSES, suspend); } } @Override public boolean isSuspendOnSubclasses() throws CoreException { return ensureMarker().getAttribute(SUSPEND_ON_SUBCLASSES, false); } @Override public boolean isUncaught() throws CoreException { return ensureMarker().getAttribute(UNCAUGHT, false); } @Override public void setUncaught(boolean uncaught) throws CoreException { if (uncaught == isUncaught()) { return; } setAttribute(UNCAUGHT, uncaught); if (uncaught && !isEnabled()) { setEnabled(true); } else if (!(uncaught || isCaught())) { setEnabled(false); } } /** * Sets the name of the exception that was last hit * * @param name * fully qualified exception name */ protected void setExceptionTypeName(String name) { fExceptionName = name; } @Override public String getExceptionTypeName() { return fExceptionName; } private static final String[] UPDATABLE_ATTRS = new String[] { IBreakpoint.ENABLED, AbstractScriptBreakpoint.HIT_CONDITION, AbstractScriptBreakpoint.HIT_VALUE, CAUGHT, UNCAUGHT, SUSPEND_ON_SUBCLASSES }; @Override public String[] getUpdatableAttributes() { return UPDATABLE_ATTRS; } @Override public String getTypeName() throws CoreException { return (String) ensureMarker().getAttribute(TYPE_NAME); } }