org.eclipse.jdt.internal.debug.ui.actions.StepIntoSelectionHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.debug.ui.actions.StepIntoSelectionHandler.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.debug.ui.actions;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventFilter;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;

/**
 * Handles stepping into a selected method, for a specific thread.
 */
public class StepIntoSelectionHandler implements IDebugEventFilter {

    /**
     * The method to step into
     */
    private IMethod fMethod;

    /**
     * Resolved signature of the method to step into
     */
    private String fResolvedSignature;

    /**
     * The thread in which to step
     */
    private IJavaThread fThread;

    /**
     * The initial stack frame
     */
    private String fOriginalName;
    private String fOriginalSignature;
    private String fOriginalTypeName;
    private int fOriginalStackDepth;

    /**
     * Whether this is the first step into.
     */
    private boolean fFirstStep = true;

    /**
     * The state of step filters before the step.
     */
    private boolean fStepFilterEnabledState;

    /**
     * Expected event kind
     */
    private int fExpectedKind = -1;

    /**
     * Expected event detail
     */
    private int fExpectedDetail = -1;

    /**
     * Constructs a step handler to step into the given method in the given thread
     * starting from the given stack frame.
     */
    public StepIntoSelectionHandler(IJavaThread thread, IJavaStackFrame frame, IMethod method) {
        fMethod = method;
        fThread = thread;
        try {
            fOriginalName = frame.getName();
            fOriginalSignature = frame.getSignature();
            fOriginalTypeName = frame.getDeclaringTypeName();
            if (method.isBinary()) {
                fResolvedSignature = method.getSignature();
            } else {
                fResolvedSignature = ToggleBreakpointAdapter.resolveMethodSignature(method);
            }
        } catch (CoreException e) {
            JDIDebugUIPlugin.log(e);
        }
    }

    /**
     * Returns the target thread for the step.
     * 
     * @return the target thread for the step
     */
    protected IJavaThread getThread() {
        return fThread;
    }

    protected IJavaDebugTarget getDebugTarget() {
        return (IJavaDebugTarget) getThread().getDebugTarget();
    }

    /**
     * Returns the method to step into
     * 
     * @return the method to step into
     */
    protected IMethod getMethod() {
        return fMethod;
    }

    /**
     * Returns the resolved signature of the method to step into
     * 
     * @return the resolved signature of the method to step into
     */
    protected String getSignature() {
        return fResolvedSignature;
    }

    /**
     * @see org.eclipse.debug.core.IDebugEventFilter#filterDebugEvents(org.eclipse.debug.core.DebugEvent)
     */
    public DebugEvent[] filterDebugEvents(DebugEvent[] events) {
        // we only expect one event from our thread - find the event
        DebugEvent event = null;
        int index = -1;
        int threadEvents = 0;
        for (int i = 0; i < events.length; i++) {
            DebugEvent e = events[i];
            if (isExpectedEvent(e)) {
                event = e;
                index = i;
                threadEvents++;
            } else if (e.getSource() == getThread()) {
                threadEvents++;
            }
        }

        if (event == null) {
            // nothing to process in this event set
            return events;
        }

        // create filtered event set
        DebugEvent[] filtered = new DebugEvent[events.length - 1];
        if (filtered.length > 0) {
            int j = 0;
            for (int i = 0; i < events.length; i++) {
                if (i != index) {
                    filtered[j] = events[i];
                    j++;
                }
            }
        }

        // if more than one event in our thread, abort (filtering our event)
        if (threadEvents > 1) {
            cleanup();
            return filtered;
        }

        // we have the one expected event - process it
        switch (event.getKind()) {
        case DebugEvent.RESUME:
            // next, we expect a step end
            setExpectedEvent(DebugEvent.SUSPEND, DebugEvent.STEP_END);
            if (fFirstStep) {
                fFirstStep = false;
                return events; // include the first resume event
            }
            // secondary step - filter the event
            return filtered;
        case DebugEvent.SUSPEND:
            // compare location to desired location
            try {
                final IJavaStackFrame frame = (IJavaStackFrame) getThread().getTopStackFrame();
                int stackDepth = frame.getThread().getStackFrames().length;
                String name = null;
                if (frame.isConstructor()) {
                    name = frame.getDeclaringTypeName();
                    index = name.lastIndexOf('.');
                    if (index >= 0) {
                        name = name.substring(index + 1);
                    }
                } else {
                    name = frame.getName();
                }
                if (name.equals(getMethod().getElementName()) && frame.getSignature().equals(getSignature())) {
                    // hit
                    cleanup();
                    return events;
                }
                // step again
                Runnable r = null;
                if (stackDepth > fOriginalStackDepth) {
                    if (frame.isSynthetic()) {
                        // step thru synthetic methods
                        r = new Runnable() {
                            public void run() {
                                try {
                                    setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_INTO);
                                    frame.stepInto();
                                } catch (DebugException e) {
                                    JDIDebugUIPlugin.log(e);
                                    cleanup();
                                    DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] {
                                            new DebugEvent(getDebugTarget(), DebugEvent.CHANGE) });
                                }
                            }
                        };
                    } else {
                        r = new Runnable() {
                            public void run() {
                                try {
                                    setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_RETURN);
                                    frame.stepReturn();
                                } catch (DebugException e) {
                                    JDIDebugUIPlugin.log(e);
                                    cleanup();
                                    DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] {
                                            new DebugEvent(getDebugTarget(), DebugEvent.CHANGE) });
                                }
                            }
                        };
                    }
                } else if (stackDepth == fOriginalStackDepth) {
                    // we should be back in the original stack frame - if not, abort
                    if (!(frame.getSignature().equals(fOriginalSignature) && frame.getName().equals(fOriginalName)
                            && frame.getDeclaringTypeName().equals(fOriginalTypeName))) {
                        missed();
                        return events;
                    }
                    r = new Runnable() {
                        public void run() {
                            try {
                                setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_INTO);
                                frame.stepInto();
                            } catch (DebugException e) {
                                JDIDebugUIPlugin.log(e);
                                cleanup();
                                DebugPlugin.getDefault().fireDebugEventSet(
                                        new DebugEvent[] { new DebugEvent(getDebugTarget(), DebugEvent.CHANGE) });
                            }
                        }
                    };
                } else {
                    // we returned from the original frame - never hit the desired method
                    missed();
                    return events;
                }
                DebugPlugin.getDefault().asyncExec(r);
                // filter the events
                return filtered;
            } catch (CoreException e) {
                // abort
                JDIDebugUIPlugin.log(e);
                cleanup();
                return events;
            }
        }
        // execution should not reach here
        return events;

    }

    /** 
     * Called when stepping returned from the original frame without entering the desired method.
     */
    protected void missed() {
        cleanup();
        Runnable r = new Runnable() {
            public void run() {
                String methodName = null;
                try {
                    methodName = Signature.toString(getMethod().getSignature(), getMethod().getElementName(),
                            getMethod().getParameterNames(), false, false);
                } catch (JavaModelException e) {
                    methodName = getMethod().getElementName();
                }
                new MessageDialog(JDIDebugUIPlugin.getActiveWorkbenchShell(),
                        ActionMessages.StepIntoSelectionHandler_1, null,
                        NLS.bind(
                                ActionMessages.StepIntoSelectionHandler_Execution_did_not_enter____0____before_the_current_method_returned__1,
                                new String[] { methodName }),
                        MessageDialog.INFORMATION, new String[] { ActionMessages.StepIntoSelectionHandler_2 }, 0)
                                .open();
            }
        };
        JDIDebugUIPlugin.getStandardDisplay().asyncExec(r);
    }

    /**
     * Performs the step.
     */
    public void step() {
        // add event filter and turn off step filters
        DebugPlugin.getDefault().addDebugEventFilter(this);
        fStepFilterEnabledState = getDebugTarget().isStepFiltersEnabled();
        getDebugTarget().setStepFiltersEnabled(false);
        try {
            fOriginalStackDepth = getThread().getStackFrames().length;
            setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_INTO);
            getThread().stepInto();
        } catch (DebugException e) {
            JDIDebugUIPlugin.log(e);
            cleanup();
            DebugPlugin.getDefault()
                    .fireDebugEventSet(new DebugEvent[] { new DebugEvent(getDebugTarget(), DebugEvent.CHANGE) });
        }
    }

    /**
     * Cleans up when the step is complete/aborted.
     */
    protected void cleanup() {
        DebugPlugin.getDefault().removeDebugEventFilter(this);
        // restore step filter state
        getDebugTarget().setStepFiltersEnabled(fStepFilterEnabledState);
    }

    /**
     * Sets the expected debug event kind and detail we are waiting for next.
     * 
     * @param kind event kind
     * @param detail event detail
     */
    private void setExpectedEvent(int kind, int detail) {
        fExpectedKind = kind;
        fExpectedDetail = detail;
    }

    /**
     * Returns whether the given event is what we expected.
     * 
     * @param event fire event
     * @return whether the event is what we expected
     */
    protected boolean isExpectedEvent(DebugEvent event) {
        return event.getSource().equals(getThread()) && event.getKind() == fExpectedKind
                && event.getDetail() == fExpectedDetail;
    }
}