1. added "copy to clipboard" action to thread dump console
[fedora-idea.git] / java / java-impl / src / com / intellij / unscramble / UnscrambleDialog.java
blob297ffd12876fbdfe8d868cfb3a363a9c3b135fa4
1 /**
2 * @author cdr
3 */
4 package com.intellij.unscramble;
6 import com.intellij.execution.ui.ConsoleView;
7 import com.intellij.ide.IdeBundle;
8 import com.intellij.ide.util.PropertiesComponent;
9 import com.intellij.openapi.actionSystem.DefaultActionGroup;
10 import com.intellij.openapi.extensions.Extensions;
11 import com.intellij.openapi.fileChooser.FileChooser;
12 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
13 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
14 import com.intellij.openapi.help.HelpManager;
15 import com.intellij.openapi.project.Project;
16 import com.intellij.openapi.ui.DialogWrapper;
17 import com.intellij.openapi.util.Comparing;
18 import com.intellij.openapi.util.io.FileUtil;
19 import com.intellij.openapi.vfs.VirtualFile;
20 import com.intellij.ui.GuiUtils;
21 import com.intellij.ui.TextFieldWithHistory;
22 import com.intellij.util.ArrayUtil;
23 import com.intellij.util.text.CharArrayUtil;
24 import org.jetbrains.annotations.NonNls;
25 import org.jetbrains.annotations.Nullable;
27 import javax.swing.*;
28 import java.awt.*;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
35 public class UnscrambleDialog extends DialogWrapper{
36 @NonNls private static final String PROPERTY_LOG_FILE_HISTORY_URLS = "UNSCRAMBLE_LOG_FILE_URL";
37 @NonNls private static final String PROPERTY_LOG_FILE_LAST_URL = "UNSCRAMBLE_LOG_FILE_LAST_URL";
38 @NonNls private static final String PROPERTY_UNSCRAMBLER_NAME_USED = "UNSCRAMBLER_NAME_USED";
40 private final Project myProject;
41 private JPanel myEditorPanel;
42 private JPanel myLogFileChooserPanel;
43 private JComboBox myUnscrambleChooser;
44 private JPanel myPanel;
45 private TextFieldWithHistory myLogFile;
46 private JCheckBox myUseUnscrambler;
47 private JPanel myUnscramblePanel;
48 protected AnalyzeStacktraceUtil.StacktraceEditorPanel myStacktraceEditorPanel;
50 public UnscrambleDialog(Project project) {
51 super(false);
52 myProject = project;
54 populateRegisteredUnscramblerList();
55 myUnscrambleChooser.addActionListener(new ActionListener() {
56 public void actionPerformed(ActionEvent e) {
57 UnscrambleSupport unscrambleSupport = getSelectedUnscrambler();
58 GuiUtils.enableChildren(myLogFileChooserPanel, unscrambleSupport != null);
60 });
61 myUseUnscrambler.addActionListener(new ActionListener() {
62 public void actionPerformed(ActionEvent e) {
63 useUnscramblerChanged();
65 });
66 createLogFileChooser();
67 createEditor();
68 reset();
70 setTitle(IdeBundle.message("unscramble.dialog.title"));
71 init();
74 private void useUnscramblerChanged() {
75 boolean selected = myUseUnscrambler.isSelected();
76 GuiUtils.enableChildren(myUnscramblePanel, selected, myUseUnscrambler);
79 private void reset() {
80 final List<String> savedUrls = getSavedLogFileUrls();
81 myLogFile.setHistorySize(10);
82 myLogFile.setHistory(savedUrls);
84 String lastUrl = getLastUsedLogUrl();
85 if (lastUrl == null && !savedUrls.isEmpty()) {
86 lastUrl = savedUrls.get(savedUrls.size() - 1);
88 if (lastUrl != null) {
89 myLogFile.setText(lastUrl);
90 myLogFile.setSelectedItem(lastUrl);
92 final UnscrambleSupport selectedUnscrambler = getSavedUnscrambler();
94 final int count = myUnscrambleChooser.getItemCount();
95 int index = 0;
96 if (selectedUnscrambler != null) {
97 for (int i = 0; i < count; i++) {
98 final UnscrambleSupport unscrambleSupport = (UnscrambleSupport)myUnscrambleChooser.getItemAt(i);
99 if (unscrambleSupport != null && Comparing.strEqual(unscrambleSupport.getPresentableName(), selectedUnscrambler.getPresentableName())) {
100 index = i;
101 break;
105 if (count > 0) {
106 myUseUnscrambler.setEnabled(true);
107 myUnscrambleChooser.setSelectedIndex(index);
108 myUseUnscrambler.setSelected(selectedUnscrambler != null);
110 else {
111 myUseUnscrambler.setEnabled(false);
114 useUnscramblerChanged();
115 myStacktraceEditorPanel.pasteTextFromClipboard();
118 public static String getLastUsedLogUrl() {
119 return PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_LAST_URL);
122 @Nullable
123 public static UnscrambleSupport getSavedUnscrambler() {
124 final List<UnscrambleSupport> registeredUnscramblers = getRegisteredUnscramblers();
125 final String savedUnscramblerName = PropertiesComponent.getInstance().getValue(PROPERTY_UNSCRAMBLER_NAME_USED);
126 UnscrambleSupport selectedUnscrambler = null;
127 for (final UnscrambleSupport unscrambleSupport : registeredUnscramblers) {
128 if (Comparing.strEqual(unscrambleSupport.getPresentableName(), savedUnscramblerName)) {
129 selectedUnscrambler = unscrambleSupport;
132 return selectedUnscrambler;
135 public static List<String> getSavedLogFileUrls() {
136 final List<String> res = new ArrayList<String>();
137 final String savedUrl = PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_HISTORY_URLS);
138 final String[] strings = savedUrl == null ? ArrayUtil.EMPTY_STRING_ARRAY : savedUrl.split(":::");
139 for (int i = 0; i != strings.length; ++i) {
140 res.add(strings[i]);
142 return res;
145 @Nullable
146 private UnscrambleSupport getSelectedUnscrambler() {
147 if (!myUseUnscrambler.isSelected()) return null;
148 return (UnscrambleSupport)myUnscrambleChooser.getSelectedItem();
151 private void createEditor() {
152 myStacktraceEditorPanel = AnalyzeStacktraceUtil.createEditorPanel(myProject, myDisposable);
153 myEditorPanel.setLayout(new BorderLayout());
154 myEditorPanel.add(myStacktraceEditorPanel, BorderLayout.CENTER);
157 protected Action[] createActions(){
158 return new Action[]{new NormalizeTextAction(), getOKAction(), getCancelAction(), getHelpAction()};
161 private void createLogFileChooser() {
162 myLogFile = new TextFieldWithHistory();
163 JPanel panel = GuiUtils.constructFieldWithBrowseButton(myLogFile, new ActionListener() {
164 public void actionPerformed(ActionEvent e) {
165 FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
166 VirtualFile[] files = FileChooser.chooseFiles(myLogFile, descriptor);
167 if (files.length != 0) {
168 myLogFile.setText(FileUtil.toSystemDependentName(files[files.length-1].getPath()));
172 myLogFileChooserPanel.setLayout(new BorderLayout());
173 myLogFileChooserPanel.add(panel, BorderLayout.CENTER);
176 private void populateRegisteredUnscramblerList() {
177 List<UnscrambleSupport> unscrambleComponents = getRegisteredUnscramblers();
179 //myUnscrambleChooser.addItem(null);
180 for (final UnscrambleSupport unscrambleSupport : unscrambleComponents) {
181 myUnscrambleChooser.addItem(unscrambleSupport);
183 myUnscrambleChooser.setRenderer(new DefaultListCellRenderer() {
184 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
185 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
186 UnscrambleSupport unscrambleSupport = (UnscrambleSupport)value;
187 setText(unscrambleSupport == null ? IdeBundle.message("unscramble.no.unscrambler.item") : unscrambleSupport.getPresentableName());
188 return this;
193 private static List<UnscrambleSupport> getRegisteredUnscramblers() {
194 final UnscrambleSupport[] components = Extensions.getExtensions(UnscrambleSupport.EP_NAME);
195 return Arrays.asList(components);
198 protected JComponent createCenterPanel() {
199 return myPanel;
202 public void dispose() {
203 if (isOK()){
204 final List list = myLogFile.getHistory();
205 String res = null;
206 for (Object aList : list) {
207 final String s = (String)aList;
208 if (res == null) {
209 res = s;
211 else {
212 res = res + ":::" + s;
215 PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_HISTORY_URLS, res);
216 UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
217 PropertiesComponent.getInstance().setValue(PROPERTY_UNSCRAMBLER_NAME_USED, selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName());
219 PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_LAST_URL, myLogFile.getText());
221 super.dispose();
224 public void setText(String trace) {
225 myStacktraceEditorPanel.setText(trace);
228 private final class NormalizeTextAction extends AbstractAction {
229 public NormalizeTextAction(){
230 putValue(Action.NAME, IdeBundle.message("unscramble.normalize.button"));
231 putValue(DEFAULT_ACTION, Boolean.FALSE);
234 public void actionPerformed(ActionEvent e){
235 String text = myStacktraceEditorPanel.getText();
236 myStacktraceEditorPanel.setText(normalizeText(text));
241 static String normalizeText(@NonNls String text) {
242 StringBuilder builder = new StringBuilder(text.length());
243 String[] lines = text.split("\n");
244 boolean first = true;
245 boolean inAuxInfo = false;
246 for (String line : lines) {
247 //noinspection HardCodedStringLiteral
248 if (!inAuxInfo && (line.startsWith("JNI global references") || line.trim().equals("Heap"))) {
249 builder.append("\n");
250 inAuxInfo = true;
252 if (inAuxInfo) {
253 builder.append(trimSuffix(line)).append("\n");
254 continue;
256 if (!first && mustHaveNewLineBefore(line)) {
257 builder.append("\n");
258 if (line.startsWith("\"")) builder.append("\n"); // Additional linebreak for thread names
260 first = false;
261 int i = builder.lastIndexOf("\n");
262 CharSequence lastLine = i == -1 ? builder : builder.subSequence(i + 1, builder.length());
263 if (lastLine.toString().matches("\\s*at") && !line.matches("\\s+.*")) builder.append(" "); // separate 'at' from file name
264 builder.append(trimSuffix(line));
266 return builder.toString();
269 private static String trimSuffix(final String line) {
270 int len = line.length();
272 while ((0 < len) && (line.charAt(len-1) <= ' ')) {
273 len--;
275 return (len < line.length()) ? line.substring(0, len) : line;
278 private static boolean mustHaveNewLineBefore(String line) {
279 final int nonws = CharArrayUtil.shiftForward(line, 0, " \t");
280 if (nonws < line.length()) {
281 line = line.substring(nonws);
284 if (line.startsWith("at")) return true; // Start of the new stackframe entry
285 if (line.startsWith("Caused")) return true; // Caused by message
286 if (line.startsWith("- locked")) return true; // "Locked a monitor" logging
287 if (line.startsWith("- waiting")) return true; // "Waiting for monitor" logging
288 if (line.startsWith("- parking to wait")) return true;
289 if (line.startsWith("java.lang.Thread.State")) return true;
290 if (line.startsWith("\"")) return true; // Start of the new thread (thread name)
292 return false;
295 protected void doOKAction() {
296 if (performUnscramble()) {
297 myLogFile.addCurrentTextToHistory();
298 close(OK_EXIT_CODE);
302 public void doHelpAction() {
303 HelpManager.getInstance().invokeHelp("find.analyzeStackTrace");
306 private boolean performUnscramble() {
307 UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
308 return showUnscrambledText(selectedUnscrambler, myLogFile.getText(), myProject, myStacktraceEditorPanel.getText());
311 static boolean showUnscrambledText(UnscrambleSupport unscrambleSupport, String logName, Project project, String textToUnscramble) {
312 String unscrambledTrace = unscrambleSupport == null ? textToUnscramble : unscrambleSupport.unscramble(project,textToUnscramble, logName);
313 if (unscrambledTrace == null) return false;
314 List<ThreadState> threadStates = ThreadDumpParser.parse(unscrambledTrace);
315 final ConsoleView consoleView = addConsole(project, threadStates);
316 AnalyzeStacktraceUtil.printStacktrace(consoleView, unscrambledTrace);
317 return true;
320 public static ConsoleView addConsole(final Project project, final List<ThreadState> threadDump) {
321 return AnalyzeStacktraceUtil.addConsole(project, threadDump.size() > 1 ? new AnalyzeStacktraceUtil.ConsoleFactory() {
322 public JComponent createConsoleComponent(ConsoleView consoleView, DefaultActionGroup toolbarActions) {
323 return new ThreadDumpPanel(project, consoleView, toolbarActions, threadDump);
325 } : null, IdeBundle.message("unscramble.unscrambled.stacktrace.tab"));
328 protected String getDimensionServiceKey(){
329 return "#com.intellij.unscramble.UnscrambleDialog";
332 public JComponent getPreferredFocusedComponent() {
333 return myStacktraceEditorPanel.getEditorComponent();