mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GT-3226 - Symbol Table - Performance improvements - disable filtering on
some columns; turn on 'Name Only' filtering by default; disable sorting by default; remove use of column mapping where not needed; fixed caching for name column
This commit is contained in:
parent
665a83d6c0
commit
fc67c6aaeb
26 changed files with 687 additions and 403 deletions
Binary file not shown.
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 58 KiB |
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The columns in the table are:</P>
|
<P>Some of the columns in the table are:</P>
|
||||||
|
|
||||||
<CENTER>
|
<CENTER>
|
||||||
<TABLE border="1" width="73%" height="159">
|
<TABLE border="1" width="73%" height="159">
|
||||||
|
@ -54,13 +54,6 @@
|
||||||
<TD width="89%" height="22">Symbol type (Function, External, Class, etc).</TD>
|
<TD width="89%" height="22">Symbol type (Function, External, Class, etc).</TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
|
||||||
<TR>
|
|
||||||
<TD width="11%" height="15"><B>Datatype</B></TD>
|
|
||||||
|
|
||||||
<TD width="89%" height="15">Datatype (i.e., byte, float, etc.) applied at symbol
|
|
||||||
address.</TD>
|
|
||||||
</TR>
|
|
||||||
|
|
||||||
<TR>
|
<TR>
|
||||||
<TD width="11%" height="21"><B>Namespace</B></TD>
|
<TD width="11%" height="21"><B>Namespace</B></TD>
|
||||||
|
|
||||||
|
@ -89,7 +82,15 @@
|
||||||
<P><I><IMG src="../../shared/note.png"> You can sort the table on any column by clicking on
|
<P><I><IMG src="../../shared/note.png"> You can sort the table on any column by clicking on
|
||||||
the column header. The column can be sorted in ascending or descending order.</I></P>
|
the column header. The column can be sorted in ascending or descending order.</I></P>
|
||||||
|
|
||||||
<P><I><IMG src="../../shared/tip.png"></I> The colors for <B><FONT color="#ff0000">bad
|
<BLOCKQUOTE>
|
||||||
|
<P><IMG src="../../shared/warning.png">
|
||||||
|
Sorting a column in the symbol table when the program has a large number of symbols
|
||||||
|
can be slow. If you do not need sorting, then you can control-click the sorted column
|
||||||
|
to remove the sort.
|
||||||
|
</p>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<P><IMG src="../../shared/tip.png"> The colors for <B><FONT color="#ff0000">bad
|
||||||
references</FONT></B>, <B><FONT color="#ff00ff">entry points</FONT></B>, <B>dead code</B>,
|
references</FONT></B>, <B><FONT color="#ff00ff">entry points</FONT></B>, <B>dead code</B>,
|
||||||
<B><FONT color="#c0c0c0">offcut code</FONT></B>, <B><FONT color="#0000ff">function
|
<B><FONT color="#c0c0c0">offcut code</FONT></B>, <B><FONT color="#0000ff">function
|
||||||
names</FONT></B>, <B><FONT color="#008040">local symbols</FONT></B>, <B><FONT color=
|
names</FONT></B>, <B><FONT color="#008040">local symbols</FONT></B>, <B><FONT color=
|
||||||
|
@ -112,6 +113,15 @@
|
||||||
|
|
||||||
<P>The <I>Name Only</I> checkbox allows you to toggle whether to filter on only the name
|
<P>The <I>Name Only</I> checkbox allows you to toggle whether to filter on only the name
|
||||||
column or all the columns in the table.</P>
|
column or all the columns in the table.</P>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P><IMG src="../../shared/tip.png">
|
||||||
|
Filtering the symbol table when the program has a large number of symbols can be slow.
|
||||||
|
When only filtering on the symbol name, via the checkbox above, the overall filtering
|
||||||
|
is considerably faster.
|
||||||
|
</p>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
<P>The filter text field will accept basic globbing characters such as '<B>*</B>' and
|
<P>The filter text field will accept basic globbing characters such as '<B>*</B>' and
|
||||||
'<B>?</B>' within the filter text unless the "Regular Expression" filter strategy is
|
'<B>?</B>' within the filter text unless the "Regular Expression" filter strategy is
|
||||||
|
|
|
@ -83,7 +83,8 @@
|
||||||
<p><img border="0" src="../../shared/tip.png" alt="">
|
<p><img border="0" src="../../shared/tip.png" alt="">
|
||||||
To remove a sort column from a multiple column sort, Ctrl-left-click that column.
|
To remove a sort column from a multiple column sort, Ctrl-left-click that column.
|
||||||
This will even work when only one column is sorted, <b><i>thus effectively disabling
|
This will even work when only one column is sorted, <b><i>thus effectively disabling
|
||||||
sorting for the table</i></b>.
|
sorting for the table</i></b>. <u>Disabling sorting can greatly increase the
|
||||||
|
table's performance when the number of rows is large</u>
|
||||||
</p>
|
</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
||||||
|
@ -212,5 +213,11 @@
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
<P class="relatedtopic">Related Topics</P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A href="help/topics/Trees/GhidraTreeFilter.html">Table Filtering</A></LI>
|
||||||
|
</UL>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -15,7 +15,11 @@
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>Most trees and tables in Ghidra support filtering and have a text filter located at the
|
<P>Most trees and tables in Ghidra support filtering and have a text filter located at the
|
||||||
bottom of the tree or table. Below is an example from the Data Type Manager. The filter is
|
bottom of the tree or table. Tables also support the concept of
|
||||||
|
<A HREF="#Column_Filters">Column Filters</A> described below.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<P>Below is an example from the Data Type Manager. The filter is
|
||||||
currently set to "Starts With", but you can select a different filter strategy.</P>
|
currently set to "Starts With", but you can select a different filter strategy.</P>
|
||||||
|
|
||||||
<CENTER>
|
<CENTER>
|
||||||
|
@ -270,6 +274,17 @@
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>Most filterable tables in Ghidra support advanced filtering based on column values. This
|
<P>Most filterable tables in Ghidra support advanced filtering based on column values. This
|
||||||
allows for complex filtering where you can logically combine column specific clauses.</P>
|
allows for complex filtering where you can logically combine column specific clauses.</P>
|
||||||
|
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P><IMG src="../../shared/warning.png">
|
||||||
|
Some columns in tables are not filterable via the filter text field below the table.
|
||||||
|
For example, many numeric columns are ignored by the text filter because they can be
|
||||||
|
slow to calculate and they are better filtered by using a range filter, as is available
|
||||||
|
using column filters.
|
||||||
|
</p>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<BR>
|
<BR>
|
||||||
|
|
||||||
|
|
||||||
|
@ -458,5 +473,20 @@
|
||||||
filter.</P>
|
filter.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- extra space at the bottom of the page (for readability) -->
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<P class="relatedtopic">Related Topics</P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI><A href="help/topics/Tables/GhidraTableHeaders.html">Table Sorting</A></LI>
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
|
||||||
</BODY>
|
</BODY>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,12 +15,20 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functionwindow;
|
package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
|
||||||
class FunctionRowObject implements Comparable<FunctionRowObject> {
|
class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||||
|
|
||||||
|
private final Function function;
|
||||||
private final long key;
|
private final long key;
|
||||||
|
|
||||||
FunctionRowObject(long key) {
|
FunctionRowObject(Function function) {
|
||||||
this.key = key;
|
this.function = function;
|
||||||
|
this.key = function.getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
Function getFunction() {
|
||||||
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -34,15 +41,19 @@ class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
if (obj == null)
|
}
|
||||||
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
if (getClass() != obj.getClass())
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
FunctionRowObject other = (FunctionRowObject) obj;
|
FunctionRowObject other = (FunctionRowObject) obj;
|
||||||
if (key != other.key)
|
if (key != other.key) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,17 @@ package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||||
|
|
||||||
public class FunctionRowObjectToAddressTableRowMapper extends
|
public class FunctionRowObjectToAddressTableRowMapper
|
||||||
ProgramLocationTableRowMapper<FunctionRowObject, Address> {
|
extends ProgramLocationTableRowMapper<FunctionRowObject, Address> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address map(FunctionRowObject rowObject, Program program, ServiceProvider serviceProvider) {
|
public Address map(FunctionRowObject rowObject, Program program,
|
||||||
FunctionManager functionManager = program.getFunctionManager();
|
ServiceProvider serviceProvider) {
|
||||||
Function function = functionManager.getFunction(rowObject.getKey());
|
Function function = rowObject.getFunction();
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,18 @@
|
||||||
package ghidra.app.plugin.core.functionwindow;
|
package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||||
|
|
||||||
public class FunctionRowObjectToFunctionTableRowMapper extends
|
public class FunctionRowObjectToFunctionTableRowMapper
|
||||||
ProgramLocationTableRowMapper<FunctionRowObject, Function> {
|
extends ProgramLocationTableRowMapper<FunctionRowObject, Function> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Function map(FunctionRowObject rowObject, Program program, ServiceProvider serviceProvider) {
|
public Function map(FunctionRowObject rowObject, Program program,
|
||||||
FunctionManager functionManager = program.getFunctionManager();
|
ServiceProvider serviceProvider) {
|
||||||
return functionManager.getFunction(rowObject.getKey());
|
Function function = rowObject.getFunction();
|
||||||
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,19 @@
|
||||||
package ghidra.app.plugin.core.functionwindow;
|
package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.FunctionSignatureFieldLocation;
|
import ghidra.program.util.FunctionSignatureFieldLocation;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||||
|
|
||||||
public class FunctionRowObjectToProgramLocationTableRowMapper extends
|
public class FunctionRowObjectToProgramLocationTableRowMapper
|
||||||
ProgramLocationTableRowMapper<FunctionRowObject, ProgramLocation> {
|
extends ProgramLocationTableRowMapper<FunctionRowObject, ProgramLocation> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProgramLocation map(FunctionRowObject rowObject, Program program,
|
public ProgramLocation map(FunctionRowObject rowObject, Program program,
|
||||||
ServiceProvider serviceProvider) {
|
ServiceProvider serviceProvider) {
|
||||||
FunctionManager functionManager = program.getFunctionManager();
|
Function function = rowObject.getFunction();
|
||||||
Function function = functionManager.getFunction(rowObject.getKey());
|
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@ package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
import docking.widgets.table.DiscoverableTableUtils;
|
import docking.widgets.table.DiscoverableTableUtils;
|
||||||
import docking.widgets.table.TableColumnDescriptor;
|
import docking.widgets.table.TableColumnDescriptor;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.util.LongIterator;
|
import ghidra.util.LongIterator;
|
||||||
|
@ -43,20 +45,19 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TableColumnDescriptor<FunctionRowObject> createTableColumnDescriptor() {
|
protected TableColumnDescriptor<FunctionRowObject> createTableColumnDescriptor() {
|
||||||
TableColumnDescriptor<FunctionRowObject> descriptor =
|
TableColumnDescriptor<FunctionRowObject> descriptor = new TableColumnDescriptor<>();
|
||||||
new TableColumnDescriptor<>();
|
|
||||||
|
|
||||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
descriptor.addVisibleColumn(new NameTableColumn());
|
||||||
new LabelTableColumn()));
|
|
||||||
descriptor.addVisibleColumn(
|
descriptor.addVisibleColumn(
|
||||||
DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true);
|
DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true);
|
||||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
descriptor.addVisibleColumn(
|
||||||
new FunctionSignatureTableColumn()));
|
DiscoverableTableUtils.adaptColumForModel(this, new FunctionSignatureTableColumn()));
|
||||||
//make function size a default column so that a user who wants to know the function size
|
|
||||||
//won't add the "Byte Count" column (which only display the number of bytes in the code
|
// Make function size a default column so that a user who wants to know the function size
|
||||||
//unit at the function's entry point).
|
// won't add the "Byte Count" column (which only display the number of bytes in the code
|
||||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
// unit at the function's entry point).
|
||||||
new FunctionBodySizeTableColumn()));
|
descriptor.addVisibleColumn(
|
||||||
|
DiscoverableTableUtils.adaptColumForModel(this, new FunctionBodySizeTableColumn()));
|
||||||
|
|
||||||
// Function tag column is not something widely used, so make hidden by default
|
// Function tag column is not something widely used, so make hidden by default
|
||||||
descriptor.addHiddenColumn(
|
descriptor.addHiddenColumn(
|
||||||
|
@ -91,13 +92,15 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||||
if (functionMgr != null) {
|
if (functionMgr != null) {
|
||||||
it = new FunctionKeyIterator(functionMgr);
|
it = new FunctionKeyIterator(functionMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor.initialize(getKeyCount());
|
monitor.initialize(getKeyCount());
|
||||||
int progress = 0;
|
int progress = 0;
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
monitor.setProgress(progress++);
|
monitor.setProgress(progress++);
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
long key = it.next();
|
long key = it.next();
|
||||||
accumulator.add(new FunctionRowObject(key));
|
Function f = functionMgr.getFunction(key);
|
||||||
|
accumulator.add(new FunctionRowObject(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,16 +136,16 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void functionAdded(Function function) {
|
void functionAdded(Function f) {
|
||||||
addObject(new FunctionRowObject(function.getID()));
|
addObject(new FunctionRowObject(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void functionRemoved(Function function) {
|
void functionRemoved(Function f) {
|
||||||
removeObject(new FunctionRowObject(function.getID()));
|
removeObject(new FunctionRowObject(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(Function function) {
|
void update(Function f) {
|
||||||
updateObject(new FunctionRowObject(function.getID()));
|
updateObject(new FunctionRowObject(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -152,4 +155,25 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||||
Function function = functionManager.getFunction(rowObject.getKey());
|
Function function = functionManager.getFunction(rowObject.getKey());
|
||||||
return function != null ? function.getEntryPoint() : null;
|
return function != null ? function.getEntryPoint() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NameTableColumn
|
||||||
|
extends AbstractProgramBasedDynamicTableColumn<FunctionRowObject, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName() {
|
||||||
|
return "Name";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue(FunctionRowObject rowObject, Settings settings, Program data,
|
||||||
|
ServiceProvider sp) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
Function function = rowObject.getFunction();
|
||||||
|
if (function == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return function.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,36 +15,38 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.symtable;
|
package ghidra.app.plugin.core.symtable;
|
||||||
|
|
||||||
import ghidra.program.model.symbol.Symbol;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
|
||||||
class SymbolEditor extends DefaultCellEditor {
|
class SymbolEditor extends DefaultCellEditor {
|
||||||
|
|
||||||
private JTextField symbolField = null;
|
private JTextField symbolField = null;
|
||||||
|
|
||||||
SymbolEditor() {
|
SymbolEditor() {
|
||||||
super(new JTextField());
|
super(new JTextField());
|
||||||
symbolField = (JTextField)super.getComponent();
|
symbolField = (JTextField) super.getComponent();
|
||||||
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCellEditorValue() {
|
public Object getCellEditorValue() {
|
||||||
return symbolField.getText().trim();
|
return symbolField.getText().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
|
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
|
||||||
if (value instanceof Symbol) {
|
int row, int column) {
|
||||||
Symbol symbol = (Symbol) value;
|
if (value instanceof SymbolTableNameValue) {
|
||||||
symbolField.setText(symbol.getName());
|
SymbolTableNameValue cellValue = (SymbolTableNameValue) value;
|
||||||
}
|
Symbol symbol = cellValue.getSymbol();
|
||||||
else {
|
symbolField.setText(symbol.getName());
|
||||||
symbolField.setText("");
|
}
|
||||||
}
|
else {
|
||||||
return symbolField;
|
symbolField.setText("");
|
||||||
}
|
}
|
||||||
|
return symbolField;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ import ghidra.util.table.*;
|
||||||
|
|
||||||
class SymbolPanel extends JPanel {
|
class SymbolPanel extends JPanel {
|
||||||
|
|
||||||
private static final boolean FILTER_NAME_ONLY_DEFAULT = false;
|
private static final boolean FILTER_NAME_ONLY_DEFAULT = true;
|
||||||
|
|
||||||
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
|
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ class SymbolPanel extends JPanel {
|
||||||
private GhidraTable symTable;
|
private GhidraTable symTable;
|
||||||
private TableModelListener listener;
|
private TableModelListener listener;
|
||||||
private FilterDialog filterDialog;
|
private FilterDialog filterDialog;
|
||||||
private PluginTool tool;
|
|
||||||
private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel;
|
private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel;
|
||||||
private GhidraTableFilterPanel<SymbolRowObject> tableFilterPanel;
|
private GhidraTableFilterPanel<SymbolRowObject> tableFilterPanel;
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ class SymbolPanel extends JPanel {
|
||||||
final PluginTool tool, GoToService gotoService) {
|
final PluginTool tool, GoToService gotoService) {
|
||||||
|
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
this.tool = tool;
|
|
||||||
this.symProvider = provider;
|
this.symProvider = provider;
|
||||||
this.tableModel = model;
|
this.tableModel = model;
|
||||||
|
|
||||||
|
@ -174,14 +173,12 @@ class SymbolPanel extends JPanel {
|
||||||
|
|
||||||
if (selectedRowCount == 1) {
|
if (selectedRowCount == 1) {
|
||||||
int selectedRow = symTable.getSelectedRow();
|
int selectedRow = symTable.getSelectedRow();
|
||||||
Object obj = symTable.getValueAt(selectedRow,
|
Symbol symbol = symProvider.getSymbolForRow(selectedRow);
|
||||||
symTable.convertColumnIndexToView(SymbolTableModel.LABEL_COL));
|
symProvider.setCurrentSymbol(symbol);
|
||||||
if (obj instanceof Symbol) {
|
}
|
||||||
symProvider.setCurrentSymbol((Symbol) obj);
|
else {
|
||||||
return;
|
symProvider.setCurrentSymbol(null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
symProvider.setCurrentSymbol(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int getActualSymbolCount() {
|
int getActualSymbolCount() {
|
||||||
|
@ -212,9 +209,10 @@ class SymbolPanel extends JPanel {
|
||||||
@Override
|
@Override
|
||||||
public List<String> transform(SymbolRowObject rowObject) {
|
public List<String> transform(SymbolRowObject rowObject) {
|
||||||
list.clear();
|
list.clear();
|
||||||
Symbol symbol = model.getSymbolForRowObject(rowObject);
|
Object value = model.getColumnValueForRow(rowObject, SymbolTableModel.LABEL_COL);
|
||||||
if (symbol != null) {
|
if (value != null) {
|
||||||
list.add(symbol.getName());
|
// the toString() returns the value for the symbol, which may be cached
|
||||||
|
list.add(value.toString());
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,10 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Symbol getSymbolForRow(int row) {
|
||||||
|
return symbolKeyModel.getRowObject(row).getSymbol();
|
||||||
|
}
|
||||||
|
|
||||||
void setCurrentSymbol(Symbol symbol) {
|
void setCurrentSymbol(Symbol symbol) {
|
||||||
plugin.getReferenceProvider().setCurrentSymbol(symbol);
|
plugin.getReferenceProvider().setCurrentSymbol(symbol);
|
||||||
}
|
}
|
||||||
|
@ -126,9 +130,15 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void symbolRemoved(long symbolID) {
|
void symbolRemoved(Symbol s) {
|
||||||
if (isVisible()) {
|
if (isVisible()) {
|
||||||
symbolKeyModel.symbolRemoved(symbolID);
|
symbolKeyModel.symbolRemoved(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbolRemoved(long symbolId) {
|
||||||
|
if (isVisible()) {
|
||||||
|
symbolKeyModel.symbolRemoved(symbolId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class SymbolRenderer extends GhidraTableCellRenderer {
|
||||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||||
|
|
||||||
super.getTableCellRendererComponent(data);
|
super.getTableCellRendererComponent(data);
|
||||||
|
|
||||||
Object value = data.getValue();
|
Object value = data.getValue();
|
||||||
int column = data.getColumnModelIndex();
|
int column = data.getColumnModelIndex();
|
||||||
boolean isSelected = data.isSelected();
|
boolean isSelected = data.isSelected();
|
||||||
|
@ -52,6 +52,10 @@ class SymbolRenderer extends GhidraTableCellRenderer {
|
||||||
if (value == null && column == SymbolTableModel.LABEL_COL) {
|
if (value == null && column == SymbolTableModel.LABEL_COL) {
|
||||||
setText("<< REMOVED >>");
|
setText("<< REMOVED >>");
|
||||||
}
|
}
|
||||||
|
else if (value instanceof SymbolTableNameValue) {
|
||||||
|
Symbol symbol = ((SymbolTableNameValue) value).getSymbol();
|
||||||
|
handleSymbol(symbol, isSelected);
|
||||||
|
}
|
||||||
else if (value instanceof Symbol) {
|
else if (value instanceof Symbol) {
|
||||||
handleSymbol(value, isSelected);
|
handleSymbol(value, isSelected);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,12 +15,28 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.symtable;
|
package ghidra.app.plugin.core.symtable;
|
||||||
|
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
|
||||||
class SymbolRowObject implements Comparable<SymbolRowObject> {
|
class SymbolRowObject implements Comparable<SymbolRowObject> {
|
||||||
|
|
||||||
|
// symbol can be null after it is deleted
|
||||||
|
private final Symbol symbol;
|
||||||
private final long key;
|
private final long key;
|
||||||
|
|
||||||
SymbolRowObject(long key) {
|
SymbolRowObject(Symbol s) {
|
||||||
this.key = key;
|
this.symbol = s;
|
||||||
|
this.key = s.getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this constructor is used to create a row object to serve as a key for deleting items
|
||||||
|
// in the model after a symbol has been deleted
|
||||||
|
SymbolRowObject(long symbolId) {
|
||||||
|
this.symbol = null;
|
||||||
|
this.key = symbolId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol getSymbol() {
|
||||||
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getKey() {
|
long getKey() {
|
||||||
|
@ -38,15 +53,19 @@ class SymbolRowObject implements Comparable<SymbolRowObject> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
if (obj == null)
|
}
|
||||||
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
if (getClass() != obj.getClass())
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
SymbolRowObject other = (SymbolRowObject) obj;
|
SymbolRowObject other = (SymbolRowObject) obj;
|
||||||
if (key != other.key)
|
if (key != other.key) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.symtable;
|
package ghidra.app.plugin.core.symtable;
|
||||||
|
|
||||||
import java.util.ConcurrentModificationException;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
|
@ -35,7 +35,6 @@ import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.datastruct.Accumulator;
|
import ghidra.util.datastruct.Accumulator;
|
||||||
import ghidra.util.datastruct.LongArrayList;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.table.AddressBasedTableModel;
|
import ghidra.util.table.AddressBasedTableModel;
|
||||||
import ghidra.util.table.column.*;
|
import ghidra.util.table.column.*;
|
||||||
|
@ -46,19 +45,11 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
static final int LABEL_COL = 0;
|
static final int LABEL_COL = 0;
|
||||||
static final int LOCATION_COL = 1;
|
static final int LOCATION_COL = 1;
|
||||||
static final int TYPE_COL = 2;
|
static final int TYPE_COL = 2;
|
||||||
static final int DATATYPE_COL = 3;
|
static final int DATA_TYPE_COL = 3;
|
||||||
static final int NAMESPACE_COL = 4;
|
static final int NAMESPACE_COL = 4;
|
||||||
static final int SOURCE_COL = 5;
|
static final int SOURCE_COL = 5;
|
||||||
static final int REFS_COL = 6;
|
static final int REFS_COL = 6;
|
||||||
|
|
||||||
static final String LABEL_COL_NAME = "Labels";
|
|
||||||
static final String LOCATION_COL_NAME = "Location";
|
|
||||||
static final String TYPE_COL_NAME = "Type";
|
|
||||||
static final String DATATYPE_COL_NAME = "Datatype";
|
|
||||||
static final String REFS_COL_NAME = "# Refs";
|
|
||||||
static final String NAMESPACE_COL_NAME = "Namespace";
|
|
||||||
static final String SOURCE_COL_NAME = "Source";
|
|
||||||
|
|
||||||
private SymbolProvider provider;
|
private SymbolProvider provider;
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private SymbolTable symbolTable;
|
private SymbolTable symbolTable;
|
||||||
|
@ -71,6 +62,9 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
this.filter = new NewSymbolFilter();
|
this.filter = new NewSymbolFilter();
|
||||||
|
|
||||||
|
// leave off default sorting, as this can be slow; the user can sort as desired
|
||||||
|
setDefaultTableSortState(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -79,9 +73,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
|
|
||||||
descriptor.addVisibleColumn(new NameTableColumn(), 1, true);
|
descriptor.addVisibleColumn(new NameTableColumn(), 1, true);
|
||||||
descriptor.addVisibleColumn(new LocationTableColumn());
|
descriptor.addVisibleColumn(new LocationTableColumn());
|
||||||
descriptor.addVisibleColumn(
|
descriptor.addVisibleColumn(new SymbolTypeTableColumn());
|
||||||
DiscoverableTableUtils.adaptColumForModel(this, new SymbolTypeTableColumn()));
|
descriptor.addHiddenColumn(new DataTypeTableColumn());
|
||||||
descriptor.addVisibleColumn(new DataTypeTableColumn());
|
|
||||||
descriptor.addVisibleColumn(new NamespaceTableColumn());
|
descriptor.addVisibleColumn(new NamespaceTableColumn());
|
||||||
descriptor.addVisibleColumn(new SourceTableColumn());
|
descriptor.addVisibleColumn(new SourceTableColumn());
|
||||||
descriptor.addVisibleColumn(new ReferenceCountTableColumn());
|
descriptor.addVisibleColumn(new ReferenceCountTableColumn());
|
||||||
|
@ -163,7 +156,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
Symbol s = it.next();
|
Symbol s = it.next();
|
||||||
if (filter.accepts(s, getProgram())) {
|
if (filter.accepts(s, getProgram())) {
|
||||||
accumulator.add(new SymbolRowObject(s.getID()));
|
accumulator.add(new SymbolRowObject(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filter.acceptsDefaultLabelSymbols()) {
|
if (filter.acceptsDefaultLabelSymbols()) {
|
||||||
|
@ -175,7 +168,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
Address a = addrIt.next();
|
Address a = addrIt.next();
|
||||||
Symbol s = symbolTable.getPrimarySymbol(a);
|
Symbol s = symbolTable.getPrimarySymbol(a);
|
||||||
if (s.isDynamic() && filter.accepts(s, getProgram())) {
|
if (s.isDynamic() && filter.accepts(s, getProgram())) {
|
||||||
accumulator.add(new SymbolRowObject(s.getID()));
|
accumulator.add(new SymbolRowObject(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,34 +199,25 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (columnIndex) {
|
if (columnIndex == LABEL_COL) {
|
||||||
case LABEL_COL:
|
String newName = aValue.toString();
|
||||||
try {
|
if (!symbol.getName().equals(newName)) {
|
||||||
String newName = aValue.toString();
|
Command renameCmd = new RenameLabelCmd(symbol.getAddress(), symbol.getName(),
|
||||||
if (!symbol.getName().equals(newName)) {
|
newName, symbol.getParentNamespace(), SourceType.USER_DEFINED);
|
||||||
Command renameCmd =
|
|
||||||
new RenameLabelCmd(symbol.getAddress(), symbol.getName(), newName,
|
|
||||||
symbol.getParentNamespace(), SourceType.USER_DEFINED);
|
|
||||||
|
|
||||||
if (!tool.execute(renameCmd, getProgram())) {
|
if (!tool.execute(renameCmd, getProgram())) {
|
||||||
Msg.showError(getClass(), provider.getComponent(),
|
Msg.showError(getClass(), provider.getComponent(), "Error Renaming Symbol",
|
||||||
"Error Renaming Symbol", renameCmd.getStatusMsg());
|
renameCmd.getStatusMsg());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (ConcurrentModificationException exc) {
|
}
|
||||||
Msg.showError(getClass(), provider.getComponent(), "Invalid Symbol",
|
|
||||||
"Symbol no longer valid.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProgramLocation getProgramLocation(int row, int column) {
|
public ProgramLocation getProgramLocation(int row, int column) {
|
||||||
Symbol s = (Symbol) getValueAt(row, LABEL_COL);
|
SymbolTableNameValue s = (SymbolTableNameValue) getValueAt(row, LABEL_COL);
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
return s.getProgramLocation();
|
return s.getSymbol().getProgramLocation();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -262,20 +246,27 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
|
|
||||||
void symbolAdded(Symbol s) {
|
void symbolAdded(Symbol s) {
|
||||||
if (filter.accepts(s, getProgram())) {
|
if (filter.accepts(s, getProgram())) {
|
||||||
addObject(new SymbolRowObject(s.getID()));
|
addObject(new SymbolRowObject(s));
|
||||||
lastSymbol = s;
|
lastSymbol = s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void symbolRemoved(long symbolID) {
|
void symbolRemoved(Symbol s) {
|
||||||
if (lastSymbol != null && lastSymbol.getID() == symbolID) {
|
if (lastSymbol != null && lastSymbol.getID() == s.getID()) {
|
||||||
lastSymbol = null;
|
lastSymbol = null;
|
||||||
}
|
}
|
||||||
removeObject(new SymbolRowObject(symbolID));
|
removeObject(new SymbolRowObject(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbolRemoved(long symbolId) {
|
||||||
|
if (lastSymbol != null && lastSymbol.getID() == symbolId) {
|
||||||
|
lastSymbol = null;
|
||||||
|
}
|
||||||
|
removeObject(new SymbolRowObject(symbolId));
|
||||||
}
|
}
|
||||||
|
|
||||||
void symbolChanged(Symbol s) {
|
void symbolChanged(Symbol s) {
|
||||||
SymbolRowObject symbolRowObject = new SymbolRowObject(s.getID());
|
SymbolRowObject symbolRowObject = new SymbolRowObject(s);
|
||||||
if (filter.accepts(s, getProgram())) {
|
if (filter.accepts(s, getProgram())) {
|
||||||
updateObject(symbolRowObject);
|
updateObject(symbolRowObject);
|
||||||
}
|
}
|
||||||
|
@ -288,8 +279,9 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
if (rowObjects == null || rowObjects.size() == 0) {
|
if (rowObjects == null || rowObjects.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tool.setStatusInfo("");
|
tool.setStatusInfo("");
|
||||||
LongArrayList deleteList = new LongArrayList();
|
List<Symbol> deleteList = new LinkedList<>();
|
||||||
CompoundCmd cmd = new CompoundCmd("Delete symbol(s)");
|
CompoundCmd cmd = new CompoundCmd("Delete symbol(s)");
|
||||||
for (int i = 0; i < rowObjects.size(); i++) {
|
for (int i = 0; i < rowObjects.size(); i++) {
|
||||||
Symbol symbol = symbolTable.getSymbol(rowObjects.get(i).getKey());
|
Symbol symbol = symbolTable.getSymbol(rowObjects.get(i).getKey());
|
||||||
|
@ -303,7 +295,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
continue;//can't delete dynamic symbols...
|
continue;//can't delete dynamic symbols...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteList.add(rowObjects.get(i).getKey());
|
|
||||||
|
deleteList.add(rowObjects.get(i).getSymbol());
|
||||||
String label = symbol.getName();
|
String label = symbol.getName();
|
||||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||||
Function function = (Function) symbol.getObject();
|
Function function = (Function) symbol.getObject();
|
||||||
|
@ -339,32 +332,6 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getSortedColumnClass(int columnIndex) {
|
|
||||||
if (columnIndex == LOCATION_COL) {
|
|
||||||
return Address.class;
|
|
||||||
}
|
|
||||||
return super.getSortedColumnClass(columnIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getPreferredWidth(int columnIndex) {
|
|
||||||
switch (columnIndex) {
|
|
||||||
case LABEL_COL:
|
|
||||||
return 140;
|
|
||||||
case LOCATION_COL:
|
|
||||||
return 40;
|
|
||||||
case DATATYPE_COL:
|
|
||||||
case TYPE_COL:
|
|
||||||
case SOURCE_COL:
|
|
||||||
return 30;
|
|
||||||
case NAMESPACE_COL:
|
|
||||||
return 80;
|
|
||||||
case REFS_COL:
|
|
||||||
return 20;
|
|
||||||
}
|
|
||||||
return 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address getAddress(int row) {
|
public Address getAddress(int row) {
|
||||||
Symbol symbol = symbolTable.getSymbol(getRowObject(row).getKey());
|
Symbol symbol = symbolTable.getSymbol(getRowObject(row).getKey());
|
||||||
|
@ -374,21 +341,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
return symbol.getAddress();
|
return symbol.getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol getSymbolForRowObject(SymbolRowObject storageObject) {
|
private AddressBasedLocation getSymbolLocation(SymbolRowObject rowObject) {
|
||||||
if (symbolTable == null) {
|
Symbol s = rowObject.getSymbol();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
long key = storageObject.getKey();
|
|
||||||
Symbol localSymbol = lastSymbol;
|
|
||||||
if (localSymbol == null || localSymbol.getID() != key) {
|
|
||||||
localSymbol = lastSymbol = symbolTable.getSymbol(key);
|
|
||||||
}
|
|
||||||
return localSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressBasedLocation getSymbolLocation(SymbolRowObject rowObject) {
|
|
||||||
Symbol s = getSymbolForRowObject(rowObject);
|
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return new AddressBasedLocation();
|
return new AddressBasedLocation();
|
||||||
}
|
}
|
||||||
|
@ -406,7 +360,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
private class NameTableColumn
|
private class NameTableColumn
|
||||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Symbol> {
|
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, SymbolTableNameValue> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName() {
|
public String getColumnName() {
|
||||||
|
@ -414,9 +368,20 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Symbol getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public SymbolTableNameValue getValue(SymbolRowObject rowObject, Settings settings,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
Program p, ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
return getSymbolForRowObject(rowObject);
|
|
||||||
|
Symbol s = rowObject.getSymbol();
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: this call is slow, especially for dynamic symbols. Caching the dynamic
|
||||||
|
// symbols in the SymbolRowObject *greatly* increases sorting and filtering performance.
|
||||||
|
// For now we assume that most users are not loading dynamic labels. If we add
|
||||||
|
// caching, then we have to deal with the stickiness of when to clear/update the cache
|
||||||
|
String name = s.toString();
|
||||||
|
return new SymbolTableNameValue(s, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +398,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
@Override
|
@Override
|
||||||
public Boolean getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public Boolean getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -468,7 +433,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
@Override
|
@Override
|
||||||
public ProgramLocation getProgramLocation(SymbolRowObject rowObject, Settings settings,
|
public ProgramLocation getProgramLocation(SymbolRowObject rowObject, Settings settings,
|
||||||
Program p, ServiceProvider svcProvider) {
|
Program p, ServiceProvider svcProvider) {
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -476,6 +441,29 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SymbolTypeTableColumn
|
||||||
|
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName() {
|
||||||
|
return "Type";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
Symbol s = rowObject.getSymbol();
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: this call is slow. If we decide that filtering/sorting on this value is
|
||||||
|
// important, then this should be cached
|
||||||
|
return SymbolUtilities.getSymbolTypeDisplayName(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class VariableSymbolLocation extends AddressBasedLocation {
|
private class VariableSymbolLocation extends AddressBasedLocation {
|
||||||
|
|
||||||
VariableSymbolLocation(Variable variable) {
|
VariableSymbolLocation(Variable variable) {
|
||||||
|
@ -495,7 +483,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
|
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -532,7 +520,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
@Override
|
@Override
|
||||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -572,7 +560,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
@Override
|
@Override
|
||||||
public SourceType getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public SourceType getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -584,6 +572,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
private class ReferenceCountTableColumn
|
private class ReferenceCountTableColumn
|
||||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
||||||
|
|
||||||
|
private ReferenceCountRenderer renderer = new ReferenceCountRenderer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName() {
|
public String getColumnName() {
|
||||||
return "Reference Count";
|
return "Reference Count";
|
||||||
|
@ -592,18 +582,32 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
@Override
|
@Override
|
||||||
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Integer.valueOf(symbol.getReferenceCount());
|
return Integer.valueOf(symbol.getReferenceCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this renderer disables the default text filtering; this column is only filterable
|
||||||
|
// via the column constraint filtering
|
||||||
|
private class ReferenceCountRenderer extends GTableCellRenderer
|
||||||
|
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||||
|
// body is handled by parents
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OffcutReferenceCountTableColumn
|
private class OffcutReferenceCountTableColumn
|
||||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
||||||
|
|
||||||
|
private OffcutReferenceCountRenderer renderer = new OffcutReferenceCountRenderer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName() {
|
public String getColumnName() {
|
||||||
return "Offcut Ref Count";
|
return "Offcut Ref Count";
|
||||||
|
@ -613,7 +617,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
|
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -637,6 +641,18 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
}
|
}
|
||||||
return Integer.valueOf(count);
|
return Integer.valueOf(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this renderer disables the default text filtering; this column is only filterable
|
||||||
|
// via the column constraint filtering
|
||||||
|
private class OffcutReferenceCountRenderer extends GTableCellRenderer
|
||||||
|
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||||
|
// body is handled by parents
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UserTableColumn
|
private class UserTableColumn
|
||||||
|
@ -656,7 +672,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
|
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null) {
|
if (symbol == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -694,7 +710,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||||
|
|
||||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
Symbol symbol = rowObject.getSymbol();
|
||||||
if (symbol == null || !symbol.isExternal()) {
|
if (symbol == null || !symbol.isExternal()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.symtable;
|
||||||
|
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple data object for the Name column table cell. This class allows us to control
|
||||||
|
* how sorting is performed by caching the slow (potentially) to calculate symbol name.
|
||||||
|
*/
|
||||||
|
class SymbolTableNameValue implements Comparable<SymbolTableNameValue> {
|
||||||
|
|
||||||
|
private Symbol symbol;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
SymbolTableNameValue(Symbol symbol, String name) {
|
||||||
|
this.symbol = symbol;
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
// name will be non-null when cached by the table model
|
||||||
|
if (name == null) {
|
||||||
|
name = symbol.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol getSymbol() {
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(SymbolTableNameValue o) {
|
||||||
|
return name.compareToIgnoreCase(o.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -190,7 +190,10 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||||
if (!symProvider.isVisible()) {
|
if (!symProvider.isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
|
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED) ||
|
||||||
|
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_ADDED) ||
|
||||||
|
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED)) {
|
||||||
|
|
||||||
symProvider.reload();
|
symProvider.reload();
|
||||||
refProvider.reload();
|
refProvider.reload();
|
||||||
return;
|
return;
|
||||||
|
@ -223,7 +226,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||||
Address addAddr = rec.getStart();
|
Address addAddr = rec.getStart();
|
||||||
Symbol primaryAtAdd = currentProgram.getSymbolTable().getPrimarySymbol(addAddr);
|
Symbol primaryAtAdd = currentProgram.getSymbolTable().getPrimarySymbol(addAddr);
|
||||||
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
||||||
symProvider.symbolRemoved(primaryAtAdd.getID());
|
symProvider.symbolRemoved(primaryAtAdd);
|
||||||
}
|
}
|
||||||
symbol = (Symbol) rec.getNewValue();
|
symbol = (Symbol) rec.getNewValue();
|
||||||
symProvider.symbolAdded(symbol);
|
symProvider.symbolAdded(symbol);
|
||||||
|
@ -301,13 +304,6 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||||
refProvider.symbolChanged(element);
|
refProvider.symbolChanged(element);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ChangeManager.DOCR_MEMORY_BLOCK_ADDED:
|
|
||||||
case ChangeManager.DOCR_MEMORY_BLOCK_REMOVED:
|
|
||||||
symProvider.reload();
|
|
||||||
refProvider.reload();
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.util.table.field;
|
package ghidra.util.table.field;
|
||||||
|
|
||||||
|
import docking.widgets.table.GTableCellRenderer;
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.table.column.AbstractWrapperTypeColumnRenderer;
|
||||||
|
import ghidra.util.table.column.GColumnRenderer;
|
||||||
|
|
||||||
public class FunctionBodySizeTableColumn
|
public class FunctionBodySizeTableColumn
|
||||||
extends ProgramBasedDynamicTableColumnExtensionPoint<Function, Integer> {
|
extends ProgramBasedDynamicTableColumnExtensionPoint<Function, Integer> {
|
||||||
|
|
||||||
|
private FunctionBodySizeRenderer renderer = new FunctionBodySizeRenderer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName() {
|
public String getColumnName() {
|
||||||
return "Function Size";
|
return "Function Size";
|
||||||
|
@ -34,4 +39,15 @@ public class FunctionBodySizeTableColumn
|
||||||
return (int) rowObject.getBody().getNumAddresses();
|
return (int) rowObject.getBody().getNumAddresses();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this renderer disables the default text filtering; this column is only filterable
|
||||||
|
// via the column constraint filtering
|
||||||
|
private class FunctionBodySizeRenderer extends GTableCellRenderer
|
||||||
|
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||||
|
// body is handled by parents
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ package ghidra.app.plugin.core.symtable;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -26,7 +27,8 @@ import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.table.*;
|
import javax.swing.table.JTableHeader;
|
||||||
|
import javax.swing.table.TableModel;
|
||||||
|
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
@ -130,40 +132,49 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testSortingLabelColumn() throws Exception {
|
public void testSortingLabelColumn() throws Exception {
|
||||||
openProgram("sample");
|
openProgram("sample");
|
||||||
|
|
||||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LABEL_COL);
|
sortAscending(SymbolTableModel.LABEL_COL);
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
|
|
||||||
TableColumn column =
|
|
||||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LABEL_COL);
|
|
||||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
|
||||||
assertTrue(renderer.isSortedAscending());
|
|
||||||
|
|
||||||
TableModel model = symbolTable.getModel();
|
TableModel model = symbolTable.getModel();
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
Symbol sym1 = (Symbol) model.getValueAt(i + 0, SymbolTableModel.LABEL_COL);
|
Symbol sym1 = getSymbol(i);
|
||||||
Symbol sym2 = (Symbol) model.getValueAt(i + 1, SymbolTableModel.LABEL_COL);
|
Symbol sym2 = getSymbol(i + 1);
|
||||||
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
||||||
assertTrue("row " + i + " not sorted correctly", (compare < 0 || compare == 0));
|
assertTrue("row " + i + " not sorted correctly", (compare < 0 || compare == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
sortDescending(SymbolTableModel.LABEL_COL);
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
assertTrue(!renderer.isSortedAscending());
|
|
||||||
|
|
||||||
model = symbolTable.getModel();
|
model = symbolTable.getModel();
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
Symbol sym1 = (Symbol) model.getValueAt(i + 0, SymbolTableModel.LABEL_COL);
|
Symbol sym1 = getSymbol(i);
|
||||||
Symbol sym2 = (Symbol) model.getValueAt(i + 1, SymbolTableModel.LABEL_COL);
|
Symbol sym2 = getSymbol(i + 1);
|
||||||
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
||||||
assertTrue("row " + i + " not sorted correctly", (compare > 0 || compare == 0));
|
assertTrue("row " + i + " not sorted correctly", (compare > 0 || compare == 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sortAscending(int column) {
|
||||||
|
runSwing(() -> symbolModel.setTableSortState(
|
||||||
|
TableSortState.createDefaultSortState(column, true)));
|
||||||
|
waitForTableModel(symbolModel);
|
||||||
|
|
||||||
|
waitForCondition(() -> {
|
||||||
|
TableSortState sort = runSwing(() -> symbolModel.getTableSortState());
|
||||||
|
return sort.getColumnSortState(column).isAscending();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortDescending(int column) {
|
||||||
|
runSwing(() -> symbolModel.setTableSortState(
|
||||||
|
TableSortState.createDefaultSortState(column, false)));
|
||||||
|
waitForTableModel(symbolModel);
|
||||||
|
|
||||||
|
waitForCondition(() -> {
|
||||||
|
TableSortState sort = runSwing(() -> symbolModel.getTableSortState());
|
||||||
|
return !sort.getColumnSortState(column).isAscending();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testColumnDiscovery() throws Exception {
|
public void testColumnDiscovery() throws Exception {
|
||||||
//
|
//
|
||||||
|
@ -205,35 +216,21 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testSortingAddressColumn() throws Exception {
|
public void testSortingAddressColumn() throws Exception {
|
||||||
openProgram("sample");
|
openProgram("sample");
|
||||||
|
|
||||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LOCATION_COL);
|
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
|
|
||||||
TableColumn column =
|
|
||||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LOCATION_COL);
|
|
||||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
|
||||||
assertTrue(renderer.isSortedAscending());
|
|
||||||
|
|
||||||
SymbolTableModel model = (SymbolTableModel) symbolTable.getModel();
|
SymbolTableModel model = (SymbolTableModel) symbolTable.getModel();
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
AddressBasedLocation refs1 =
|
AddressBasedLocation loc1 = getLocation(i);
|
||||||
(AddressBasedLocation) model.getValueAt(i + 0, SymbolTableModel.LOCATION_COL);
|
AddressBasedLocation loc2 = getLocation(i + 0);
|
||||||
AddressBasedLocation refs2 =
|
assertTrue(loc1.compareTo(loc2) <= 0);
|
||||||
(AddressBasedLocation) model.getValueAt(i + 1, SymbolTableModel.LOCATION_COL);
|
|
||||||
assertTrue(refs1.compareTo(refs2) <= 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
sortDescending(SymbolTableModel.LOCATION_COL);
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
assertTrue(!renderer.isSortedAscending());
|
|
||||||
|
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
AddressBasedLocation refs1 =
|
AddressBasedLocation loc1 = getLocation(i);
|
||||||
(AddressBasedLocation) model.getValueAt(i + 0, SymbolTableModel.LOCATION_COL);
|
AddressBasedLocation loc2 = getLocation(i + 0);
|
||||||
AddressBasedLocation refs2 =
|
assertTrue(loc1.compareTo(loc2) >= 0);
|
||||||
(AddressBasedLocation) model.getValueAt(i + 1, SymbolTableModel.LOCATION_COL);
|
|
||||||
assertTrue(refs1.compareTo(refs2) >= 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,26 +238,20 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testSortingReferenceColumn() throws Exception {
|
public void testSortingReferenceColumn() throws Exception {
|
||||||
openProgram("sample");
|
openProgram("sample");
|
||||||
|
|
||||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
sortAscending(SymbolTableModel.REFS_COL);
|
||||||
|
|
||||||
TableColumn column =
|
|
||||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.REFS_COL);
|
|
||||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
|
||||||
assertTrue(renderer.isSortedAscending());
|
|
||||||
|
|
||||||
TableModel model = symbolTable.getModel();
|
TableModel model = symbolTable.getModel();
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
Integer refs1 = (Integer) model.getValueAt(i + 0, SymbolTableModel.REFS_COL);
|
Integer refs1 = getRefCount(i);
|
||||||
Integer refs2 = (Integer) model.getValueAt(i + 1, SymbolTableModel.REFS_COL);
|
Integer refs2 = getRefCount(i + 1);
|
||||||
assertTrue(refs1.compareTo(refs2) <= 0);
|
assertTrue(refs1.compareTo(refs2) <= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
sortDescending(SymbolTableModel.REFS_COL);
|
||||||
assertTrue(!renderer.isSortedAscending());
|
|
||||||
|
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
Integer refs1 = (Integer) model.getValueAt(i + 0, SymbolTableModel.REFS_COL);
|
Integer refs1 = getRefCount(i);
|
||||||
Integer refs2 = (Integer) model.getValueAt(i + 1, SymbolTableModel.REFS_COL);
|
Integer refs2 = getRefCount(i + 1);
|
||||||
assertTrue(refs1.compareTo(refs2) >= 0);
|
assertTrue(refs1.compareTo(refs2) >= 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,7 +350,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
|
||||||
int row = findRow("ghidra", "Global");
|
String symbolName = "ghidra";
|
||||||
|
int row = findRow(symbolName, "Global");
|
||||||
|
|
||||||
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL);
|
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
@ -368,6 +360,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
Component editor = symbolTable.getEditorComponent();
|
Component editor = symbolTable.getEditorComponent();
|
||||||
assertNotNull(editor);
|
assertNotNull(editor);
|
||||||
JTextField textField = (JTextField) editor;
|
JTextField textField = (JTextField) editor;
|
||||||
|
String currentText = getText(textField);
|
||||||
|
assertEquals(symbolName, currentText);
|
||||||
|
|
||||||
triggerActionKey(textField, 0, KeyEvent.VK_END);
|
triggerActionKey(textField, 0, KeyEvent.VK_END);
|
||||||
myTypeText(editor, ".Is.Cool");
|
myTypeText(editor, ".Is.Cool");
|
||||||
runSwing(() -> symbolTable.editingStopped(new ChangeEvent(symbolTable)));
|
runSwing(() -> symbolTable.editingStopped(new ChangeEvent(symbolTable)));
|
||||||
|
@ -376,7 +371,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
assertTrue(!symbolTable.isEditing());
|
assertTrue(!symbolTable.isEditing());
|
||||||
|
|
||||||
Symbol s = (Symbol) symbolTable.getValueAt(row, SymbolTableModel.LABEL_COL);
|
Symbol s = getSymbol(row);
|
||||||
assertEquals("ghidra.Is.Cool", s.getName());
|
assertEquals("ghidra.Is.Cool", s.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,7 +562,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
int[] selectedRows = symbolTable.getSelectedRows();
|
int[] selectedRows = symbolTable.getSelectedRows();
|
||||||
assertEquals(3, selectedRows.length);
|
assertEquals(3, selectedRows.length);
|
||||||
for (int selectedRow : selectedRows) {
|
for (int selectedRow : selectedRows) {
|
||||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
Symbol symbol = getSymbol(selectedRow);
|
||||||
assertTrue(!symbol.isPinned());
|
assertTrue(!symbol.isPinned());
|
||||||
}
|
}
|
||||||
assertTrue(setPinnedAction.isEnabledForContext(actionContext));
|
assertTrue(setPinnedAction.isEnabledForContext(actionContext));
|
||||||
|
@ -576,14 +571,14 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
performAction(setPinnedAction, actionContext, true);
|
performAction(setPinnedAction, actionContext, true);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
for (int selectedRow : selectedRows) {
|
for (int selectedRow : selectedRows) {
|
||||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
Symbol symbol = getSymbol(selectedRow);
|
||||||
assertTrue(symbol.isPinned());
|
assertTrue(symbol.isPinned());
|
||||||
}
|
}
|
||||||
|
|
||||||
performAction(clearPinnedAction, actionContext, true);
|
performAction(clearPinnedAction, actionContext, true);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
for (int selectedRow : selectedRows) {
|
for (int selectedRow : selectedRows) {
|
||||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
Symbol symbol = getSymbol(selectedRow);
|
||||||
assertTrue(!symbol.isPinned());
|
assertTrue(!symbol.isPinned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,11 +595,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
int[] selectedRows = symbolTable.getSelectedRows();
|
int[] selectedRows = symbolTable.getSelectedRows();
|
||||||
|
|
||||||
for (int selectedRow : selectedRows) {
|
for (int selectedRow : selectedRows) {
|
||||||
Symbol symbol = (Symbol) symbolTable.getValueAt(selectedRow, 0);
|
Symbol symbol = getSymbol(selectedRow);
|
||||||
assertTrue(!symbol.isPinned());
|
assertFalse(symbol.isPinned());
|
||||||
}
|
}
|
||||||
assertTrue(!setPinnedAction.isEnabledForContext(actionContext));
|
assertFalse(setPinnedAction.isEnabledForContext(actionContext));
|
||||||
assertTrue(!clearPinnedAction.isEnabledForContext(actionContext));
|
assertFalse(clearPinnedAction.isEnabledForContext(actionContext));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,13 +615,13 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
sym = st.createLabel(sample.getNewAddress(0x01007000), "Zeus", SourceType.USER_DEFINED);
|
sym = st.createLabel(sample.getNewAddress(0x01007000), "Zeus", SourceType.USER_DEFINED);
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
assertEquals(rowCount + 1, symbolTable.getRowCount());
|
assertEquals(rowCount + 1, symbolTable.getRowCount());
|
||||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) >= 0);
|
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym)) >= 0);
|
||||||
|
|
||||||
sym =
|
sym =
|
||||||
st.createLabel(sample.getNewAddress(0x01007100), "Athena", SourceType.USER_DEFINED);
|
st.createLabel(sample.getNewAddress(0x01007100), "Athena", SourceType.USER_DEFINED);
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
assertEquals(rowCount + 2, symbolTable.getRowCount());
|
assertEquals(rowCount + 2, symbolTable.getRowCount());
|
||||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) >= 0);
|
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym)) >= 0);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
prog.endTransaction(id, true);
|
prog.endTransaction(id, true);
|
||||||
|
@ -659,7 +654,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
sym =
|
sym =
|
||||||
st.createLabel(sample.getNewAddress(0x01007000), "saaaa", SourceType.USER_DEFINED);
|
st.createLabel(sample.getNewAddress(0x01007000), "saaaa", SourceType.USER_DEFINED);
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) >= 0);
|
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym)) >= 0);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
prog.endTransaction(id, true);
|
prog.endTransaction(id, true);
|
||||||
|
@ -722,7 +717,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
// entry symbol replaced by dynamic External Entry symbol
|
// entry symbol replaced by dynamic External Entry symbol
|
||||||
assertNull(getUniqueSymbol(prog, "entry"));
|
assertNull(getUniqueSymbol(prog, "entry"));
|
||||||
assertNotNull(getUniqueSymbol(prog, "EXT_00000051"));
|
assertNotNull(getUniqueSymbol(prog, "EXT_00000051"));
|
||||||
assertTrue(symbolModel.getRowIndex(new SymbolRowObject(sym.getID())) == -1);
|
assertTrue("Deleted symbol not removed from table",
|
||||||
|
symbolModel.getRowIndex(new SymbolRowObject(sym)) < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -732,8 +728,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
Symbol s = getUniqueSymbol(prog, "entry");
|
Symbol s = getUniqueSymbol(prog, "entry");
|
||||||
|
|
||||||
int row = symbolModel.getRowIndex(new SymbolRowObject(s.getID()));
|
int row = symbolModel.getRowIndex(new SymbolRowObject(s));
|
||||||
Integer refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
Integer refCount = getRefCount(row);
|
||||||
assertNotNull(refCount);
|
assertNotNull(refCount);
|
||||||
assertEquals(3, refCount.intValue());
|
assertEquals(3, refCount.intValue());
|
||||||
|
|
||||||
|
@ -749,9 +745,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
|
||||||
row = symbolModel.getRowIndex(new SymbolRowObject(s.getID()));
|
row = symbolModel.getRowIndex(new SymbolRowObject(s));
|
||||||
|
|
||||||
refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
refCount = getRefCount(row);
|
||||||
assertNotNull(refCount);
|
assertNotNull(refCount);
|
||||||
assertEquals(4, refCount.intValue());
|
assertEquals(4, refCount.intValue());
|
||||||
}
|
}
|
||||||
|
@ -763,9 +759,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
Symbol s = getUniqueSymbol(prog, "doStuff");
|
Symbol s = getUniqueSymbol(prog, "doStuff");
|
||||||
|
|
||||||
int row = symbolModel.getRowIndex(new SymbolRowObject(s.getID()));
|
int row = symbolModel.getRowIndex(new SymbolRowObject(s));
|
||||||
|
|
||||||
Integer refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
Integer refCount = getRefCount(row);
|
||||||
assertNotNull(refCount);
|
assertNotNull(refCount);
|
||||||
assertEquals(4, refCount.intValue());
|
assertEquals(4, refCount.intValue());
|
||||||
|
|
||||||
|
@ -791,7 +787,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
|
||||||
refCount = (Integer) symbolTable.getValueAt(row, SymbolTableModel.REFS_COL);
|
refCount = getRefCount(row);
|
||||||
assertNotNull(refCount);
|
assertNotNull(refCount);
|
||||||
assertEquals(3, refCount.intValue());
|
assertEquals(3, refCount.intValue());
|
||||||
}
|
}
|
||||||
|
@ -853,23 +849,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
/************** LABEL **********************/
|
/************** LABEL **********************/
|
||||||
|
|
||||||
Rectangle rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LABEL_COL);
|
sortAscending(SymbolTableModel.LABEL_COL);
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
|
|
||||||
TableColumn column =
|
|
||||||
symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LABEL_COL);
|
|
||||||
GTableHeaderRenderer renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
|
||||||
assertTrue(renderer.isSortedAscending());
|
|
||||||
|
|
||||||
TableModel model = symbolTable.getModel();
|
TableModel model = symbolTable.getModel();
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
Symbol sym1 = (Symbol) model.getValueAt(i + 0, SymbolTableModel.LABEL_COL);
|
Symbol sym1 = getSymbol(i);
|
||||||
Symbol sym2 = (Symbol) model.getValueAt(i + 1, SymbolTableModel.LABEL_COL);
|
Symbol sym2 = getSymbol(i + 1);
|
||||||
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
int compare = sym1.getName().compareToIgnoreCase(sym2.getName());
|
||||||
assertTrue("Symbol \"" + sym1 + "\" is not sorted as less than symbol \"" + sym2 + "\"",
|
assertTrue("Symbol \"" + sym1 + "\" is not sorted as less than symbol \"" + sym2 + "\"",
|
||||||
compare <= 0);
|
compare <= 0);
|
||||||
|
@ -877,21 +862,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
/************** ADDRESS **********************/
|
/************** ADDRESS **********************/
|
||||||
|
|
||||||
rect = symbolTableHeader.getHeaderRect(SymbolTableModel.LOCATION_COL);
|
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||||
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
|
|
||||||
column = symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.LOCATION_COL);
|
|
||||||
renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
|
||||||
assertTrue(renderer.isSortedAscending());
|
|
||||||
|
|
||||||
model = symbolTable.getModel();
|
model = symbolTable.getModel();
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
AddressBasedLocation loc1 =
|
AddressBasedLocation loc1 = getLocation(i);
|
||||||
(AddressBasedLocation) model.getValueAt(i, SymbolTableModel.LOCATION_COL);
|
AddressBasedLocation loc2 = getLocation(i + 1);
|
||||||
AddressBasedLocation loc2 =
|
|
||||||
(AddressBasedLocation) model.getValueAt(i + 1, SymbolTableModel.LOCATION_COL);
|
|
||||||
int compare = SystemUtilities.compareTo(loc1, loc2);
|
int compare = SystemUtilities.compareTo(loc1, loc2);
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Location1 \"" + loc1 + "\"is not sorted as less than location2 \"" + loc2 + "\"",
|
"Location1 \"" + loc1 + "\"is not sorted as less than location2 \"" + loc2 + "\"",
|
||||||
|
@ -900,18 +876,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
/************** REFERENCES **********************/
|
/************** REFERENCES **********************/
|
||||||
|
|
||||||
rect = symbolTableHeader.getHeaderRect(SymbolTableModel.REFS_COL);
|
sortAscending(SymbolTableModel.REFS_COL);
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
|
|
||||||
column = symbolTableHeader.getColumnModel().getColumn(SymbolTableModel.REFS_COL);
|
|
||||||
renderer = (GTableHeaderRenderer) column.getHeaderRenderer();
|
|
||||||
assertTrue(renderer.isSortedAscending());
|
|
||||||
|
|
||||||
model = symbolTable.getModel();
|
model = symbolTable.getModel();
|
||||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||||
Integer refs1 = (Integer) model.getValueAt(i + 0, SymbolTableModel.REFS_COL);
|
Integer refs1 = getRefCount(i);
|
||||||
Integer refs2 = (Integer) model.getValueAt(i + 1, SymbolTableModel.REFS_COL);
|
Integer refs2 = getRefCount(i + 1);
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"The number of references (\"" + refs1 + "\") for row did not " +
|
"The number of references (\"" + refs1 + "\") for row did not " +
|
||||||
"compare as less than the number for the following row (\"" + refs2 + "\")",
|
"compare as less than the number for the following row (\"" + refs2 + "\")",
|
||||||
|
@ -1035,7 +1005,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
deleteText(textField);
|
deleteText(textField);
|
||||||
|
|
||||||
// sort on a different column to trigger the other kind of filtering
|
// sort on a different column to trigger the other kind of filtering
|
||||||
sortOnColumn(SymbolTableModel.REFS_COL);
|
sortAscending(SymbolTableModel.REFS_COL);
|
||||||
|
|
||||||
text = "_";
|
text = "_";
|
||||||
myTypeText(textField, text);
|
myTypeText(textField, text);
|
||||||
|
@ -1106,7 +1076,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
deleteText(textField);
|
deleteText(textField);
|
||||||
|
|
||||||
// sort on a different column to trigger the other kind of filtering
|
// sort on a different column to trigger the other kind of filtering
|
||||||
sortOnColumn(SymbolTableModel.LOCATION_COL);
|
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||||
|
|
||||||
text = "_";
|
text = "_";
|
||||||
myTypeText(textField, text);
|
myTypeText(textField, text);
|
||||||
|
@ -1285,6 +1255,24 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Symbol getSymbol(int row) {
|
||||||
|
SymbolRowObject rowObject = symbolModel.getRowObject(row);
|
||||||
|
return rowObject.getSymbol();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getRefCount(int row) {
|
||||||
|
Integer count =
|
||||||
|
runSwing(() -> (Integer) symbolModel.getValueAt(row, SymbolTableModel.REFS_COL));
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AddressBasedLocation getLocation(int row) {
|
||||||
|
AddressBasedLocation location =
|
||||||
|
runSwing(() -> (AddressBasedLocation) symbolModel.getValueAt(row,
|
||||||
|
SymbolTableModel.LOCATION_COL));
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
private void assertReferencesAddressColumnValue(int row, long value) {
|
private void assertReferencesAddressColumnValue(int row, long value) {
|
||||||
Address addr =
|
Address addr =
|
||||||
(Address) referenceTable.getValueAt(row, SymbolReferenceModel.ADDRESS_COLUMN);
|
(Address) referenceTable.getValueAt(row, SymbolReferenceModel.ADDRESS_COLUMN);
|
||||||
|
@ -1292,12 +1280,6 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
addr.getOffset());
|
addr.getOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sortOnColumn(int column) throws Exception {
|
|
||||||
Rectangle rect = symbolTableHeader.getHeaderRect(column);
|
|
||||||
clickMouse(symbolTableHeader, MouseEvent.BUTTON1, rect.x + 10, rect.y + 10, 1, 0);
|
|
||||||
waitForNotBusy(symbolTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteText(JTextField field) throws Exception {
|
private void deleteText(JTextField field) throws Exception {
|
||||||
deleteText(field, false, false);
|
deleteText(field, false, false);
|
||||||
}
|
}
|
||||||
|
@ -1328,14 +1310,10 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFilterOptions(TextFilterStrategy filterStrategy, boolean caseSensitive) {
|
private void setFilterOptions(TextFilterStrategy filterStrategy, boolean caseSensitive)
|
||||||
|
throws Exception {
|
||||||
filterPanel.setFilterOptions(new FilterOptions(filterStrategy, true, caseSensitive, false));
|
filterPanel.setFilterOptions(new FilterOptions(filterStrategy, true, caseSensitive, false));
|
||||||
waitForTable();
|
waitForNotBusy(symbolTable);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitForTable() {
|
|
||||||
waitForSwing();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private JTextField getFilterTextField() {
|
private JTextField getFilterTextField() {
|
||||||
|
@ -1376,12 +1354,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
private int getRowForSymbol(Symbol symbol) {
|
private int getRowForSymbol(Symbol symbol) {
|
||||||
for (int i = 0; i < symbolTable.getRowCount(); i++) {
|
for (int i = 0; i < symbolTable.getRowCount(); i++) {
|
||||||
Object name = symbolTable.getValueAt(i, 0);
|
Symbol rowSymbol = getSymbol(i);
|
||||||
if (name.toString().equals(symbol.getName())) {
|
if (rowSymbol.equals(symbol)) {
|
||||||
Object namespace = symbolTable.getValueAt(i, 4);
|
// if (rowSymbol.getParentNamespace().equals(symbol.getParentNamespace())) {
|
||||||
if (namespace.toString().equals(symbol.getParentNamespace().getName())) {
|
// return i;
|
||||||
return i;
|
// }
|
||||||
}
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert.fail("Didn't find symbol in symbol table: " + symbol.getName());
|
Assert.fail("Didn't find symbol in symbol table: " + symbol.getName());
|
||||||
|
@ -1478,7 +1456,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
"There are no filtered matches as expected from filter string: " + filterString,
|
"There are no filtered matches as expected from filter string: " + filterString,
|
||||||
rowCount > 0);
|
rowCount > 0);
|
||||||
for (int i = 0; i < rowCount; i++) {
|
for (int i = 0; i < rowCount; i++) {
|
||||||
Symbol symbol = (Symbol) symbolModel.getValueAt(i, SymbolTableModel.LABEL_COL);
|
Symbol symbol = getSymbol(i);
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Found an entry in the symbol table model that " + "does not match the given " +
|
"Found an entry in the symbol table model that " + "does not match the given " +
|
||||||
"filter: " + filterString + " and symbol: " + symbol.getName(),
|
"filter: " + filterString + " and symbol: " + symbol.getName(),
|
||||||
|
@ -1495,7 +1473,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
String filterText = string;
|
String filterText = string;
|
||||||
|
|
||||||
for (int i = 0; i < rowCount; i++) {
|
for (int i = 0; i < rowCount; i++) {
|
||||||
Symbol symbol = (Symbol) symbolModel.getValueAt(i, SymbolTableModel.LABEL_COL);
|
Symbol symbol = getSymbol(i);
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Found an entry in the symbol table model that does not match the given " +
|
"Found an entry in the symbol table model that does not match the given " +
|
||||||
"filter: " + string + " and symbol: " + symbol.getName(),
|
"filter: " + string + " and symbol: " + symbol.getName(),
|
||||||
|
@ -1504,8 +1482,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForNotBusy(GTable table) throws Exception {
|
private void waitForNotBusy(GTable table) throws Exception {
|
||||||
prog.flushEvents();
|
waitForProgram(prog);
|
||||||
|
|
||||||
ThreadedTableModel<?, ?> model = (ThreadedTableModel<?, ?>) table.getModel();
|
ThreadedTableModel<?, ?> model = (ThreadedTableModel<?, ?>) table.getModel();
|
||||||
waitForTableModel(model);
|
waitForTableModel(model);
|
||||||
}
|
}
|
||||||
|
@ -1632,6 +1609,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
waitForNotBusy(symbolTable);
|
waitForNotBusy(symbolTable);
|
||||||
|
|
||||||
symbolTableHeader = symbolTable.getTableHeader();
|
symbolTableHeader = symbolTable.getTableHeader();
|
||||||
|
|
||||||
|
sortAscending(SymbolTableModel.LABEL_COL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showReferencesTable() {
|
private void showReferencesTable() {
|
||||||
|
@ -1679,7 +1658,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
private int findRow(String symbolName, String namespace) {
|
private int findRow(String symbolName, String namespace) {
|
||||||
int max = symbolTable.getRowCount();
|
int max = symbolTable.getRowCount();
|
||||||
for (int i = 0; i < max; i++) {
|
for (int i = 0; i < max; i++) {
|
||||||
Symbol s = (Symbol) symbolTable.getValueAt(i, SymbolTableModel.LABEL_COL);
|
SymbolTableNameValue symbolNameValue =
|
||||||
|
(SymbolTableNameValue) symbolTable.getValueAt(i, SymbolTableModel.LABEL_COL);
|
||||||
|
Symbol s = symbolNameValue.getSymbol();
|
||||||
if (symbolName.equals(s.getName()) &&
|
if (symbolName.equals(s.getName()) &&
|
||||||
namespace.equals(s.getParentNamespace().getName())) {
|
namespace.equals(s.getParentNamespace().getName())) {
|
||||||
return i;
|
return i;
|
||||||
|
|
|
@ -92,8 +92,8 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the given row object in this model; -1 if the model does not contain
|
* Returns the index of the given row object in this model; a negative value if the model
|
||||||
* the given object.
|
* does not contain the given object.
|
||||||
*
|
*
|
||||||
* <p>Warning: if the this model has no sort applied, then performance will be O(n). If
|
* <p>Warning: if the this model has no sort applied, then performance will be O(n). If
|
||||||
* sorted, then performance is O(log n). You can call {@link #isSorted()} to know when
|
* sorted, then performance is O(log n). You can call {@link #isSorted()} to know when
|
||||||
|
@ -132,6 +132,9 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPrimarySortColumnIndex() {
|
public int getPrimarySortColumnIndex() {
|
||||||
|
if (sortState.isUnsorted()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return sortState.iterator().next().getColumnModelIndex();
|
return sortState.iterator().next().getColumnModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,13 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (columnUsesConstraintFilteringOnly(column)) {
|
||||||
|
// This allows columns to be ignored for default text filtering while still being
|
||||||
|
// filterable through the column constraints API
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: this call can be slow when columns dynamically calculate values from the database
|
||||||
Object value = model.getColumnValueForRow(rowObject, column);
|
Object value = model.getColumnValueForRow(rowObject, column);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -96,7 +103,22 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private boolean columnUsesConstraintFilteringOnly(int column) {
|
||||||
|
if (!(model instanceof DynamicColumnTableModel)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel =
|
||||||
|
(DynamicColumnTableModel<ROW_OBJECT>) model;
|
||||||
|
GColumnRenderer<Object> renderer = getColumnRenderer(columnBasedModel, column);
|
||||||
|
if (renderer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
|
||||||
|
return mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
private String getRenderedColumnValue(Object columnValue, int columnIndex) {
|
private String getRenderedColumnValue(Object columnValue, int columnIndex) {
|
||||||
|
|
||||||
if (!(model instanceof DynamicColumnTableModel)) {
|
if (!(model instanceof DynamicColumnTableModel)) {
|
||||||
|
@ -110,11 +132,6 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
|
|
||||||
if (mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) {
|
|
||||||
return null; // this renderer does not support text
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings settings = columnBasedModel.getColumnSettings(columnIndex);
|
Settings settings = columnBasedModel.getColumnSettings(columnIndex);
|
||||||
String s = renderer.getFilterString(columnValue, settings);
|
String s = renderer.getFilterString(columnValue, settings);
|
||||||
return s;
|
return s;
|
||||||
|
|
|
@ -33,7 +33,7 @@ import ghidra.util.datastruct.WeakSet;
|
||||||
public class GTableColumnModel
|
public class GTableColumnModel
|
||||||
implements TableColumnModel, PropertyChangeListener, ListSelectionListener {
|
implements TableColumnModel, PropertyChangeListener, ListSelectionListener {
|
||||||
|
|
||||||
private List<TableColumn> visibleList = new ArrayList<>();
|
private VisibleColumns visibleColumns = new VisibleColumns();
|
||||||
private List<TableColumn> completeList = new ArrayList<>();
|
private List<TableColumn> completeList = new ArrayList<>();
|
||||||
private int totalColumnWidth;
|
private int totalColumnWidth;
|
||||||
private int columnMargin;
|
private int columnMargin;
|
||||||
|
@ -67,18 +67,8 @@ public class GTableColumnModel
|
||||||
|
|
||||||
void removeAllColumns() {
|
void removeAllColumns() {
|
||||||
|
|
||||||
TableColumn[] asArray = visibleList.toArray(new TableColumn[visibleList.size()]);
|
fireColumnRemoved(new TableColumnModelEvent(this, 0, visibleColumns.size() - 1));
|
||||||
for (int i = 0; i < asArray.length; i++) {
|
visibleColumns.clear();
|
||||||
TableColumn column = asArray[i];
|
|
||||||
visibleList.remove(column);
|
|
||||||
fireColumnRemoved(new TableColumnModelEvent(this, i, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO replace the above snippet with this code, after the upcoming release
|
|
||||||
fireColumnRemoved(new TableColumnModelEvent(this, 0, visibleList.size() - 1));
|
|
||||||
visibleList.clear();
|
|
||||||
*/
|
|
||||||
|
|
||||||
// no need to fire the removed event for items in the complete list, as the clients
|
// no need to fire the removed event for items in the complete list, as the clients
|
||||||
// only know about the visible columns
|
// only know about the visible columns
|
||||||
|
@ -90,7 +80,7 @@ public class GTableColumnModel
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
listeners.clear();
|
listeners.clear();
|
||||||
visibleList.clear();
|
visibleColumns.clear();
|
||||||
completeList.clear();
|
completeList.clear();
|
||||||
columnModelState.dispose();
|
columnModelState.dispose();
|
||||||
}
|
}
|
||||||
|
@ -101,7 +91,7 @@ public class GTableColumnModel
|
||||||
* @return true if the given column is visible.
|
* @return true if the given column is visible.
|
||||||
*/
|
*/
|
||||||
public boolean isVisible(TableColumn column) {
|
public boolean isVisible(TableColumn column) {
|
||||||
return visibleList.contains(column);
|
return visibleColumns.contains(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +109,7 @@ public class GTableColumnModel
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVisible(TableColumn column, boolean visible) {
|
public void setVisible(TableColumn column, boolean visible) {
|
||||||
boolean isVisible = visibleList.contains(column);
|
boolean isVisible = visibleColumns.contains(column);
|
||||||
|
|
||||||
if (visible == isVisible) {
|
if (visible == isVisible) {
|
||||||
return;
|
return;
|
||||||
|
@ -127,12 +117,12 @@ public class GTableColumnModel
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
int insertIndex = findVisibleInsertionIndex(column);
|
int insertIndex = findVisibleInsertionIndex(column);
|
||||||
visibleList.add(insertIndex, column);
|
visibleColumns.add(insertIndex, column);
|
||||||
fireColumnAdded(new TableColumnModelEvent(this, insertIndex, insertIndex));
|
fireColumnAdded(new TableColumnModelEvent(this, insertIndex, insertIndex));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int columnIndex = visibleList.indexOf(column);
|
int columnIndex = visibleColumns.indexOf(column);
|
||||||
visibleList.remove(columnIndex);
|
visibleColumns.remove(columnIndex);
|
||||||
// Adjust for the selection
|
// Adjust for the selection
|
||||||
if (selectionModel != null) {
|
if (selectionModel != null) {
|
||||||
selectionModel.removeIndexInterval(columnIndex, columnIndex);
|
selectionModel.removeIndexInterval(columnIndex, columnIndex);
|
||||||
|
@ -146,12 +136,12 @@ public class GTableColumnModel
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findVisibleInsertionIndex(TableColumn column) {
|
private int findVisibleInsertionIndex(TableColumn column) {
|
||||||
int completeIndex = completeList.indexOf(column);
|
int completeIndex = visibleColumns.indexOf(column);
|
||||||
|
|
||||||
int size = visibleList.size();
|
int size = visibleColumns.size();
|
||||||
for (int i = completeIndex + 1; i < size; i++) {
|
for (int i = completeIndex + 1; i < size; i++) {
|
||||||
TableColumn nextColumn = completeList.get(i);
|
TableColumn nextColumn = completeList.get(i);
|
||||||
int visibleIndex = visibleList.indexOf(nextColumn);
|
int visibleIndex = visibleColumns.indexOf(nextColumn);
|
||||||
if (visibleIndex != -1) {
|
if (visibleIndex != -1) {
|
||||||
return visibleIndex;
|
return visibleIndex;
|
||||||
}
|
}
|
||||||
|
@ -169,7 +159,7 @@ public class GTableColumnModel
|
||||||
removeColumnWithModelIndex(aColumn.getModelIndex()); // dedup
|
removeColumnWithModelIndex(aColumn.getModelIndex()); // dedup
|
||||||
|
|
||||||
completeList.add(aColumn);
|
completeList.add(aColumn);
|
||||||
visibleList.add(aColumn);
|
visibleColumns.add(aColumn);
|
||||||
|
|
||||||
aColumn.addPropertyChangeListener(this);
|
aColumn.addPropertyChangeListener(this);
|
||||||
|
|
||||||
|
@ -205,7 +195,7 @@ public class GTableColumnModel
|
||||||
}
|
}
|
||||||
|
|
||||||
completeList.remove(tableColumn);
|
completeList.remove(tableColumn);
|
||||||
visibleList.remove(tableColumn);
|
visibleColumns.remove(tableColumn);
|
||||||
tableColumn.removePropertyChangeListener(this);
|
tableColumn.removePropertyChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,15 +206,15 @@ public class GTableColumnModel
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableColumn getColumn(int columnIndex) {
|
public TableColumn getColumn(int columnIndex) {
|
||||||
if ((columnIndex < 0) || (columnIndex >= visibleList.size())) {
|
if ((columnIndex < 0) || (columnIndex >= visibleColumns.size())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return visibleList.get(columnIndex);
|
return visibleColumns.get(columnIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getColumnCount() {
|
public int getColumnCount() {
|
||||||
return visibleList.size();
|
return visibleColumns.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -232,8 +222,8 @@ public class GTableColumnModel
|
||||||
if (columnIdentifier == null) {
|
if (columnIdentifier == null) {
|
||||||
throw new IllegalArgumentException("Identifier is null");
|
throw new IllegalArgumentException("Identifier is null");
|
||||||
}
|
}
|
||||||
for (int i = 0; i < visibleList.size(); i++) {
|
for (int i = 0; i < visibleColumns.size(); i++) {
|
||||||
TableColumn tableColumn = visibleList.get(i);
|
TableColumn tableColumn = visibleColumns.get(i);
|
||||||
if (columnIdentifier.equals(tableColumn.getIdentifier())) {
|
if (columnIdentifier.equals(tableColumn.getIdentifier())) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
@ -271,7 +261,7 @@ public class GTableColumnModel
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enumeration<TableColumn> getColumns() {
|
public Enumeration<TableColumn> getColumns() {
|
||||||
return Collections.enumeration(visibleList);
|
return visibleColumns.toEnumeration();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -355,8 +345,8 @@ public class GTableColumnModel
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the visible list
|
// update the visible list
|
||||||
TableColumn movedColumn = visibleList.remove(columnIndex);
|
TableColumn movedColumn = visibleColumns.remove(columnIndex);
|
||||||
visibleList.add(newIndex, movedColumn);
|
visibleColumns.add(newIndex, movedColumn);
|
||||||
|
|
||||||
// update the complete list
|
// update the complete list
|
||||||
completeList.remove(movedColumn);
|
completeList.remove(movedColumn);
|
||||||
|
@ -364,7 +354,7 @@ public class GTableColumnModel
|
||||||
|
|
||||||
// get the item at the index after the new index (since we are moving up, we know
|
// get the item at the index after the new index (since we are moving up, we know
|
||||||
// that there are columns below the new index)
|
// that there are columns below the new index)
|
||||||
TableColumn column = visibleList.get(newIndex + 1);
|
TableColumn column = visibleColumns.get(newIndex + 1);
|
||||||
|
|
||||||
// find this column in the complete list and then place the moved column before that
|
// find this column in the complete list and then place the moved column before that
|
||||||
// position in the complete list
|
// position in the complete list
|
||||||
|
@ -375,7 +365,7 @@ public class GTableColumnModel
|
||||||
|
|
||||||
// get the item at the index before the new index (since we are moving down, we know
|
// get the item at the index before the new index (since we are moving down, we know
|
||||||
// that there are columns above the new index)
|
// that there are columns above the new index)
|
||||||
TableColumn column = visibleList.get(newIndex - 1);
|
TableColumn column = visibleColumns.get(newIndex - 1);
|
||||||
|
|
||||||
// find this column in the complete list and then place the moved column after that
|
// find this column in the complete list and then place the moved column after that
|
||||||
// position in the complete list
|
// position in the complete list
|
||||||
|
@ -402,9 +392,9 @@ public class GTableColumnModel
|
||||||
public void removeColumn(TableColumn column) {
|
public void removeColumn(TableColumn column) {
|
||||||
completeList.remove(column);
|
completeList.remove(column);
|
||||||
|
|
||||||
int index = visibleList.indexOf(column);
|
int index = visibleColumns.indexOf(column);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
visibleList.remove(index);
|
visibleColumns.remove(index);
|
||||||
// Adjust for the selection
|
// Adjust for the selection
|
||||||
if (selectionModel != null) {
|
if (selectionModel != null) {
|
||||||
selectionModel.removeIndexInterval(index, index);
|
selectionModel.removeIndexInterval(index, index);
|
||||||
|
@ -459,7 +449,7 @@ public class GTableColumnModel
|
||||||
*/
|
*/
|
||||||
private void recalcWidthCache() {
|
private void recalcWidthCache() {
|
||||||
totalColumnWidth = 0;
|
totalColumnWidth = 0;
|
||||||
for (TableColumn tableColumn : visibleList) {
|
for (TableColumn tableColumn : visibleColumns.getColumns()) {
|
||||||
totalColumnWidth += tableColumn.getWidth();
|
totalColumnWidth += tableColumn.getWidth();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,7 +461,7 @@ public class GTableColumnModel
|
||||||
void restoreState(List<TableColumn> newCompleteList, List<Settings> newSettingsList,
|
void restoreState(List<TableColumn> newCompleteList, List<Settings> newSettingsList,
|
||||||
List<TableColumn> newVisibleList) {
|
List<TableColumn> newVisibleList) {
|
||||||
this.completeList = newCompleteList;
|
this.completeList = newCompleteList;
|
||||||
this.visibleList = newVisibleList;
|
this.visibleColumns = new VisibleColumns(newVisibleList);
|
||||||
|
|
||||||
TableModel model = table.getModel();
|
TableModel model = table.getModel();
|
||||||
if (model instanceof ConfigurableColumnTableModel) {
|
if (model instanceof ConfigurableColumnTableModel) {
|
||||||
|
@ -484,10 +474,6 @@ public class GTableColumnModel
|
||||||
configurableModel.setAllColumnSettings(columnIndexAndSettings);
|
configurableModel.setAllColumnSettings(columnIndexAndSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: at some point in the future (like a year or more) we can remove this, when
|
|
||||||
// we know the new code below it works
|
|
||||||
// fireColumnMarginChanged(); // let the system know to rebuild the GUI (Java HACK!)
|
|
||||||
|
|
||||||
// signal a change; we've added/removed columns, but we don't need to be specific
|
// signal a change; we've added/removed columns, but we don't need to be specific
|
||||||
TableColumnModelEvent e = new TableColumnModelEvent(this, 0, getColumnCount() - 1);
|
TableColumnModelEvent e = new TableColumnModelEvent(this, 0, getColumnCount() - 1);
|
||||||
fireColumnAdded(e);
|
fireColumnAdded(e);
|
||||||
|
@ -515,6 +501,74 @@ public class GTableColumnModel
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A small class to provide a method to quickly see if a column is visible by calling contains
|
||||||
|
* on a hash set
|
||||||
|
*/
|
||||||
|
private class VisibleColumns {
|
||||||
|
private Set<TableColumn> visibleSet = new HashSet<>();
|
||||||
|
private List<TableColumn> visibleList = new ArrayList<>();
|
||||||
|
|
||||||
|
public VisibleColumns() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public VisibleColumns(List<TableColumn> newVisibleList) {
|
||||||
|
this.visibleList = newVisibleList;
|
||||||
|
visibleSet.addAll(visibleList);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TableColumn> getColumns() {
|
||||||
|
return visibleList;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return visibleList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(TableColumn column) {
|
||||||
|
visibleList.remove(column);
|
||||||
|
visibleSet.remove(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(TableColumn column) {
|
||||||
|
visibleList.add(column);
|
||||||
|
visibleSet.add(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration<TableColumn> toEnumeration() {
|
||||||
|
return Collections.enumeration(visibleList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableColumn get(int index) {
|
||||||
|
return visibleList.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOf(TableColumn column) {
|
||||||
|
return visibleList.indexOf(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableColumn remove(int index) {
|
||||||
|
|
||||||
|
TableColumn column = visibleList.remove(index);
|
||||||
|
visibleSet.remove(column);
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int insertIndex, TableColumn column) {
|
||||||
|
visibleList.add(insertIndex, column);
|
||||||
|
visibleSet.add(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
visibleList.clear();
|
||||||
|
visibleSet.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean contains(TableColumn c) {
|
||||||
|
return visibleSet.contains(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Listener and event methods
|
// Listener and event methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
|
@ -40,7 +40,7 @@ public interface SortedTableModel extends TableModel {
|
||||||
public boolean isSortable(int columnIndex);
|
public boolean isSortable(int columnIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the column index that is the primary sorted column
|
* Returns the column index that is the primary sorted column; -1 if no column is sorted
|
||||||
*
|
*
|
||||||
* @return the index
|
* @return the index
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Comparator;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.util.table.column.GColumnRenderer;
|
import ghidra.util.table.column.GColumnRenderer;
|
||||||
|
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A special version of the backup comparator that uses the column's rendered value for
|
* A special version of the backup comparator that uses the column's rendered value for
|
||||||
|
@ -33,10 +34,25 @@ public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T>
|
||||||
protected int sortColumn;
|
protected int sortColumn;
|
||||||
protected DynamicColumnTableModel<T> model;
|
protected DynamicColumnTableModel<T> model;
|
||||||
|
|
||||||
|
// columns do not support sorting via their rendered value if the are marked
|
||||||
|
// for column filtering only
|
||||||
|
private boolean supportsColumnSorting = true;
|
||||||
|
|
||||||
public ColumnRenderedValueBackupRowComparator(DynamicColumnTableModel<T> model,
|
public ColumnRenderedValueBackupRowComparator(DynamicColumnTableModel<T> model,
|
||||||
int sortColumn) {
|
int sortColumn) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.sortColumn = sortColumn;
|
this.sortColumn = sortColumn;
|
||||||
|
|
||||||
|
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
||||||
|
if (renderer != null) {
|
||||||
|
if (renderer.getColumnConstraintFilterMode() == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) {
|
||||||
|
// this implies that the column has signaled that it does not support
|
||||||
|
// filtering/sorting using its rendered value
|
||||||
|
supportsColumnSorting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,9 +77,13 @@ public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T>
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private String getRenderedColumnStringValue(T t) {
|
private String getRenderedColumnStringValue(T t) {
|
||||||
|
|
||||||
|
if (!supportsColumnSorting) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
||||||
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
||||||
Object o = model.getColumnValueForRow(t, sortColumn);
|
Object o = getColumnValue(t);
|
||||||
if (renderer == null) {
|
if (renderer == null) {
|
||||||
return o == null ? null : o.toString();
|
return o == null ? null : o.toString();
|
||||||
}
|
}
|
||||||
|
@ -72,6 +92,7 @@ public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T>
|
||||||
return renderer.getFilterString(o, settings);
|
return renderer.getFilterString(o, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this may be overridden to use caching
|
||||||
protected Object getColumnValue(T t) {
|
protected Object getColumnValue(T t) {
|
||||||
return model.getColumnValueForRow(t, sortColumn);
|
return model.getColumnValueForRow(t, sortColumn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,7 +239,7 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeCache() {
|
void initializeCache() {
|
||||||
threadLocalColumnCache.set(new LRUMap<ROW_OBJECT, Map<Integer, Object>>(20000));
|
threadLocalColumnCache.set(new LRUMap<ROW_OBJECT, Map<Integer, Object>>(1000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearCache() {
|
void clearCache() {
|
||||||
|
@ -747,10 +747,6 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
||||||
return maxUpdateDelayMillis;
|
return maxUpdateDelayMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Class<?> getSortedColumnClass(int columnIndex) {
|
|
||||||
return getColumnClass(columnIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadedTableModelUpdateMgr<ROW_OBJECT> getUpdateManager() {
|
ThreadedTableModelUpdateMgr<ROW_OBJECT> getUpdateManager() {
|
||||||
return updateManager;
|
return updateManager;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
ComponentProvider provider = getProvider("Symbol Table");
|
ComponentProvider provider = getProvider("Symbol Table");
|
||||||
tool.showComponentProvider(provider, true);
|
tool.showComponentProvider(provider, true);
|
||||||
|
|
||||||
moveProviderToItsOwnWindow(provider, 950, 750);
|
moveProviderToItsOwnWindow(provider, 950, 400);
|
||||||
GTable table = getTable(provider);
|
GTable table = getTable(provider);
|
||||||
setColumnSizes(table);
|
setColumnSizes(table);
|
||||||
|
|
||||||
|
@ -172,13 +172,28 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
TableColumn column = columnModel.getColumn(i);
|
TableColumn column = columnModel.getColumn(i);
|
||||||
Object headerValue = column.getHeaderValue();
|
Object headerValue = column.getHeaderValue();
|
||||||
if ("Name".equals(headerValue)) {
|
if ("Name".equals(headerValue)) {
|
||||||
column.setPreferredWidth(175);
|
column.setPreferredWidth(300);
|
||||||
}
|
}
|
||||||
else if ("Reference Count".equals(headerValue)) {
|
else if ("Reference Count".equals(headerValue)) {
|
||||||
column.setPreferredWidth(15);
|
column.setPreferredWidth(25);
|
||||||
}
|
}
|
||||||
else if ("Offcut Ref Count".equals(headerValue)) {
|
else if ("Offcut Ref Count".equals(headerValue)) {
|
||||||
column.setPreferredWidth(15);
|
column.setPreferredWidth(25);
|
||||||
|
}
|
||||||
|
else if ("Namespace".equals(headerValue)) {
|
||||||
|
column.setPreferredWidth(160);
|
||||||
|
}
|
||||||
|
else if ("Location".equals(headerValue)) {
|
||||||
|
column.setPreferredWidth(170);
|
||||||
|
}
|
||||||
|
else if ("Namespace".equals(headerValue)) {
|
||||||
|
column.setPreferredWidth(170);
|
||||||
|
}
|
||||||
|
else if ("Source".equals(headerValue)) {
|
||||||
|
column.setPreferredWidth(170);
|
||||||
|
}
|
||||||
|
else if ("Type".equals(headerValue)) {
|
||||||
|
column.setPreferredWidth(170);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue