Assignee field in error reporter

This commit is contained in:
Pavel Fatin
2012-03-01 19:31:18 +04:00
parent 7237e93d32
commit c57d332908
10 changed files with 374 additions and 8 deletions
@@ -28,6 +28,7 @@ public abstract class AbstractMessage {
private SubmittedReportInfo mySubmissionInfo;
private String myAdditionalInfo;
private Notification myNotification;
private Integer myAssigneeId;
private final Date myDate;
@@ -90,4 +91,11 @@ public abstract class AbstractMessage {
return myDate;
}
public Integer getAssigneeId() {
return myAssigneeId;
}
public void setAssigneeId(Integer assigneeId) {
myAssigneeId = assigneeId;
}
}
@@ -0,0 +1,71 @@
/*
* 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.diagnostic;
public class Developer {
public static final Developer NULL = new NullDeveloper();
private Integer myId;
private String myName;
private Developer() {
}
public Developer(int id, String name) {
myId = id;
myName = name;
}
public Integer getId() {
return myId;
}
public String getDisplayText() {
return myName;
}
public String getSearchableText() {
return myName;
}
@Override
public String toString() {
return String.format("%d - %s", myId, myName);
}
private static class NullDeveloper extends Developer {
@Override
public Integer getId() {
return null;
}
@Override
public String getDisplayText() {
return "<none>";
}
@Override
public String getSearchableText() {
return "";
}
@Override
public String toString() {
return "NullDeveloper";
}
}
}
@@ -0,0 +1,60 @@
/*
* 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.diagnostic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
class DevelopersLoader {
private static final String DEVELOPERS_LIST_URL = "http://ea-engine.labs.intellij.net/data?category=developers";
private static final String DATA_CHARSET = "utf-8";
private DevelopersLoader() {
}
public static Collection<Developer> fetchDevelopers() throws IOException {
try {
List<Developer> developers = new LinkedList<Developer>();
developers.add(Developer.NULL);
URL url = new URL(DEVELOPERS_LIST_URL);
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), DATA_CHARSET));
try {
while (reader.ready()) {
String line = reader.readLine();
if (line == null) break;
int i = line.indexOf('\t');
if (i == -1) throw new IOException("Protocol error");
int id = Integer.parseInt(line.substring(0, i));
String name = line.substring(i + 1);
developers.add(new Developer(id, name));
}
return developers;
} finally {
reader.close();
}
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}
@@ -121,8 +121,14 @@ public class ITNReporter extends ErrorReportSubmitter {
}
}
if (event.getData() instanceof LogMessageEx) {
errorBean.setAttachments(((LogMessageEx)event.getData()).getAttachments());
Object data = event.getData();
if (data instanceof AbstractMessage) {
errorBean.setAssigneeId(((AbstractMessage)data).getAssigneeId());
}
if (data instanceof LogMessageEx) {
errorBean.setAttachments(((LogMessageEx)data).getAttachments());
}
@NonNls String login = errorReportConfigurable.ITN_LOGIN;
@@ -8,6 +8,10 @@ import com.intellij.ide.impl.DataManagerImpl;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
@@ -22,6 +26,9 @@ 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;
@@ -42,15 +49,15 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
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 javax.swing.event.*;
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;
@@ -63,6 +70,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
@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;
@@ -105,6 +113,38 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
}
setCancelButtonText(CommonBundle.message("close.action.name"));
setModal(false);
if (INTERNAL_MODE) {
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();
} 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);
ourDevelopersList = developers;
}
};
ProgressManager.getInstance().run(task);
}
private boolean moveSelectionToMessage(LogMessage defaultMessage) {
@@ -304,6 +344,18 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
}
});
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;
}
@@ -377,6 +429,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
final AbstractMessage message = getSelectedMessage();
updateInfoLabel(message);
updateCredentialsPane(message);
updateAssigneePane(message);
updateAttachmentWarning(message);
myDisableLink.setVisible(canDisablePlugin(message));
updateForeignPluginLabel(message != null ? message : null);
@@ -457,6 +510,11 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
myCredentialsPanel.setVisible(false);
}
private void updateAssigneePane(AbstractMessage message) {
final ErrorReportSubmitter submitter = getSubmitter(message.getThrowable());
myDetailsTabForm.setAssigneeVisible(submitter instanceof ITNReporter && INTERNAL_MODE);
}
private void updateInfoLabel(AbstractMessage message) {
if (message == null) {
myInfoLabel.setText("");
@@ -632,6 +690,8 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
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()) {
@@ -891,7 +951,12 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
if (logMessage instanceof LogMessageEx) {
return ((LogMessageEx)logMessage).toEvent();
}
return new IdeaLoggingEvent(logMessage.getMessage(), logMessage.getThrowable());
return new IdeaLoggingEvent(logMessage.getMessage(), logMessage.getThrowable()) {
@Override
public AbstractMessage getData() {
return logMessage;
}
};
}
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.diagnostic.errordialog.DetailsTabForm">
<grid id="27dc6" binding="myContentPane" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myContentPane" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="589" height="400"/>
@@ -52,6 +52,31 @@
<text value="Analyze Stacktrace"/>
</properties>
</component>
<grid id="4d3ba" binding="myAssigneePanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="2ae62" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="6901a"/>
<text value="&amp;Assignee:"/>
</properties>
</component>
<component id="e5fc4" class="javax.swing.JComboBox" binding="myAssigneeComboBox">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
</children>
</grid>
</form>
@@ -1,13 +1,19 @@
package com.intellij.diagnostic.errordialog;
import com.intellij.diagnostic.Developer;
import com.intellij.diagnostic.DiagnosticBundle;
import com.intellij.diagnostic.IdeErrorsDialog;
import com.intellij.ui.ComboboxSpeedSearch;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.util.containers.ComparatorUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
/**
* @author ksafonov
@@ -18,6 +24,10 @@ public class DetailsTabForm {
private LabeledTextComponent myCommentsArea;
private JPanel myDetailsHolder;
private JButton myAnalyzeStacktraceButton;
private JComboBox myAssigneeComboBox;
private JPanel myAssigneePanel;
private Integer myAssigneeId;
private boolean myProcessEvents = true;
public DetailsTabForm(@Nullable Action analyzeAction) {
myCommentsArea.setTitle(DiagnosticBundle.message("error.dialog.comment.prompt"));
@@ -30,6 +40,20 @@ public class DetailsTabForm {
else {
myAnalyzeStacktraceButton.setVisible(false);
}
myAssigneeComboBox.setRenderer(new DeveloperRenderer(myAssigneeComboBox.getRenderer()));
myAssigneeComboBox.setPrototypeDisplayValue(new Developer(0, "Here Goes Some Very Long String"));
myAssigneeComboBox.addActionListener(new ActionListenerProxy(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myAssigneeId = getAssigneeId();
}
}));
new ComboboxSpeedSearch(myAssigneeComboBox) {
@Override
protected String getElementText(Object element) {
return element == null ? "" : ((Developer) element).getSearchableText();
}
};
}
public void setCommentsAreaVisible(boolean b) {
@@ -65,4 +89,68 @@ public class DetailsTabForm {
myCommentsArea.addCommentsListener(l);
}
public void setAssigneeVisible(boolean visible) {
myAssigneePanel.setVisible(visible);
}
public void setDevelopers(Collection<Developer> developers) {
myAssigneeComboBox.setModel(new DefaultComboBoxModel(developers.toArray()));
updateSelectedDeveloper();
}
public void setAssigneeId(@Nullable Integer assigneeId) {
myAssigneeId = assigneeId;
if (myAssigneeComboBox.getItemCount() > 0) {
updateSelectedDeveloper();
}
}
private void updateSelectedDeveloper() {
myProcessEvents = false;
Integer index = null;
for (int i = 0; i < myAssigneeComboBox.getItemCount(); i++) {
Developer developer = (Developer) myAssigneeComboBox.getItemAt(i);
if (ComparatorUtil.equalsNullable(developer.getId(), myAssigneeId)) {
index = i;
break;
}
}
setSelectedAssigneeIndex(index);
myProcessEvents = true;
}
private void setSelectedAssigneeIndex(Integer index) {
if (index == null) {
myAssigneeComboBox.setSelectedItem(null);
} else {
myAssigneeComboBox.setSelectedIndex(index);
}
}
@Nullable
public Integer getAssigneeId() {
Developer assignee = (Developer) myAssigneeComboBox.getSelectedItem();
return assignee == null ? null : assignee.getId();
}
public void addAssigneeListener(ActionListener listener) {
myAssigneeComboBox.addActionListener(new ActionListenerProxy(listener));
}
private class ActionListenerProxy implements ActionListener {
private final ActionListener myDelegate;
public ActionListenerProxy(ActionListener delegate) {
myDelegate = delegate;
}
@Override
public void actionPerformed(ActionEvent e) {
if (myProcessEvents) {
myDelegate.actionPerformed(e);
}
}
}
}
@@ -0,0 +1,32 @@
/*
* 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.diagnostic.errordialog;
import com.intellij.diagnostic.Developer;
import com.intellij.ide.ui.ListCellRendererWrapper;
import javax.swing.*;
class DeveloperRenderer extends ListCellRendererWrapper {
DeveloperRenderer(ListCellRenderer listCellRenderer) {
super(listCellRenderer);
}
@Override
public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
setText(value == null ? "<unavailable>" : ((Developer) value).getDisplayText());
}
}
@@ -38,6 +38,7 @@ public class ErrorBean {
private String message;
private String stackTrace;
private String description;
private Integer assigneeId;
private List<Attachment> attachments = Collections.emptyList();
@@ -107,4 +108,12 @@ public class ErrorBean {
public List<Attachment> getAttachments() {
return attachments;
}
public Integer getAssigneeId() {
return assigneeId;
}
public void setAssigneeId(Integer assigneeId) {
this.assigneeId = assigneeId;
}
}
@@ -144,6 +144,8 @@ public class ITNProxy {
params.add(Pair.create("error.description", error.getDescription()));
params.add(Pair.create("assignee.id", error.getAssigneeId() == null ? null : Integer.toString(error.getAssigneeId())));
for (Attachment attachment : error.getAttachments()) {
params.add(Pair.create("attachment.name", attachment.getName()));
params.add(Pair.create("attachment.value", attachment.getEncodedBytes()));