com.intellij.debugger.ui.DebuggerSessionTab.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.debugger.ui.DebuggerSessionTab.java

Source

/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.debugger.ui;

import java.util.List;

import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.debugger.DebugUIEnvironment;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.actions.DebuggerActions;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerContextListener;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerStateManager;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.ui.impl.MainWatchPanel;
import com.intellij.debugger.ui.impl.ThreadsPanel;
import com.intellij.debugger.ui.impl.VariablesPanel;
import com.intellij.debugger.ui.impl.WatchDebuggerTree;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeExpression;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.filters.ExceptionFilters;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ExecutionConsoleEx;
import com.intellij.execution.ui.RunnerLayoutUi;
import com.intellij.execution.ui.layout.PlaceInGrid;
import com.intellij.icons.AllIcons;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.content.AlertIcon;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManagerAdapter;
import com.intellij.ui.content.ContentManagerEvent;
import com.intellij.ui.content.tabs.PinToolwindowTabAction;
import com.intellij.unscramble.ThreadDumpPanel;
import com.intellij.unscramble.ThreadState;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.impl.actions.XDebuggerActions;
import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager;
import com.intellij.xdebugger.impl.ui.DebuggerSessionTabBase;
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;

public class DebuggerSessionTab extends DebuggerSessionTabBase implements Disposable {
    private static final Logger LOG = Logger.getInstance(DebuggerSessionTab.class);

    private final VariablesPanel myVariablesPanel;
    private final MainWatchPanel myWatchPanel;

    private volatile DebuggerSession myDebuggerSession;

    private final MyDebuggerStateManager myStateManager = new MyDebuggerStateManager();

    private final FramesPanel myFramesPanel;
    private final DebugUIEnvironment myDebugUIEnvironment;

    private final ThreadsPanel myThreadsPanel;
    private static final String THREAD_DUMP_CONTENT_PREFIX = "Dump";

    public DebuggerSessionTab(final Project project, final String sessionName,
            @NotNull final DebugUIEnvironment environment, @NotNull DebuggerSession debuggerSession) {
        super(project, "JavaDebugger", sessionName, debuggerSession.getSearchScope());

        myDebuggerSession = debuggerSession;
        myDebugUIEnvironment = environment;

        final DefaultActionGroup focus = new DefaultActionGroup();
        focus.add(ActionManager.getInstance().getAction("Debugger.FocusOnBreakpoint"));
        myUi.getOptions().setAdditionalFocusActions(focus);

        final DebuggerSettings debuggerSettings = DebuggerSettings.getInstance();
        if (!ApplicationManager.getApplication().isUnitTestMode()) {
            getContextManager().addListener(new DebuggerContextListener() {
                @Override
                public void changeEvent(DebuggerContextImpl newContext, int event) {
                    switch (event) {
                    case DebuggerSession.EVENT_DETACHED:
                        myUi.updateActionsNow();

                        if (XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings()
                                .isHideDebuggerOnProcessTermination()) {
                            try {
                                ExecutionManager.getInstance(project).getContentManager().hideRunContent(
                                        DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor);
                            } catch (NullPointerException e) {
                                //if we can get closeProcess after the project have been closed
                                LOG.debug(e);
                            }
                        }
                        break;
                    }
                }
            });
        }

        DefaultActionGroup topToolbar = new DefaultActionGroup();
        ActionManager actionManager = ActionManager.getInstance();
        topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP));
        topToolbar.add(actionManager.getAction(DebuggerActions.POP_FRAME),
                new Constraints(Anchor.AFTER, XDebuggerActions.STEP_OUT));
        topToolbar.add(AnSeparator.getInstance(), new Constraints(Anchor.BEFORE, DebuggerActions.POP_FRAME));
        topToolbar.add(AnSeparator.getInstance(), new Constraints(Anchor.AFTER, DebuggerActions.POP_FRAME));
        myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR);

        myWatchPanel = new MainWatchPanel(project, getContextManager());
        myFramesPanel = new FramesPanel(project, getContextManager());

        final AlertIcon breakpointAlert = new AlertIcon(AllIcons.Debugger.BreakpointAlert);

        // watches
        Content watches = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchPanel,
                XDebuggerBundle.message("debugger.session.tab" + ".watches.title"), AllIcons.Debugger.Watches,
                null);
        watches.setCloseable(false);
        watches.setAlertIcon(breakpointAlert);
        myUi.addContent(watches, 0, PlaceInGrid.right, false);

        // frames
        Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, myFramesPanel,
                XDebuggerBundle.message("debugger.session.tab" + ".frames.title"), AllIcons.Debugger.Frame,
                myFramesPanel.getFramesList());
        framesContent.setCloseable(false);
        framesContent.setAlertIcon(breakpointAlert);

        myUi.addContent(framesContent, 0, PlaceInGrid.left, false);

        // variables
        myVariablesPanel = new VariablesPanel(project, myStateManager, this);
        myVariablesPanel.getFrameTree().setAutoVariablesMode(debuggerSettings.AUTO_VARIABLES_MODE);
        Content vars = myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, myVariablesPanel,
                XDebuggerBundle.message("debugger.session.tab" + ".variables.title"), AllIcons.Debugger.Value,
                null);
        vars.setCloseable(false);
        vars.setAlertIcon(breakpointAlert);
        myUi.addContent(vars, 0, PlaceInGrid.center, false);

        // threads
        myThreadsPanel = new ThreadsPanel(project, getContextManager());
        Content threadsContent = myUi.createContent(DebuggerContentInfo.THREADS_CONTENT, myThreadsPanel,
                XDebuggerBundle.message("debugger.session" + ".tab.threads.title"), AllIcons.Debugger.Threads,
                null);
        threadsContent.setCloseable(false);
        //threadsContent.setAlertIcon(breakpointAlert);

        //final DefaultActionGroup threadsGroup = new DefaultActionGroup();
        //threadsContent.setActions(threadsGroup, ActionPlaces.DEBUGGER_TOOLBAR, threadsPanel.getThreadsTree());

        myUi.addContent(threadsContent, 0, PlaceInGrid.left, true);

        for (Content each : myUi.getContents()) {
            updateStatus(each);
        }

        myUi.addListener(new ContentManagerAdapter() {
            @Override
            public void selectionChanged(ContentManagerEvent event) {
                updateStatus(event.getContent());
            }
        }, this);

        debuggerSession.getContextManager().addListener(new DebuggerContextListener() {
            @Override
            public void changeEvent(DebuggerContextImpl newContext, int event) {
                if (!myUi.isDisposed()) {
                    attractFramesOnPause(event);
                    myStateManager.fireStateChanged(newContext, event);
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            if (!myUi.isDisposed()) {
                                myUi.updateActionsNow();
                            }
                        }
                    });
                }
            }
        });

        //    ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult();
        //    myConsole = executionResult.getExecutionConsole();
        //    myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(),
        // getSessionName(),
        //                                                      environment.getIcon());
        //    initUI(executionResult);
    }

    private static void updateStatus(final Content content) {
        if (content.getComponent() instanceof DebuggerView) {
            final DebuggerView view = (DebuggerView) content.getComponent();
            if (content.isSelected()) {
                view.setUpdateEnabled(true);
                if (view.isRefreshNeeded()) {
                    view.rebuildIfVisible(DebuggerSession.EVENT_CONTEXT);
                }
            } else {
                view.setUpdateEnabled(false);
            }
        }
    }

    public MainWatchPanel getWatchPanel() {
        return myWatchPanel;
    }

    private void initUI(ExecutionResult executionResult) {
        if (ApplicationManager.getApplication().isUnitTestMode()) {
            return;
        }

        myUi.removeContent(myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT), true);

        Content console = null;
        if (myConsole instanceof ExecutionConsoleEx) {
            ((ExecutionConsoleEx) myConsole).buildUi(myUi);
            console = myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT);
            if (console == null) {
                LOG.debug("Reuse console created with non-debug runner");
            }
        }
        if (console == null) {
            console = myUi.createContent(DebuggerContentInfo.CONSOLE_CONTENT, myConsole.getComponent(),
                    XDebuggerBundle.message("debugger.session" + ".tab.console.content.name"),
                    AllIcons.Debugger.Console, myConsole.getPreferredFocusableComponent());

            console.setCloseable(false);
            myUi.addContent(console, 1, PlaceInGrid.bottom, false);
        }
        attachNotificationTo(console);

        if (myConsole != null) {
            Disposer.register(this, myConsole);
        }

        final DefaultActionGroup consoleActions = new DefaultActionGroup();
        if (myConsole instanceof ConsoleView) {
            AnAction[] actions = ((ConsoleView) myConsole).createConsoleActions();
            for (AnAction goAction : actions) {
                consoleActions.add(goAction);
            }
        }
        console.setActions(consoleActions, ActionPlaces.DEBUGGER_TOOLBAR,
                myConsole.getPreferredFocusableComponent());

        //    myDebugUIEnvironment.initLogs(myRunContentDescriptor, myManager);

        DefaultActionGroup leftToolbar = new DefaultActionGroup();

        if (executionResult instanceof DefaultExecutionResult) {
            final AnAction[] actions = ((DefaultExecutionResult) executionResult).getRestartActions();
            if (actions != null) {
                leftToolbar.addAll(actions);
                if (actions.length > 0) {
                    leftToolbar.addSeparator();
                }
            }
        }
        final AnAction[] profileActions = executionResult.getActions();
        leftToolbar.addAll(profileActions);

        leftToolbar.add(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_LEFT_TOOLBAR_GROUP));
        if (executionResult instanceof DefaultExecutionResult) {
            AnAction[] actions = ((DefaultExecutionResult) executionResult).getAdditionalStopActions();
            for (AnAction action : actions) {
                leftToolbar.add(action, new Constraints(Anchor.AFTER, IdeActions.ACTION_STOP_PROGRAM));
            }
        }

        leftToolbar.addSeparator();
        addAction(leftToolbar, DebuggerActions.EXPORT_THREADS);
        addAction(leftToolbar, DebuggerActions.DUMP_THREADS);
        leftToolbar.addSeparator();

        leftToolbar.add(myUi.getOptions().getLayoutActions());

        final AnAction[] commonSettings = myUi.getOptions().getSettingsActionsList();
        final AnAction commonSettingsList = myUi.getOptions().getSettingsActions();

        final DefaultActionGroup settings = new DefaultActionGroup("DebuggerSettings", true) {
            @Override
            public void update(AnActionEvent e) {
                e.getPresentation().setText(ActionsBundle.message("group.XDebugger.settings.text"));
                e.getPresentation().setIcon(commonSettingsList.getTemplatePresentation().getIcon());
            }

            @Override
            public boolean isDumbAware() {
                return true;
            }
        };
        for (AnAction each : commonSettings) {
            settings.add(each);
        }
        if (commonSettings.length > 0) {
            settings.addSeparator();
        }
        settings.add(new WatchLastMethodReturnValueAction());
        settings.add(new AutoVarsSwitchAction());
        settings.addSeparator();
        addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP);

        leftToolbar.add(settings);

        leftToolbar.addSeparator();

        addActionToGroup(leftToolbar, PinToolwindowTabAction.ACTION_NAME);

        myDebugUIEnvironment.initActions(myRunContentDescriptor, leftToolbar);

        myUi.getOptions().setLeftToolbar(leftToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
    }

    private static void addAction(DefaultActionGroup group, String actionId) {
        group.add(ActionManager.getInstance().getAction(actionId));
    }

    private static void addActionToGroup(final DefaultActionGroup group, final String actionId) {
        AnAction action = ActionManager.getInstance().getAction(actionId);
        if (action != null) {
            group.add(action);
        }
    }

    @Override
    public void dispose() {
        disposeSession();
        myFramesPanel.dispose();
        myVariablesPanel.dispose();
        myWatchPanel.dispose();
        myThreadsPanel.dispose();
        myConsole = null;
        super.dispose();
    }

    private void disposeSession() {
        final DebuggerSession session = myDebuggerSession;
        myDebuggerSession = null;
        if (session != null) {
            session.dispose();
        }
    }

    @Nullable
    private DebugProcessImpl getDebugProcess() {
        final DebuggerSession session = myDebuggerSession;
        return session != null ? session.getProcess() : null;
    }

    public void reuse(DebuggerSessionTab reuseSession) {
        DebuggerTreeNodeImpl[] watches = reuseSession.getWatchPanel().getWatchTree().getWatches();

        final WatchDebuggerTree watchTree = getWatchPanel().getWatchTree();
        for (DebuggerTreeNodeImpl watch : watches) {
            watchTree.addWatch((WatchItemDescriptor) watch.getDescriptor());
        }

        DebugProcessImpl process = getDebugProcess();
        DebugProcessImpl reuseProcess = reuseSession.getDebugProcess();
        if (process != null && reuseProcess != null) {
            //process.setBreakpointsMuted(reuseProcess.areBreakpointsMuted());
        }
    }

    public String getSessionName() {
        return myDebugUIEnvironment.getEnvironment().getSessionName();
    }

    public DebuggerStateManager getContextManager() {
        return myStateManager;
    }

    @Nullable
    public TextWithImports getSelectedExpression() {
        final DebuggerSession session = myDebuggerSession;
        if (session == null || session.getState() != DebuggerSession.STATE_PAUSED) {
            return null;
        }
        JTree tree = myVariablesPanel.getFrameTree();
        if (tree == null || !tree.hasFocus()) {
            tree = myWatchPanel.getWatchTree();
            if (tree == null || !tree.hasFocus()) {
                return null;
            }
        }
        TreePath path = tree.getSelectionPath();
        if (path == null) {
            return null;
        }
        DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl) path.getLastPathComponent();
        if (node == null) {
            return null;
        }
        NodeDescriptorImpl descriptor = node.getDescriptor();
        if (!(descriptor instanceof ValueDescriptorImpl)) {
            return null;
        }
        if (descriptor instanceof WatchItemDescriptor) {
            return ((WatchItemDescriptor) descriptor).getEvaluationText();
        }
        try {
            return DebuggerTreeNodeExpression.createEvaluationText(node, getContextManager().getContext());
        } catch (EvaluateException e) {
            return null;
        }
    }

    @Nullable
    @Override
    protected RunProfile getRunProfile() {
        return myDebugUIEnvironment.getRunProfile();
    }

    private void attractFramesOnPause(final int event) {
        if (DebuggerSession.EVENT_PAUSE == event) {
            myUi.attractBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
        } else if (DebuggerSession.EVENT_RESUME == event) {
            myUi.clearAttractionBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
        }
    }

    public DebuggerSession getSession() {
        return myDebuggerSession;
    }

    public void showFramePanel() {
        myUi.selectAndFocus(myUi.findContent(DebuggerContentInfo.FRAME_CONTENT), true, false);
    }

    private static int myThreadDumpsCount = 0;
    private static int myCurrentThreadDumpId = 1;

    public static void addThreadDump(Project project, List<ThreadState> threads, final RunnerLayoutUi ui,
            DebuggerSession session) {
        final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
        consoleBuilder.filters(ExceptionFilters.getFilters(session.getSearchScope()));
        final ConsoleView consoleView = consoleBuilder.getConsole();
        final DefaultActionGroup toolbarActions = new DefaultActionGroup();
        consoleView.allowHeavyFilters();
        final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads);

        final String id = createThreadDumpContentId();
        final Content content = ui.createContent(id, panel, id, null, null);
        content.setCloseable(true);
        content.setDescription("Thread Dump");
        ui.addContent(content);
        ui.selectAndFocus(content, true, true);
        myThreadDumpsCount += 1;
        myCurrentThreadDumpId += 1;
        //Disposer.register(this, new Disposable() {
        //  @Override
        //  public void dispose() {
        //    ui.removeContent(content, true);
        //  }
        //});
        Disposer.register(content, new Disposable() {
            @Override
            public void dispose() {
                myThreadDumpsCount -= 1;
                if (myThreadDumpsCount == 0) {
                    myCurrentThreadDumpId = 1;
                }
            }
        });
        Disposer.register(content, consoleView);
        ui.selectAndFocus(content, true, false);
        if (threads.size() > 0) {
            panel.selectStackFrame(0);
        }
    }

    private static String createThreadDumpContentId() {
        return THREAD_DUMP_CONTENT_PREFIX + " #" + myCurrentThreadDumpId;
    }

    private class MyDebuggerStateManager extends DebuggerStateManager {
        @Override
        public void fireStateChanged(DebuggerContextImpl newContext, int event) {
            super.fireStateChanged(newContext, event);
        }

        @Override
        public DebuggerContextImpl getContext() {
            final DebuggerSession session = myDebuggerSession;
            return session != null ? session.getContextManager().getContext() : DebuggerContextImpl.EMPTY_CONTEXT;
        }

        @Override
        public void setState(DebuggerContextImpl context, int state, int event, String description) {
            final DebuggerSession session = myDebuggerSession;
            if (session != null) {
                session.getContextManager().setState(context, state, event, description);
            }
        }
    }

    private class AutoVarsSwitchAction extends ToggleAction {
        private volatile boolean myAutoModeEnabled;

        public AutoVarsSwitchAction() {
            super("", "", AllIcons.Debugger.AutoVariablesMode);
            myAutoModeEnabled = DebuggerSettings.getInstance().AUTO_VARIABLES_MODE;
        }

        @Override
        public void update(final AnActionEvent e) {
            super.update(e);
            final Presentation presentation = e.getPresentation();
            final boolean autoModeEnabled = (Boolean) presentation.getClientProperty(SELECTED_PROPERTY);
            presentation.setText(autoModeEnabled ? "All-Variables Mode" : "Auto-Variables Mode");
        }

        @Override
        public boolean isSelected(AnActionEvent e) {
            return myAutoModeEnabled;
        }

        @Override
        public void setSelected(AnActionEvent e, boolean enabled) {
            myAutoModeEnabled = enabled;
            DebuggerSettings.getInstance().AUTO_VARIABLES_MODE = enabled;
            myVariablesPanel.getFrameTree().setAutoVariablesMode(enabled);
        }
    }

    private class WatchLastMethodReturnValueAction extends ToggleAction {
        private volatile boolean myWatchesReturnValues;
        private final String myTextEnable;
        private final String myTextUnavailable;
        private final String myMyTextDisable;

        public WatchLastMethodReturnValueAction() {
            super("", DebuggerBundle.message("action.watch.method.return.value.description"), null);
            myWatchesReturnValues = DebuggerSettings.getInstance().WATCH_RETURN_VALUES;
            myTextEnable = DebuggerBundle.message("action.watches.method.return.value.enable");
            myMyTextDisable = DebuggerBundle.message("action.watches.method.return.value.disable");
            myTextUnavailable = DebuggerBundle.message("action.watches.method.return.value.unavailable.reason");
        }

        @Override
        public void update(final AnActionEvent e) {
            super.update(e);
            final Presentation presentation = e.getPresentation();
            final boolean watchValues = (Boolean) presentation.getClientProperty(SELECTED_PROPERTY);
            final DebugProcessImpl process = getDebugProcess();
            final String actionText = watchValues ? myMyTextDisable : myTextEnable;
            if (process != null && process.canGetMethodReturnValue()) {
                presentation.setEnabled(true);
                presentation.setText(actionText);
            } else {
                presentation.setEnabled(false);
                presentation.setText(process == null ? actionText : myTextUnavailable);
            }
        }

        @Override
        public boolean isSelected(AnActionEvent e) {
            return myWatchesReturnValues;
        }

        @Override
        public void setSelected(AnActionEvent e, boolean watch) {
            myWatchesReturnValues = watch;
            DebuggerSettings.getInstance().WATCH_RETURN_VALUES = watch;
            final DebugProcessImpl process = getDebugProcess();
            if (process != null) {
                process.setWatchMethodReturnValuesEnabled(watch);
            }
        }
    }
}