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:
dragonmacher 2019-10-22 18:36:41 -04:00
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

Before After
Before After

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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;
} }
} }

View file

@ -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;
} }

View file

@ -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();
}
}
} }

View file

@ -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;
}
} }

View file

@ -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;
} }

View file

@ -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);
} }
} }

View file

@ -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);
} }

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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;
}
}

View file

@ -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;
} }
} }
} }

View file

@ -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
}
} }

View file

@ -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;

View file

@ -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();
} }

View file

@ -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;

View file

@ -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
//================================================================================================== //==================================================================================================

View file

@ -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
*/ */

View file

@ -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);
} }

View file

@ -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;
} }

View file

@ -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);
} }
} }
}); });