mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-24 13:05:55 +08:00
Merge remote-tracking branch 'origin/GP-65-dragonmacher-error-dialog'
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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 docking;
|
||||
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* A dialog that is meant to be extended for showing exceptions
|
||||
*/
|
||||
abstract class AbstractErrDialog extends DialogComponentProvider {
|
||||
|
||||
// at some point, there are too many exceptions to show
|
||||
protected static final int MAX_EXCEPTIONS = 100;
|
||||
protected static final String TITLE_TEXT = "Multiple Errors";
|
||||
|
||||
private Callback closedCallback = Callback.dummy();
|
||||
|
||||
protected AbstractErrDialog(String title) {
|
||||
super(title, true, false, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void dialogClosed() {
|
||||
closedCallback.call();
|
||||
}
|
||||
|
||||
abstract void addException(String message, Throwable t);
|
||||
|
||||
abstract int getExceptionCount();
|
||||
|
||||
void setClosedCallback(Callback callback) {
|
||||
closedCallback = Callback.dummyIfNull(callback);
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,23 @@ package docking;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.io.*;
|
||||
|
||||
import docking.widgets.OkDialog;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.MultipleCauses;
|
||||
|
||||
public class DockingErrorDisplay implements ErrorDisplay {
|
||||
|
||||
private static final int TRACE_BUFFER_SIZE = 250;
|
||||
/**
|
||||
* Error dialog used to append exceptions.
|
||||
*
|
||||
* <p>While this dialog is showing all new exceptions will be added to the dialog. When
|
||||
* this dialog is closed, this reference will be cleared.
|
||||
*
|
||||
* <p>Note: all use of this variable <b>must be on the Swing thread</b> to avoid thread
|
||||
* visibility issues.
|
||||
*/
|
||||
private static AbstractErrDialog activeDialog;
|
||||
|
||||
ConsoleErrorDisplay consoleDisplay = new ConsoleErrorDisplay();
|
||||
|
||||
@@ -52,8 +59,8 @@ public class DockingErrorDisplay implements ErrorDisplay {
|
||||
|
||||
private void displayMessage(MessageType messageType, ErrorLogger errorLogger, Object originator,
|
||||
Component parent, String title, Object message, Throwable throwable) {
|
||||
int dialogType = OptionDialog.PLAIN_MESSAGE;
|
||||
|
||||
int dialogType = OptionDialog.PLAIN_MESSAGE;
|
||||
String messageString = message != null ? message.toString() : null;
|
||||
String rawMessage = HTMLUtilities.fromHTML(messageString);
|
||||
switch (messageType) {
|
||||
@@ -75,7 +82,7 @@ public class DockingErrorDisplay implements ErrorDisplay {
|
||||
break;
|
||||
}
|
||||
|
||||
showDialog(title, message, throwable, dialogType, messageString, getWindow(parent));
|
||||
showDialog(title, throwable, dialogType, messageString, getWindow(parent));
|
||||
}
|
||||
|
||||
private Component getWindow(Component component) {
|
||||
@@ -85,33 +92,36 @@ public class DockingErrorDisplay implements ErrorDisplay {
|
||||
return component;
|
||||
}
|
||||
|
||||
private void showDialog(final String title, final Object message, final Throwable throwable,
|
||||
private void showDialog(final String title, final Throwable throwable,
|
||||
final int dialogType, final String messageString, final Component parent) {
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(
|
||||
() -> doShowDialog(title, message, throwable, dialogType, messageString, parent));
|
||||
Swing.runIfSwingOrRunLater(
|
||||
() -> showDialogOnSwing(title, throwable, dialogType, messageString, parent));
|
||||
}
|
||||
|
||||
private void doShowDialog(final String title, final Object message, final Throwable throwable,
|
||||
private void showDialogOnSwing(String title, Throwable throwable,
|
||||
int dialogType, String messageString, Component parent) {
|
||||
DialogComponentProvider dialog = null;
|
||||
if (throwable != null) {
|
||||
dialog = createErrorDialog(title, message, throwable, messageString);
|
||||
|
||||
if (activeDialog != null) {
|
||||
activeDialog.addException(messageString, throwable);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
dialog = new OkDialog(title, messageString, dialogType);
|
||||
}
|
||||
DockingWindowManager.showDialog(parent, dialog);
|
||||
|
||||
activeDialog = createErrorDialog(title, throwable, messageString);
|
||||
activeDialog.setClosedCallback(() -> {
|
||||
activeDialog.setClosedCallback(null);
|
||||
activeDialog = null;
|
||||
});
|
||||
DockingWindowManager.showDialog(parent, activeDialog);
|
||||
}
|
||||
|
||||
private DialogComponentProvider createErrorDialog(final String title, final Object message,
|
||||
final Throwable throwable, String messageString) {
|
||||
private AbstractErrDialog createErrorDialog(String title, Throwable throwable,
|
||||
String messageString) {
|
||||
|
||||
if (containsMultipleCauses(throwable)) {
|
||||
return new ErrLogExpandableDialog(title, messageString, throwable);
|
||||
}
|
||||
|
||||
return ErrLogDialog.createExceptionDialog(title, messageString,
|
||||
buildStackTrace(throwable, message == null ? throwable.getMessage() : messageString));
|
||||
return ErrLogDialog.createExceptionDialog(title, messageString, throwable);
|
||||
}
|
||||
|
||||
private boolean containsMultipleCauses(Throwable throwable) {
|
||||
@@ -125,34 +135,4 @@ public class DockingErrorDisplay implements ErrorDisplay {
|
||||
|
||||
return containsMultipleCauses(throwable.getCause());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a displayable stack trace from a Throwable
|
||||
*
|
||||
* @param t the throwable
|
||||
* @param msg message prefix
|
||||
* @return multi-line stack trace
|
||||
*/
|
||||
private String buildStackTrace(Throwable t, String msg) {
|
||||
StringBuffer sb = new StringBuffer(TRACE_BUFFER_SIZE);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintStream ps = new PrintStream(baos);
|
||||
|
||||
if (msg != null) {
|
||||
ps.println(msg);
|
||||
}
|
||||
|
||||
t.printStackTrace(ps);
|
||||
sb.append(baos.toString());
|
||||
ps.close();
|
||||
try {
|
||||
baos.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// shouldn't happen--not really connected to the system
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,12 +32,12 @@ import docking.widgets.label.GHtmlLabel;
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.support.GTreeDragNDropHandler;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.exception.MultipleCauses;
|
||||
import ghidra.util.html.HTMLElement;
|
||||
import resources.ResourceManager;
|
||||
import util.CollectionUtils;
|
||||
|
||||
public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
public class ErrLogExpandableDialog extends AbstractErrDialog {
|
||||
public static ImageIcon IMG_REPORT = ResourceManager.loadImage("images/report.png");
|
||||
public static ImageIcon IMG_EXCEPTION = ResourceManager.loadImage("images/exception.png");
|
||||
public static ImageIcon IMG_FRAME_ELEMENT =
|
||||
@@ -53,113 +53,21 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
private static boolean showingDetails = false;
|
||||
|
||||
protected ReportRootNode root;
|
||||
protected GTree excTree;
|
||||
protected GTree tree;
|
||||
private List<Throwable> errors = new ArrayList<>();
|
||||
|
||||
/** This spacer addresses the optical impression that the message panel changes size when showing details */
|
||||
protected Component horizontalSpacer;
|
||||
protected JButton detailButton;
|
||||
protected JButton sendButton;
|
||||
protected boolean hasConsole = false;
|
||||
|
||||
protected JPopupMenu popup;
|
||||
|
||||
protected static class ExcTreeTransferHandler extends TransferHandler
|
||||
implements GTreeDragNDropHandler {
|
||||
protected ErrLogExpandableDialog(String title, String msg, Throwable throwable) {
|
||||
super(title);
|
||||
|
||||
protected ReportRootNode root;
|
||||
errors.add(throwable);
|
||||
|
||||
public ExcTreeTransferHandler(ReportRootNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> transferNodes) {
|
||||
return new DataFlavor[] { DataFlavor.stringFlavor };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
ArrayList<GTreeNode> nodes = new ArrayList<>();
|
||||
for (TreePath path : ((JTree) c).getSelectionPaths()) {
|
||||
nodes.add((GTreeNode) path.getLastPathComponent());
|
||||
}
|
||||
try {
|
||||
return new StringSelection(
|
||||
(String) getTransferData(nodes, DataFlavor.stringFlavor));
|
||||
}
|
||||
catch (UnsupportedFlavorException e) {
|
||||
Msg.debug(this, e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransferData(List<GTreeNode> transferNodes, DataFlavor flavor)
|
||||
throws UnsupportedFlavorException {
|
||||
if (flavor != DataFlavor.stringFlavor) {
|
||||
throw new UnsupportedFlavorException(flavor);
|
||||
}
|
||||
if (transferNodes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (transferNodes.size() == 1) {
|
||||
GTreeNode node = transferNodes.get(0);
|
||||
if (node instanceof NodeWithText) {
|
||||
return ((NodeWithText) node).collectReportText(transferNodes, 0).trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return root.collectReportText(transferNodes, 0).trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStartDragOk(List<GTreeNode> dragUserData, int dragAction) {
|
||||
for (GTreeNode node : dragUserData) {
|
||||
if (node instanceof NodeWithText) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSupportedDragActions() {
|
||||
return DnDConstants.ACTION_COPY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) {
|
||||
return COPY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDropSiteOk(GTreeNode destUserData, DataFlavor[] flavors, int dropAction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(GTreeNode destUserData, Transferable transferable, int dropAction) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public ErrLogExpandableDialog(String title, String msg, MultipleCauses mc) {
|
||||
this(title, msg, mc.getCauses(), null, true, true);
|
||||
}
|
||||
|
||||
public ErrLogExpandableDialog(String title, String msg, Throwable exc) {
|
||||
this(title, msg, Collections.singletonList(exc), HasConsoleText.Util.get(exc), true, true);
|
||||
}
|
||||
|
||||
public ErrLogExpandableDialog(String title, String msg, Collection<Throwable> report) {
|
||||
this(title, msg, report, null, false, false);
|
||||
}
|
||||
|
||||
protected ErrLogExpandableDialog(String title, String msg, Collection<Throwable> report,
|
||||
String console, boolean modal, boolean hasDismiss) {
|
||||
super(title, modal);
|
||||
|
||||
hasConsole = console != null;
|
||||
popup = new JPopupMenu();
|
||||
JMenuItem menuCopy = new JMenuItem("Copy");
|
||||
menuCopy.setActionCommand((String) TransferHandler.getCopyAction().getValue(Action.NAME));
|
||||
@@ -173,13 +81,12 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
msgPanel.setLayout(new BorderLayout(16, 16));
|
||||
msgPanel.setBorder(new EmptyBorder(16, 16, 16, 16));
|
||||
{
|
||||
JLabel msgText = new GHtmlLabel(getHTML(msg, report)) {
|
||||
JLabel msgText = new GHtmlLabel(getHTML(msg, CollectionUtils.asSet(throwable))) {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
// when rendering HTML the label can expand larger than the screen;
|
||||
// keep it reasonable
|
||||
// rendering HTML the label can expand larger than the screen; keep it reasonable
|
||||
Dimension size = super.getPreferredSize();
|
||||
size.width = 500;
|
||||
size.width = 300;
|
||||
return size;
|
||||
}
|
||||
};
|
||||
@@ -206,7 +113,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
msgPanel.add(buttonBox, BorderLayout.EAST);
|
||||
|
||||
horizontalSpacer = Box.createVerticalStrut(10);
|
||||
horizontalSpacer.setVisible(showingDetails | hasConsole);
|
||||
horizontalSpacer.setVisible(showingDetails);
|
||||
msgPanel.add(horizontalSpacer, BorderLayout.SOUTH);
|
||||
}
|
||||
workPanel.add(msgPanel, BorderLayout.NORTH);
|
||||
@@ -214,29 +121,8 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
Box workBox = Box.createVerticalBox();
|
||||
{
|
||||
|
||||
if (hasConsole) {
|
||||
JTextArea consoleText = new JTextArea(console);
|
||||
JScrollPane consoleScroll =
|
||||
new JScrollPane(consoleText, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
|
||||
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED) {
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension dim = super.getPreferredSize();
|
||||
dim.height = 400;
|
||||
dim.width = 800; // trial and error?
|
||||
return dim;
|
||||
}
|
||||
};
|
||||
consoleText.setEditable(false);
|
||||
consoleText.setBackground(Color.BLACK);
|
||||
consoleText.setForeground(Color.WHITE);
|
||||
consoleText.setFont(Font.decode("Monospaced"));
|
||||
workBox.add(consoleScroll);
|
||||
}
|
||||
|
||||
root = new ReportRootNode(getTitle(), report);
|
||||
excTree = new GTree(root) {
|
||||
root = new ReportRootNode(getTitle(), CollectionUtils.asSet(throwable));
|
||||
tree = new GTree(root) {
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
@@ -249,19 +135,19 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
|
||||
for (GTreeNode node : CollectionUtils.asIterable(root.iterator(true))) {
|
||||
if (node instanceof ReportExceptionNode) {
|
||||
excTree.expandTree(node);
|
||||
tree.expandTree(node);
|
||||
}
|
||||
}
|
||||
|
||||
excTree.setSelectedNode(root.getChild(0));
|
||||
excTree.setVisible(showingDetails);
|
||||
tree.setSelectedNode(root.getChild(0));
|
||||
tree.setVisible(showingDetails);
|
||||
ExcTreeTransferHandler handler = new ExcTreeTransferHandler(root);
|
||||
excTree.setDragNDropHandler(handler);
|
||||
excTree.setTransferHandler(handler);
|
||||
ActionMap map = excTree.getActionMap();
|
||||
tree.setDragNDropHandler(handler);
|
||||
tree.setTransferHandler(handler);
|
||||
ActionMap map = tree.getActionMap();
|
||||
map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
|
||||
TransferHandler.getCopyAction());
|
||||
excTree.addMouseListener(new MouseAdapter() {
|
||||
tree.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
maybeShowPopup(e);
|
||||
@@ -279,22 +165,19 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
}
|
||||
});
|
||||
|
||||
workBox.add(excTree);
|
||||
workBox.add(tree);
|
||||
}
|
||||
workPanel.add(workBox, BorderLayout.CENTER);
|
||||
repack();
|
||||
|
||||
addWorkPanel(workPanel);
|
||||
|
||||
if (hasDismiss) {
|
||||
addDismissButton();
|
||||
}
|
||||
addDismissButton();
|
||||
}
|
||||
|
||||
private String getHTML(String msg, Collection<Throwable> report) {
|
||||
|
||||
//
|
||||
// TODO
|
||||
// Usage question: The content herein will be escaped unless you call addHTMLContenet().
|
||||
// Further, clients can provide messages that contain HTML. Is there a
|
||||
// use case where we want to show escaped HTML content?
|
||||
@@ -332,14 +215,6 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
|
||||
String htmlTMsg = addBR(tMsg);
|
||||
body.addElement("p").addHTMLContent(htmlTMsg);
|
||||
if (t instanceof CausesImportant) { // I choose not to recurse
|
||||
HTMLElement ul = body.addElement("ul");
|
||||
for (Throwable ts : MultipleCauses.Util.iterCauses(t)) {
|
||||
String tsMsg = getMessage(ts);
|
||||
String htmlTSMsg = addBR(tsMsg);
|
||||
ul.addElement("li").addHTMLContent(htmlTSMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return html.toString();
|
||||
}
|
||||
@@ -357,15 +232,15 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
return t.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
void detailCallback() {
|
||||
private void detailCallback() {
|
||||
showingDetails = !showingDetails;
|
||||
excTree.setVisible(showingDetails);
|
||||
horizontalSpacer.setVisible(showingDetails | hasConsole);
|
||||
tree.setVisible(showingDetails);
|
||||
horizontalSpacer.setVisible(showingDetails);
|
||||
detailButton.setText(showingDetails ? CLOSE : DETAIL);
|
||||
repack();
|
||||
}
|
||||
|
||||
void sendCallback() {
|
||||
private void sendCallback() {
|
||||
String details = root.collectReportText(null, 0).trim();
|
||||
String title = getTitle();
|
||||
close();
|
||||
@@ -379,6 +254,24 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
return dim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addException(String message, Throwable t) {
|
||||
|
||||
int n = errors.size();
|
||||
if (n > MAX_EXCEPTIONS) {
|
||||
return;
|
||||
}
|
||||
|
||||
errors.add(t);
|
||||
setTitle(TITLE_TEXT + " (" + n + 1 + ")"); // signal the new error
|
||||
root.addNode(new ReportExceptionNode(t));
|
||||
}
|
||||
|
||||
@Override
|
||||
int getExceptionCount() {
|
||||
return root.getChildCount();
|
||||
}
|
||||
|
||||
static interface NodeWithText {
|
||||
public String getReportText();
|
||||
|
||||
@@ -528,9 +421,6 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
|
||||
@Override
|
||||
public String getReportText() {
|
||||
if (exc instanceof HasConsoleText) {
|
||||
return getName() + "\n" + HasConsoleText.Util.get(exc);
|
||||
}
|
||||
return getName();
|
||||
}
|
||||
|
||||
@@ -661,6 +551,87 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExcTreeTransferHandler extends TransferHandler
|
||||
implements GTreeDragNDropHandler {
|
||||
|
||||
protected ReportRootNode root;
|
||||
|
||||
public ExcTreeTransferHandler(ReportRootNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> transferNodes) {
|
||||
return new DataFlavor[] { DataFlavor.stringFlavor };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
ArrayList<GTreeNode> nodes = new ArrayList<>();
|
||||
for (TreePath path : ((JTree) c).getSelectionPaths()) {
|
||||
nodes.add((GTreeNode) path.getLastPathComponent());
|
||||
}
|
||||
try {
|
||||
return new StringSelection(
|
||||
(String) getTransferData(nodes, DataFlavor.stringFlavor));
|
||||
}
|
||||
catch (UnsupportedFlavorException e) {
|
||||
Msg.debug(this, e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransferData(List<GTreeNode> transferNodes, DataFlavor flavor)
|
||||
throws UnsupportedFlavorException {
|
||||
if (flavor != DataFlavor.stringFlavor) {
|
||||
throw new UnsupportedFlavorException(flavor);
|
||||
}
|
||||
if (transferNodes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (transferNodes.size() == 1) {
|
||||
GTreeNode node = transferNodes.get(0);
|
||||
if (node instanceof NodeWithText) {
|
||||
return ((NodeWithText) node).collectReportText(transferNodes, 0).trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return root.collectReportText(transferNodes, 0).trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStartDragOk(List<GTreeNode> dragUserData, int dragAction) {
|
||||
for (GTreeNode node : dragUserData) {
|
||||
if (node instanceof NodeWithText) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSupportedDragActions() {
|
||||
return DnDConstants.ACTION_COPY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) {
|
||||
return COPY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDropSiteOk(GTreeNode destUserData, DataFlavor[] flavors, int dropAction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(GTreeNode destUserData, Transferable transferable, int dropAction) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TransferActionListener implements ActionListener, PropertyChangeListener {
|
||||
|
||||
@@ -102,7 +102,7 @@ public class ScrollableTextArea extends JScrollPane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the text to the text area maintained in this scrollpane
|
||||
* Appends the text to the text area maintained in this scroll pane
|
||||
* @param text the text to append.
|
||||
*/
|
||||
public void append(String text) {
|
||||
@@ -111,13 +111,15 @@ public class ScrollableTextArea extends JScrollPane {
|
||||
|
||||
/**
|
||||
* Returns the number of lines current set in the text area
|
||||
* @return the count
|
||||
*/
|
||||
public int getLineCount() {
|
||||
return textArea.getLineCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tabsize set in the text area
|
||||
* Returns the tab size set in the text area
|
||||
* @return the size
|
||||
*/
|
||||
public int getTabSize() {
|
||||
return textArea.getTabSize();
|
||||
@@ -125,6 +127,7 @@ public class ScrollableTextArea extends JScrollPane {
|
||||
|
||||
/**
|
||||
* Returns the total area height of the text area (row height * line count)
|
||||
* @return the height
|
||||
*/
|
||||
public int getTextAreaHeight() {
|
||||
return (textArea.getAreaHeight());
|
||||
@@ -132,6 +135,7 @@ public class ScrollableTextArea extends JScrollPane {
|
||||
|
||||
/**
|
||||
* Returns the visible height of the text area
|
||||
* @return the height
|
||||
*/
|
||||
public int getTextVisibleHeight() {
|
||||
return textArea.getVisibleHeight();
|
||||
@@ -200,6 +204,7 @@ public class ScrollableTextArea extends JScrollPane {
|
||||
|
||||
/**
|
||||
* Returns the text contained within the text area
|
||||
* @return the text
|
||||
*/
|
||||
public String getText() {
|
||||
return textArea.getText();
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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 ghidra.util.table.column;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import docking.widgets.table.GTableCellRenderingData;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.util.DateUtils;
|
||||
|
||||
/**
|
||||
* A renderer for clients that wish to display a {@link Date} as a timestamp with the
|
||||
* date and time.
|
||||
*/
|
||||
public class DefaultTimestampRenderer extends AbstractGColumnRenderer<Date> {
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
Date value = (Date) data.getValue();
|
||||
|
||||
if (value != null) {
|
||||
label.setText(DateUtils.formatDateTimestamp(value));
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(Date t, Settings settings) {
|
||||
return DateUtils.formatDateTimestamp(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnConstraintFilterMode getColumnConstraintFilterMode() {
|
||||
// This allows for text filtering in the table and date filtering on columns
|
||||
return ColumnConstraintFilterMode.ALLOW_ALL_FILTERS;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class DockingErrorDisplayTest extends AbstractDockingTest {
|
||||
DockingErrorDisplay display = new DockingErrorDisplay();
|
||||
DefaultErrorLogger logger = new DefaultErrorLogger();
|
||||
Exception exception = new Exception("My test exception");
|
||||
doDisplay(display, logger, exception);
|
||||
reportException(display, logger, exception);
|
||||
|
||||
assertErrLogDialog();
|
||||
}
|
||||
@@ -46,11 +46,29 @@ public class DockingErrorDisplayTest extends AbstractDockingTest {
|
||||
DefaultErrorLogger logger = new DefaultErrorLogger();
|
||||
Exception nestedException = new Exception("My nested test exception");
|
||||
Exception exception = new Exception("My test exception", nestedException);
|
||||
doDisplay(display, logger, exception);
|
||||
reportException(display, logger, exception);
|
||||
|
||||
assertErrLogDialog();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultErrorDisplay_MultipleAsynchronousExceptions() {
|
||||
|
||||
DockingErrorDisplay display = new DockingErrorDisplay();
|
||||
DefaultErrorLogger logger = new DefaultErrorLogger();
|
||||
Exception exception = new Exception("My test exception");
|
||||
reportException(display, logger, exception);
|
||||
|
||||
ErrLogDialog dialog = getErrLogDialog();
|
||||
|
||||
assertExceptionCount(dialog, 1);
|
||||
|
||||
reportException(display, logger, new NullPointerException("It is null!"));
|
||||
assertExceptionCount(dialog, 2);
|
||||
|
||||
close(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleCausesErrorDisplay() {
|
||||
DockingErrorDisplay display = new DockingErrorDisplay();
|
||||
@@ -58,43 +76,51 @@ public class DockingErrorDisplayTest extends AbstractDockingTest {
|
||||
|
||||
Throwable firstCause = new Exception("My test exception - first cause");
|
||||
MultipleCauses exception = new MultipleCauses(Collections.singletonList(firstCause));
|
||||
doDisplay(display, logger, exception);
|
||||
reportException(display, logger, exception);
|
||||
|
||||
assertErrLogExpandableDialog();
|
||||
ErrLogExpandableDialog dialog = assertErrLogExpandableDialog();
|
||||
assertExceptionCount(dialog, 1);
|
||||
|
||||
reportException(display, logger, new NullPointerException("It is null!"));
|
||||
assertExceptionCount(dialog, 2);
|
||||
|
||||
close(dialog);
|
||||
}
|
||||
|
||||
private void assertErrLogExpandableDialog() {
|
||||
Window w = waitForWindow(TEST_TITLE, 2000);
|
||||
assertNotNull(w);
|
||||
private void assertExceptionCount(AbstractErrDialog errDialog, int n) {
|
||||
|
||||
final ErrLogExpandableDialog errDialog =
|
||||
int actual = errDialog.getExceptionCount();
|
||||
assertEquals(n, actual);
|
||||
}
|
||||
|
||||
private ErrLogExpandableDialog assertErrLogExpandableDialog() {
|
||||
Window w = waitForWindow(TEST_TITLE);
|
||||
|
||||
ErrLogExpandableDialog errDialog =
|
||||
getDialogComponentProvider(w, ErrLogExpandableDialog.class);
|
||||
assertNotNull(errDialog);
|
||||
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
errDialog.close();
|
||||
}
|
||||
});
|
||||
return errDialog;
|
||||
}
|
||||
|
||||
private void assertErrLogDialog() {
|
||||
Window w = waitForWindow(TEST_TITLE, 2000);
|
||||
Window w = waitForWindow(TEST_TITLE);
|
||||
assertNotNull(w);
|
||||
|
||||
final ErrLogDialog errDialog = getDialogComponentProvider(w, ErrLogDialog.class);
|
||||
ErrLogDialog errDialog = getDialogComponentProvider(w, ErrLogDialog.class);
|
||||
assertNotNull(errDialog);
|
||||
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
errDialog.close();
|
||||
}
|
||||
});
|
||||
close(errDialog);
|
||||
}
|
||||
|
||||
private void doDisplay(final DockingErrorDisplay display, final DefaultErrorLogger logger,
|
||||
private ErrLogDialog getErrLogDialog() {
|
||||
Window w = waitForWindow(TEST_TITLE);
|
||||
assertNotNull(w);
|
||||
|
||||
ErrLogDialog errDialog = getDialogComponentProvider(w, ErrLogDialog.class);
|
||||
assertNotNull(errDialog);
|
||||
return errDialog;
|
||||
}
|
||||
|
||||
private void reportException(final DockingErrorDisplay display, final DefaultErrorLogger logger,
|
||||
final Throwable throwable) {
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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 ghidra.util.exception;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public interface CausesImportant {
|
||||
public static class Util {
|
||||
public static String getMessages(Throwable exc) {
|
||||
if (exc instanceof CausesImportant) {
|
||||
StringBuilder result = new StringBuilder(exc.getMessage());
|
||||
for (Throwable cause : MultipleCauses.Util.iterCauses(exc)) {
|
||||
result.append(
|
||||
StringUtils.join(Arrays.asList(cause.getMessage().split("\n")), "\n\t"));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
return exc.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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 ghidra.util.exception;
|
||||
|
||||
public interface HasConsoleText {
|
||||
public String getConsoleText();
|
||||
|
||||
public static class Util {
|
||||
public static String get(Throwable exc) {
|
||||
if (exc instanceof HasConsoleText) {
|
||||
return ((HasConsoleText) exc).getConsoleText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-14
@@ -89,7 +89,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
|
||||
protected DBHandle dbHandle;
|
||||
private AddressMap addrMap;
|
||||
private ErrorHandler errHandler;
|
||||
private ErrorHandler errHandler = new DbErrorHandler();
|
||||
private DataTypeConflictHandler currentHandler;
|
||||
|
||||
private CategoryDB root;
|
||||
@@ -168,12 +168,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
*/
|
||||
protected DataTypeManagerDB() {
|
||||
this.lock = new Lock("DataTypeManagerDB");
|
||||
errHandler = new ErrorHandler() {
|
||||
@Override
|
||||
public void dbError(IOException e) {
|
||||
Msg.showError(this, null, "IO ERROR", e.getMessage(), e);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
dbHandle = new DBHandle();
|
||||
int id = startTransaction("");
|
||||
@@ -213,13 +208,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
") for read-only Datatype Archive: " + packedDBfile.getAbsolutePath());
|
||||
}
|
||||
|
||||
errHandler = new ErrorHandler() {
|
||||
@Override
|
||||
public void dbError(IOException e) {
|
||||
Msg.showError(this, null, "IO ERROR", e.getMessage(), e);
|
||||
}
|
||||
};
|
||||
|
||||
// Open packed database archive
|
||||
boolean openSuccess = false;
|
||||
PackedDatabase pdb = null;
|
||||
@@ -4089,6 +4077,20 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DbErrorHandler implements ErrorHandler {
|
||||
|
||||
@Override
|
||||
public void dbError(IOException e) {
|
||||
|
||||
String message = e.getMessage();
|
||||
if (e instanceof ClosedException) {
|
||||
message = "Data type archive is closed: " + getName();
|
||||
}
|
||||
|
||||
Msg.showError(this, null, "IO ERROR", message, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+21
-4
@@ -499,14 +499,31 @@ public class ReflectionUtilities {
|
||||
* @return the string
|
||||
*/
|
||||
public static String stackTraceToString(Throwable t) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
return stackTraceToString(t.getMessage(), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the given {@link Throwable} into a String version of its
|
||||
* {@link Throwable#printStackTrace()} method.
|
||||
*
|
||||
* @param message the preferred message to use. If null, the throwable message will be used
|
||||
* @param t the throwable
|
||||
* @return the string
|
||||
*/
|
||||
public static String stackTraceToString(String message, Throwable t) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintStream ps = new PrintStream(baos);
|
||||
|
||||
String msg = t.getMessage();
|
||||
if (msg != null) {
|
||||
ps.println(msg);
|
||||
if (message != null) {
|
||||
ps.println(message);
|
||||
}
|
||||
else {
|
||||
String throwableMessage = t.getMessage();
|
||||
if (throwableMessage != null) {
|
||||
ps.println(throwableMessage);
|
||||
}
|
||||
}
|
||||
|
||||
t.printStackTrace(ps);
|
||||
|
||||
@@ -55,8 +55,8 @@ public class IntroScreenShots extends GhidraScreenShotGenerator {
|
||||
@Test
|
||||
public void testErr_Dialog() {
|
||||
runSwing(() -> {
|
||||
ErrLogDialog dialog = ErrLogDialog.createLogMessageDialog("Unexpected Error",
|
||||
"Oops, this is really bad!", "");
|
||||
ErrLogDialog dialog = ErrLogDialog.createExceptionDialog("Unexpected Error",
|
||||
"Oops, this is really bad!", new Throwable());
|
||||
DockingWindowManager.showDialog(null, dialog);
|
||||
}, false);
|
||||
waitForSwing();
|
||||
|
||||
Reference in New Issue
Block a user