mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Updated the DBViewer to allow for filtering
This commit is contained in:
parent
5fd01c739d
commit
ff7c8929bc
18 changed files with 343 additions and 565 deletions
|
@ -17,20 +17,20 @@ package ghidra.app.plugin.debug;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.FlowLayout;
|
import java.awt.FlowLayout;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.TableModel;
|
|
||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
import ghidra.app.plugin.debug.dbtable.*;
|
import docking.widgets.table.GTableFilterPanel;
|
||||||
|
import docking.widgets.table.threaded.GThreadedTablePanel;
|
||||||
|
import ghidra.app.plugin.debug.dbtable.DbSmallTableModel;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
import ghidra.util.task.SwingUpdateManager;
|
import ghidra.util.task.SwingUpdateManager;
|
||||||
|
@ -39,25 +39,27 @@ class DbViewerComponent extends JPanel {
|
||||||
|
|
||||||
private static Table[] NO_TABLES = new Table[0];
|
private static Table[] NO_TABLES = new Table[0];
|
||||||
|
|
||||||
private static Comparator<Table> TABLE_NAME_COMPARATOR = new Comparator<>() {
|
private static Comparator<Table> TABLE_NAME_COMPARATOR =
|
||||||
@Override
|
(o1, o2) -> (o1).getName().compareTo((o2).getName());
|
||||||
public int compare(Table o1, Table o2) {
|
|
||||||
return (o1).getName().compareTo((o2).getName());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private DBHandle dbh;
|
private DBHandle dbh;
|
||||||
private DBListener dbListener;
|
private DBListener dbListener;
|
||||||
private JPanel southPanel;
|
private JPanel centerPanel;
|
||||||
|
private JComponent southComponent;
|
||||||
private JLabel dbLabel;
|
private JLabel dbLabel;
|
||||||
private JComboBox<TableItem> combo;
|
private JComboBox<TableItem> combo;
|
||||||
private Table[] tables = NO_TABLES;
|
private Table[] tables = NO_TABLES;
|
||||||
private Hashtable<String, TableStatistics[]> tableStats = new Hashtable<>();
|
private Map<String, TableStatistics[]> tableStats = new HashMap<>();
|
||||||
|
|
||||||
private SwingUpdateManager updateMgr;
|
private SwingUpdateManager updateMgr;
|
||||||
|
|
||||||
DbViewerComponent() {
|
private PluginTool tool;
|
||||||
|
|
||||||
|
private GTableFilterPanel<DBRecord> tableFilterPanel;
|
||||||
|
|
||||||
|
DbViewerComponent(PluginTool tool) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
JPanel subNorthPanel = new JPanel(new PairLayout(4, 10));
|
JPanel subNorthPanel = new JPanel(new PairLayout(4, 10));
|
||||||
|
@ -66,32 +68,19 @@ class DbViewerComponent extends JPanel {
|
||||||
subNorthPanel.add(dbLabel);
|
subNorthPanel.add(dbLabel);
|
||||||
subNorthPanel.add(new GLabel("Tables:"));
|
subNorthPanel.add(new GLabel("Tables:"));
|
||||||
combo = new GComboBox<>();
|
combo = new GComboBox<>();
|
||||||
combo.addActionListener(new ActionListener() {
|
combo.addActionListener(e -> refreshTable());
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
refreshTable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
subNorthPanel.add(combo);
|
subNorthPanel.add(combo);
|
||||||
northPanel.add(subNorthPanel);
|
northPanel.add(subNorthPanel);
|
||||||
add(northPanel, BorderLayout.NORTH);
|
add(northPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
updateMgr = new SwingUpdateManager(100, 2000, new Runnable() {
|
updateMgr = new SwingUpdateManager(100, 2000, () -> refresh());
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void closeDatabase() {
|
synchronized void closeDatabase() {
|
||||||
if (dbh != null) {
|
if (dbh != null) {
|
||||||
combo.removeAllItems();
|
combo.removeAllItems();
|
||||||
dbLabel.setText("");
|
dbLabel.setText("");
|
||||||
if (southPanel != null) {
|
removeWidgets();
|
||||||
remove(southPanel);
|
|
||||||
southPanel = null;
|
|
||||||
}
|
|
||||||
tables = NO_TABLES;
|
tables = NO_TABLES;
|
||||||
tableStats.clear();
|
tableStats.clear();
|
||||||
dbh = null;
|
dbh = null;
|
||||||
|
@ -101,6 +90,15 @@ class DbViewerComponent extends JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeWidgets() {
|
||||||
|
if (centerPanel != null) {
|
||||||
|
remove(centerPanel);
|
||||||
|
remove(southComponent);
|
||||||
|
centerPanel = null;
|
||||||
|
southComponent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
synchronized void openDatabase(String name, DBHandle handle) {
|
synchronized void openDatabase(String name, DBHandle handle) {
|
||||||
|
|
||||||
closeDatabase();
|
closeDatabase();
|
||||||
|
@ -110,7 +108,7 @@ class DbViewerComponent extends JPanel {
|
||||||
dbLabel.setText(name);
|
dbLabel.setText(name);
|
||||||
updateTableChoices(null);
|
updateTableChoices(null);
|
||||||
|
|
||||||
dbListener = getNewDBListener();
|
dbListener = new InternalDBListener();
|
||||||
handle.addListener(dbListener);
|
handle.addListener(dbListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,10 +124,7 @@ class DbViewerComponent extends JPanel {
|
||||||
|
|
||||||
synchronized void refreshTable() {
|
synchronized void refreshTable() {
|
||||||
if (dbh == null) {
|
if (dbh == null) {
|
||||||
if (southPanel != null) {
|
removeWidgets();
|
||||||
remove(southPanel);
|
|
||||||
southPanel = null;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
synchronized (dbh) {
|
synchronized (dbh) {
|
||||||
|
@ -139,6 +134,7 @@ class DbViewerComponent extends JPanel {
|
||||||
|
|
||||||
synchronized void dispose() {
|
synchronized void dispose() {
|
||||||
updateMgr.dispose();
|
updateMgr.dispose();
|
||||||
|
tableFilterPanel.dispose();
|
||||||
closeDatabase();
|
closeDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,35 +193,20 @@ class DbViewerComponent extends JPanel {
|
||||||
|
|
||||||
private void updateTable() {
|
private void updateTable() {
|
||||||
|
|
||||||
if (southPanel != null) {
|
removeWidgets();
|
||||||
remove(southPanel);
|
|
||||||
southPanel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TableItem t = (TableItem) combo.getSelectedItem();
|
TableItem t = (TableItem) combo.getSelectedItem();
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
southPanel = createSouthPanel(t.table);
|
centerPanel = createCenterPanel(t.table);
|
||||||
add(southPanel, BorderLayout.CENTER);
|
add(centerPanel, BorderLayout.CENTER);
|
||||||
|
southComponent = createSouthComponent(t.table);
|
||||||
|
add(southComponent, BorderLayout.SOUTH);
|
||||||
|
|
||||||
}
|
}
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel createSouthPanel(Table table) {
|
private JComponent createSouthComponent(Table table) {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
|
||||||
TableModel model = null;
|
|
||||||
GTable gTable = new GTable();
|
|
||||||
if (table.getRecordCount() <= 10000) {
|
|
||||||
model = new DbSmallTableModel(table);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model = new DbLargeTableModel(table);
|
|
||||||
}
|
|
||||||
gTable.setModel(model);
|
|
||||||
gTable.setDefaultRenderer(Long.class, new LongRenderer());
|
|
||||||
|
|
||||||
JScrollPane scroll = new JScrollPane(gTable);
|
|
||||||
panel.add(scroll, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
TableStatistics[] stats = getStats(table);
|
TableStatistics[] stats = getStats(table);
|
||||||
String recCnt = "Records: " + Integer.toString(table.getRecordCount());
|
String recCnt = "Records: " + Integer.toString(table.getRecordCount());
|
||||||
String intNodeCnt = "";
|
String intNodeCnt = "";
|
||||||
|
@ -244,15 +225,22 @@ class DbViewerComponent extends JPanel {
|
||||||
size += " / " + Integer.toString(stats[1].size / 1024);
|
size += " / " + Integer.toString(stats[1].size / 1024);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel.add(new GLabel(
|
return new GLabel(
|
||||||
recCnt + " " + intNodeCnt + " " + recNodeCnt + " " + chainBufCnt + " " + size),
|
recCnt + " " + intNodeCnt + " " + recNodeCnt + " " + chainBufCnt + " " + size);
|
||||||
BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
return panel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DBListener getNewDBListener() {
|
private JPanel createCenterPanel(Table table) {
|
||||||
return new InternalDBListener();
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
DbSmallTableModel model = new DbSmallTableModel(tool, table);
|
||||||
|
|
||||||
|
GThreadedTablePanel<DBRecord> threadedPanel = new GThreadedTablePanel<>(model);
|
||||||
|
GTable gTable = threadedPanel.getTable();
|
||||||
|
|
||||||
|
tableFilterPanel = new GTableFilterPanel<>(gTable, model);
|
||||||
|
|
||||||
|
panel.add(threadedPanel, BorderLayout.CENTER);
|
||||||
|
panel.add(tableFilterPanel, BorderLayout.SOUTH);
|
||||||
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
|
@ -41,7 +41,7 @@ import resources.Icons;
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class DbViewerPlugin extends Plugin {
|
public class DbViewerPlugin extends Plugin {
|
||||||
|
|
||||||
private DbViewerProvider viewer;
|
private DbViewerProvider provider;
|
||||||
private DockingAction refreshAction;
|
private DockingAction refreshAction;
|
||||||
|
|
||||||
public DbViewerPlugin(PluginTool tool) {
|
public DbViewerPlugin(PluginTool tool) {
|
||||||
|
@ -52,11 +52,11 @@ public class DbViewerPlugin extends Plugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispose() {
|
protected void dispose() {
|
||||||
if (viewer != null) {
|
if (provider != null) {
|
||||||
deactivateViewer();
|
deactivateViewer();
|
||||||
tool.removeComponentProvider(viewer);
|
tool.removeComponentProvider(provider);
|
||||||
viewer.dispose();
|
provider.dispose();
|
||||||
viewer = null;
|
provider = null;
|
||||||
}
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,8 @@ public class DbViewerPlugin extends Plugin {
|
||||||
refreshAction = new DockingAction("Refresh", getName()) {
|
refreshAction = new DockingAction("Refresh", getName()) {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
if (viewer != null) {
|
if (provider != null) {
|
||||||
viewer.refresh();
|
provider.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -78,19 +78,19 @@ public class DbViewerPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void activateViewer(DomainObjectAdapterDB dobj) {
|
private void activateViewer(DomainObjectAdapterDB dobj) {
|
||||||
if (viewer == null) {
|
if (provider == null) {
|
||||||
viewer = new DbViewerProvider(this);
|
provider = new DbViewerProvider(this);
|
||||||
tool.addComponentProvider(viewer, false);
|
tool.addComponentProvider(provider, false);
|
||||||
tool.addLocalAction(viewer, refreshAction);
|
tool.addLocalAction(provider, refreshAction);
|
||||||
}
|
}
|
||||||
viewer.openDatabase(dobj.getName(), dobj.getDBHandle());
|
provider.openDatabase(dobj.getName(), dobj.getDBHandle());
|
||||||
refreshAction.setEnabled(true);
|
refreshAction.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deactivateViewer() {
|
private void deactivateViewer() {
|
||||||
if (viewer != null) {
|
if (provider != null) {
|
||||||
refreshAction.setEnabled(false);
|
refreshAction.setEnabled(false);
|
||||||
viewer.closeDatabase();
|
provider.closeDatabase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class DbViewerProvider extends ComponentProviderAdapter {
|
||||||
@Override
|
@Override
|
||||||
public JComponent getComponent() {
|
public JComponent getComponent() {
|
||||||
if (comp == null) {
|
if (comp == null) {
|
||||||
comp = new DbViewerComponent();
|
comp = new DbViewerComponent(tool);
|
||||||
if (dbh != null) {
|
if (dbh != null) {
|
||||||
comp.openDatabase(dbName, dbh);
|
comp.openDatabase(dbName, dbh);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,51 @@
|
||||||
package ghidra.app.plugin.debug.dbtable;
|
package ghidra.app.plugin.debug.dbtable;
|
||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
|
import docking.widgets.table.AbstractDynamicTableColumnStub;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
|
|
||||||
abstract class AbstractColumnAdapter {
|
abstract class AbstractColumnAdapter extends AbstractDynamicTableColumnStub<DBRecord, Object> {
|
||||||
|
|
||||||
|
protected LongRenderer longRenderer = new LongRenderer();
|
||||||
|
|
||||||
|
protected int column;
|
||||||
|
private String columnName;
|
||||||
|
|
||||||
|
AbstractColumnAdapter(String columnName, int column) {
|
||||||
|
this.column = column;
|
||||||
|
this.columnName = columnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(DBRecord rowObject, Settings settings, ServiceProvider serviceProvider)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
if (column == 0) {
|
||||||
|
return getKeyValue(rowObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -1, since the DB indices do not have the key column included
|
||||||
|
int dbColumn = column - 1;
|
||||||
|
return getValue(rowObject, dbColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Class<Object> getColumnClass() {
|
||||||
|
return (Class<Object>) getValueClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName() {
|
||||||
|
return columnName;
|
||||||
|
}
|
||||||
|
|
||||||
abstract Class<?> getValueClass();
|
abstract Class<?> getValueClass();
|
||||||
|
|
||||||
abstract Object getKeyValue(DBRecord rec);
|
abstract Object getKeyValue(DBRecord rec);
|
||||||
|
|
||||||
abstract Object getValue(DBRecord rec, int col);
|
abstract Object getValue(DBRecord rec, int dbColumn);
|
||||||
|
|
||||||
protected String getByteString(byte b) {
|
protected String getByteString(byte b) {
|
||||||
String str = Integer.toHexString(b);
|
String str = Integer.toHexString(b);
|
||||||
|
@ -33,20 +70,4 @@ abstract class AbstractColumnAdapter {
|
||||||
return "0x" + str;
|
return "0x" + str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private String format(long l, int size) {
|
|
||||||
// String hex = Long.toHexString(l);
|
|
||||||
// if (hex.length() > size) {
|
|
||||||
// hex = hex.substring(hex.length()-size);
|
|
||||||
// }
|
|
||||||
// else if (hex.length() < size) {
|
|
||||||
// StringBuffer b = new StringBuffer(20);
|
|
||||||
// for(int i=hex.length();i<size;i++) {
|
|
||||||
// b.append("");
|
|
||||||
// }
|
|
||||||
// b.append(hex);
|
|
||||||
// hex = b.toString();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return hex;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ import db.DBRecord;
|
||||||
|
|
||||||
public class BinaryColumnAdapter extends AbstractColumnAdapter {
|
public class BinaryColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
|
BinaryColumnAdapter(String columnName, int column) {
|
||||||
|
super(columnName, column);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?> getValueClass() {
|
Class<?> getValueClass() {
|
||||||
return String.class;
|
return String.class;
|
||||||
|
@ -28,7 +32,7 @@ public class BinaryColumnAdapter extends AbstractColumnAdapter {
|
||||||
@Override
|
@Override
|
||||||
Object getKeyValue(DBRecord rec) {
|
Object getKeyValue(DBRecord rec) {
|
||||||
byte[] bytes = ((BinaryField) rec.getKeyField()).getBinaryData();
|
byte[] bytes = ((BinaryField) rec.getKeyField()).getBinaryData();
|
||||||
StringBuffer buf = new StringBuffer(" byte[" + bytes.length + "] = ");
|
StringBuffer buf = new StringBuffer("byte[" + bytes.length + "] = ");
|
||||||
if (bytes.length > 0) {
|
if (bytes.length > 0) {
|
||||||
int len = Math.min(bytes.length, 20);
|
int len = Math.min(bytes.length, 20);
|
||||||
buf.append(bytes[0]);
|
buf.append(bytes[0]);
|
||||||
|
@ -49,7 +53,7 @@ public class BinaryColumnAdapter extends AbstractColumnAdapter {
|
||||||
if (bytes == null) {
|
if (bytes == null) {
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
StringBuffer buf = new StringBuffer(" byte[" + bytes.length + "] = ");
|
StringBuilder buf = new StringBuilder("byte[" + bytes.length + "] = ");
|
||||||
if (bytes.length > 0) {
|
if (bytes.length > 0) {
|
||||||
int len = Math.min(bytes.length, 20);
|
int len = Math.min(bytes.length, 20);
|
||||||
String str = getByteString(bytes[0]);
|
String str = getByteString(bytes[0]);
|
||||||
|
|
|
@ -17,9 +17,23 @@ package ghidra.app.plugin.debug.dbtable;
|
||||||
|
|
||||||
import db.BooleanField;
|
import db.BooleanField;
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
|
import docking.widgets.table.GBooleanCellRenderer;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.util.table.column.GColumnRenderer;
|
||||||
|
|
||||||
public class BooleanColumnAdapter extends AbstractColumnAdapter {
|
public class BooleanColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
|
private BooleanRenderer renderer = new BooleanRenderer();
|
||||||
|
|
||||||
|
BooleanColumnAdapter(String columnName, int column) {
|
||||||
|
super(columnName, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnPreferredWidth() {
|
||||||
|
return 75;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?> getValueClass() {
|
Class<?> getValueClass() {
|
||||||
return Boolean.class;
|
return Boolean.class;
|
||||||
|
@ -31,8 +45,23 @@ public class BooleanColumnAdapter extends AbstractColumnAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Object getValue(DBRecord rec, int col) {
|
Object getValue(DBRecord rec, int dbColumn) {
|
||||||
return Boolean.valueOf(rec.getBooleanValue(col));
|
return Boolean.valueOf(rec.getBooleanValue(dbColumn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BooleanRenderer getColumnRenderer() {
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BooleanRenderer extends GBooleanCellRenderer implements GColumnRenderer<Object> {
|
||||||
|
@Override
|
||||||
|
public String getFilterString(Object t, Settings settings) {
|
||||||
|
Boolean b = (Boolean) t;
|
||||||
|
if (b == null) {
|
||||||
|
return Boolean.FALSE.toString();
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,15 @@ import db.DBRecord;
|
||||||
|
|
||||||
public class ByteColumnAdapter extends AbstractColumnAdapter {
|
public class ByteColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
|
ByteColumnAdapter(String columnName, int column) {
|
||||||
|
super(columnName, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnPreferredWidth() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?> getValueClass() {
|
Class<?> getValueClass() {
|
||||||
return Byte.class;
|
return Byte.class;
|
||||||
|
@ -31,8 +40,12 @@ public class ByteColumnAdapter extends AbstractColumnAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Object getValue(DBRecord rec, int col) {
|
Object getValue(DBRecord rec, int dbColumn) {
|
||||||
return Byte.valueOf(rec.getByteValue(col));
|
return Byte.valueOf(rec.getByteValue(dbColumn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongRenderer getColumnRenderer() {
|
||||||
|
return longRenderer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,274 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.plugin.debug.dbtable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.swing.event.TableModelListener;
|
|
||||||
import javax.swing.table.TableModel;
|
|
||||||
|
|
||||||
import db.*;
|
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.exception.AssertException;
|
|
||||||
|
|
||||||
public class DbLargeTableModel implements TableModel {
|
|
||||||
private ArrayList<TableModelListener> listeners = new ArrayList<TableModelListener>();
|
|
||||||
private Table table;
|
|
||||||
private Schema schema;
|
|
||||||
private List<AbstractColumnAdapter> columns = new ArrayList<AbstractColumnAdapter>();
|
|
||||||
private RecordIterator recIt;
|
|
||||||
private DBRecord lastRecord;
|
|
||||||
private int lastIndex;
|
|
||||||
private Field minKey;
|
|
||||||
private Field maxKey;
|
|
||||||
private Field keyType;
|
|
||||||
|
|
||||||
public DbLargeTableModel(Table table) {
|
|
||||||
this.table = table;
|
|
||||||
schema = table.getSchema();
|
|
||||||
try {
|
|
||||||
keyType = schema.getKeyFieldType();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
recIt = table.iterator();
|
|
||||||
lastRecord = recIt.next();
|
|
||||||
lastIndex = 0;
|
|
||||||
findMaxKey();
|
|
||||||
findMinKey();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
columns.add(getColumn(schema.getKeyFieldType()));
|
|
||||||
|
|
||||||
Field[] fields = schema.getFields();
|
|
||||||
for (Field field : fields) {
|
|
||||||
columns.add(getColumn(field));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private AbstractColumnAdapter getColumn(Field field) {
|
|
||||||
if (field instanceof ByteField) {
|
|
||||||
return new ByteColumnAdapter();
|
|
||||||
}
|
|
||||||
else if (field instanceof BooleanField) {
|
|
||||||
return new BooleanColumnAdapter();
|
|
||||||
}
|
|
||||||
else if (field instanceof ShortField) {
|
|
||||||
return new ShortColumnAdapter();
|
|
||||||
}
|
|
||||||
else if (field instanceof IntField) {
|
|
||||||
return new IntegerColumnAdapter();
|
|
||||||
}
|
|
||||||
else if (field instanceof LongField) {
|
|
||||||
return new LongColumnAdapter();
|
|
||||||
}
|
|
||||||
else if (field instanceof StringField) {
|
|
||||||
return new StringColumnAdapter();
|
|
||||||
}
|
|
||||||
else if (field instanceof BinaryField) {
|
|
||||||
return new BinaryColumnAdapter();
|
|
||||||
}
|
|
||||||
throw new AssertException(
|
|
||||||
"New, unexpected DB column type: " + field.getClass().getSimpleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findMinKey() throws IOException {
|
|
||||||
RecordIterator iter = table.iterator();
|
|
||||||
DBRecord rec = iter.next();
|
|
||||||
minKey = rec.getKeyField();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findMaxKey() throws IOException {
|
|
||||||
Field max = keyType.newField();
|
|
||||||
if (table.useLongKeys()) {
|
|
||||||
max.setLongValue(Long.MAX_VALUE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
byte[] maxBytes = new byte[128];
|
|
||||||
Arrays.fill(maxBytes, 0, 128, (byte) 0x7f);
|
|
||||||
max.setBinaryData(maxBytes);
|
|
||||||
}
|
|
||||||
RecordIterator iter = table.iterator(max);
|
|
||||||
DBRecord rec = iter.previous();
|
|
||||||
maxKey = rec.getKeyField();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTableModelListener(TableModelListener l) {
|
|
||||||
listeners.add(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getColumnClass(int columnIndex) {
|
|
||||||
return columns.get(columnIndex).getValueClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColumnCount() {
|
|
||||||
return schema.getFieldCount() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getColumnName(int columnIndex) {
|
|
||||||
if (columnIndex == 0) {
|
|
||||||
return schema.getKeyName();
|
|
||||||
}
|
|
||||||
--columnIndex;
|
|
||||||
int[] indexCols = table.getIndexedColumns();
|
|
||||||
boolean isIndexed = false;
|
|
||||||
for (int i = 0; i < indexCols.length; i++) {
|
|
||||||
if (indexCols[i] == columnIndex) {
|
|
||||||
isIndexed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return schema.getFieldNames()[columnIndex] + (isIndexed ? "*" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRowCount() {
|
|
||||||
return table.getRecordCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
|
||||||
DBRecord rec = getRecord(rowIndex);
|
|
||||||
if (columnIndex == 0) { // key column
|
|
||||||
return columns.get(columnIndex).getKeyValue(rec);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dbColumn = columnIndex - 1; // -1, since the DB indices do not have the key column included
|
|
||||||
return columns.get(columnIndex).getValue(rec, dbColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeTableModelListener(TableModelListener l) {
|
|
||||||
listeners.remove(l);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
|
||||||
// no!
|
|
||||||
}
|
|
||||||
|
|
||||||
private DBRecord getRecord(int index) {
|
|
||||||
try {
|
|
||||||
if (index == lastIndex + 1) {
|
|
||||||
if (recIt.hasNext()) {
|
|
||||||
lastRecord = recIt.next();
|
|
||||||
lastIndex = index;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// iterator ran out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index != lastIndex) {
|
|
||||||
if (index < lastIndex && (lastIndex - index) < 200) {
|
|
||||||
int backup = lastIndex - index + 1;
|
|
||||||
for (int i = 0; i < backup; i++) {
|
|
||||||
if (recIt.hasPrevious()) {
|
|
||||||
recIt.previous();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DBRecord rec = recIt.next();
|
|
||||||
if (rec != null) {
|
|
||||||
lastRecord = rec;
|
|
||||||
lastIndex = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
findRecord(index);
|
|
||||||
lastRecord = recIt.next();
|
|
||||||
lastIndex = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lastRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findRecord(int index) throws IOException {
|
|
||||||
if (index < 1000) {
|
|
||||||
recIt = table.iterator();
|
|
||||||
for (int i = 0; i < index; i++) {
|
|
||||||
recIt.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index > table.getRecordCount() - 1000) {
|
|
||||||
recIt = table.iterator(maxKey);
|
|
||||||
if (recIt.hasNext()) {
|
|
||||||
recIt.next();
|
|
||||||
}
|
|
||||||
for (int i = 0; i < table.getRecordCount() - index; i++) {
|
|
||||||
recIt.previous();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
recIt = table.iterator(approxKey(index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Field approxKey(int index) {
|
|
||||||
Field key = keyType.newField();
|
|
||||||
if (table.useLongKeys()) {
|
|
||||||
long min = minKey.getLongValue();
|
|
||||||
long max = maxKey.getLongValue();
|
|
||||||
long k = min + ((max - min) * index / table.getRecordCount());
|
|
||||||
key.setLongValue(k);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
long min = getLong(minKey.getBinaryData());
|
|
||||||
long max = getLong(maxKey.getBinaryData());
|
|
||||||
long k = min + ((max - min) * index / table.getRecordCount());
|
|
||||||
byte[] bytes = new byte[8];
|
|
||||||
for (int i = 7; i >= 0; i--) {
|
|
||||||
bytes[i] = (byte) k;
|
|
||||||
k >>= 8;
|
|
||||||
}
|
|
||||||
key.setBinaryData(bytes);
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getLong(byte[] bytes) {
|
|
||||||
if (bytes == null || bytes.length == 0)
|
|
||||||
return 0;
|
|
||||||
long value = 0;
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
value <<= 8;
|
|
||||||
if (i < bytes.length) {
|
|
||||||
value += bytes[i] & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,37 +16,40 @@
|
||||||
package ghidra.app.plugin.debug.dbtable;
|
package ghidra.app.plugin.debug.dbtable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import docking.widgets.table.AbstractSortedTableModel;
|
import docking.widgets.table.TableColumnDescriptor;
|
||||||
|
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||||
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Accumulator;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class DbSmallTableModel extends AbstractSortedTableModel<DBRecord> {
|
public class DbSmallTableModel extends ThreadedTableModel<DBRecord, Object> {
|
||||||
private Table table;
|
private Table table;
|
||||||
private Schema schema;
|
private Schema schema;
|
||||||
private List<AbstractColumnAdapter> columns = new ArrayList<>();
|
|
||||||
private List<DBRecord> records;
|
|
||||||
|
|
||||||
public DbSmallTableModel(Table table) {
|
public DbSmallTableModel(ServiceProvider serviceProvider, Table table) {
|
||||||
|
super("DB Records Model", serviceProvider);
|
||||||
this.table = table;
|
this.table = table;
|
||||||
schema = table.getSchema();
|
schema = table.getSchema();
|
||||||
|
|
||||||
records = new ArrayList<>(table.getRecordCount());
|
reloadColumns(); // we must do this after 'schema' has been set
|
||||||
|
}
|
||||||
|
|
||||||
columns.add(getColumn(schema.getKeyFieldType()));
|
@Override
|
||||||
|
protected void doLoad(Accumulator<DBRecord> accumulator, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
Field[] fields = schema.getFields();
|
monitor.initialize(table.getRecordCount());
|
||||||
for (Field field : fields) {
|
|
||||||
columns.add(getColumn(field));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RecordIterator it = table.iterator();
|
RecordIterator it = table.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
records.add(it.next());
|
monitor.checkCancelled();
|
||||||
|
accumulator.add(it.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
@ -54,50 +57,35 @@ public class DbSmallTableModel extends AbstractSortedTableModel<DBRecord> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractColumnAdapter getColumn(Field field) {
|
private AbstractColumnAdapter getColumn(Field field, int column) {
|
||||||
|
|
||||||
|
String columnName = loadColumnName(column);
|
||||||
if (field instanceof ByteField) {
|
if (field instanceof ByteField) {
|
||||||
return new ByteColumnAdapter();
|
return new ByteColumnAdapter(columnName, column);
|
||||||
}
|
}
|
||||||
else if (field instanceof BooleanField) {
|
else if (field instanceof BooleanField) {
|
||||||
return new BooleanColumnAdapter();
|
return new BooleanColumnAdapter(columnName, column);
|
||||||
}
|
}
|
||||||
else if (field instanceof ShortField) {
|
else if (field instanceof ShortField) {
|
||||||
return new ShortColumnAdapter();
|
return new ShortColumnAdapter(columnName, column);
|
||||||
}
|
}
|
||||||
else if (field instanceof IntField) {
|
else if (field instanceof IntField) {
|
||||||
return new IntegerColumnAdapter();
|
return new IntegerColumnAdapter(columnName, column);
|
||||||
}
|
}
|
||||||
else if (field instanceof LongField) {
|
else if (field instanceof LongField) {
|
||||||
return new LongColumnAdapter();
|
return new LongColumnAdapter(columnName, column);
|
||||||
}
|
}
|
||||||
else if (field instanceof StringField) {
|
else if (field instanceof StringField) {
|
||||||
return new StringColumnAdapter();
|
return new StringColumnAdapter(columnName, column);
|
||||||
}
|
}
|
||||||
else if (field instanceof BinaryField) {
|
else if (field instanceof BinaryField) {
|
||||||
return new BinaryColumnAdapter();
|
return new BinaryColumnAdapter(columnName, column);
|
||||||
}
|
}
|
||||||
throw new AssertException(
|
throw new AssertException(
|
||||||
"New, unexpected DB column type: " + field.getClass().getSimpleName());
|
"New, unexpected DB column type: " + field.getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private String loadColumnName(int columnIndex) {
|
||||||
public String getName() {
|
|
||||||
return "DB Small Table";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getColumnClass(int columnIndex) {
|
|
||||||
return columns.get(columnIndex).getValueClass();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColumnCount() {
|
|
||||||
return schema.getFieldCount() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getColumnName(int columnIndex) {
|
|
||||||
if (columnIndex == 0) {
|
if (columnIndex == 0) {
|
||||||
return schema.getKeyName();
|
return schema.getKeyName();
|
||||||
}
|
}
|
||||||
|
@ -114,8 +102,23 @@ public class DbSmallTableModel extends AbstractSortedTableModel<DBRecord> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRowCount() {
|
protected TableColumnDescriptor<DBRecord> createTableColumnDescriptor() {
|
||||||
return table.getRecordCount();
|
|
||||||
|
TableColumnDescriptor<DBRecord> descriptor = new TableColumnDescriptor<>();
|
||||||
|
if (schema == null) {
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 is the key
|
||||||
|
descriptor.addVisibleColumn(getColumn(schema.getKeyFieldType(), 0));
|
||||||
|
|
||||||
|
Field[] fields = schema.getFields();
|
||||||
|
int offset = 1;
|
||||||
|
for (Field field : fields) {
|
||||||
|
descriptor.addVisibleColumn(getColumn(field, offset++));
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -124,22 +127,7 @@ public class DbSmallTableModel extends AbstractSortedTableModel<DBRecord> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getColumnValueForRow(DBRecord rec, int columnIndex) {
|
public Object getDataSource() {
|
||||||
if (columnIndex == 0) { // key column
|
return null;
|
||||||
return columns.get(columnIndex).getKeyValue(rec);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dbColumn = columnIndex - 1; // -1, since the DB indices do not have the key column included
|
|
||||||
return columns.get(columnIndex).getValue(rec, dbColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<DBRecord> getModelData() {
|
|
||||||
return records;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSortable(int columnIndex) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,20 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.debug.dbtable;
|
package ghidra.app.plugin.debug.dbtable;
|
||||||
|
|
||||||
import db.IntField;
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
|
import db.IntField;
|
||||||
|
|
||||||
public class IntegerColumnAdapter extends AbstractColumnAdapter {
|
public class IntegerColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
|
IntegerColumnAdapter(String columnName, int column) {
|
||||||
|
super(columnName, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnPreferredWidth() {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?> getValueClass() {
|
Class<?> getValueClass() {
|
||||||
return Integer.class;
|
return Integer.class;
|
||||||
|
@ -35,4 +44,8 @@ public class IntegerColumnAdapter extends AbstractColumnAdapter {
|
||||||
return Integer.valueOf(rec.getIntValue(col));
|
return Integer.valueOf(rec.getIntValue(col));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongRenderer getColumnRenderer() {
|
||||||
|
return longRenderer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,10 @@ import db.DBRecord;
|
||||||
|
|
||||||
public class LongColumnAdapter extends AbstractColumnAdapter {
|
public class LongColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
|
LongColumnAdapter(String columnName, int column) {
|
||||||
|
super(columnName, column);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?> getValueClass() {
|
Class<?> getValueClass() {
|
||||||
return Long.class;
|
return Long.class;
|
||||||
|
@ -33,4 +37,9 @@ public class LongColumnAdapter extends AbstractColumnAdapter {
|
||||||
Object getValue(DBRecord rec, int col) {
|
Object getValue(DBRecord rec, int col) {
|
||||||
return Long.valueOf(rec.getLongValue(col));
|
return Long.valueOf(rec.getLongValue(col));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongRenderer getColumnRenderer() {
|
||||||
|
return longRenderer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,11 @@ import java.awt.Component;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
import docking.widgets.table.GTableCellRenderer;
|
|
||||||
import docking.widgets.table.GTableCellRenderingData;
|
import docking.widgets.table.GTableCellRenderingData;
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.util.table.column.AbstractGColumnRenderer;
|
||||||
|
|
||||||
public class LongRenderer extends GTableCellRenderer {
|
public class LongRenderer extends AbstractGColumnRenderer<Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||||
|
@ -38,11 +38,17 @@ public class LongRenderer extends GTableCellRenderer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getText(Object value) {
|
protected String getText(Object value) {
|
||||||
return value == null ? "" : "0x" + Long.toHexString((Long) value);
|
return value == null ? "" : "0x" + Long.toHexString(((Number) value).longValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String formatNumber(Number value, Settings settings) {
|
protected String formatNumber(Number value, Settings settings) {
|
||||||
return getText(value);
|
return getText(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilterString(Object t, Settings settings) {
|
||||||
|
// have the filter text match the display string
|
||||||
|
return getText(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ import db.ShortField;
|
||||||
|
|
||||||
public class ShortColumnAdapter extends AbstractColumnAdapter {
|
public class ShortColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
|
ShortColumnAdapter(String columnName, int column) {
|
||||||
|
super(columnName, column);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?> getValueClass() {
|
Class<?> getValueClass() {
|
||||||
return Short.class;
|
return Short.class;
|
||||||
|
@ -35,4 +39,8 @@ public class ShortColumnAdapter extends AbstractColumnAdapter {
|
||||||
return Short.valueOf(rec.getShortValue(col));
|
return Short.valueOf(rec.getShortValue(col));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongRenderer getColumnRenderer() {
|
||||||
|
return longRenderer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ import db.StringField;
|
||||||
|
|
||||||
public class StringColumnAdapter extends AbstractColumnAdapter {
|
public class StringColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
|
StringColumnAdapter(String columnName, int column) {
|
||||||
|
super(columnName, column);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?> getValueClass() {
|
Class<?> getValueClass() {
|
||||||
return String.class;
|
return String.class;
|
||||||
|
@ -32,6 +36,6 @@ public class StringColumnAdapter extends AbstractColumnAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Object getValue(DBRecord rec, int col) {
|
Object getValue(DBRecord rec, int col) {
|
||||||
return " " + rec.getString(col);
|
return rec.getString(col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,10 @@
|
||||||
package db;
|
package db;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.TableModel;
|
|
||||||
|
|
||||||
import db.buffers.LocalBufferFile;
|
import db.buffers.LocalBufferFile;
|
||||||
import docking.framework.DockingApplicationConfiguration;
|
import docking.framework.DockingApplicationConfiguration;
|
||||||
|
@ -30,10 +28,12 @@ import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
|
import docking.widgets.table.GTable;
|
||||||
|
import docking.widgets.table.GTableFilterPanel;
|
||||||
import generic.application.GenericApplicationLayout;
|
import generic.application.GenericApplicationLayout;
|
||||||
import ghidra.app.plugin.debug.dbtable.DbLargeTableModel;
|
|
||||||
import ghidra.app.plugin.debug.dbtable.DbSmallTableModel;
|
import ghidra.app.plugin.debug.dbtable.DbSmallTableModel;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
|
import ghidra.framework.plugintool.ServiceProviderStub;
|
||||||
import ghidra.framework.store.db.PackedDatabase;
|
import ghidra.framework.store.db.PackedDatabase;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
import ghidra.util.filechooser.ExtensionFileFilter;
|
||||||
|
@ -56,6 +56,7 @@ public class DbViewer extends JFrame {
|
||||||
private JComboBox<String> combo;
|
private JComboBox<String> combo;
|
||||||
private Table[] tables;
|
private Table[] tables;
|
||||||
private Hashtable<String, TableStatistics[]> tableStats = new Hashtable<>();
|
private Hashtable<String, TableStatistics[]> tableStats = new Hashtable<>();
|
||||||
|
private GTableFilterPanel<DBRecord> tableFilterPanel;
|
||||||
|
|
||||||
DbViewer() {
|
DbViewer() {
|
||||||
super("Database Viewer");
|
super("Database Viewer");
|
||||||
|
@ -177,17 +178,13 @@ public class DbViewer extends JFrame {
|
||||||
|
|
||||||
private JPanel createSouthPanel(Table table) {
|
private JPanel createSouthPanel(Table table) {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
TableModel model = null;
|
DbSmallTableModel model = new DbSmallTableModel(new ServiceProviderStub(), table);
|
||||||
if (table.getRecordCount() <= 10000) {
|
GTable gTable = new GTable(model);
|
||||||
model = new DbSmallTableModel(table);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model = new DbLargeTableModel(table);
|
|
||||||
}
|
|
||||||
JTable jtable = new JTable(model);
|
|
||||||
|
|
||||||
JScrollPane scroll = new JScrollPane(jtable);
|
tableFilterPanel = new GTableFilterPanel<>(gTable, model);
|
||||||
|
JScrollPane scroll = new JScrollPane(gTable);
|
||||||
panel.add(scroll, BorderLayout.CENTER);
|
panel.add(scroll, BorderLayout.CENTER);
|
||||||
|
panel.add(tableFilterPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
TableStatistics[] stats = getStats(table);
|
TableStatistics[] stats = getStats(table);
|
||||||
String recCnt = "Records: " + Integer.toString(table.getRecordCount());
|
String recCnt = "Records: " + Integer.toString(table.getRecordCount());
|
||||||
|
@ -216,10 +213,9 @@ public class DbViewer extends JFrame {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the statistics for the specified table.
|
* Get the statistics for the specified table.
|
||||||
* @param table
|
* @param table the table
|
||||||
* @return arrays containing statistics. Element 0 provides
|
* @return arrays containing statistics. Element 0 provides statistics for primary table,
|
||||||
* statsitics for primary table, element 1 provides combined
|
* element 1 provides combined statistics for all index tables. Remaining array elements
|
||||||
* statsitics for all index tables. Remaining array elements
|
|
||||||
* should be ignored since they have been combined into element 1.
|
* should be ignored since they have been combined into element 1.
|
||||||
*/
|
*/
|
||||||
private TableStatistics[] getStats(Table table) {
|
private TableStatistics[] getStats(Table table) {
|
||||||
|
@ -238,39 +234,21 @@ public class DbViewer extends JFrame {
|
||||||
tableStats.put(table.getName(), stats);
|
tableStats.put(table.getName(), stats);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
Msg.error(this, "Exception loading stats", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static void main(String[] args) throws FileNotFoundException {
|
||||||
* Launch the DbViewer application.
|
|
||||||
* @param args (not used)
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
|
|
||||||
ApplicationLayout layout = new GenericApplicationLayout("DB Viewer", "1.0");
|
ApplicationLayout layout = new GenericApplicationLayout("DB Viewer", "1.0");
|
||||||
|
|
||||||
DockingApplicationConfiguration configuration = new DockingApplicationConfiguration();
|
DockingApplicationConfiguration configuration = new DockingApplicationConfiguration();
|
||||||
configuration.setShowSplashScreen(false);
|
configuration.setShowSplashScreen(false);
|
||||||
|
|
||||||
try {
|
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException e) {
|
|
||||||
}
|
|
||||||
catch (InstantiationException e) {
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException e) {
|
|
||||||
}
|
|
||||||
catch (UnsupportedLookAndFeelException e) {
|
|
||||||
}
|
|
||||||
Application.initializeApplication(layout, configuration);
|
Application.initializeApplication(layout, configuration);
|
||||||
|
|
||||||
DbViewer viewer = new DbViewer();
|
DbViewer viewer = new DbViewer();
|
||||||
viewer.setSize(new Dimension(500, 400));
|
viewer.setSize(new Dimension(500, 400));
|
||||||
viewer.setVisible(true);
|
viewer.setVisible(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,7 +293,6 @@ public class FidDebugPlugin extends ProgramPlugin implements ChangeListener {
|
||||||
* @param choices array of choices for the users
|
* @param choices array of choices for the users
|
||||||
* @param defaultValue the default value to select
|
* @param defaultValue the default value to select
|
||||||
* @return the user's choice, or null
|
* @return the user's choice, or null
|
||||||
* @throws CancelledException if the user cancels
|
|
||||||
*/
|
*/
|
||||||
protected <T> T askChoice(String title, String message, List<T> choices, T defaultValue) {
|
protected <T> T askChoice(String title, String message, List<T> choices, T defaultValue) {
|
||||||
AskDialog<T> dialog =
|
AskDialog<T> dialog =
|
||||||
|
|
|
@ -23,6 +23,8 @@ import ghidra.framework.plugintool.ServiceProvider;
|
||||||
* the DATA_SOURCE parameter of DynamicTableColumn. This class will stub the default
|
* the DATA_SOURCE parameter of DynamicTableColumn. This class will stub the default
|
||||||
* {@link #getValue(Object, Settings, Object, ServiceProvider)} method and
|
* {@link #getValue(Object, Settings, Object, ServiceProvider)} method and
|
||||||
* call a version of the method that does not have the DATA_SOURCE parameter.
|
* call a version of the method that does not have the DATA_SOURCE parameter.
|
||||||
|
* @param <ROW_TYPE> the row type
|
||||||
|
* @param <COLUMN_TYPE> the column type
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractDynamicTableColumnStub<ROW_TYPE, COLUMN_TYPE> extends
|
public abstract class AbstractDynamicTableColumnStub<ROW_TYPE, COLUMN_TYPE> extends
|
||||||
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> {
|
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> {
|
||||||
|
|
|
@ -15,13 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.util;
|
package ghidra.util;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import org.apache.commons.collections4.IteratorUtils;
|
import org.apache.commons.collections4.IteratorUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
@ -58,34 +57,39 @@ public final class NumericUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
* Parses the given string as a numeric value, detecting whether or not it begins with a hex
|
||||||
* prefix, and if not, parses as a long int value.
|
* prefix, and if not, parses as a long int value.
|
||||||
*
|
*
|
||||||
* @param numStr the number string
|
* @param numStr the number string
|
||||||
* @return the long value or 0
|
* @return the long value or 0
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public static long parseNumber(String numStr) {
|
public static long parseNumber(String numStr) {
|
||||||
return parseNumber(numStr, Long.valueOf(0));
|
return parseNumber(numStr, Long.valueOf(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Long parseNumber(String numStr, Long defaultValue) {
|
/**
|
||||||
|
* Parses the given string as a numeric value, detecting whether or not it begins with a hex
|
||||||
numStr = (numStr == null ? "" : numStr.trim());
|
* prefix, and if not, parses as a long int value.
|
||||||
if (numStr.length() == 0) {
|
* @param s the string to parse
|
||||||
|
* @param defaultValue the default value to use if the string cannot be parsed
|
||||||
|
* @return the long value
|
||||||
|
*/
|
||||||
|
public static Long parseNumber(String s, Long defaultValue) {
|
||||||
|
s = (s == null ? "" : s.trim());
|
||||||
|
if (s.length() == 0) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
long value = 0;
|
long value = 0;
|
||||||
try {
|
try {
|
||||||
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
||||||
value = Integer.parseInt(numStr.substring(2), 16);
|
value = Integer.parseInt(s.substring(2), 16);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
value = Integer.parseInt(numStr);
|
value = Integer.parseInt(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (NumberFormatException exc) {
|
catch (NumberFormatException e) {
|
||||||
// do nothing special; use default value
|
// do nothing special; use default value
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -94,41 +98,43 @@ public final class NumericUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
* Parses the given string as a numeric value, detecting whether or not it begins with a hex
|
||||||
* prefix, and if not, parses as a long int value.
|
* prefix, and if not, parses as a long int value.
|
||||||
|
* @param s the string to parse
|
||||||
|
* @return the long value
|
||||||
|
* @throws NumberFormatException if the string is blank or has too many digits
|
||||||
*/
|
*/
|
||||||
public static long parseLong(String numStr) {
|
public static long parseLong(String s) {
|
||||||
String origStr = numStr;
|
String origStr = s;
|
||||||
long value = 0;
|
long value = 0;
|
||||||
long sign = 1;
|
long sign = 1;
|
||||||
|
|
||||||
numStr = (numStr == null ? "" : numStr.trim());
|
s = (s == null ? "" : s.trim());
|
||||||
if (numStr.length() == 0) {
|
if (s.length() == 0) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
if (numStr.startsWith("-")) {
|
if (s.startsWith("-")) {
|
||||||
sign = -1;
|
sign = -1;
|
||||||
numStr = numStr.substring(1);
|
s = s.substring(1);
|
||||||
}
|
}
|
||||||
int radix = 10;
|
int radix = 10;
|
||||||
|
|
||||||
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
||||||
if (numStr.length() > 18) {
|
if (s.length() > 18) {
|
||||||
throw new NumberFormatException(numStr + " has too many digits.");
|
throw new NumberFormatException(s + " has too many digits.");
|
||||||
}
|
}
|
||||||
numStr = numStr.substring(2);
|
s = s.substring(2);
|
||||||
radix = 16;
|
radix = 16;
|
||||||
}
|
}
|
||||||
if (numStr.length() == 0) {
|
if (s.length() == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
BigInteger bi = new BigInteger(numStr, radix);
|
BigInteger bi = new BigInteger(s, radix);
|
||||||
return bi.longValue() * sign;
|
return bi.longValue() * sign;
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
// This is a little hacky, but the message should be complete and report about the
|
// A little hacky, but the message should be complete and report the original string
|
||||||
// original string
|
|
||||||
NumberFormatException e2 =
|
NumberFormatException e2 =
|
||||||
new NumberFormatException("Cannot parse long from " + origStr);
|
new NumberFormatException("Cannot parse long from " + origStr);
|
||||||
e2.setStackTrace(e.getStackTrace());
|
e2.setStackTrace(e.getStackTrace());
|
||||||
|
@ -140,81 +146,64 @@ public final class NumericUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
* Parses the given string as a hex long value, detecting whether or not it begins with a hex
|
||||||
* prefix, and if not, parses as a long int value.
|
* prefix, and if not, parses as a long int value.
|
||||||
|
* @param s the string to parse
|
||||||
|
* @return the long value
|
||||||
|
* @throws NumberFormatException if the string is blank
|
||||||
*/
|
*/
|
||||||
public static long parseOctLong(String numStr) {
|
public static long parseHexLong(String s) {
|
||||||
|
return parseHexBigInteger(s).longValue();
|
||||||
long value = 0;
|
|
||||||
long sign = 1;
|
|
||||||
|
|
||||||
numStr = (numStr == null ? "" : numStr.trim());
|
|
||||||
if (numStr.length() == 0) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numStr.startsWith("-")) {
|
|
||||||
sign = -1;
|
|
||||||
numStr = numStr.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int radix = 8;
|
|
||||||
if (numStr.startsWith("0")) {
|
|
||||||
if (numStr.length() > 18) {
|
|
||||||
throw new NumberFormatException(numStr + " has too many digits.");
|
|
||||||
}
|
|
||||||
numStr = numStr.substring(1);
|
|
||||||
}
|
|
||||||
BigInteger bi = new BigInteger(numStr, radix);
|
|
||||||
|
|
||||||
return bi.longValue() * sign;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long parseHexLong(String numStr) {
|
|
||||||
|
|
||||||
return parseHexBigInteger(numStr).longValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BigInteger parseHexBigInteger(String numStr) {
|
|
||||||
|
|
||||||
numStr = (numStr == null ? "" : numStr.trim());
|
|
||||||
if (numStr.length() == 0) {
|
|
||||||
throw new NumberFormatException(numStr + " no digits.");
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean negative = false;
|
|
||||||
if (numStr.startsWith("-")) {
|
|
||||||
negative = true;
|
|
||||||
numStr = numStr.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
|
||||||
numStr = numStr.substring(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (negative) {
|
|
||||||
numStr = "-" + numStr;
|
|
||||||
}
|
|
||||||
return new BigInteger(numStr, 16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
* Parses the given hex string as a BigIntge value, detecting whether or not it begins with a
|
||||||
* string.
|
* hex prefix, and if not, parses as a long int value.
|
||||||
|
* @param s the string to parse
|
||||||
|
* @return the long value
|
||||||
|
* @throws NumberFormatException if the string is blank
|
||||||
|
*/
|
||||||
|
public static BigInteger parseHexBigInteger(String s) {
|
||||||
|
|
||||||
|
s = (s == null ? "" : s.trim());
|
||||||
|
if (s.length() == 0) {
|
||||||
|
throw new NumberFormatException(s + " no digits.");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean negative = false;
|
||||||
|
if (s.startsWith("-")) {
|
||||||
|
negative = true;
|
||||||
|
s = s.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
||||||
|
s = s.substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
s = "-" + s;
|
||||||
|
}
|
||||||
|
return new BigInteger(s, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value of the specified long as hexadecimal, prefixing with the
|
||||||
|
* {@link #HEX_PREFIX_x} string.
|
||||||
*
|
*
|
||||||
* @param value the long value to convert
|
* @param value the long value to convert
|
||||||
|
* @return the string
|
||||||
*/
|
*/
|
||||||
public final static String toHexString(long value) {
|
public final static String toHexString(long value) {
|
||||||
return HEX_PREFIX_x + Long.toHexString(value);
|
return HEX_PREFIX_x + Long.toHexString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
* returns the value of the specified long as hexadecimal, prefixing with the
|
||||||
* string.
|
* {@link #HEX_PREFIX_x} string.
|
||||||
*
|
*
|
||||||
* @param value the long value to convert
|
* @param value the long value to convert
|
||||||
* @param size number of bytes to be represented
|
* @param size number of bytes to be represented
|
||||||
|
* @return the string
|
||||||
*/
|
*/
|
||||||
public final static String toHexString(long value, int size) {
|
public final static String toHexString(long value, int size) {
|
||||||
if (size > 0 && size < 8) {
|
if (size > 0 && size < 8) {
|
||||||
|
@ -225,9 +214,10 @@ public final class NumericUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the value of the specified long as signed hexadecimal, prefixing with the
|
* returns the value of the specified long as signed hexadecimal, prefixing with the
|
||||||
* HEX_PREFIX_x string.
|
* {@link #HEX_PREFIX_x} string.
|
||||||
*
|
*
|
||||||
* @param value the long value to convert
|
* @param value the long value to convert
|
||||||
|
* @return the string
|
||||||
*/
|
*/
|
||||||
public final static String toSignedHexString(long value) {
|
public final static String toSignedHexString(long value) {
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue