blob: 14b0cc550e45dfbfbbcbbd67f7722385ced543a7 [file] [log] [blame]
/*
* Copyright 2000-2014 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.diagnostic;
import com.intellij.CommonBundle;
import com.intellij.ExtensionPoints;
import com.intellij.diagnostic.errordialog.*;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.impl.DataManagerImpl;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.ide.plugins.PluginManagerMain;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.*;
import com.intellij.openapi.extensions.ExtensionException;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAware;
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.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.ui.HeaderlessTabbedPane;
import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.List;
public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListener, TypeSafeDataProvider {
private static final Logger LOG = Logger.getInstance(IdeErrorsDialog.class.getName());
private final boolean myInternalMode;
@NonNls private static final String ACTIVE_TAB_OPTION = IdeErrorsDialog.class.getName() + "activeTab";
public static DataKey<String> CURRENT_TRACE_KEY = DataKey.create("current_stack_trace_key");
public static final int COMPONENTS_WIDTH = 670;
public static Collection<Developer> ourDevelopersList = Collections.emptyList();
private JPanel myContentPane;
private JPanel myBackButtonPanel;
private HyperlinkLabel.Croppable myInfoLabel;
private JPanel myNextButtonPanel;
private JPanel myTabsPanel;
private JLabel myCountLabel;
private HyperlinkLabel.Croppable myForeignPluginWarningLabel;
private HyperlinkLabel.Croppable myDisableLink;
private JPanel myCredentialsPanel;
private HyperlinkLabel myCredentialsLabel;
private JPanel myForeignPluginWarningPanel;
private JPanel myAttachmentWarningPanel;
private HyperlinkLabel myAttachmentWarningLabel;
private int myIndex = 0;
private final List<ArrayList<AbstractMessage>> myMergedMessages = new ArrayList<ArrayList<AbstractMessage>>();
private List<AbstractMessage> myRawMessages;
private final MessagePool myMessagePool;
private HeaderlessTabbedPane myTabs;
@Nullable
private CommentsTabForm myCommentsTabForm;
private DetailsTabForm myDetailsTabForm;
private AttachmentsTabForm myAttachmentsTabForm;
private ClearFatalsAction myClearAction = new ClearFatalsAction();
private BlameAction myBlameAction;
@Nullable
private AnalyzeAction myAnalyzeAction;
private boolean myMute;
public IdeErrorsDialog(MessagePool messagePool, @Nullable LogMessage defaultMessage) {
super(JOptionPane.getRootFrame(), false);
myMessagePool = messagePool;
ApplicationEx app = ApplicationManagerEx.getApplicationEx();
myInternalMode = app != null && app.isInternal();
setTitle(DiagnosticBundle.message("error.list.title"));
init();
rebuildHeaders();
if (defaultMessage == null || !moveSelectionToMessage(defaultMessage)) {
moveSelectionToEarliestMessage();
}
setCancelButtonText(CommonBundle.message("close.action.name"));
setModal(false);
if (myInternalMode) {
if (ourDevelopersList.isEmpty()) {
loadDevelopersAsynchronously();
} else {
myDetailsTabForm.setDevelopers(ourDevelopersList);
}
}
}
private void loadDevelopersAsynchronously() {
Task.Backgroundable task = new Task.Backgroundable(null, "Loading developers list", true) {
private final Collection[] myDevelopers = new Collection[]{Collections.emptyList()};
@Override
public void run(@NotNull ProgressIndicator indicator) {
try {
myDevelopers[0] = DevelopersLoader.fetchDevelopers(indicator);
} catch (IOException e) {
//Notifications.Bus.register("Error reporter", NotificationDisplayType.BALLOON);
//Notifications.Bus.notify(new Notification("Error reporter", "Communication error",
// "Unable to load developers list from server.", NotificationType.WARNING));
}
}
@Override
public void onSuccess() {
Collection<Developer> developers = myDevelopers[0];
myDetailsTabForm.setDevelopers(developers);
//noinspection AssignmentToStaticFieldFromInstanceMethod
ourDevelopersList = developers;
}
};
ProgressManager.getInstance().run(task);
}
private boolean moveSelectionToMessage(LogMessage defaultMessage) {
int index = -1;
for (int i = 0; i < myMergedMessages.size(); i++) {
final AbstractMessage each = getMessageAt(i);
if (each == defaultMessage) {
index = i;
break;
}
}
if (index >= 0) {
myIndex = index;
updateControls();
return true;
}
else {
return false;
}
}
public void newEntryAdded() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
rebuildHeaders();
updateControls();
}
});
}
public void poolCleared() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doOKAction();
}
});
}
@Override
public void entryWasRead() {
}
@NotNull
@Override
protected Action[] createActions() {
if (SystemInfo.isMac) {
return new Action[]{getCancelAction(), myClearAction, myBlameAction};
}
else {
return new Action[]{myClearAction, myBlameAction, getCancelAction()};
}
}
@Override
protected void createDefaultActions() {
super.createDefaultActions();
myBlameAction = new BlameAction();
myBlameAction.putValue(DialogWrapper.DEFAULT_ACTION, true);
}
private class ForwardAction extends AnAction implements DumbAware {
public ForwardAction() {
super("Next", null, AllIcons.Actions.Forward);
AnAction forward = ActionManager.getInstance().getAction("Forward");
if (forward != null) {
registerCustomShortcutSet(forward.getShortcutSet(), getRootPane(), getDisposable());
}
}
public void actionPerformed(AnActionEvent e) {
goForward();
}
public void update(AnActionEvent e) {
Presentation presentation = e.getPresentation();
presentation.setEnabled(myIndex < myMergedMessages.size() - 1);
}
}
private class BackAction extends AnAction implements DumbAware {
public BackAction() {
super("Previous", null, AllIcons.Actions.Back);
AnAction back = ActionManager.getInstance().getAction("Back");
if (back != null) {
registerCustomShortcutSet(back.getShortcutSet(), getRootPane(), getDisposable());
}
}
public void actionPerformed(AnActionEvent e) {
goBack();
}
public void update(AnActionEvent e) {
Presentation presentation = e.getPresentation();
presentation.setEnabled(myIndex > 0);
}
}
@Override
protected JComponent createCenterPanel() {
DefaultActionGroup goBack = new DefaultActionGroup();
BackAction back = new BackAction();
goBack.add(back);
ActionToolbar backToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, goBack, true);
backToolbar.getComponent().setBorder(IdeBorderFactory.createEmptyBorder());
backToolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY);
myBackButtonPanel.add(backToolbar.getComponent(), BorderLayout.CENTER);
DefaultActionGroup goForward = new DefaultActionGroup();
ForwardAction forward = new ForwardAction();
goForward.add(forward);
ActionToolbar forwardToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, goForward, true);
forwardToolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY);
forwardToolbar.getComponent().setBorder(IdeBorderFactory.createEmptyBorder());
myNextButtonPanel.add(forwardToolbar.getComponent(), BorderLayout.CENTER);
myTabs = new HeaderlessTabbedPane(getDisposable());
final LabeledTextComponent.TextListener commentsListener = new LabeledTextComponent.TextListener() {
@Override
public void textChanged(String newText) {
if (myMute) {
return;
}
AbstractMessage message = getSelectedMessage();
if (message != null) {
message.setAdditionalInfo(newText);
}
}
};
if (!myInternalMode) {
myDetailsTabForm = new DetailsTabForm(null, myInternalMode);
myCommentsTabForm = new CommentsTabForm();
myCommentsTabForm.addCommentsListener(commentsListener);
myTabs.addTab(DiagnosticBundle.message("error.comments.tab.title"), myCommentsTabForm.getContentPane());
myDetailsTabForm.setCommentsAreaVisible(false);
}
else {
final AnAction analyzePlatformAction = ActionManager.getInstance().getAction("AnalyzeStacktraceOnError");
if (analyzePlatformAction != null) {
myAnalyzeAction = new AnalyzeAction(analyzePlatformAction);
}
myDetailsTabForm = new DetailsTabForm(myAnalyzeAction, myInternalMode);
myDetailsTabForm.setCommentsAreaVisible(true);
myDetailsTabForm.addCommentsListener(commentsListener);
}
myTabs.addTab(DiagnosticBundle.message("error.details.tab.title"), myDetailsTabForm.getContentPane());
myAttachmentsTabForm = new AttachmentsTabForm();
myAttachmentsTabForm.addInclusionListener(new ChangeListener() {
public void stateChanged(final ChangeEvent e) {
updateAttachmentWarning(getSelectedMessage());
}
});
int activeTabIndex = Integer.parseInt(PropertiesComponent.getInstance().getValue(ACTIVE_TAB_OPTION, "0"));
if (activeTabIndex >= myTabs.getTabCount() || activeTabIndex < 0) {
activeTabIndex = 0; // may happen if myInternalMode changed since last open
}
myTabs.setSelectedIndex(activeTabIndex);
myTabs.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
final JComponent c = getPreferredFocusedComponent();
if (c != null) {
IdeFocusManager.findInstanceByComponent(myContentPane).requestFocus(c, true);
}
}
});
myTabsPanel.add(myTabs, BorderLayout.CENTER);
myDisableLink.setHyperlinkText(UIUtil.removeMnemonic(DiagnosticBundle.message("error.list.disable.plugin")));
myDisableLink.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
disablePlugin();
}
}
});
myCredentialsLabel.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
new JetBrainsAccountDialog(getRootPane()).show();
updateCredentialsPane(getSelectedMessage());
}
}
});
myAttachmentWarningLabel.setIcon(UIUtil.getBalloonWarningIcon());
myAttachmentWarningLabel.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(final HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
myTabs.setSelectedIndex(myTabs.indexOfComponent(myAttachmentsTabForm.getContentPane()));
myAttachmentsTabForm.selectFirstIncludedAttachment();
}
}
});
myDetailsTabForm.addAssigneeListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (myMute) return;
AbstractMessage message = getSelectedMessage();
if (message != null) {
message.setAssigneeId(myDetailsTabForm.getAssigneeId());
}
}
});
return myContentPane;
}
private void moveSelectionToEarliestMessage() {
myIndex = 0;
for (int i = 0; i < myMergedMessages.size(); i++) {
final AbstractMessage each = getMessageAt(i);
if (!each.isRead()) {
myIndex = i;
break;
}
}
updateControls();
}
private void disablePlugin() {
final PluginId pluginId = findPluginId(getSelectedMessage().getThrowable());
if (pluginId == null) {
return;
}
IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId);
final Ref<Boolean> hasDependants = new Ref<Boolean>(false);
PluginManager.checkDependants(plugin, new Function<PluginId, IdeaPluginDescriptor>() {
@Override
public IdeaPluginDescriptor fun(PluginId pluginId) {
return PluginManager.getPlugin(pluginId);
}
}, new Condition<PluginId>() {
@Override
public boolean value(PluginId pluginId) {
if (PluginManagerCore.CORE_PLUGIN_ID.equals(pluginId.getIdString())) {
return true;
}
hasDependants.set(true);
return false;
}
}
);
Application app = ApplicationManager.getApplication();
DisablePluginWarningDialog d =
new DisablePluginWarningDialog(getRootPane(), plugin.getName(), hasDependants.get(), app.isRestartCapable());
d.show();
switch (d.getExitCode()) {
case CANCEL_EXIT_CODE:
return;
case DisablePluginWarningDialog.DISABLE_EXIT_CODE:
PluginManager.disablePlugin(pluginId.getIdString());
break;
case DisablePluginWarningDialog.DISABLE_AND_RESTART_EXIT_CODE:
PluginManager.disablePlugin(pluginId.getIdString());
app.restart();
break;
}
}
private void goBack() {
myIndex--;
updateControls();
}
private void goForward() {
myIndex++;
updateControls();
}
private void updateControls() {
updateCountLabel();
final AbstractMessage message = getSelectedMessage();
updateInfoLabel(message);
updateCredentialsPane(message);
updateAssigneePane(message);
updateAttachmentWarning(message);
myDisableLink.setVisible(canDisablePlugin(message));
updateForeignPluginLabel(message != null ? message : null);
updateTabs();
myClearAction.update();
myBlameAction.update();
if (myAnalyzeAction != null) {
myAnalyzeAction.update();
}
}
private void updateAttachmentWarning(final AbstractMessage message) {
final List<Attachment> includedAttachments;
if (message instanceof LogMessageEx &&
!(includedAttachments = ContainerUtil.filter(((LogMessageEx)message).getAttachments(), new Condition<Attachment>() {
public boolean value(final Attachment attachment) {
return attachment.isIncluded();
}
})).isEmpty()) {
myAttachmentWarningPanel.setVisible(true);
if (includedAttachments.size() == 1) {
myAttachmentWarningLabel.setHtmlText(
DiagnosticBundle.message("diagnostic.error.report.include.attachment.warning", includedAttachments.get(0).getName()));
}
else {
myAttachmentWarningLabel.setHtmlText(
DiagnosticBundle.message("diagnostic.error.report.include.attachments.warning", includedAttachments.size()));
}
}
else {
myAttachmentWarningPanel.setVisible(false);
}
}
private static boolean canDisablePlugin(AbstractMessage message) {
if (message == null) {
return false;
}
PluginId pluginId = findPluginId(message.getThrowable());
return pluginId != null;
}
private void updateCountLabel() {
if (myMergedMessages.isEmpty()) {
myCountLabel.setText(DiagnosticBundle.message("error.list.empty"));
}
else {
myCountLabel
.setText(DiagnosticBundle.message("error.list.message.index.count", Integer.toString(myIndex + 1), myMergedMessages.size()));
}
}
private void updateCredentialsPane(AbstractMessage message) {
if (message != null) {
final ErrorReportSubmitter submitter = getSubmitter(message.getThrowable());
if (submitter instanceof ITNReporter) {
myCredentialsPanel.setVisible(true);
String userName = ErrorReportConfigurable.getInstance().ITN_LOGIN;
if (StringUtil.isEmpty(userName)) {
myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.error.anonymously"));
}
else {
myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.report.as", userName));
}
return;
}
}
myCredentialsPanel.setVisible(false);
}
private void updateAssigneePane(AbstractMessage message) {
final ErrorReportSubmitter submitter = message != null ? getSubmitter(message.getThrowable()) : null;
myDetailsTabForm.setAssigneeVisible(submitter instanceof ITNReporter && myInternalMode);
}
private void updateInfoLabel(AbstractMessage message) {
if (message == null) {
myInfoLabel.setText("");
return;
}
final Throwable throwable = message.getThrowable();
if (throwable instanceof MessagePool.TooManyErrorsException) {
myInfoLabel.setText("");
return;
}
StringBuilder text = new StringBuilder();
PluginId pluginId = findPluginId(throwable);
if (pluginId == null) {
if (throwable instanceof AbstractMethodError) {
text.append(DiagnosticBundle.message("error.list.message.blame.unknown.plugin"));
}
else {
text.append(DiagnosticBundle.message("error.list.message.blame.core", ApplicationNamesInfo.getInstance().getProductName()));
}
}
else {
text.append(DiagnosticBundle.message("error.list.message.blame.plugin", PluginManager.getPlugin(pluginId).getName()));
}
text.append(" ").append(DiagnosticBundle.message("error.list.message.info",
DateFormatUtil.formatPrettyDateTime(message.getDate()),
myMergedMessages.get(myIndex).size()));
String url = null;
if (message.isSubmitted()) {
SubmittedReportInfo info = message.getSubmissionInfo();
url = info.getURL();
appendSubmissionInformation(info, text);
text.append(". ");
}
else if (message.isSubmitting()) {
text.append(" Submitting...");
}
else if (!message.isRead()) {
text.append(" ").append(DiagnosticBundle.message("error.list.message.unread"));
}
myInfoLabel.setHtmlText(XmlStringUtil.wrapInHtml(text));
myInfoLabel.setHyperlinkTarget(url);
}
public static void appendSubmissionInformation(SubmittedReportInfo info, StringBuilder out) {
if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED) {
out.append(" ").append(DiagnosticBundle.message("error.list.message.submission.failed"));
}
else if (info.getURL() != null && info.getLinkText() != null) {
out.append(" ").append(DiagnosticBundle.message("error.list.message.submitted.as.link", info.getURL(), info.getLinkText()));
if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.DUPLICATE) {
out.append(" ").append(DiagnosticBundle.message("error.list.message.duplicate"));
}
}
else {
out.append(DiagnosticBundle.message("error.list.message.submitted"));
}
}
private void updateForeignPluginLabel(AbstractMessage message) {
if (message != null) {
final Throwable throwable = message.getThrowable();
ErrorReportSubmitter submitter = getSubmitter(throwable);
if (submitter == null) {
PluginId pluginId = findPluginId(throwable);
IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId);
if (plugin == null || PluginManagerMain.isJetBrainsPlugin(plugin)) {
myForeignPluginWarningPanel.setVisible(false);
return;
}
myForeignPluginWarningPanel.setVisible(true);
String vendor = plugin.getVendor();
String contactInfo = plugin.getVendorUrl();
if (StringUtil.isEmpty(contactInfo)) {
contactInfo = plugin.getVendorEmail();
}
if (StringUtil.isEmpty(vendor)) {
if (StringUtil.isEmpty(contactInfo)) {
myForeignPluginWarningLabel.setText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text"));
}
else {
myForeignPluginWarningLabel
.setHyperlinkText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text.vendor") + " ",
contactInfo, ".");
myForeignPluginWarningLabel.setHyperlinkTarget(contactInfo);
}
}
else {
if (StringUtil.isEmpty(contactInfo)) {
myForeignPluginWarningLabel.setText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text.vendor") +
" " + vendor + ".");
}
else {
myForeignPluginWarningLabel
.setHyperlinkText(
DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text.vendor") + " " + vendor + " (",
contactInfo, ").");
myForeignPluginWarningLabel.setHyperlinkTarget(contactInfo);
}
}
myForeignPluginWarningPanel.setVisible(true);
return;
}
}
myForeignPluginWarningPanel.setVisible(false);
}
private void updateTabs() {
myMute = true;
try {
if (myInternalMode) {
boolean hasAttachment = false;
for (ArrayList<AbstractMessage> merged : myMergedMessages) {
final AbstractMessage message = merged.get(0);
if (message instanceof LogMessageEx && !((LogMessageEx)message).getAttachments().isEmpty()) {
hasAttachment = true;
break;
}
}
myTabs.setHeaderVisible(hasAttachment);
}
final AbstractMessage message = getSelectedMessage();
if (myCommentsTabForm != null) {
if (message != null) {
String msg = message.getMessage();
int i = msg.indexOf("\n");
if (i != -1) {
// take first line
msg = msg.substring(0, i);
}
myCommentsTabForm.setErrorText(msg);
}
else {
myCommentsTabForm.setErrorText(null);
}
if (message != null) {
myCommentsTabForm.setCommentText(message.getAdditionalInfo());
myCommentsTabForm.setCommentsTextEnabled(true);
}
else {
myCommentsTabForm.setCommentText(null);
myCommentsTabForm.setCommentsTextEnabled(false);
}
}
myDetailsTabForm.setDetailsText(message != null ? getDetailsText(message) : null);
if (message != null) {
myDetailsTabForm.setCommentsText(message.getAdditionalInfo());
myDetailsTabForm.setCommentsTextEnabled(true);
}
else {
myDetailsTabForm.setCommentsText(null);
myDetailsTabForm.setCommentsTextEnabled(false);
}
myDetailsTabForm.setAssigneeId(message == null ? null : message.getAssigneeId());
List<Attachment> attachments =
message instanceof LogMessageEx ? ((LogMessageEx)message).getAttachments() : Collections.<Attachment>emptyList();
if (!attachments.isEmpty()) {
if (myTabs.indexOfComponent(myAttachmentsTabForm.getContentPane()) == -1) {
myTabs.addTab(DiagnosticBundle.message("error.attachments.tab.title"), myAttachmentsTabForm.getContentPane());
}
myAttachmentsTabForm.setAttachments(attachments);
}
else {
int index = myTabs.indexOfComponent(myAttachmentsTabForm.getContentPane());
if (index != -1) {
myTabs.removeTabAt(index);
}
}
}
finally {
myMute = false;
}
}
private static String getDetailsText(AbstractMessage message) {
final Throwable throwable = message.getThrowable();
if (throwable instanceof MessagePool.TooManyErrorsException) {
return throwable.getMessage();
}
else {
return new StringBuffer().append(message.getMessage()).append("\n").append(message.getThrowableText()).toString();
}
}
private void rebuildHeaders() {
myMergedMessages.clear();
myRawMessages = myMessagePool.getFatalErrors(true, true);
Map<String, ArrayList<AbstractMessage>> hash2Messages = mergeMessages(myRawMessages);
for (final ArrayList<AbstractMessage> abstractMessages : hash2Messages.values()) {
myMergedMessages.add(abstractMessages);
}
}
private void markAllAsRead() {
for (AbstractMessage each : myRawMessages) {
each.setRead(true);
}
}
@Override
public JComponent getPreferredFocusedComponent() {
final int selectedIndex = myTabs.getSelectedIndex();
JComponent result;
if (selectedIndex == 0) {
result = myInternalMode ? myDetailsTabForm.getPreferredFocusedComponent() : myCommentsTabForm.getPreferredFocusedComponent();
}
else if (selectedIndex == 1) {
result = myInternalMode ? myAttachmentsTabForm.getPreferredFocusedComponent() : myDetailsTabForm.getPreferredFocusedComponent();
}
else {
result = myAttachmentsTabForm.getPreferredFocusedComponent();
}
return result != null ? result : super.getPreferredFocusedComponent();
}
private static Map<String, ArrayList<AbstractMessage>> mergeMessages(List<AbstractMessage> aErrors) {
Map<String, ArrayList<AbstractMessage>> hash2Messages = new LinkedHashMap<String, ArrayList<AbstractMessage>>();
for (final AbstractMessage each : aErrors) {
final String hashCode = getThrowableHashCode(each.getThrowable());
ArrayList<AbstractMessage> list;
if (hash2Messages.containsKey(hashCode)) {
list = hash2Messages.get(hashCode);
}
else {
list = new ArrayList<AbstractMessage>();
hash2Messages.put(hashCode, list);
}
list.add(0, each);
}
return hash2Messages;
}
private AbstractMessage getSelectedMessage() {
return getMessageAt(myIndex);
}
private AbstractMessage getMessageAt(int idx) {
if (idx < 0 || idx >= myMergedMessages.size()) return null;
return myMergedMessages.get(idx).get(0);
}
@Nullable
public static PluginId findPluginId(Throwable t) {
if (t instanceof PluginException) {
return ((PluginException)t).getPluginId();
}
Set<String> visitedClassNames = ContainerUtil.newHashSet();
for (StackTraceElement element : t.getStackTrace()) {
if (element != null) {
String className = element.getClassName();
if (visitedClassNames.add(className) && PluginManagerCore.isPluginClass(className)) {
return PluginManagerCore.getPluginByClassName(className);
}
}
}
if (t instanceof NoSuchMethodException) {
// check is method called from plugin classes
if (t.getMessage() != null) {
String className = "";
StringTokenizer tok = new StringTokenizer(t.getMessage(), ".");
while (tok.hasMoreTokens()) {
String token = tok.nextToken();
if (token.length() > 0 && Character.isJavaIdentifierStart(token.charAt(0))) {
className += token;
}
}
if (PluginManager.isPluginClass(className)) {
return PluginManager.getPluginByClassName(className);
}
}
}
else if (t instanceof ClassNotFoundException) {
// check is class from plugin classes
if (t.getMessage() != null) {
String className = t.getMessage();
if (PluginManager.isPluginClass(className)) {
return PluginManager.getPluginByClassName(className);
}
}
}
else if (t instanceof AbstractMethodError && t.getMessage() != null) {
String s = t.getMessage();
// org.antlr.works.plugin.intellij.PIFileType.getHighlighter(Lcom/intellij/openapi/project/Project;Lcom/intellij/openapi/vfs/VirtualFile;)Lcom/intellij/openapi/fileTypes/SyntaxHighlighter;
int pos = s.indexOf('(');
if (pos >= 0) {
s = s.substring(0, pos);
pos = s.lastIndexOf('.');
if (pos >= 0) {
s = s.substring(0, pos);
if (PluginManager.isPluginClass(s)) {
return PluginManager.getPluginByClassName(s);
}
}
}
}
else if (t instanceof ExtensionException) {
String className = ((ExtensionException)t).getExtensionClass().getName();
if (PluginManager.isPluginClass(className)) {
return PluginManager.getPluginByClassName(className);
}
}
return null;
}
private class ClearFatalsAction extends AbstractAction {
protected ClearFatalsAction() {
super(DiagnosticBundle.message("error.dialog.clear.action"));
}
@Override
public void actionPerformed(ActionEvent e) {
myMessagePool.clearFatals();
doOKAction();
}
public void update() {
putValue(NAME, DiagnosticBundle.message(myMergedMessages.size() > 1 ? "error.dialog.clear.all.action" : "error.dialog.clear.action"));
setEnabled(!myMergedMessages.isEmpty());
}
}
private class BlameAction extends AbstractAction {
protected BlameAction() {
super(DiagnosticBundle.message("error.report.to.jetbrains.action"));
}
public void update() {
AbstractMessage logMessage = getSelectedMessage();
if (logMessage != null) {
ErrorReportSubmitter submitter = getSubmitter(logMessage.getThrowable());
if (submitter != null) {
putValue(NAME, submitter.getReportActionText());
setEnabled(!logMessage.isSubmitted());
return;
}
}
putValue(NAME, DiagnosticBundle.message("error.report.to.jetbrains.action"));
setEnabled(false);
}
public void actionPerformed(ActionEvent e) {
boolean closeDialog = myMergedMessages.size() == 1;
final AbstractMessage logMessage = getSelectedMessage();
boolean reportingStarted = reportMessage(logMessage, closeDialog);
if (closeDialog) {
if (reportingStarted) {
doOKAction();
}
}
else {
rebuildHeaders();
updateControls();
}
}
private boolean reportMessage(final AbstractMessage logMessage, final boolean dialogClosed) {
final ErrorReportSubmitter submitter = getSubmitter(logMessage.getThrowable());
if (submitter == null) return false;
logMessage.setSubmitting(true);
if (!dialogClosed) {
updateControls();
}
Container parentComponent;
if (dialogClosed) {
IdeFrame ideFrame = UIUtil.getParentOfType(IdeFrame.class, getContentPane());
parentComponent = ideFrame.getComponent();
}
else {
parentComponent = getContentPane();
}
return submitter.submit(
getEvents(logMessage), logMessage.getAdditionalInfo(), parentComponent, new Consumer<SubmittedReportInfo>() {
@Override
public void consume(final SubmittedReportInfo submittedReportInfo) {
logMessage.setSubmitting(false);
logMessage.setSubmitted(submittedReportInfo);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (!dialogClosed) {
updateOnSubmit();
}
}
});
}
});
}
private IdeaLoggingEvent[] getEvents(final AbstractMessage logMessage) {
if (logMessage instanceof GroupedLogMessage) {
final List<AbstractMessage> messages = ((GroupedLogMessage)logMessage).getMessages();
IdeaLoggingEvent[] res = new IdeaLoggingEvent[messages.size()];
for (int i = 0; i < res.length; i++) {
res[i] = getEvent(messages.get(i));
}
return res;
}
return new IdeaLoggingEvent[]{getEvent(logMessage)};
}
private IdeaLoggingEvent getEvent(final AbstractMessage logMessage) {
if (logMessage instanceof LogMessageEx) {
return ((LogMessageEx)logMessage).toEvent();
}
return new IdeaLoggingEvent(logMessage.getMessage(), logMessage.getThrowable()) {
@Override
public AbstractMessage getData() {
return logMessage;
}
};
}
}
protected void updateOnSubmit() {
updateControls();
}
public void calcData(DataKey key, DataSink sink) {
if (CURRENT_TRACE_KEY == key) {
final AbstractMessage message = getSelectedMessage();
if (message != null) {
sink.put(CURRENT_TRACE_KEY, getDetailsText(message));
}
}
}
@Nullable
public static ErrorReportSubmitter getSubmitter(final Throwable throwable) {
if (throwable instanceof MessagePool.TooManyErrorsException || throwable instanceof AbstractMethodError) {
return null;
}
final PluginId pluginId = findPluginId(throwable);
final ErrorReportSubmitter[] reporters;
try {
reporters = Extensions.getExtensions(ExtensionPoints.ERROR_HANDLER_EP);
}
catch (Throwable t) {
return null;
}
IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId);
if (plugin == null || PluginManagerMain.isJetBrainsPlugin(plugin)) {
return getCorePluginSubmitter(reporters);
}
for (ErrorReportSubmitter reporter : reporters) {
final PluginDescriptor descriptor = reporter.getPluginDescriptor();
if (descriptor != null && Comparing.equal(pluginId, descriptor.getPluginId())) {
return reporter;
}
}
return null;
}
@Nullable
private static ErrorReportSubmitter getCorePluginSubmitter(ErrorReportSubmitter[] reporters) {
for (ErrorReportSubmitter reporter : reporters) {
final PluginDescriptor descriptor = reporter.getPluginDescriptor();
if (descriptor == null || PluginId.getId(PluginManagerCore.CORE_PLUGIN_ID) == descriptor.getPluginId()) {
return reporter;
}
}
return null;
}
@Override
public void doOKAction() {
onClose();
super.doOKAction();
}
private void onClose() {
markAllAsRead();
PropertiesComponent.getInstance().setValue(ACTIVE_TAB_OPTION, String.valueOf(myTabs.getSelectedIndex()));
}
@Override
public void doCancelAction() {
onClose();
super.doCancelAction();
}
@Override
protected String getDimensionServiceKey() {
return "IdeErrosDialog";
}
private static String getThrowableHashCode(Throwable exception) {
try {
return md5(StringUtil.getThrowableText(exception), "stack-trace");
}
catch (NoSuchAlgorithmException e) {
LOG.error(e);
return "";
}
}
private static String md5(String buffer, @NonNls String key) throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(buffer.getBytes());
byte[] code = md5.digest(key.getBytes());
BigInteger bi = new BigInteger(code).abs();
return bi.abs().toString(16);
}
private class AnalyzeAction extends AbstractAction {
private final AnAction myAnalyze;
public AnalyzeAction(AnAction analyze) {
super(analyze.getTemplatePresentation().getText());
putValue(Action.MNEMONIC_KEY, analyze.getTemplatePresentation().getMnemonic());
myAnalyze = analyze;
}
public void update() {
setEnabled(getSelectedMessage() != null);
}
public void actionPerformed(ActionEvent e) {
final DataContext dataContext = ((DataManagerImpl)DataManager.getInstance()).getDataContextTest((Component)e.getSource());
AnActionEvent event = new AnActionEvent(
null, dataContext,
ActionPlaces.UNKNOWN,
myAnalyze.getTemplatePresentation(),
ActionManager.getInstance(),
e.getModifiers()
);
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project != null) {
myAnalyze.actionPerformed(event);
doOKAction();
}
}
}
}