ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / java / java-impl / src / com / intellij / unscramble / UnscrambleDialog.java
blob32325bc8102abeffac6671a427f0f9de35a5ab86
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /**
18 * @author cdr
20 package com.intellij.unscramble;
22 import com.intellij.execution.ui.ConsoleView;
23 import com.intellij.ide.IdeBundle;
24 import com.intellij.ide.util.PropertiesComponent;
25 import com.intellij.openapi.actionSystem.DefaultActionGroup;
26 import com.intellij.openapi.extensions.Extensions;
27 import com.intellij.openapi.fileChooser.FileChooser;
28 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
29 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
30 import com.intellij.openapi.help.HelpManager;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.ui.DialogWrapper;
33 import com.intellij.openapi.util.Comparing;
34 import com.intellij.openapi.util.io.FileUtil;
35 import com.intellij.openapi.vfs.VirtualFile;
36 import com.intellij.ui.GuiUtils;
37 import com.intellij.ui.TextFieldWithHistory;
38 import com.intellij.util.ArrayUtil;
39 import com.intellij.util.text.CharArrayUtil;
40 import org.jetbrains.annotations.NonNls;
41 import org.jetbrains.annotations.Nullable;
43 import javax.swing.*;
44 import java.awt.*;
45 import java.awt.event.ActionEvent;
46 import java.awt.event.ActionListener;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.List;
51 public class UnscrambleDialog extends DialogWrapper{
52 @NonNls private static final String PROPERTY_LOG_FILE_HISTORY_URLS = "UNSCRAMBLE_LOG_FILE_URL";
53 @NonNls private static final String PROPERTY_LOG_FILE_LAST_URL = "UNSCRAMBLE_LOG_FILE_LAST_URL";
54 @NonNls private static final String PROPERTY_UNSCRAMBLER_NAME_USED = "UNSCRAMBLER_NAME_USED";
56 private final Project myProject;
57 private JPanel myEditorPanel;
58 private JPanel myLogFileChooserPanel;
59 private JComboBox myUnscrambleChooser;
60 private JPanel myPanel;
61 private TextFieldWithHistory myLogFile;
62 private JCheckBox myUseUnscrambler;
63 private JPanel myUnscramblePanel;
64 protected AnalyzeStacktraceUtil.StacktraceEditorPanel myStacktraceEditorPanel;
66 public UnscrambleDialog(Project project) {
67 super(false);
68 myProject = project;
70 populateRegisteredUnscramblerList();
71 myUnscrambleChooser.addActionListener(new ActionListener() {
72 public void actionPerformed(ActionEvent e) {
73 UnscrambleSupport unscrambleSupport = getSelectedUnscrambler();
74 GuiUtils.enableChildren(myLogFileChooserPanel, unscrambleSupport != null);
76 });
77 myUseUnscrambler.addActionListener(new ActionListener() {
78 public void actionPerformed(ActionEvent e) {
79 useUnscramblerChanged();
81 });
82 createLogFileChooser();
83 createEditor();
84 reset();
86 setTitle(IdeBundle.message("unscramble.dialog.title"));
87 init();
90 private void useUnscramblerChanged() {
91 boolean selected = myUseUnscrambler.isSelected();
92 GuiUtils.enableChildren(myUnscramblePanel, selected, myUseUnscrambler);
95 private void reset() {
96 final List<String> savedUrls = getSavedLogFileUrls();
97 myLogFile.setHistorySize(10);
98 myLogFile.setHistory(savedUrls);
100 String lastUrl = getLastUsedLogUrl();
101 if (lastUrl == null && !savedUrls.isEmpty()) {
102 lastUrl = savedUrls.get(savedUrls.size() - 1);
104 if (lastUrl != null) {
105 myLogFile.setText(lastUrl);
106 myLogFile.setSelectedItem(lastUrl);
108 final UnscrambleSupport selectedUnscrambler = getSavedUnscrambler();
110 final int count = myUnscrambleChooser.getItemCount();
111 int index = 0;
112 if (selectedUnscrambler != null) {
113 for (int i = 0; i < count; i++) {
114 final UnscrambleSupport unscrambleSupport = (UnscrambleSupport)myUnscrambleChooser.getItemAt(i);
115 if (unscrambleSupport != null && Comparing.strEqual(unscrambleSupport.getPresentableName(), selectedUnscrambler.getPresentableName())) {
116 index = i;
117 break;
121 if (count > 0) {
122 myUseUnscrambler.setEnabled(true);
123 myUnscrambleChooser.setSelectedIndex(index);
124 myUseUnscrambler.setSelected(selectedUnscrambler != null);
126 else {
127 myUseUnscrambler.setEnabled(false);
130 useUnscramblerChanged();
131 myStacktraceEditorPanel.pasteTextFromClipboard();
134 public static String getLastUsedLogUrl() {
135 return PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_LAST_URL);
138 @Nullable
139 public static UnscrambleSupport getSavedUnscrambler() {
140 final List<UnscrambleSupport> registeredUnscramblers = getRegisteredUnscramblers();
141 final String savedUnscramblerName = PropertiesComponent.getInstance().getValue(PROPERTY_UNSCRAMBLER_NAME_USED);
142 UnscrambleSupport selectedUnscrambler = null;
143 for (final UnscrambleSupport unscrambleSupport : registeredUnscramblers) {
144 if (Comparing.strEqual(unscrambleSupport.getPresentableName(), savedUnscramblerName)) {
145 selectedUnscrambler = unscrambleSupport;
148 return selectedUnscrambler;
151 public static List<String> getSavedLogFileUrls() {
152 final List<String> res = new ArrayList<String>();
153 final String savedUrl = PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_HISTORY_URLS);
154 final String[] strings = savedUrl == null ? ArrayUtil.EMPTY_STRING_ARRAY : savedUrl.split(":::");
155 for (int i = 0; i != strings.length; ++i) {
156 res.add(strings[i]);
158 return res;
161 @Nullable
162 private UnscrambleSupport getSelectedUnscrambler() {
163 if (!myUseUnscrambler.isSelected()) return null;
164 return (UnscrambleSupport)myUnscrambleChooser.getSelectedItem();
167 private void createEditor() {
168 myStacktraceEditorPanel = AnalyzeStacktraceUtil.createEditorPanel(myProject, myDisposable);
169 myEditorPanel.setLayout(new BorderLayout());
170 myEditorPanel.add(myStacktraceEditorPanel, BorderLayout.CENTER);
173 protected Action[] createActions(){
174 return new Action[]{createNormalizeTextAction(), getOKAction(), getCancelAction(), getHelpAction()};
177 private void createLogFileChooser() {
178 myLogFile = new TextFieldWithHistory();
179 JPanel panel = GuiUtils.constructFieldWithBrowseButton(myLogFile, new ActionListener() {
180 public void actionPerformed(ActionEvent e) {
181 FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
182 VirtualFile[] files = FileChooser.chooseFiles(myLogFile, descriptor);
183 if (files.length != 0) {
184 myLogFile.setText(FileUtil.toSystemDependentName(files[files.length-1].getPath()));
188 myLogFileChooserPanel.setLayout(new BorderLayout());
189 myLogFileChooserPanel.add(panel, BorderLayout.CENTER);
192 private void populateRegisteredUnscramblerList() {
193 List<UnscrambleSupport> unscrambleComponents = getRegisteredUnscramblers();
195 //myUnscrambleChooser.addItem(null);
196 for (final UnscrambleSupport unscrambleSupport : unscrambleComponents) {
197 myUnscrambleChooser.addItem(unscrambleSupport);
199 myUnscrambleChooser.setRenderer(new DefaultListCellRenderer() {
200 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
201 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
202 UnscrambleSupport unscrambleSupport = (UnscrambleSupport)value;
203 setText(unscrambleSupport == null ? IdeBundle.message("unscramble.no.unscrambler.item") : unscrambleSupport.getPresentableName());
204 return this;
209 private static List<UnscrambleSupport> getRegisteredUnscramblers() {
210 final UnscrambleSupport[] components = Extensions.getExtensions(UnscrambleSupport.EP_NAME);
211 return Arrays.asList(components);
214 protected JComponent createCenterPanel() {
215 return myPanel;
218 public void dispose() {
219 if (isOK()){
220 final List list = myLogFile.getHistory();
221 String res = null;
222 for (Object aList : list) {
223 final String s = (String)aList;
224 if (res == null) {
225 res = s;
227 else {
228 res = res + ":::" + s;
231 PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_HISTORY_URLS, res);
232 UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
233 PropertiesComponent.getInstance().setValue(PROPERTY_UNSCRAMBLER_NAME_USED, selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName());
235 PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_LAST_URL, myLogFile.getText());
237 super.dispose();
240 public void setText(String trace) {
241 myStacktraceEditorPanel.setText(trace);
244 public Action createNormalizeTextAction() {
245 return new NormalizeTextAction();
248 private final class NormalizeTextAction extends AbstractAction {
249 public NormalizeTextAction(){
250 putValue(NAME, IdeBundle.message("unscramble.normalize.button"));
251 putValue(DEFAULT_ACTION, Boolean.FALSE);
254 public void actionPerformed(ActionEvent e){
255 String text = myStacktraceEditorPanel.getText();
256 myStacktraceEditorPanel.setText(normalizeText(text));
261 public static String normalizeText(@NonNls String text) {
262 StringBuilder builder = new StringBuilder(text.length());
264 text = text.replaceAll("(\\S[ \\t\\x0B\\f\\r]+)(at\\s+)", "$1\n$2");
265 String[] lines = text.split("\n");
267 boolean first = true;
268 boolean inAuxInfo = false;
269 for (String line : lines) {
270 //noinspection HardCodedStringLiteral
271 if (!inAuxInfo && (line.startsWith("JNI global references") || line.trim().equals("Heap"))) {
272 builder.append("\n");
273 inAuxInfo = true;
275 if (inAuxInfo) {
276 builder.append(trimSuffix(line)).append("\n");
277 continue;
279 if (!first && mustHaveNewLineBefore(line)) {
280 builder.append("\n");
281 if (line.startsWith("\"")) builder.append("\n"); // Additional linebreak for thread names
283 first = false;
284 int i = builder.lastIndexOf("\n");
285 CharSequence lastLine = i == -1 ? builder : builder.subSequence(i + 1, builder.length());
286 if (lastLine.toString().matches("\\s*at") && !line.matches("\\s+.*")) builder.append(" "); // separate 'at' from file name
287 builder.append(trimSuffix(line));
289 return builder.toString();
292 private static String trimSuffix(final String line) {
293 int len = line.length();
295 while ((0 < len) && (line.charAt(len-1) <= ' ')) {
296 len--;
298 return (len < line.length()) ? line.substring(0, len) : line;
301 private static boolean mustHaveNewLineBefore(String line) {
302 final int nonws = CharArrayUtil.shiftForward(line, 0, " \t");
303 if (nonws < line.length()) {
304 line = line.substring(nonws);
307 if (line.startsWith("at")) return true; // Start of the new stackframe entry
308 if (line.startsWith("Caused")) return true; // Caused by message
309 if (line.startsWith("- locked")) return true; // "Locked a monitor" logging
310 if (line.startsWith("- waiting")) return true; // "Waiting for monitor" logging
311 if (line.startsWith("- parking to wait")) return true;
312 if (line.startsWith("java.lang.Thread.State")) return true;
313 if (line.startsWith("\"")) return true; // Start of the new thread (thread name)
315 return false;
318 protected void doOKAction() {
319 if (performUnscramble()) {
320 myLogFile.addCurrentTextToHistory();
321 close(OK_EXIT_CODE);
325 public void doHelpAction() {
326 HelpManager.getInstance().invokeHelp("find.analyzeStackTrace");
329 private boolean performUnscramble() {
330 UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
331 return showUnscrambledText(selectedUnscrambler, myLogFile.getText(), myProject, myStacktraceEditorPanel.getText());
334 static boolean showUnscrambledText(UnscrambleSupport unscrambleSupport, String logName, Project project, String textToUnscramble) {
335 String unscrambledTrace = unscrambleSupport == null ? textToUnscramble : unscrambleSupport.unscramble(project,textToUnscramble, logName);
336 if (unscrambledTrace == null) return false;
337 List<ThreadState> threadStates = ThreadDumpParser.parse(unscrambledTrace);
338 final ConsoleView consoleView = addConsole(project, threadStates);
339 AnalyzeStacktraceUtil.printStacktrace(consoleView, unscrambledTrace);
340 return true;
343 public static ConsoleView addConsole(final Project project, final List<ThreadState> threadDump) {
344 return AnalyzeStacktraceUtil.addConsole(project, threadDump.size() > 1 ? new AnalyzeStacktraceUtil.ConsoleFactory() {
345 public JComponent createConsoleComponent(ConsoleView consoleView, DefaultActionGroup toolbarActions) {
346 return new ThreadDumpPanel(project, consoleView, toolbarActions, threadDump);
348 } : null, IdeBundle.message("unscramble.unscrambled.stacktrace.tab"));
351 protected String getDimensionServiceKey(){
352 return "#com.intellij.unscramble.UnscrambleDialog";
355 public JComponent getPreferredFocusedComponent() {
356 return myStacktraceEditorPanel.getEditorComponent();