4961 make internal listeners for HexView and RejTreeView to avoid leaking this

This commit is contained in:
William Schaefer 2019-05-10 18:28:20 -04:00
parent 711ae01f20
commit 6feed3db22
2 changed files with 168 additions and 158 deletions

View File

@ -51,7 +51,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* estimate it to load three strings with length equal to the given ByteBuffer. * estimate it to load three strings with length equal to the given ByteBuffer.
* So its probably not good to use this view with large files. * So its probably not good to use this view with large files.
*/ */
final class HexView extends JPanel implements CaretListener { final class HexView extends JPanel {
private final static int DEFAULT_BYTES_PER_LINE = 0x10; private final static int DEFAULT_BYTES_PER_LINE = 0x10;
private final static char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; private final static char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
@ -59,6 +59,7 @@ final class HexView extends JPanel implements CaretListener {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final int bytesPerLine; private final int bytesPerLine;
private final ByteBuffer buf; private final ByteBuffer buf;
private final HexViewListener listener = new HexViewListener();
private final JTextComponent offsetView; private final JTextComponent offsetView;
private final JTextComponent hexView; private final JTextComponent hexView;
private final JTextComponent asciiView; private final JTextComponent asciiView;
@ -155,147 +156,151 @@ final class HexView extends JPanel implements CaretListener {
this.offsetView.setText(offsetSB.toString()); this.offsetView.setText(offsetSB.toString());
this.hexView.setText(hexSB.toString()); this.hexView.setText(hexSB.toString());
this.asciiView.setText(asciiSB.toString()); this.asciiView.setText(asciiSB.toString());
this.hexView.addCaretListener(this); this.hexView.addCaretListener(listener);
this.asciiView.addCaretListener(this); this.asciiView.addCaretListener(listener);
this.asciiView.setSelectedTextColor(this.asciiView.getForeground()); this.asciiView.setSelectedTextColor(this.asciiView.getForeground());
this.hexView.setSelectedTextColor(this.asciiView.getForeground()); this.hexView.setSelectedTextColor(this.asciiView.getForeground());
this.highlightColor = this.hexView.getSelectionColor(); this.highlightColor = this.hexView.getSelectionColor();
this.highlighterPainter = new DefaultHighlighter.DefaultHighlightPainter(this.highlightColor); this.highlighterPainter = new DefaultHighlighter.DefaultHighlightPainter(this.highlightColor);
} }
/** private class HexViewListener implements CaretListener {
* clearHighlight removes any colors applied to the text views.
*/
private void clearHighlight() {
this.asciiView.getHighlighter().removeAllHighlights();
this.hexView.getHighlighter().removeAllHighlights();
}
/** @Override
* setHighlight colors the given byte range. public void caretUpdate(CaretEvent e) {
* if (e.getMark() == e.getDot()) {
* @param startByte The starting byte index of the selection. this.clearHighlight();
* @param endByte The ending byte index of the selection. }
*/
private void setHighlight(int startByte, int endByte) {
int startRows = (startByte - (startByte % this.bytesPerLine)) / this.bytesPerLine;
int endRows = (endByte - (endByte % this.bytesPerLine)) / this.bytesPerLine;
this.clearHighlight(); if (e.getSource() == asciiView) {
int startByte = e.getMark();
int endByte = e.getDot();
try { if (startByte > endByte) {
this.asciiView.getHighlighter().addHighlight(startByte + startRows, endByte + endRows, this.highlighterPainter); int t = endByte;
this.hexView.getHighlighter().addHighlight((startByte * 3) + startRows, (endByte * 3) + endRows, this.highlighterPainter); endByte = startByte;
} catch (BadLocationException ex) { startByte = t;
logger.log(Level.WARNING, "bad location", ex); }
// the number of line endings before the start,end points
int startRows = (startByte - (startByte % bytesPerLine)) / bytesPerLine;
int endRows = (endByte - (endByte % bytesPerLine)) / bytesPerLine;
// the byte index of the start,end points in the ASCII view
startByte -= startRows;
endByte -= endRows;
// avoid the loop
if (asciiLastSelectionStart == startByte && asciiLastSelectionEnd == endByte) {
return;
}
asciiLastSelectionStart = startByte;
asciiLastSelectionEnd = endByte;
this.setSelection(startByte, endByte);
} else if (e.getSource() == hexView) {
int startByte = e.getMark();
int endByte = e.getDot();
if (startByte > endByte) {
int t = endByte;
endByte = startByte;
startByte = t;
}
// the number of line endings before the start,end points
int startRows = (startByte - (startByte % bytesPerLine)) / (3 * bytesPerLine);
int endRows = (endByte - (endByte % bytesPerLine)) / (3 * bytesPerLine);
// the byte index of the start,end points in the ASCII view
startByte -= startRows;
startByte /= 3;
endByte -= endRows;
endByte /= 3;
if (hexLastSelectionStart == startByte && hexLastSelectionEnd == endByte) {
return;
}
hexLastSelectionStart = startByte;
hexLastSelectionEnd = endByte;
this.setSelection(startByte, endByte);
} else {
logger.log(Level.INFO, "from unknown");
}
} }
}
/** /**
* setSelection sets the given byte range as "selected", which from a GUI * setSelection sets the given byte range as "selected", which from a
* perspective means the bytes are highlighted, and the status bar updated. * GUI perspective means the bytes are highlighted, and the status bar
* * updated.
* @param startByte The starting byte index of the selection. *
* @param endByte The ending byte index of the selection. * @param startByte The starting byte index of the selection.
*/ * @param endByte The ending byte index of the selection.
@Messages({"# {0} - startByteD", */
"# {1} - endByteD", @Messages({"# {0} - startByteD",
"# {2} - lengthD", "# {1} - endByteD",
"# {3} - startByteH", "# {2} - lengthD",
"# {4} - endByteH", "# {3} - startByteH",
"# {5} - lengthH", "# {4} - endByteH",
"HexView.statusTemplate.nonZeroLength=Selection: {0} to {1} (len: {2}) [{3} to {4} (len: {5})", "# {5} - lengthH",
"# {0} - startByteDec", "HexView.statusTemplate.nonZeroLength=Selection: {0} to {1} (len: {2}) [{3} to {4} (len: {5})",
"# {1} - startByteHex", "# {0} - startByteDec",
"HexView.statusTemplate.zeroLength=Position: {0} [{1}])"}) "# {1} - startByteHex",
private void setSelection(int startByte, int endByte) { "HexView.statusTemplate.zeroLength=Position: {0} [{1}])"})
this.setHighlight(startByte, endByte); private void setSelection(int startByte, int endByte) {
this.setHighlight(startByte, endByte);
if (startByte != endByte) { if (startByte != endByte) {
/** /**
* @param 1 Start * @param 1 Start
* @param 2 End * @param 2 End
* @param 3 Len * @param 3 Len
*/ */
int length = endByte - startByte; int length = endByte - startByte;
String text = Bundle.HexView_statusTemplate_nonZeroLength( String text = Bundle.HexView_statusTemplate_nonZeroLength(
startByte, startByte,
endByte, endByte,
length, length,
String.format("0x%1$x", startByte), String.format("0x%1$x", startByte),
String.format("0x%1$x", endByte), String.format("0x%1$x", endByte),
String.format("0x%1$x", length)); String.format("0x%1$x", length));
this.statusLabel.setText(text); statusLabel.setText(text);
} else { } else {
/** /**
* @param 1 Start * @param 1 Start
*/ */
String text = Bundle.HexView_statusTemplate_zeroLength(startByte, String.format("0x%1$x", startByte)); String text = Bundle.HexView_statusTemplate_zeroLength(startByte, String.format("0x%1$x", startByte));
this.statusLabel.setText(text); statusLabel.setText(text);
}
} }
}
@Override /**
public void caretUpdate(CaretEvent e) { * clearHighlight removes any colors applied to the text views.
if (e.getMark() == e.getDot()) { */
private void clearHighlight() {
asciiView.getHighlighter().removeAllHighlights();
hexView.getHighlighter().removeAllHighlights();
}
/**
* setHighlight colors the given byte range.
*
* @param startByte The starting byte index of the selection.
* @param endByte The ending byte index of the selection.
*/
private void setHighlight(int startByte, int endByte) {
int startRows = (startByte - (startByte % bytesPerLine)) / bytesPerLine;
int endRows = (endByte - (endByte % bytesPerLine)) / bytesPerLine;
this.clearHighlight(); this.clearHighlight();
}
if (e.getSource() == this.asciiView) { try {
int startByte = e.getMark(); asciiView.getHighlighter().addHighlight(startByte + startRows, endByte + endRows, highlighterPainter);
int endByte = e.getDot(); hexView.getHighlighter().addHighlight((startByte * 3) + startRows, (endByte * 3) + endRows, highlighterPainter);
} catch (BadLocationException ex) {
if (startByte > endByte) { logger.log(Level.WARNING, "bad location", ex);
int t = endByte;
endByte = startByte;
startByte = t;
} }
// the number of line endings before the start,end points
int startRows = (startByte - (startByte % this.bytesPerLine)) / this.bytesPerLine;
int endRows = (endByte - (endByte % this.bytesPerLine)) / this.bytesPerLine;
// the byte index of the start,end points in the ASCII view
startByte -= startRows;
endByte -= endRows;
// avoid the loop
if (asciiLastSelectionStart == startByte && asciiLastSelectionEnd == endByte) {
return;
}
asciiLastSelectionStart = startByte;
asciiLastSelectionEnd = endByte;
this.setSelection(startByte, endByte);
} else if (e.getSource() == this.hexView) {
int startByte = e.getMark();
int endByte = e.getDot();
if (startByte > endByte) {
int t = endByte;
endByte = startByte;
startByte = t;
}
// the number of line endings before the start,end points
int startRows = (startByte - (startByte % this.bytesPerLine)) / (3 * this.bytesPerLine);
int endRows = (endByte - (endByte % this.bytesPerLine)) / (3 * this.bytesPerLine);
// the byte index of the start,end points in the ASCII view
startByte -= startRows;
startByte /= 3;
endByte -= endRows;
endByte /= 3;
if (hexLastSelectionStart == startByte && hexLastSelectionEnd == endByte) {
return;
}
hexLastSelectionStart = startByte;
hexLastSelectionEnd = endByte;
this.setSelection(startByte, endByte);
} else {
logger.log(Level.INFO, "from unknown");
} }
} }
} }

View File

@ -38,14 +38,16 @@ import javax.swing.JTree;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
final class RejTreeView extends JScrollPane implements TreeExpansionListener, TreeSelectionListener { final class RejTreeView extends JScrollPane {
private static final Logger logger = Logger.getLogger(HexView.class.getName()); private static final Logger logger = Logger.getLogger(HexView.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final DefaultTreeModel treeModel; private final DefaultTreeModel treeModel;
private final RejTreeViewListener listener = new RejTreeViewListener();
private final RegistryHive hive; private final RegistryHive hive;
private final CopyOnWriteArrayList<RejTreeNodeSelectionListener> nodeSelectionListeners; private final CopyOnWriteArrayList<RejTreeNodeSelectionListener> nodeSelectionListeners;
private final JTree tree; private final JTree tree;
@NbBundle.Messages({"RejTreeView.failureValueName.text=PARSE FAILED"}) @NbBundle.Messages({"RejTreeView.failureValueName.text=PARSE FAILED"})
RejTreeView(RegistryHive hive) { RejTreeView(RegistryHive hive) {
this.hive = hive; this.hive = hive;
@ -63,13 +65,13 @@ final class RejTreeView extends JScrollPane implements TreeExpansionListener, Tr
this.treeModel.setAsksAllowsChildren(true); this.treeModel.setAsksAllowsChildren(true);
this.tree = new JTree(this.treeModel); this.tree = new JTree(this.treeModel);
this.tree.addTreeExpansionListener(this); this.tree.addTreeExpansionListener(listener);
this.tree.addTreeSelectionListener(this); this.tree.addTreeSelectionListener(listener);
// here's a bit of a hack to force the children to be loaded and shown // here's a bit of a hack to force the children to be loaded and shown
this.tree.collapsePath(new TreePath(rootNode.getPath())); this.tree.collapsePath(new TreePath(rootNode.getPath()));
this.tree.expandPath(new TreePath(rootNode.getPath())); this.tree.expandPath(new TreePath(rootNode.getPath()));
setViewportView(this.tree); setViewportView(this.tree);
setPreferredSize(new Dimension(350,20)); setPreferredSize(new Dimension(350, 20));
} }
/** /**
@ -83,31 +85,6 @@ final class RejTreeView extends JScrollPane implements TreeExpansionListener, Tr
return ret; return ret;
} }
@Override
public void treeExpanded(TreeExpansionEvent event) {
TreePath path = event.getPath();
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
if (node.getChildCount() == 0 && node.getUserObject() instanceof RejTreeNode) {
RejTreeNode n = (RejTreeNode) node.getUserObject();
for (RejTreeNode rejTreeNode : n.getChildren()) {
node.add(getTreeNode(rejTreeNode));
}
this.treeModel.nodeStructureChanged(node);
}
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
}
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath path = e.getPath();
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
this.triggerRejTreeNodeSelection((RejTreeNode) node.getUserObject());
}
void addRejTreeNodeSelectionListener(RejTreeNodeSelectionListener l) { void addRejTreeNodeSelectionListener(RejTreeNodeSelectionListener l) {
this.nodeSelectionListeners.add(l); this.nodeSelectionListeners.add(l);
} }
@ -116,10 +93,38 @@ final class RejTreeView extends JScrollPane implements TreeExpansionListener, Tr
this.nodeSelectionListeners.remove(l); this.nodeSelectionListeners.remove(l);
} }
void triggerRejTreeNodeSelection(RejTreeNode n) { private class RejTreeViewListener implements TreeExpansionListener, TreeSelectionListener {
RejTreeNodeSelectionEvent e = new RejTreeNodeSelectionEvent(n);
for (RejTreeNodeSelectionListener listener : this.nodeSelectionListeners) { @Override
listener.nodeSelected(e); public void treeExpanded(TreeExpansionEvent event) {
TreePath path = event.getPath();
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
if (node.getChildCount() == 0 && node.getUserObject() instanceof RejTreeNode) {
RejTreeNode n = (RejTreeNode) node.getUserObject();
for (RejTreeNode rejTreeNode : n.getChildren()) {
node.add(getTreeNode(rejTreeNode));
}
treeModel.nodeStructureChanged(node);
}
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
}
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath path = e.getPath();
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
this.triggerRejTreeNodeSelection((RejTreeNode) node.getUserObject());
}
void triggerRejTreeNodeSelection(RejTreeNode n) {
RejTreeNodeSelectionEvent e = new RejTreeNodeSelectionEvent(n);
for (RejTreeNodeSelectionListener listener : nodeSelectionListeners) {
listener.nodeSelected(e);
}
} }
} }
} }