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.
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
;
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
) {
70 populateRegisteredUnscramblerList();
71 myUnscrambleChooser
.addActionListener(new ActionListener() {
72 public void actionPerformed(ActionEvent e
) {
73 UnscrambleSupport unscrambleSupport
= getSelectedUnscrambler();
74 GuiUtils
.enableChildren(myLogFileChooserPanel
, unscrambleSupport
!= null);
77 myUseUnscrambler
.addActionListener(new ActionListener() {
78 public void actionPerformed(ActionEvent e
) {
79 useUnscramblerChanged();
82 createLogFileChooser();
86 setTitle(IdeBundle
.message("unscramble.dialog.title"));
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();
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())) {
122 myUseUnscrambler
.setEnabled(true);
123 myUnscrambleChooser
.setSelectedIndex(index
);
124 myUseUnscrambler
.setSelected(selectedUnscrambler
!= null);
127 myUseUnscrambler
.setEnabled(false);
130 useUnscramblerChanged();
131 myStacktraceEditorPanel
.pasteTextFromClipboard();
134 public static String
getLastUsedLogUrl() {
135 return PropertiesComponent
.getInstance().getValue(PROPERTY_LOG_FILE_LAST_URL
);
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
) {
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());
209 private static List
<UnscrambleSupport
> getRegisteredUnscramblers() {
210 final UnscrambleSupport
[] components
= Extensions
.getExtensions(UnscrambleSupport
.EP_NAME
);
211 return Arrays
.asList(components
);
214 protected JComponent
createCenterPanel() {
218 public void dispose() {
220 final List list
= myLogFile
.getHistory();
222 for (Object aList
: list
) {
223 final String s
= (String
)aList
;
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());
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");
276 builder
.append(trimSuffix(line
)).append("\n");
279 if (!first
&& mustHaveNewLineBefore(line
)) {
280 builder
.append("\n");
281 if (line
.startsWith("\"")) builder
.append("\n"); // Additional linebreak for thread names
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) <= ' ')) {
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)
318 protected void doOKAction() {
319 if (performUnscramble()) {
320 myLogFile
.addCurrentTextToHistory();
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
);
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();