| /******************************************************************************* |
| * Copyright (c) 2000, 2019 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 |
| * Jesper Steen Moller - Enhancement 254677 - filter getters/setters |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.ui; |
| |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IMarkerDelta; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceRuleFactory; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.ICoreRunnable; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IBreakpointsListener; |
| import org.eclipse.debug.core.IDebugEventSetListener; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchListener; |
| import org.eclipse.debug.core.model.IBreakpoint; |
| import org.eclipse.debug.core.model.IDebugTarget; |
| import org.eclipse.debug.ui.DebugUITools; |
| import org.eclipse.debug.ui.sourcelookup.ISourceLookupResult; |
| import org.eclipse.jdt.core.dom.Message; |
| import org.eclipse.jdt.debug.core.IJavaBreakpoint; |
| import org.eclipse.jdt.debug.core.IJavaBreakpointListener; |
| import org.eclipse.jdt.debug.core.IJavaDebugTarget; |
| import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint; |
| import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint.SuspendOnRecurrenceStrategy; |
| import org.eclipse.jdt.debug.core.IJavaLineBreakpoint; |
| import org.eclipse.jdt.debug.core.IJavaMethodBreakpoint; |
| import org.eclipse.jdt.debug.core.IJavaMethodEntryBreakpoint; |
| import org.eclipse.jdt.debug.core.IJavaStackFrame; |
| import org.eclipse.jdt.debug.core.IJavaThread; |
| import org.eclipse.jdt.debug.core.IJavaType; |
| import org.eclipse.jdt.debug.core.IJavaWatchpoint; |
| import org.eclipse.jdt.debug.core.JDIDebugModel; |
| import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants; |
| import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; |
| import org.eclipse.jdt.internal.debug.core.breakpoints.JavaExceptionBreakpoint; |
| import org.eclipse.jdt.internal.debug.core.logicalstructures.IJavaStructuresListener; |
| import org.eclipse.jdt.internal.debug.core.logicalstructures.JavaLogicalStructures; |
| import org.eclipse.jdt.internal.debug.core.model.JDIThread; |
| import org.eclipse.jdt.internal.debug.ui.actions.JavaBreakpointPropertiesAction; |
| import org.eclipse.jdt.internal.debug.ui.breakpoints.SuspendOnCompilationErrorListener; |
| import org.eclipse.jdt.internal.debug.ui.breakpoints.SuspendOnUncaughtExceptionListener; |
| import org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookLauncher; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import com.sun.jdi.InvocationException; |
| import com.sun.jdi.ObjectReference; |
| |
| /** |
| * Manages options for the Java Debugger:<ul> |
| * <li>Suspend on compilation errors</li> |
| * <li>Suspend on uncaught exceptions</li> |
| * <li>Step filters</li> |
| * <li>Sets a system property that the Java debugger is active if |
| * there are launches that contain running debug targets. Used for Java |
| * debug action visibility. |
| * </ul> |
| */ |
| public class JavaDebugOptionsManager implements IDebugEventSetListener, IPropertyChangeListener, IJavaBreakpointListener, ILaunchListener, IBreakpointsListener, IJavaStructuresListener { |
| |
| /** |
| * Singleton options manager |
| */ |
| private static JavaDebugOptionsManager fgOptionsManager = null; |
| |
| /** |
| * Breakpoint used to suspend on uncaught exceptions |
| */ |
| private IJavaExceptionBreakpoint fSuspendOnExceptionBreakpoint = null; |
| |
| /** |
| * Breakpoint used to suspend on compilation errors |
| */ |
| private IJavaExceptionBreakpoint fSuspendOnErrorBreakpoint = null; |
| |
| /** |
| * A label provider |
| */ |
| private static ILabelProvider fLabelProvider= DebugUITools.newDebugModelPresentation(); |
| |
| /** |
| * Constants indicating whether a breakpoint |
| * is added, removed, or changed. |
| */ |
| private static final int ADDED = 0; |
| private static final int REMOVED = 1; |
| private static final int CHANGED = 2; |
| |
| /** |
| * Local cache of active step filters. |
| */ |
| private String[] fActiveStepFilters = null; |
| |
| /** |
| * Preferences that affect variable display options. |
| * |
| * @since 3.3 |
| */ |
| private static Set<String> fgDisplayOptions; |
| |
| static { |
| fgDisplayOptions = new HashSet<>(); |
| fgDisplayOptions.add(IJDIPreferencesConstants.PREF_SHOW_CHAR); |
| fgDisplayOptions.add(IJDIPreferencesConstants.PREF_SHOW_HEX); |
| fgDisplayOptions.add(IJDIPreferencesConstants.PREF_SHOW_UNSIGNED); |
| } |
| |
| /** |
| * Whether the manager has been activated |
| */ |
| private boolean fActivated = false; |
| |
| class InitJob extends Job { |
| |
| public InitJob() { |
| super(DebugUIMessages.JavaDebugOptionsManager_0); |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| MultiStatus status = new MultiStatus(JDIDebugUIPlugin.getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, "Java debug options failed to initialize", null); //$NON-NLS-1$ |
| // compilation error breakpoint |
| try { |
| IJavaExceptionBreakpoint bp = JDIDebugModel.createExceptionBreakpoint(ResourcesPlugin.getWorkspace().getRoot(),"java.lang.Error", true, true, false, false, null); //$NON-NLS-1$ |
| bp.setPersisted(false); |
| bp.addBreakpointListener(SuspendOnCompilationErrorListener.ID_COMPILATION_ERROR_LISTENER); |
| setSuspendOnCompilationErrorsBreakpoint(bp); |
| } catch (CoreException e) { |
| status.add(e.getStatus()); |
| } |
| |
| // uncaught exception breakpoint |
| try { |
| IJavaExceptionBreakpoint bp = JDIDebugModel.createExceptionBreakpoint(ResourcesPlugin.getWorkspace().getRoot(),"java.lang.Throwable", false, true, false, false, null); //$NON-NLS-1$ |
| ((JavaExceptionBreakpoint)bp).setSuspendOnSubclasses(true); |
| bp.setPersisted(false); |
| bp.addBreakpointListener(SuspendOnUncaughtExceptionListener.ID_UNCAUGHT_EXCEPTION_LISTENER); |
| setSuspendOnUncaughtExceptionBreakpoint(bp); |
| } catch (CoreException e) { |
| status.add(e.getStatus()); |
| } |
| |
| if (status.getChildren().length == 0) { |
| return Status.OK_STATUS; |
| } |
| return status; |
| } |
| } |
| |
| /** |
| * Not to be instantiated |
| * |
| * @see JavaDebugOptionsManager#getDefault(); |
| */ |
| private JavaDebugOptionsManager() { |
| } |
| |
| /** |
| * Return the default options manager |
| */ |
| public static JavaDebugOptionsManager getDefault() { |
| if (fgOptionsManager == null) { |
| fgOptionsManager = new JavaDebugOptionsManager(); |
| } |
| return fgOptionsManager; |
| } |
| |
| /** |
| * Called at startup by the Java debug ui plug-in |
| */ |
| public void startup() { |
| // lazy initialization will occur on the first launch |
| DebugPlugin debugPlugin = DebugPlugin.getDefault(); |
| debugPlugin.getLaunchManager().addLaunchListener(this); |
| debugPlugin.getBreakpointManager().addBreakpointListener(this); |
| EvaluationContextManager.startup(); |
| } |
| |
| /** |
| * Called at shutdown by the Java debug ui plug-in |
| */ |
| public void shutdown() { |
| Job.getJobManager().cancel(JavaDebugOptionsManager.class); |
| DebugPlugin debugPlugin = DebugPlugin.getDefault(); |
| debugPlugin.removeDebugEventListener(this); |
| debugPlugin.getLaunchManager().removeLaunchListener(this); |
| debugPlugin.getBreakpointManager().removeBreakpointListener(this); |
| if (!JDIDebugUIPlugin.getDefault().isShuttingDown()) { |
| //avert restoring the preference store at shutdown |
| JDIDebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); |
| } |
| JDIDebugModel.removeJavaBreakpointListener(this); |
| JavaLogicalStructures.removeStructuresListener(this); |
| System.getProperties().remove(JDIDebugUIPlugin.getUniqueIdentifier() + ".debuggerActive"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Initializes compilation error handling and suspending |
| * on uncaught exceptions. |
| */ |
| protected void initializeProblemHandling() { |
| InitJob job = new InitJob(); |
| job.setSystem(true); |
| job.schedule(); |
| } |
| |
| /** |
| * Notifies java debug targets of the given breakpoint |
| * addition or removal. |
| * |
| * @param breakpoint a breakpoint |
| * @param kind ADDED, REMOVED, or CHANGED |
| */ |
| protected void notifyTargets(IBreakpoint breakpoint, int kind) { |
| IDebugTarget[] targets = DebugPlugin.getDefault().getLaunchManager().getDebugTargets(); |
| for (int i = 0; i < targets.length; i++) { |
| if (targets[i] instanceof IJavaDebugTarget) { |
| IJavaDebugTarget target = (IJavaDebugTarget)targets[i]; |
| notifyTarget(target, breakpoint, kind); |
| } |
| } |
| } |
| |
| /** |
| * Notifies the give debug target of filter specifications |
| * |
| * @param target Java debug target |
| */ |
| protected void notifyTargetOfFilters(IJavaDebugTarget target) { |
| |
| IPreferenceStore store = JDIDebugUIPlugin.getDefault().getPreferenceStore(); |
| |
| target.setFilterConstructors(store.getBoolean(IJDIPreferencesConstants.PREF_FILTER_CONSTRUCTORS)); |
| target.setFilterStaticInitializers(store.getBoolean(IJDIPreferencesConstants.PREF_FILTER_STATIC_INITIALIZERS)); |
| target.setFilterSynthetics(store.getBoolean(IJDIPreferencesConstants.PREF_FILTER_SYNTHETICS)); |
| target.setFilterGetters(store.getBoolean(IJDIPreferencesConstants.PREF_FILTER_GETTERS)); |
| target.setFilterSetters(store.getBoolean(IJDIPreferencesConstants.PREF_FILTER_SETTERS)); |
| target.setStepThruFilters(store.getBoolean(IJDIPreferencesConstants.PREF_STEP_THRU_FILTERS)); |
| target.setStepFilters(getActiveStepFilters()); |
| |
| } |
| |
| /** |
| * Notifies all targets of current filter specifications. |
| */ |
| protected void notifyTargetsOfFilters() { |
| IDebugTarget[] targets = DebugPlugin.getDefault().getLaunchManager().getDebugTargets(); |
| for (int i = 0; i < targets.length; i++) { |
| if (targets[i] instanceof IJavaDebugTarget) { |
| IJavaDebugTarget target = (IJavaDebugTarget)targets[i]; |
| notifyTargetOfFilters(target); |
| } |
| } |
| } |
| |
| /** |
| * Notifies the given target of the given breakpoint |
| * addition or removal. |
| * |
| * @param target Java debug target |
| * @param breakpoint a breakpoint |
| * @param kind ADDED, REMOVED, or CHANGED |
| */ |
| protected void notifyTarget(IJavaDebugTarget target, IBreakpoint breakpoint, int kind) { |
| switch (kind) { |
| case ADDED: |
| target.breakpointAdded(breakpoint); |
| break; |
| case REMOVED: |
| target.breakpointRemoved(breakpoint,null); |
| break; |
| case CHANGED: |
| target.breakpointChanged(breakpoint,null); |
| break; |
| } |
| } |
| |
| /** |
| * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) |
| */ |
| @Override |
| public void propertyChange(PropertyChangeEvent event) { |
| String property = event.getProperty(); |
| if (property.equals(IJDIPreferencesConstants.PREF_SUSPEND_ON_COMPILATION_ERRORS)) { |
| IBreakpoint breakpoint = getSuspendOnCompilationErrorBreakpoint(); |
| if (breakpoint != null) { |
| int kind = REMOVED; |
| if (isSuspendOnCompilationErrors()) { |
| kind = ADDED; |
| } |
| notifyTargets(breakpoint, kind); |
| } |
| } else if (property.equals(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS)) { |
| IBreakpoint breakpoint = getSuspendOnUncaughtExceptionBreakpoint(); |
| if (breakpoint != null) { |
| int kind = REMOVED; |
| if (isSuspendOnUncaughtExceptions()) { |
| kind = ADDED; |
| } |
| notifyTargets(breakpoint, kind); |
| } |
| } else if (fgDisplayOptions.contains(property)) { |
| variableViewSettingsChanged(); |
| } else if (isUseFilterProperty(property)) { |
| notifyTargetsOfFilters(); |
| } else if (isFilterListProperty(property)) { |
| updateActiveFilters(); |
| } |
| } |
| |
| /** |
| * Returns whether the given property is a property that affects whether |
| * or not step filters are used. |
| */ |
| private boolean isUseFilterProperty(String property) { |
| return property.equals(IJDIPreferencesConstants.PREF_FILTER_CONSTRUCTORS) || |
| property.equals(IJDIPreferencesConstants.PREF_FILTER_STATIC_INITIALIZERS) || |
| property.equals(IJDIPreferencesConstants.PREF_FILTER_GETTERS) || |
| property.equals(IJDIPreferencesConstants.PREF_FILTER_SETTERS) || |
| property.equals(IJDIPreferencesConstants.PREF_FILTER_SYNTHETICS) || |
| property.equals(IJDIPreferencesConstants.PREF_STEP_THRU_FILTERS); |
| } |
| |
| /** |
| * Returns whether the given property is a property that affects |
| * the list of active or inactive step filters. |
| */ |
| private boolean isFilterListProperty(String property) { |
| return property.equals(IJDIPreferencesConstants.PREF_ACTIVE_FILTERS_LIST) || |
| property.equals(IJDIPreferencesConstants.PREF_INACTIVE_FILTERS_LIST); |
| } |
| |
| /** |
| * Enable/Disable the given breakpoint and notify |
| * targets of the change. |
| * |
| * @param breakpoint a breakpoint |
| * @param enabled whether enabeld |
| */ |
| protected void setEnabled(IBreakpoint breakpoint, boolean enabled) { |
| try { |
| breakpoint.setEnabled(enabled); |
| notifyTargets(breakpoint, CHANGED); |
| } catch (CoreException e) { |
| JDIDebugUIPlugin.log(e); |
| } |
| } |
| |
| /** |
| * Returns whether suspend on compilation errors is |
| * enabled. |
| * |
| * @return whether suspend on compilation errors is |
| * enabled |
| */ |
| public boolean isSuspendOnCompilationErrors() { |
| return JDIDebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IJDIPreferencesConstants.PREF_SUSPEND_ON_COMPILATION_ERRORS); |
| } |
| |
| /** |
| * Returns whether suspend on uncaught exception is |
| * enabled |
| * |
| * @return whether suspend on uncaught exception is |
| * enabled |
| */ |
| protected boolean isSuspendOnUncaughtExceptions() { |
| return JDIDebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS); |
| } |
| |
| /** |
| * Sets the breakpoint used to suspend on uncaught exceptions |
| * |
| * @param breakpoint exception breakpoint |
| */ |
| private void setSuspendOnUncaughtExceptionBreakpoint(IJavaExceptionBreakpoint breakpoint) { |
| fSuspendOnExceptionBreakpoint = breakpoint; |
| } |
| |
| /** |
| * Returns the breakpoint used to suspend on uncaught exceptions |
| * |
| * @return exception breakpoint |
| */ |
| protected IJavaExceptionBreakpoint getSuspendOnUncaughtExceptionBreakpoint() { |
| return fSuspendOnExceptionBreakpoint; |
| } |
| |
| /** |
| * Sets the breakpoint used to suspend on compilation |
| * errors. |
| * |
| * @param breakpoint exception breakpoint |
| */ |
| private void setSuspendOnCompilationErrorsBreakpoint(IJavaExceptionBreakpoint breakpoint) { |
| fSuspendOnErrorBreakpoint = breakpoint; |
| } |
| |
| /** |
| * Returns the breakpoint used to suspend on compilation |
| * errors |
| * |
| * @return exception breakpoint |
| */ |
| protected IJavaExceptionBreakpoint getSuspendOnCompilationErrorBreakpoint() { |
| return fSuspendOnErrorBreakpoint; |
| } |
| |
| /** |
| * Parses the comma separated string into an array of strings |
| * |
| * @return list |
| */ |
| public static String[] parseList(String listString) { |
| List<String> list = new ArrayList<>(10); |
| StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$ |
| while (tokenizer.hasMoreTokens()) { |
| String token = tokenizer.nextToken(); |
| list.add(token); |
| } |
| return list.toArray(new String[list.size()]); |
| } |
| |
| /** |
| * Serializes the array of strings into one comma |
| * separated string. |
| * |
| * @param list array of strings |
| * @return a single string composed of the given list |
| */ |
| public static String serializeList(String[] list) { |
| if (list == null) { |
| return ""; //$NON-NLS-1$ |
| } |
| StringBuilder buffer = new StringBuilder(); |
| for (int i = 0; i < list.length; i++) { |
| if (i > 0) { |
| buffer.append(','); |
| } |
| buffer.append(list[i]); |
| } |
| return buffer.toString(); |
| } |
| |
| /** |
| * Returns the current list of active step filters. |
| * |
| * @return current list of active step filters |
| */ |
| protected String[] getActiveStepFilters() { |
| if (fActiveStepFilters == null) { |
| fActiveStepFilters= parseList(JDIDebugUIPlugin.getDefault().getPreferenceStore().getString(IJDIPreferencesConstants.PREF_ACTIVE_FILTERS_LIST)); |
| // After active filters are cached, register to hear about future changes |
| JDIDebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); |
| } |
| return fActiveStepFilters; |
| } |
| |
| /** |
| * Updates local copy of active step filters and |
| * notifies targets. |
| */ |
| protected void updateActiveFilters() { |
| fActiveStepFilters= parseList(JDIDebugUIPlugin.getDefault().getPreferenceStore().getString(IJDIPreferencesConstants.PREF_ACTIVE_FILTERS_LIST)); |
| notifyTargetsOfFilters(); |
| } |
| |
| /** |
| * When a Java debug target is created, install options in |
| * the target and set that the Java debugger is active. |
| * When all Java debug targets are terminated set that that Java debugger is |
| * no longer active. |
| * |
| * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[]) |
| */ |
| @Override |
| public void handleDebugEvents(DebugEvent[] events) { |
| for (int i = 0; i < events.length; i++) { |
| DebugEvent event = events[i]; |
| if (event.getKind() == DebugEvent.CREATE) { |
| Object source = event.getSource(); |
| if (source instanceof IJavaDebugTarget) { |
| IJavaDebugTarget javaTarget = (IJavaDebugTarget)source; |
| |
| // compilation breakpoints |
| if (isSuspendOnCompilationErrors()) { |
| notifyTarget(javaTarget, getSuspendOnCompilationErrorBreakpoint(), ADDED); |
| } |
| |
| // uncaught exception breakpoint |
| if (isSuspendOnUncaughtExceptions()) { |
| ILaunchConfiguration launchConfiguration = javaTarget.getLaunch().getLaunchConfiguration(); |
| boolean isSnippetEditor = false; |
| |
| try { |
| isSnippetEditor = (launchConfiguration.getAttribute(ScrapbookLauncher.SCRAPBOOK_LAUNCH, (String)null) != null); |
| } catch (CoreException e) { |
| } |
| |
| if (!isSnippetEditor) { |
| notifyTarget(javaTarget, getSuspendOnUncaughtExceptionBreakpoint(), ADDED); |
| } |
| } |
| |
| // step filters |
| notifyTargetOfFilters(javaTarget); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @see IJavaBreakpointListener#addingBreakpoint(IJavaDebugTarget, IJavaBreakpoint) |
| */ |
| @Override |
| public void addingBreakpoint(IJavaDebugTarget target, IJavaBreakpoint breakpoint) { |
| } |
| |
| /** |
| * @see IJavaBreakpointListener#installingBreakpoint(IJavaDebugTarget, IJavaBreakpoint, IJavaType) |
| */ |
| @Override |
| public int installingBreakpoint(IJavaDebugTarget target, IJavaBreakpoint breakpoint, IJavaType type) { |
| return DONT_CARE; |
| } |
| |
| /** |
| * @see IJavaBreakpointListener#breakpointHit(IJavaThread, IJavaBreakpoint) |
| */ |
| @Override |
| public int breakpointHit(IJavaThread thread, IJavaBreakpoint breakpoint) { |
| if (thread instanceof JDIThread && breakpoint instanceof IJavaExceptionBreakpoint) { |
| try { |
| String[] breakpointListeners = breakpoint.getBreakpointListeners(); |
| if (breakpointListeners.length == 1 |
| && SuspendOnCompilationErrorListener.ID_COMPILATION_ERROR_LISTENER.equals(breakpointListeners[0])) { |
| return DONT_CARE; // not a user breakpoint |
| } |
| } catch (CoreException e1) { |
| // continue |
| } |
| if (shouldSkipSubsequentOccurrence((JDIThread) thread, (IJavaExceptionBreakpoint) breakpoint)) { |
| return DONT_SUSPEND; |
| } |
| } |
| return DONT_CARE; |
| } |
| |
| /** |
| * Figure out whether suspending on an exceptionBreakpoint should be skipped due to the user's choice regarding |
| * {@link IJavaExceptionBreakpoint#setSuspendOnRecurrenceStrategy(int)}. |
| * <p> |
| * If the current hit is indeed recurrence of an already-seen exception instance, and if the user has not yet made a choice for this breakpoint, |
| * then a question dialog will be opened to request the user's choice. |
| * </p> |
| * |
| * @param thread |
| * the thread where an exception occurred |
| * @param exceptionBreakpoint |
| * the exceptionBreakpoint that just fired |
| * @return {@code true} if the current breakpoint hit should be skipped. |
| */ |
| public boolean shouldSkipSubsequentOccurrence(JDIThread thread, IJavaExceptionBreakpoint exceptionBreakpoint) { |
| if (exceptionBreakpoint == fSuspendOnExceptionBreakpoint) { |
| // this breakpoint doesn't have the recurrence property, wait until we are called from SuspendOnUncaughtExceptionListener itself |
| return false; |
| } |
| SuspendOnRecurrenceStrategy skip = thread.shouldSkipExceptionRecurrence(exceptionBreakpoint); |
| if (skip == null) { |
| return false; // not a recurrence |
| } |
| if (skip == SuspendOnRecurrenceStrategy.RECURRENCE_UNCONFIGURED) { |
| skip = new AskRecurrenceDialog(JDIDebugUIPlugin.getShell()).getAnswer(); |
| } |
| switch (skip) { |
| case SKIP_RECURRENCES: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| private static final class AskRecurrenceDialog extends MessageDialog { |
| |
| private boolean fRememberChoice; |
| private SuspendOnRecurrenceStrategy fStrategy; |
| |
| private AskRecurrenceDialog(Shell parentShell) { |
| super(parentShell, DebugUIMessages.JavaDebugOptionsManager_exceptionRecurrence_dialogTitle, null, // |
| DebugUIMessages.JavaDebugOptionsManager_exceptionRecurrence_dialogMessage, |
| MessageDialog.QUESTION, 0, // |
| DebugUIMessages.JavaDebugOptionsManager_skip_buttonLabel, // |
| DebugUIMessages.JavaDebugOptionsManager_suspend_buttonLabel); |
| parentShell.getDisplay().syncExec(() -> open()); |
| } |
| |
| @Override |
| protected Control createCustomArea(Composite parent) { |
| Composite panel = new Composite(parent, SWT.NONE); |
| panel.setFont(parent.getFont()); |
| |
| GridLayout layout = new GridLayout(1, false); |
| layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); |
| panel.setLayout(layout); |
| |
| GridData data = new GridData(GridData.FILL_BOTH); |
| data.verticalAlignment = GridData.END; |
| panel.setLayoutData(data); |
| |
| Button button = new Button(panel, SWT.CHECK); |
| button.setText(DebugUIMessages.JavaDebugOptionsManager_exceptionRecurrence_remember_decision); |
| button.setSelection(false); |
| button.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| fRememberChoice = !fRememberChoice; |
| } |
| }); |
| return panel; |
| } |
| |
| @Override |
| public int open() { |
| int ret = super.open(); |
| switch (ret) { |
| case 0: |
| fStrategy = SuspendOnRecurrenceStrategy.SKIP_RECURRENCES; |
| break; |
| case 1: |
| fStrategy = SuspendOnRecurrenceStrategy.SUSPEND_ALWAYS; |
| break; |
| } |
| if (fRememberChoice) { |
| IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(JDIDebugPlugin.getUniqueIdentifier()); |
| if (prefs != null) { |
| prefs.put(JDIDebugModel.PREF_SUSPEND_ON_RECURRENCE_STRATEGY, fStrategy.name()); |
| } |
| } |
| return ret; |
| } |
| |
| public SuspendOnRecurrenceStrategy getAnswer() { |
| return fStrategy; |
| } |
| } |
| |
| /** |
| * @see IJavaBreakpointListener#breakpointInstalled(IJavaDebugTarget, IJavaBreakpoint) |
| */ |
| @Override |
| public void breakpointInstalled(IJavaDebugTarget target, IJavaBreakpoint breakpoint) { |
| } |
| |
| /** |
| * @see IJavaBreakpointListener#breakpointRemoved(IJavaDebugTarget, IJavaBreakpoint) |
| */ |
| @Override |
| public void breakpointRemoved(IJavaDebugTarget target, IJavaBreakpoint breakpoint) { |
| } |
| |
| /** |
| * Returns any problem marker associated with the current location |
| * of the given stack frame, or <code>null</code> if none. |
| * |
| * @param frame stack frame |
| * @return marker representing compilation problem, or <code>null</code> |
| * @throws DebugException if an exception occurrs retrieveing the problem |
| */ |
| public IMarker getProblem(IJavaStackFrame frame) { |
| ILaunch launch = frame.getLaunch(); |
| if (launch != null) { |
| ISourceLookupResult result = DebugUITools.lookupSource(frame, null); |
| Object sourceElement = result.getSourceElement(); |
| if (sourceElement instanceof IResource) { |
| try { |
| IResource resource = (IResource) sourceElement; |
| IMarker[] markers = resource.findMarkers("org.eclipse.jdt.core.problem", true, IResource.DEPTH_INFINITE); //$NON-NLS-1$ |
| int line = frame.getLineNumber(); |
| for (int i = 0; i < markers.length; i++) { |
| IMarker marker = markers[i]; |
| if (marker.getAttribute(IMarker.LINE_NUMBER, -1) == line && marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR) { |
| return marker; |
| } |
| } |
| } catch (CoreException e) { |
| } |
| } |
| } |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.debug.core.IJavaBreakpointListener#breakpointHasRuntimeException(org.eclipse.jdt.debug.core.IJavaLineBreakpoint, org.eclipse.debug.core.DebugException) |
| */ |
| @Override |
| public void breakpointHasRuntimeException(final IJavaLineBreakpoint breakpoint, final DebugException exception) { |
| IStatus status; |
| Throwable wrappedException= exception.getStatus().getException(); |
| if (wrappedException instanceof InvocationException) { |
| InvocationException ie= (InvocationException) wrappedException; |
| ObjectReference ref= ie.exception(); |
| status= new Status(IStatus.ERROR,JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, ref.referenceType().name(), null); |
| } else { |
| status= exception.getStatus(); |
| } |
| openConditionErrorDialog(breakpoint, DebugUIMessages.JavaDebugOptionsManager_Conditional_breakpoint_encountered_runtime_exception__1, status); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.debug.core.IJavaBreakpointListener#breakpointHasCompilationErrors(org.eclipse.jdt.debug.core.IJavaLineBreakpoint, org.eclipse.jdt.core.dom.Message[]) |
| */ |
| @Override |
| public void breakpointHasCompilationErrors(final IJavaLineBreakpoint breakpoint, final Message[] errors) { |
| StringBuilder message= new StringBuilder(); |
| Message error; |
| for (int i=0, numErrors= errors.length; i < numErrors; i++) { |
| error= errors[i]; |
| message.append(error.getMessage()); |
| message.append("\n "); //$NON-NLS-1$ |
| } |
| IStatus status= new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, message.toString(), null); |
| openConditionErrorDialog(breakpoint, DebugUIMessages.JavaDebugOptionsManager_Conditional_breakpoint_has_compilation_error_s___2, status); |
| } |
| |
| private void openConditionErrorDialog(final IJavaLineBreakpoint breakpoint, final String errorMessage, final IStatus status) { |
| final Display display= JDIDebugUIPlugin.getStandardDisplay(); |
| if (display.isDisposed()) { |
| return; |
| } |
| final String message= NLS.bind(errorMessage, new String[] {fLabelProvider.getText(breakpoint)}); |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (display.isDisposed()) { |
| return; |
| } |
| Shell shell= JDIDebugUIPlugin.getActiveWorkbenchShell(); |
| ConditionalBreakpointErrorDialog dialog= new ConditionalBreakpointErrorDialog(shell, message, status); |
| int result = dialog.open(); |
| if (result == Window.OK) { |
| JavaBreakpointPropertiesAction action= new JavaBreakpointPropertiesAction(); |
| action.selectionChanged(null, new StructuredSelection(breakpoint)); |
| action.run(null); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Activates this debug options manager. When active, this |
| * manager becomes a listener to many notifications and updates |
| * running debug targets based on these notifications. |
| * |
| * A debug options manager does not need to be activated until |
| * there is a running debug target. |
| */ |
| private void activate() { |
| if (fActivated) { |
| return; |
| } |
| fActivated = true; |
| initializeProblemHandling(); |
| notifyTargetsOfFilters(); |
| DebugPlugin.getDefault().addDebugEventListener(this); |
| JDIDebugModel.addJavaBreakpointListener(this); |
| JavaLogicalStructures.addStructuresListener(this); |
| } |
| |
| /** |
| * Startup problem handling on the first launch. |
| * |
| * @see ILaunchListener#launchAdded(ILaunch) |
| */ |
| @Override |
| public void launchAdded(ILaunch launch) { |
| launchChanged(launch); |
| } |
| /** |
| * @see ILaunchListener#launchChanged(ILaunch) |
| */ |
| @Override |
| public void launchChanged(ILaunch launch) { |
| activate(); |
| DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this); |
| } |
| |
| /** |
| * @see ILaunchListener#launchRemoved(ILaunch) |
| */ |
| @Override |
| public void launchRemoved(ILaunch launch) { |
| } |
| |
| /** |
| * Adds message attributes to java breakpoints. |
| * |
| * @see org.eclipse.debug.core.IBreakpointsListener#breakpointsAdded(org.eclipse.debug.core.model.IBreakpoint[]) |
| */ |
| @Override |
| public void breakpointsAdded(final IBreakpoint[] breakpoints) { |
| // if a breakpoint is added, but already has a message, do not update it |
| List<IBreakpoint> update = new ArrayList<>(); |
| for (int i = 0; i < breakpoints.length; i++) { |
| IBreakpoint breakpoint = breakpoints[i]; |
| try { |
| if (breakpoint instanceof IJavaBreakpoint && breakpoint.getMarker().getAttribute(IMarker.MESSAGE) == null) { |
| update.add(breakpoint); |
| } |
| } catch (CoreException e) { |
| JDIDebugUIPlugin.log(e); |
| } |
| } |
| if (!update.isEmpty()) { |
| updateBreakpointMessages(update.toArray(new IBreakpoint[update.size()])); |
| } |
| } |
| |
| /** |
| * Updates message attributes on the given java breakpoints. |
| * |
| * @see org.eclipse.debug.core.IBreakpointsListener#breakpointsAdded(org.eclipse.debug.core.model.IBreakpoint[]) |
| */ |
| private void updateBreakpointMessages(final IBreakpoint[] breakpoints) { |
| IWorkspaceRunnable runnable = new IWorkspaceRunnable() { |
| @Override |
| public void run(IProgressMonitor monitor) throws CoreException { |
| for (int i = 0; i < breakpoints.length; i++) { |
| IBreakpoint breakpoint = breakpoints[i]; |
| if (breakpoint instanceof IJavaBreakpoint) { |
| String info = fLabelProvider.getText(breakpoint); |
| String type = DebugUIMessages.JavaDebugOptionsManager_Breakpoint___1; |
| if (breakpoint instanceof IJavaMethodBreakpoint || breakpoint instanceof IJavaMethodEntryBreakpoint) { |
| type = DebugUIMessages.JavaDebugOptionsManager_Method_breakpoint___2; |
| } else if (breakpoint instanceof IJavaWatchpoint) { |
| type = DebugUIMessages.JavaDebugOptionsManager_Watchpoint___3; |
| } else if (breakpoint instanceof IJavaLineBreakpoint) { |
| type = DebugUIMessages.JavaDebugOptionsManager_Line_breakpoint___4; |
| } |
| IMarker marker = breakpoint.getMarker(); |
| if (marker.exists()) { |
| marker.setAttribute(IMarker.MESSAGE, type + info); |
| } |
| } |
| } |
| } |
| }; |
| |
| final ISchedulingRule modifyWorkspaceRule = modifyWorkspaceRule(); |
| |
| ISchedulingRule currentRule = Job.getJobManager().currentRule(); |
| if (currentRule != null && currentRule.contains(modifyWorkspaceRule)) { |
| try { |
| ResourcesPlugin.getWorkspace().run(runnable, null, 0, null); |
| } catch (CoreException e) { |
| JDIDebugUIPlugin.log(e); |
| } |
| } else { |
| UpdateBreakpointMessagesJob updateBreakpointMessagesJob = new UpdateBreakpointMessagesJob(modifyWorkspaceRule, runnable); |
| updateBreakpointMessagesJob.schedule(); |
| } |
| } |
| |
| private static ISchedulingRule modifyWorkspaceRule() { |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| IWorkspaceRoot workspaceRoot = workspace.getRoot(); |
| IResourceRuleFactory rootFactory = workspace.getRuleFactory(); |
| ISchedulingRule modifyWorkspaceRule = rootFactory.modifyRule(workspaceRoot); |
| return modifyWorkspaceRule; |
| } |
| |
| private static class UpdateBreakpointMessagesJob extends Job { |
| |
| private ISchedulingRule rule; |
| private ICoreRunnable runnable; |
| |
| UpdateBreakpointMessagesJob(ISchedulingRule rule, ICoreRunnable runnable) { |
| super("Updating breakpoint messages"); //$NON-NLS-1$ |
| this.rule = rule; |
| this.runnable = runnable; |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| try { |
| ResourcesPlugin.getWorkspace().run(runnable, rule, 0, monitor); |
| } catch (CoreException e) { |
| String errorMessage = "Failed to update breakpoint messages"; //$NON-NLS-1$ |
| IStatus errorStatus = new Status(IStatus.ERROR, IJavaDebugUIConstants.PLUGIN_ID, errorMessage, e); |
| return errorStatus; |
| } |
| return Status.OK_STATUS; |
| } |
| |
| @Override |
| public boolean belongsTo(Object family) { |
| return JavaDebugOptionsManager.class == family; |
| } |
| } |
| |
| /** |
| * Updates message attributes on java breakpoints. |
| * |
| * @see org.eclipse.debug.core.IBreakpointsListener#breakpointsChanged(org.eclipse.debug.core.model.IBreakpoint[], org.eclipse.core.resources.IMarkerDelta[]) |
| */ |
| @Override |
| public void breakpointsChanged( |
| IBreakpoint[] breakpoints, |
| IMarkerDelta[] deltas) { |
| updateBreakpointMessages(breakpoints); |
| |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.IBreakpointsListener#breakpointsRemoved(org.eclipse.debug.core.model.IBreakpoint[], org.eclipse.core.resources.IMarkerDelta[]) |
| */ |
| @Override |
| public void breakpointsRemoved( |
| IBreakpoint[] breakpoints, |
| IMarkerDelta[] deltas) { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.internal.debug.core.logicalstructures.IJavaStructuresListener#logicalStructuresChanged() |
| */ |
| @Override |
| public void logicalStructuresChanged() { |
| variableViewSettingsChanged(); |
| } |
| |
| /** |
| * Refreshes the variables view by firing a change event on a stack frame (active |
| * debug context). |
| */ |
| protected void variableViewSettingsChanged() { |
| // If a Java stack frame is selected in the Debug view, fire a change event on |
| // it so the variables view will update for any structure changes. |
| IAdaptable selected = DebugUITools.getDebugContext(); |
| if (selected != null) { |
| IJavaStackFrame frame = selected.getAdapter(IJavaStackFrame.class); |
| if (frame != null) { |
| DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { |
| new DebugEvent(frame, DebugEvent.CHANGE) |
| }); |
| } |
| } |
| } |
| |
| } |