com.intellij.unscramble.UnscrambleDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.unscramble.UnscrambleDialog.java

Source

/*
 * Copyright 2000-2012 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.unscramble;

import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.configurable.VcsContentAnnotationConfigurable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.GuiUtils;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.TextFieldWithHistory;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author cdr
 */
public class UnscrambleDialog extends DialogWrapper {
    @NonNls
    private static final String PROPERTY_LOG_FILE_HISTORY_URLS = "UNSCRAMBLE_LOG_FILE_URL";
    @NonNls
    private static final String PROPERTY_LOG_FILE_LAST_URL = "UNSCRAMBLE_LOG_FILE_LAST_URL";
    @NonNls
    private static final String PROPERTY_UNSCRAMBLER_NAME_USED = "UNSCRAMBLER_NAME_USED";
    private static final Condition<ThreadState> DEADLOCK_CONDITION = new Condition<ThreadState>() {
        @Override
        public boolean value(ThreadState state) {
            return state.isDeadlocked();
        }
    };

    private final Project myProject;
    private JPanel myEditorPanel;
    private JPanel myLogFileChooserPanel;
    private JComboBox myUnscrambleChooser;
    private JPanel myPanel;
    private TextFieldWithHistory myLogFile;
    private JCheckBox myUseUnscrambler;
    private JPanel myUnscramblePanel;
    private JCheckBox myOnTheFly;
    private JPanel myBottomPanel;
    protected AnalyzeStacktraceUtil.StacktraceEditorPanel myStacktraceEditorPanel;
    private VcsContentAnnotationConfigurable myConfigurable;

    public UnscrambleDialog(Project project) {
        super(false);
        myProject = project;

        populateRegisteredUnscramblerList();
        myUnscrambleChooser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                UnscrambleSupport unscrambleSupport = getSelectedUnscrambler();
                GuiUtils.enableChildren(myLogFileChooserPanel, unscrambleSupport != null);
            }
        });
        myUseUnscrambler.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                useUnscramblerChanged();
            }
        });
        myOnTheFly.setSelected(Registry.get("analyze.exceptions.on.the.fly").asBoolean());
        myOnTheFly.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Registry.get("analyze.exceptions.on.the.fly").setValue(myOnTheFly.isSelected());
            }
        });
        createLogFileChooser();
        createEditor();
        reset();

        setTitle(IdeBundle.message("unscramble.dialog.title"));
        init();
    }

    private void useUnscramblerChanged() {
        boolean selected = myUseUnscrambler.isSelected();
        GuiUtils.enableChildren(myUnscramblePanel, selected, myUseUnscrambler);
    }

    private void reset() {
        final List<String> savedUrls = getSavedLogFileUrls();
        myLogFile.setHistorySize(10);
        myLogFile.setHistory(savedUrls);

        String lastUrl = getLastUsedLogUrl();
        if (lastUrl == null && !savedUrls.isEmpty()) {
            lastUrl = savedUrls.get(savedUrls.size() - 1);
        }
        if (lastUrl != null) {
            myLogFile.setText(lastUrl);
            myLogFile.setSelectedItem(lastUrl);
        }
        final UnscrambleSupport selectedUnscrambler = getSavedUnscrambler();

        final int count = myUnscrambleChooser.getItemCount();
        int index = 0;
        if (selectedUnscrambler != null) {
            for (int i = 0; i < count; i++) {
                final UnscrambleSupport unscrambleSupport = (UnscrambleSupport) myUnscrambleChooser.getItemAt(i);
                if (unscrambleSupport != null && Comparing.strEqual(unscrambleSupport.getPresentableName(),
                        selectedUnscrambler.getPresentableName())) {
                    index = i;
                    break;
                }
            }
        }
        if (count > 0) {
            myUseUnscrambler.setEnabled(true);
            myUnscrambleChooser.setSelectedIndex(index);
            myUseUnscrambler.setSelected(selectedUnscrambler != null);
        } else {
            myUseUnscrambler.setEnabled(false);
        }

        useUnscramblerChanged();
        myStacktraceEditorPanel.pasteTextFromClipboard();
    }

    private void createUIComponents() {
        myBottomPanel = new JPanel(new BorderLayout());
        if (ProjectLevelVcsManager.getInstance(myProject).hasActiveVcss()) {
            myConfigurable = new VcsContentAnnotationConfigurable(myProject);
            myBottomPanel.add(myConfigurable.createComponent(), BorderLayout.CENTER);
            myConfigurable.reset();
        }
    }

    public static String getLastUsedLogUrl() {
        return PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_LAST_URL);
    }

    @Nullable
    public static UnscrambleSupport getSavedUnscrambler() {
        final List<UnscrambleSupport> registeredUnscramblers = getRegisteredUnscramblers();
        final String savedUnscramblerName = PropertiesComponent.getInstance()
                .getValue(PROPERTY_UNSCRAMBLER_NAME_USED);
        UnscrambleSupport selectedUnscrambler = null;
        for (final UnscrambleSupport unscrambleSupport : registeredUnscramblers) {
            if (Comparing.strEqual(unscrambleSupport.getPresentableName(), savedUnscramblerName)) {
                selectedUnscrambler = unscrambleSupport;
            }
        }
        return selectedUnscrambler;
    }

    public static List<String> getSavedLogFileUrls() {
        final List<String> res = new ArrayList<String>();
        final String savedUrl = PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_HISTORY_URLS);
        final String[] strings = savedUrl == null ? ArrayUtil.EMPTY_STRING_ARRAY : savedUrl.split(":::");
        for (int i = 0; i != strings.length; ++i) {
            res.add(strings[i]);
        }
        return res;
    }

    @Nullable
    private UnscrambleSupport getSelectedUnscrambler() {
        if (!myUseUnscrambler.isSelected())
            return null;
        return (UnscrambleSupport) myUnscrambleChooser.getSelectedItem();
    }

    private void createEditor() {
        myStacktraceEditorPanel = AnalyzeStacktraceUtil.createEditorPanel(myProject, myDisposable);
        myEditorPanel.setLayout(new BorderLayout());
        myEditorPanel.add(myStacktraceEditorPanel, BorderLayout.CENTER);
    }

    @NotNull
    protected Action[] createActions() {
        return new Action[] { createNormalizeTextAction(), getOKAction(), getCancelAction(), getHelpAction() };
    }

    private void createLogFileChooser() {
        myLogFile = new TextFieldWithHistory();
        JPanel panel = GuiUtils.constructFieldWithBrowseButton(myLogFile, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
                FileChooser.chooseFiles(descriptor, myProject, null, new Consumer<List<VirtualFile>>() {
                    @Override
                    public void consume(List<VirtualFile> files) {
                        myLogFile.setText(FileUtil.toSystemDependentName(files.get(files.size() - 1).getPath()));
                    }
                });
            }
        });
        myLogFileChooserPanel.setLayout(new BorderLayout());
        myLogFileChooserPanel.add(panel, BorderLayout.CENTER);
    }

    private void populateRegisteredUnscramblerList() {
        List<UnscrambleSupport> unscrambleComponents = getRegisteredUnscramblers();
        for (final UnscrambleSupport unscrambleSupport : unscrambleComponents) {
            myUnscrambleChooser.addItem(unscrambleSupport);
        }
        myUnscrambleChooser.setRenderer(new ListCellRendererWrapper<UnscrambleSupport>() {
            @Override
            public void customize(JList list, UnscrambleSupport unscrambleSupport, int index, boolean selected,
                    boolean hasFocus) {
                setText(unscrambleSupport == null ? IdeBundle.message("unscramble.no.unscrambler.item")
                        : unscrambleSupport.getPresentableName());
            }
        });
    }

    private static List<UnscrambleSupport> getRegisteredUnscramblers() {
        final UnscrambleSupport[] components = Extensions.getExtensions(UnscrambleSupport.EP_NAME);
        return Arrays.asList(components);
    }

    protected JComponent createCenterPanel() {
        return myPanel;
    }

    public void dispose() {
        if (isOK()) {
            final List list = myLogFile.getHistory();
            String res = null;
            for (Object aList : list) {
                final String s = (String) aList;
                if (res == null) {
                    res = s;
                } else {
                    res = res + ":::" + s;
                }
            }
            PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_HISTORY_URLS, res);
            UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
            PropertiesComponent.getInstance().setValue(PROPERTY_UNSCRAMBLER_NAME_USED,
                    selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName());

            PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_LAST_URL, myLogFile.getText());
        }
        super.dispose();
    }

    public void setText(String trace) {
        myStacktraceEditorPanel.setText(trace);
    }

    public Action createNormalizeTextAction() {
        return new NormalizeTextAction();
    }

    private final class NormalizeTextAction extends AbstractAction {
        public NormalizeTextAction() {
            putValue(NAME, IdeBundle.message("unscramble.normalize.button"));
            putValue(DEFAULT_ACTION, Boolean.FALSE);
        }

        public void actionPerformed(ActionEvent e) {
            String text = myStacktraceEditorPanel.getText();
            myStacktraceEditorPanel.setText(normalizeText(text));
        }

    }

    public static String normalizeText(@NonNls String text) {
        StringBuilder builder = new StringBuilder(text.length());

        text = text.replaceAll("(\\S[ \\t\\x0B\\f\\r]+)(at\\s+)", "$1\n$2");
        String[] lines = text.split("\n");

        boolean first = true;
        boolean inAuxInfo = false;
        for (String line : lines) {
            //noinspection HardCodedStringLiteral
            if (!inAuxInfo && (line.startsWith("JNI global references") || line.trim().equals("Heap"))) {
                builder.append("\n");
                inAuxInfo = true;
            }
            if (inAuxInfo) {
                builder.append(trimSuffix(line)).append("\n");
                continue;
            }
            if (!first && mustHaveNewLineBefore(line)) {
                builder.append("\n");
                if (line.startsWith("\""))
                    builder.append("\n"); // Additional line break for thread names
            }
            first = false;
            int i = builder.lastIndexOf("\n");
            CharSequence lastLine = i == -1 ? builder : builder.subSequence(i + 1, builder.length());
            if (lastLine.toString().matches("\\s*at") && !line.matches("\\s+.*"))
                builder.append(" "); // separate 'at' from file name
            builder.append(trimSuffix(line));
        }
        return builder.toString();
    }

    private static String trimSuffix(final String line) {
        int len = line.length();

        while ((0 < len) && (line.charAt(len - 1) <= ' ')) {
            len--;
        }
        return (len < line.length()) ? line.substring(0, len) : line;
    }

    private static boolean mustHaveNewLineBefore(String line) {
        final int nonWs = CharArrayUtil.shiftForward(line, 0, " \t");
        if (nonWs < line.length()) {
            line = line.substring(nonWs);
        }

        if (line.startsWith("at"))
            return true; // Start of the new stack frame entry
        if (line.startsWith("Caused"))
            return true; // Caused by message
        if (line.startsWith("- locked"))
            return true; // "Locked a monitor" logging
        if (line.startsWith("- waiting"))
            return true; // "Waiting for monitor" logging
        if (line.startsWith("- parking to wait"))
            return true;
        if (line.startsWith("java.lang.Thread.State"))
            return true;
        if (line.startsWith("\""))
            return true; // Start of the new thread (thread name)

        return false;
    }

    protected void doOKAction() {
        if (myConfigurable != null && myConfigurable.isModified()) {
            try {
                myConfigurable.apply();
            } catch (ConfigurationException e) {
                setText(e.getMessage());
                return;
            }
        }
        if (performUnscramble()) {
            myLogFile.addCurrentTextToHistory();
            close(OK_EXIT_CODE);
        }
    }

    public void doHelpAction() {
        HelpManager.getInstance().invokeHelp("find.analyzeStackTrace");
    }

    private boolean performUnscramble() {
        UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
        return showUnscrambledText(selectedUnscrambler, myLogFile.getText(), myProject,
                myStacktraceEditorPanel.getText()) != null;
    }

    @Nullable
    static RunContentDescriptor showUnscrambledText(@Nullable UnscrambleSupport unscrambleSupport, String logName,
            Project project, String textToUnscramble) {
        String unscrambledTrace = unscrambleSupport == null ? textToUnscramble
                : unscrambleSupport.unscramble(project, textToUnscramble, logName);
        if (unscrambledTrace == null)
            return null;
        List<ThreadState> threadStates = ThreadDumpParser.parse(unscrambledTrace);
        return addConsole(project, threadStates, unscrambledTrace);
    }

    private static RunContentDescriptor addConsole(final Project project, final List<ThreadState> threadDump,
            String unscrambledTrace) {
        Icon icon = null;
        String message = IdeBundle.message("unscramble.unscrambled.stacktrace.tab");
        if (!threadDump.isEmpty()) {
            message = IdeBundle.message("unscramble.unscrambled.threaddump.tab");
            icon = AllIcons.Debugger.ThreadStates.Threaddump;
        } else {
            String name = getExceptionName(unscrambledTrace);
            if (name != null) {
                message = name;
                icon = AllIcons.Debugger.ThreadStates.Exception;
            }
        }
        if (ContainerUtil.find(threadDump, DEADLOCK_CONDITION) != null) {
            message = IdeBundle.message("unscramble.unscrambled.deadlock.tab");
            icon = AllIcons.Debugger.KillProcess;
        }
        return AnalyzeStacktraceUtil.addConsole(project,
                threadDump.size() > 1 ? new AnalyzeStacktraceUtil.ConsoleFactory() {
                    public JComponent createConsoleComponent(ConsoleView consoleView,
                            DefaultActionGroup toolbarActions) {
                        return new ThreadDumpPanel(project, consoleView, toolbarActions, threadDump);
                    }
                } : null, message, unscrambledTrace, icon);
    }

    protected String getDimensionServiceKey() {
        return "#com.intellij.unscramble.UnscrambleDialog";
    }

    public JComponent getPreferredFocusedComponent() {
        return myStacktraceEditorPanel.getEditorComponent();
    }

    @Nullable
    private static String getExceptionName(String unscrambledTrace) {
        @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
        BufferedReader reader = new BufferedReader(new StringReader(unscrambledTrace));
        for (int i = 0; i < 3; i++) {
            try {
                String line = reader.readLine();
                if (line == null)
                    return null;
                line = line.trim();
                String name = getExceptionAbbreviation(line);
                if (name != null)
                    return name;
            } catch (IOException e) {
                return null;
            }
        }
        return null;
    }

    @Nullable
    private static String getExceptionAbbreviation(String line) {
        int lastDelimiter = 0;
        for (int j = 0; j < line.length(); j++) {
            char c = line.charAt(j);
            if (c == '.' || c == '$') {
                lastDelimiter = j;
                continue;
            }
            if (!StringUtil.isJavaIdentifierPart(c)) {
                return null;
            }
        }
        String clazz = line.substring(lastDelimiter);
        String abbreviate = abbreviate(clazz);
        return abbreviate.length() > 1 ? abbreviate : clazz;
    }

    private static String abbreviate(String s) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (Character.isUpperCase(c)) {
                builder.append(c);
            }
        }
        return builder.toString();
    }

}