mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch
'origin/GP-5963_dragonmacher_PR-7346_fmagin_fmagin_table_accessor' (Closes #7346)
This commit is contained in:
commit
87a32d568c
2 changed files with 192 additions and 10 deletions
|
@ -15,18 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.table;
|
package docking.widgets.table;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.widgets.table.model.DirData;
|
import docking.widgets.table.model.*;
|
||||||
import docking.widgets.table.model.TestGDynamicColumnTableModel;
|
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.util.table.GhidraTable;
|
import ghidra.util.table.GhidraTable;
|
||||||
|
|
||||||
|
@ -73,9 +73,8 @@ public class GTableDynamicColumnModelTest extends AbstractGhidraHeadedIntegratio
|
||||||
@Test
|
@Test
|
||||||
public void testAddColumn() throws Exception {
|
public void testAddColumn() throws Exception {
|
||||||
|
|
||||||
// Grab a column in the middle to remove
|
|
||||||
int count = model.getColumnCount();
|
int count = model.getColumnCount();
|
||||||
DynamicTableColumn<DirData, ?, ?> column = model.getColumn(2);
|
DynamicTableColumn<DirData, ?, ?> column = new DirDataSizeColumn();
|
||||||
|
|
||||||
int index = 2; // in the middle
|
int index = 2; // in the middle
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
|
@ -86,6 +85,93 @@ public class GTableDynamicColumnModelTest extends AbstractGhidraHeadedIntegratio
|
||||||
assertColumnPresent(column);
|
assertColumnPresent(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTableColumnDescriptor_ShortcutColumnCreation() throws Exception {
|
||||||
|
|
||||||
|
frame.dispose();
|
||||||
|
|
||||||
|
model = new TestGDynamicColumnTableModel() {
|
||||||
|
@Override
|
||||||
|
protected TableColumnDescriptor<DirData> createTableColumnDescriptor() {
|
||||||
|
TableColumnDescriptor<DirData> descriptor = new TableColumnDescriptor<>();
|
||||||
|
descriptor.addVisibleColumn("Name", String.class, data -> data.getName());
|
||||||
|
descriptor.addVisibleColumn("Size", Integer.class, data -> data.getSize());
|
||||||
|
descriptor.addHiddenColumn("Date", String.class, data -> data.getTime());
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
table = new GhidraTable(model);
|
||||||
|
|
||||||
|
frame = new JFrame("Ghidra Table Test");
|
||||||
|
frame.getContentPane().setLayout(new BorderLayout());
|
||||||
|
frame.getContentPane().add(new JScrollPane(table));
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
// Note: when building the column model, all columns are visible by default, including those
|
||||||
|
// created as hidden columns. A swing process will run to update the hidden columns. Thus,
|
||||||
|
// we need to flush the swing processes to get that update to happen.
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
assertEquals(3, model.getColumnCount());
|
||||||
|
|
||||||
|
GTableColumnModel columnModel = (GTableColumnModel) table.getColumnModel();
|
||||||
|
assertEquals(2, columnModel.getColumnCount());
|
||||||
|
|
||||||
|
assertColumns("Name", "Size");
|
||||||
|
|
||||||
|
showHiddenColumn("Date");
|
||||||
|
assertColumns("Name", "Size", "Date");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showHiddenColumn(String name) {
|
||||||
|
TableColumn tableColumn = getHiddenTableColumn(name);
|
||||||
|
GTableColumnModel columnModel = (GTableColumnModel) table.getColumnModel();
|
||||||
|
runSwing(() -> columnModel.setVisible(tableColumn, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TableColumn getHiddenTableColumn(String name) {
|
||||||
|
GTableColumnModel columnModel = (GTableColumnModel) table.getColumnModel();
|
||||||
|
TableColumn tableColumn = runSwing(() -> {
|
||||||
|
List<TableColumn> allColumns = columnModel.getAllColumns();
|
||||||
|
for (TableColumn column : allColumns) {
|
||||||
|
Object headerValue = column.getHeaderValue();
|
||||||
|
if (headerValue.equals(name)) {
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
assertNotNull(tableColumn);
|
||||||
|
|
||||||
|
boolean isVisible = runSwing(() -> {
|
||||||
|
int index = tableColumn.getModelIndex();
|
||||||
|
return columnModel.isVisible(index);
|
||||||
|
});
|
||||||
|
assertFalse(isVisible);
|
||||||
|
return tableColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DynamicTableColumn<DirData, ?, ?> getColumn(String name) {
|
||||||
|
int count = model.getColumnCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
|
||||||
|
DynamicTableColumn<DirData, ?, ?> column = model.getColumn(i);
|
||||||
|
String columnName = column.getColumnName();
|
||||||
|
if (columnName.equals(name)) {
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertColumns(String... expectedNames) {
|
||||||
|
for (String expectedName : expectedNames) {
|
||||||
|
DynamicTableColumn<DirData, ?, ?> column = getColumn(expectedName);
|
||||||
|
assertNotNull("Column not found in model - " + expectedName, column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void assertColumnPresent(DynamicTableColumn<DirData, ?, ?> column) {
|
private void assertColumnPresent(DynamicTableColumn<DirData, ?, ?> column) {
|
||||||
int count = model.getColumnCount();
|
int count = model.getColumnCount();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
|
|
|
@ -16,7 +16,10 @@
|
||||||
package docking.widgets.table;
|
package docking.widgets.table;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class TableColumnDescriptor<ROW_TYPE> {
|
public class TableColumnDescriptor<ROW_TYPE> {
|
||||||
|
@ -106,6 +109,99 @@ public class TableColumnDescriptor<ROW_TYPE> {
|
||||||
columns.add(new TableColumnInfo(column, true, sortOrdinal, ascending));
|
columns.add(new TableColumnInfo(column, true, sortOrdinal, ascending));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a column to the descriptor via an anonymous accessor function instead.
|
||||||
|
* <P>
|
||||||
|
* If you would like to control the sorting behavior of your column, then use
|
||||||
|
* {@link #addVisibleColumn(String, Class, Function, int, boolean)}.
|
||||||
|
* <P>
|
||||||
|
* Note: any columns created via this method will not be discoverable by other tables. To use
|
||||||
|
* that feature, you must create a separate column class that extends
|
||||||
|
* {@link DynamicTableColumnExtensionPoint}.
|
||||||
|
*
|
||||||
|
* @param name the column name, visible in the UI
|
||||||
|
* @param columnTypeClass the column class type
|
||||||
|
* @param rowToColumnFunction a function to convert a row object to the column object
|
||||||
|
* @param <COLUMN_TYPE> the column type
|
||||||
|
*/
|
||||||
|
public <COLUMN_TYPE> void addVisibleColumn(String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||||
|
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction) {
|
||||||
|
addVisibleColumn(name, columnTypeClass, rowToColumnFunction, -1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a column to the descriptor via an anonymous accessor function instead.
|
||||||
|
* <P>
|
||||||
|
* Note: any columns created via this method will not be discoverable by other tables. To use
|
||||||
|
* that feature, you must create a separate column class that extends
|
||||||
|
* {@link DynamicTableColumnExtensionPoint}.
|
||||||
|
*
|
||||||
|
* @param name the column name, visible in the UI
|
||||||
|
* @param columnTypeClass the column class type
|
||||||
|
* @param rowToColumnFunction a function to convert a row object to the column object
|
||||||
|
* @param sortOrdinal the <b>ordinal (i.e., 1, 2, 3...n)</b>, not the index (i.e, 0, 1, 2...n)
|
||||||
|
* @param ascending true for sort ascending; false for descending
|
||||||
|
* @param <COLUMN_TYPE> the column type
|
||||||
|
*/
|
||||||
|
public <COLUMN_TYPE> void addVisibleColumn(String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||||
|
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction, int sortOrdinal,
|
||||||
|
boolean ascending) {
|
||||||
|
|
||||||
|
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> column =
|
||||||
|
createColumnStub(name, columnTypeClass, rowToColumnFunction);
|
||||||
|
addVisibleColumn(column, sortOrdinal, ascending);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a column to the descriptor via an anonymous accessor function instead. The column added
|
||||||
|
* will not be displayed until enabled by the user.
|
||||||
|
* <P>
|
||||||
|
* Note: any columns created via this method will not be discoverable by other tables. To use
|
||||||
|
* that feature, you must create a separate column class that extends
|
||||||
|
* {@link DynamicTableColumnExtensionPoint}.
|
||||||
|
*
|
||||||
|
* @param name the column name, visible in the UI
|
||||||
|
* @param columnTypeClass the column class type
|
||||||
|
* @param rowToColumnFunction a function to convert a row object to the column object
|
||||||
|
* @param <COLUMN_TYPE> the column type
|
||||||
|
*/
|
||||||
|
public <COLUMN_TYPE> void addHiddenColumn(String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||||
|
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction) {
|
||||||
|
|
||||||
|
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> column =
|
||||||
|
createColumnStub(name, columnTypeClass, rowToColumnFunction);
|
||||||
|
addHiddenColumn(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <COLUMN_TYPE> AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> createColumnStub(
|
||||||
|
String name, Class<COLUMN_TYPE> columnTypeClass,
|
||||||
|
Function<ROW_TYPE, COLUMN_TYPE> rowToColumnFunction) {
|
||||||
|
|
||||||
|
return new AbstractDynamicTableColumn<>() {
|
||||||
|
@Override
|
||||||
|
public String getColumnName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public COLUMN_TYPE getValue(ROW_TYPE rowObject, Settings settings, Object data,
|
||||||
|
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
||||||
|
return rowToColumnFunction.apply(rowObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<COLUMN_TYPE> getColumnClass() {
|
||||||
|
return columnTypeClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<ROW_TYPE> getSupportedRowType() {
|
||||||
|
// returning null means this column will not be available to use in other tables
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private class TableColumnInfo implements Comparable<TableColumnInfo> {
|
private class TableColumnInfo implements Comparable<TableColumnInfo> {
|
||||||
private DynamicTableColumn<ROW_TYPE, ?, ?> column;
|
private DynamicTableColumn<ROW_TYPE, ?, ?> column;
|
||||||
private boolean isVisible = false;
|
private boolean isVisible = false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue