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.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import db.*;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
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.layout.PairLayout;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
|
@ -39,25 +39,27 @@ class DbViewerComponent extends JPanel {
|
|||
|
||||
private static Table[] NO_TABLES = new Table[0];
|
||||
|
||||
private static Comparator<Table> TABLE_NAME_COMPARATOR = new Comparator<>() {
|
||||
@Override
|
||||
public int compare(Table o1, Table o2) {
|
||||
return (o1).getName().compareTo((o2).getName());
|
||||
}
|
||||
};
|
||||
private static Comparator<Table> TABLE_NAME_COMPARATOR =
|
||||
(o1, o2) -> (o1).getName().compareTo((o2).getName());
|
||||
|
||||
private DBHandle dbh;
|
||||
private DBListener dbListener;
|
||||
private JPanel southPanel;
|
||||
private JPanel centerPanel;
|
||||
private JComponent southComponent;
|
||||
private JLabel dbLabel;
|
||||
private JComboBox<TableItem> combo;
|
||||
private Table[] tables = NO_TABLES;
|
||||
private Hashtable<String, TableStatistics[]> tableStats = new Hashtable<>();
|
||||
private Map<String, TableStatistics[]> tableStats = new HashMap<>();
|
||||
|
||||
private SwingUpdateManager updateMgr;
|
||||
|
||||
DbViewerComponent() {
|
||||
private PluginTool tool;
|
||||
|
||||
private GTableFilterPanel<DBRecord> tableFilterPanel;
|
||||
|
||||
DbViewerComponent(PluginTool tool) {
|
||||
super(new BorderLayout());
|
||||
this.tool = tool;
|
||||
|
||||
JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
JPanel subNorthPanel = new JPanel(new PairLayout(4, 10));
|
||||
|
@ -66,32 +68,19 @@ class DbViewerComponent extends JPanel {
|
|||
subNorthPanel.add(dbLabel);
|
||||
subNorthPanel.add(new GLabel("Tables:"));
|
||||
combo = new GComboBox<>();
|
||||
combo.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
refreshTable();
|
||||
}
|
||||
});
|
||||
combo.addActionListener(e -> refreshTable());
|
||||
subNorthPanel.add(combo);
|
||||
northPanel.add(subNorthPanel);
|
||||
add(northPanel, BorderLayout.NORTH);
|
||||
|
||||
updateMgr = new SwingUpdateManager(100, 2000, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
updateMgr = new SwingUpdateManager(100, 2000, () -> refresh());
|
||||
}
|
||||
|
||||
synchronized void closeDatabase() {
|
||||
if (dbh != null) {
|
||||
combo.removeAllItems();
|
||||
dbLabel.setText("");
|
||||
if (southPanel != null) {
|
||||
remove(southPanel);
|
||||
southPanel = null;
|
||||
}
|
||||
removeWidgets();
|
||||
tables = NO_TABLES;
|
||||
tableStats.clear();
|
||||
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) {
|
||||
|
||||
closeDatabase();
|
||||
|
@ -110,7 +108,7 @@ class DbViewerComponent extends JPanel {
|
|||
dbLabel.setText(name);
|
||||
updateTableChoices(null);
|
||||
|
||||
dbListener = getNewDBListener();
|
||||
dbListener = new InternalDBListener();
|
||||
handle.addListener(dbListener);
|
||||
}
|
||||
|
||||
|
@ -126,10 +124,7 @@ class DbViewerComponent extends JPanel {
|
|||
|
||||
synchronized void refreshTable() {
|
||||
if (dbh == null) {
|
||||
if (southPanel != null) {
|
||||
remove(southPanel);
|
||||
southPanel = null;
|
||||
}
|
||||
removeWidgets();
|
||||
return;
|
||||
}
|
||||
synchronized (dbh) {
|
||||
|
@ -139,6 +134,7 @@ class DbViewerComponent extends JPanel {
|
|||
|
||||
synchronized void dispose() {
|
||||
updateMgr.dispose();
|
||||
tableFilterPanel.dispose();
|
||||
closeDatabase();
|
||||
}
|
||||
|
||||
|
@ -197,35 +193,20 @@ class DbViewerComponent extends JPanel {
|
|||
|
||||
private void updateTable() {
|
||||
|
||||
if (southPanel != null) {
|
||||
remove(southPanel);
|
||||
southPanel = null;
|
||||
}
|
||||
removeWidgets();
|
||||
|
||||
TableItem t = (TableItem) combo.getSelectedItem();
|
||||
if (t != null) {
|
||||
southPanel = createSouthPanel(t.table);
|
||||
add(southPanel, BorderLayout.CENTER);
|
||||
centerPanel = createCenterPanel(t.table);
|
||||
add(centerPanel, BorderLayout.CENTER);
|
||||
southComponent = createSouthComponent(t.table);
|
||||
add(southComponent, BorderLayout.SOUTH);
|
||||
|
||||
}
|
||||
revalidate();
|
||||
}
|
||||
|
||||
private JPanel createSouthPanel(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);
|
||||
|
||||
private JComponent createSouthComponent(Table table) {
|
||||
TableStatistics[] stats = getStats(table);
|
||||
String recCnt = "Records: " + Integer.toString(table.getRecordCount());
|
||||
String intNodeCnt = "";
|
||||
|
@ -244,15 +225,22 @@ class DbViewerComponent extends JPanel {
|
|||
size += " / " + Integer.toString(stats[1].size / 1024);
|
||||
}
|
||||
}
|
||||
panel.add(new GLabel(
|
||||
recCnt + " " + intNodeCnt + " " + recNodeCnt + " " + chainBufCnt + " " + size),
|
||||
BorderLayout.SOUTH);
|
||||
|
||||
return panel;
|
||||
return new GLabel(
|
||||
recCnt + " " + intNodeCnt + " " + recNodeCnt + " " + chainBufCnt + " " + size);
|
||||
}
|
||||
|
||||
private DBListener getNewDBListener() {
|
||||
return new InternalDBListener();
|
||||
private JPanel createCenterPanel(Table table) {
|
||||
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
|
||||
public class DbViewerPlugin extends Plugin {
|
||||
|
||||
private DbViewerProvider viewer;
|
||||
private DbViewerProvider provider;
|
||||
private DockingAction refreshAction;
|
||||
|
||||
public DbViewerPlugin(PluginTool tool) {
|
||||
|
@ -52,11 +52,11 @@ public class DbViewerPlugin extends Plugin {
|
|||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
if (viewer != null) {
|
||||
if (provider != null) {
|
||||
deactivateViewer();
|
||||
tool.removeComponentProvider(viewer);
|
||||
viewer.dispose();
|
||||
viewer = null;
|
||||
tool.removeComponentProvider(provider);
|
||||
provider.dispose();
|
||||
provider = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
@ -66,8 +66,8 @@ public class DbViewerPlugin extends Plugin {
|
|||
refreshAction = new DockingAction("Refresh", getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (viewer != null) {
|
||||
viewer.refresh();
|
||||
if (provider != null) {
|
||||
provider.refresh();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -78,19 +78,19 @@ public class DbViewerPlugin extends Plugin {
|
|||
}
|
||||
|
||||
private void activateViewer(DomainObjectAdapterDB dobj) {
|
||||
if (viewer == null) {
|
||||
viewer = new DbViewerProvider(this);
|
||||
tool.addComponentProvider(viewer, false);
|
||||
tool.addLocalAction(viewer, refreshAction);
|
||||
if (provider == null) {
|
||||
provider = new DbViewerProvider(this);
|
||||
tool.addComponentProvider(provider, false);
|
||||
tool.addLocalAction(provider, refreshAction);
|
||||
}
|
||||
viewer.openDatabase(dobj.getName(), dobj.getDBHandle());
|
||||
provider.openDatabase(dobj.getName(), dobj.getDBHandle());
|
||||
refreshAction.setEnabled(true);
|
||||
}
|
||||
|
||||
private void deactivateViewer() {
|
||||
if (viewer != null) {
|
||||
if (provider != null) {
|
||||
refreshAction.setEnabled(false);
|
||||
viewer.closeDatabase();
|
||||
provider.closeDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ public class DbViewerProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
public JComponent getComponent() {
|
||||
if (comp == null) {
|
||||
comp = new DbViewerComponent();
|
||||
comp = new DbViewerComponent(tool);
|
||||
if (dbh != null) {
|
||||
comp.openDatabase(dbName, dbh);
|
||||
}
|
||||
|
|
|
@ -16,14 +16,51 @@
|
|||
package ghidra.app.plugin.debug.dbtable;
|
||||
|
||||
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 Object getKeyValue(DBRecord rec);
|
||||
|
||||
abstract Object getValue(DBRecord rec, int col);
|
||||
abstract Object getValue(DBRecord rec, int dbColumn);
|
||||
|
||||
protected String getByteString(byte b) {
|
||||
String str = Integer.toHexString(b);
|
||||
|
@ -33,20 +70,4 @@ abstract class AbstractColumnAdapter {
|
|||
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 {
|
||||
|
||||
BinaryColumnAdapter(String columnName, int column) {
|
||||
super(columnName, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> getValueClass() {
|
||||
return String.class;
|
||||
|
@ -49,7 +53,7 @@ public class BinaryColumnAdapter extends AbstractColumnAdapter {
|
|||
if (bytes == null) {
|
||||
return "null";
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(" byte[" + bytes.length + "] = ");
|
||||
StringBuilder buf = new StringBuilder("byte[" + bytes.length + "] = ");
|
||||
if (bytes.length > 0) {
|
||||
int len = Math.min(bytes.length, 20);
|
||||
String str = getByteString(bytes[0]);
|
||||
|
|
|
@ -17,9 +17,23 @@ package ghidra.app.plugin.debug.dbtable;
|
|||
|
||||
import db.BooleanField;
|
||||
import db.DBRecord;
|
||||
import docking.widgets.table.GBooleanCellRenderer;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
|
||||
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
|
||||
Class<?> getValueClass() {
|
||||
return Boolean.class;
|
||||
|
@ -31,8 +45,23 @@ public class BooleanColumnAdapter extends AbstractColumnAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
Object getValue(DBRecord rec, int col) {
|
||||
return Boolean.valueOf(rec.getBooleanValue(col));
|
||||
Object getValue(DBRecord rec, int dbColumn) {
|
||||
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 {
|
||||
|
||||
ByteColumnAdapter(String columnName, int column) {
|
||||
super(columnName, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnPreferredWidth() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> getValueClass() {
|
||||
return Byte.class;
|
||||
|
@ -31,8 +40,12 @@ public class ByteColumnAdapter extends AbstractColumnAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
Object getValue(DBRecord rec, int col) {
|
||||
return Byte.valueOf(rec.getByteValue(col));
|
||||
Object getValue(DBRecord rec, int dbColumn) {
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.datastruct.Accumulator;
|
||||
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 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;
|
||||
schema = table.getSchema();
|
||||
|
||||
records = new ArrayList<>(table.getRecordCount());
|
||||
|
||||
columns.add(getColumn(schema.getKeyFieldType()));
|
||||
|
||||
Field[] fields = schema.getFields();
|
||||
for (Field field : fields) {
|
||||
columns.add(getColumn(field));
|
||||
reloadColumns(); // we must do this after 'schema' has been set
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(Accumulator<DBRecord> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
monitor.initialize(table.getRecordCount());
|
||||
|
||||
try {
|
||||
RecordIterator it = table.iterator();
|
||||
while (it.hasNext()) {
|
||||
records.add(it.next());
|
||||
monitor.checkCancelled();
|
||||
accumulator.add(it.next());
|
||||
}
|
||||
}
|
||||
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) {
|
||||
return new ByteColumnAdapter();
|
||||
return new ByteColumnAdapter(columnName, column);
|
||||
}
|
||||
else if (field instanceof BooleanField) {
|
||||
return new BooleanColumnAdapter();
|
||||
return new BooleanColumnAdapter(columnName, column);
|
||||
}
|
||||
else if (field instanceof ShortField) {
|
||||
return new ShortColumnAdapter();
|
||||
return new ShortColumnAdapter(columnName, column);
|
||||
}
|
||||
else if (field instanceof IntField) {
|
||||
return new IntegerColumnAdapter();
|
||||
return new IntegerColumnAdapter(columnName, column);
|
||||
}
|
||||
else if (field instanceof LongField) {
|
||||
return new LongColumnAdapter();
|
||||
return new LongColumnAdapter(columnName, column);
|
||||
}
|
||||
else if (field instanceof StringField) {
|
||||
return new StringColumnAdapter();
|
||||
return new StringColumnAdapter(columnName, column);
|
||||
}
|
||||
else if (field instanceof BinaryField) {
|
||||
return new BinaryColumnAdapter();
|
||||
return new BinaryColumnAdapter(columnName, column);
|
||||
}
|
||||
throw new AssertException(
|
||||
"New, unexpected DB column type: " + field.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
private String loadColumnName(int columnIndex) {
|
||||
if (columnIndex == 0) {
|
||||
return schema.getKeyName();
|
||||
}
|
||||
|
@ -114,8 +102,23 @@ public class DbSmallTableModel extends AbstractSortedTableModel<DBRecord> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return table.getRecordCount();
|
||||
protected TableColumnDescriptor<DBRecord> createTableColumnDescriptor() {
|
||||
|
||||
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
|
||||
|
@ -124,22 +127,7 @@ public class DbSmallTableModel extends AbstractSortedTableModel<DBRecord> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(DBRecord rec, int columnIndex) {
|
||||
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 List<DBRecord> getModelData() {
|
||||
return records;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSortable(int columnIndex) {
|
||||
return true;
|
||||
public Object getDataSource() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,20 @@
|
|||
*/
|
||||
package ghidra.app.plugin.debug.dbtable;
|
||||
|
||||
import db.IntField;
|
||||
import db.DBRecord;
|
||||
import db.IntField;
|
||||
|
||||
public class IntegerColumnAdapter extends AbstractColumnAdapter {
|
||||
|
||||
IntegerColumnAdapter(String columnName, int column) {
|
||||
super(columnName, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnPreferredWidth() {
|
||||
return 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> getValueClass() {
|
||||
return Integer.class;
|
||||
|
@ -35,4 +44,8 @@ public class IntegerColumnAdapter extends AbstractColumnAdapter {
|
|||
return Integer.valueOf(rec.getIntValue(col));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRenderer getColumnRenderer() {
|
||||
return longRenderer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@ import db.DBRecord;
|
|||
|
||||
public class LongColumnAdapter extends AbstractColumnAdapter {
|
||||
|
||||
LongColumnAdapter(String columnName, int column) {
|
||||
super(columnName, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> getValueClass() {
|
||||
return Long.class;
|
||||
|
@ -33,4 +37,9 @@ public class LongColumnAdapter extends AbstractColumnAdapter {
|
|||
Object getValue(DBRecord rec, int 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.SwingConstants;
|
||||
|
||||
import docking.widgets.table.GTableCellRenderer;
|
||||
import docking.widgets.table.GTableCellRenderingData;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.util.table.column.AbstractGColumnRenderer;
|
||||
|
||||
public class LongRenderer extends GTableCellRenderer {
|
||||
public class LongRenderer extends AbstractGColumnRenderer<Object> {
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
@ -38,11 +38,17 @@ public class LongRenderer extends GTableCellRenderer {
|
|||
|
||||
@Override
|
||||
protected String getText(Object value) {
|
||||
return value == null ? "" : "0x" + Long.toHexString((Long) value);
|
||||
return value == null ? "" : "0x" + Long.toHexString(((Number) value).longValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatNumber(Number value, Settings settings) {
|
||||
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 {
|
||||
|
||||
ShortColumnAdapter(String columnName, int column) {
|
||||
super(columnName, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> getValueClass() {
|
||||
return Short.class;
|
||||
|
@ -35,4 +39,8 @@ public class ShortColumnAdapter extends AbstractColumnAdapter {
|
|||
return Short.valueOf(rec.getShortValue(col));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRenderer getColumnRenderer() {
|
||||
return longRenderer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@ import db.StringField;
|
|||
|
||||
public class StringColumnAdapter extends AbstractColumnAdapter {
|
||||
|
||||
StringColumnAdapter(String columnName, int column) {
|
||||
super(columnName, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> getValueClass() {
|
||||
return String.class;
|
||||
|
@ -32,6 +36,6 @@ public class StringColumnAdapter extends AbstractColumnAdapter {
|
|||
|
||||
@Override
|
||||
Object getValue(DBRecord rec, int col) {
|
||||
return " " + rec.getString(col);
|
||||
return rec.getString(col);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,10 @@
|
|||
package db;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import db.buffers.LocalBufferFile;
|
||||
import docking.framework.DockingApplicationConfiguration;
|
||||
|
@ -30,10 +28,12 @@ import docking.widgets.filechooser.GhidraFileChooser;
|
|||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.table.GTable;
|
||||
import docking.widgets.table.GTableFilterPanel;
|
||||
import generic.application.GenericApplicationLayout;
|
||||
import ghidra.app.plugin.debug.dbtable.DbLargeTableModel;
|
||||
import ghidra.app.plugin.debug.dbtable.DbSmallTableModel;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.plugintool.ServiceProviderStub;
|
||||
import ghidra.framework.store.db.PackedDatabase;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
||||
|
@ -56,6 +56,7 @@ public class DbViewer extends JFrame {
|
|||
private JComboBox<String> combo;
|
||||
private Table[] tables;
|
||||
private Hashtable<String, TableStatistics[]> tableStats = new Hashtable<>();
|
||||
private GTableFilterPanel<DBRecord> tableFilterPanel;
|
||||
|
||||
DbViewer() {
|
||||
super("Database Viewer");
|
||||
|
@ -177,17 +178,13 @@ public class DbViewer extends JFrame {
|
|||
|
||||
private JPanel createSouthPanel(Table table) {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
TableModel model = null;
|
||||
if (table.getRecordCount() <= 10000) {
|
||||
model = new DbSmallTableModel(table);
|
||||
}
|
||||
else {
|
||||
model = new DbLargeTableModel(table);
|
||||
}
|
||||
JTable jtable = new JTable(model);
|
||||
DbSmallTableModel model = new DbSmallTableModel(new ServiceProviderStub(), table);
|
||||
GTable gTable = new GTable(model);
|
||||
|
||||
JScrollPane scroll = new JScrollPane(jtable);
|
||||
tableFilterPanel = new GTableFilterPanel<>(gTable, model);
|
||||
JScrollPane scroll = new JScrollPane(gTable);
|
||||
panel.add(scroll, BorderLayout.CENTER);
|
||||
panel.add(tableFilterPanel, BorderLayout.SOUTH);
|
||||
|
||||
TableStatistics[] stats = getStats(table);
|
||||
String recCnt = "Records: " + Integer.toString(table.getRecordCount());
|
||||
|
@ -216,10 +213,9 @@ public class DbViewer extends JFrame {
|
|||
|
||||
/**
|
||||
* Get the statistics for the specified table.
|
||||
* @param table
|
||||
* @return arrays containing statistics. Element 0 provides
|
||||
* statsitics for primary table, element 1 provides combined
|
||||
* statsitics for all index tables. Remaining array elements
|
||||
* @param table the table
|
||||
* @return arrays containing statistics. Element 0 provides statistics for primary table,
|
||||
* element 1 provides combined statistics for all index tables. Remaining array elements
|
||||
* should be ignored since they have been combined into element 1.
|
||||
*/
|
||||
private TableStatistics[] getStats(Table table) {
|
||||
|
@ -238,39 +234,21 @@ public class DbViewer extends JFrame {
|
|||
tableStats.put(table.getName(), stats);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(this, "Exception loading stats", e);
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the DbViewer application.
|
||||
* @param args (not used)
|
||||
*/
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException {
|
||||
ApplicationLayout layout = new GenericApplicationLayout("DB Viewer", "1.0");
|
||||
|
||||
DockingApplicationConfiguration configuration = new DockingApplicationConfiguration();
|
||||
configuration.setShowSplashScreen(false);
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
}
|
||||
catch (UnsupportedLookAndFeelException e) {
|
||||
}
|
||||
Application.initializeApplication(layout, configuration);
|
||||
|
||||
DbViewer viewer = new DbViewer();
|
||||
viewer.setSize(new Dimension(500, 400));
|
||||
viewer.setVisible(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -293,7 +293,6 @@ public class FidDebugPlugin extends ProgramPlugin implements ChangeListener {
|
|||
* @param choices array of choices for the users
|
||||
* @param defaultValue the default value to select
|
||||
* @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) {
|
||||
AskDialog<T> dialog =
|
||||
|
|
|
@ -23,6 +23,8 @@ import ghidra.framework.plugintool.ServiceProvider;
|
|||
* the DATA_SOURCE parameter of DynamicTableColumn. This class will stub the default
|
||||
* {@link #getValue(Object, Settings, Object, ServiceProvider)} method and
|
||||
* 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
|
||||
AbstractDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, Object> {
|
||||
|
|
|
@ -15,13 +15,12 @@
|
|||
*/
|
||||
package ghidra.util;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
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.
|
||||
*
|
||||
* @param numStr the number string
|
||||
* @return the long value or 0
|
||||
*
|
||||
*/
|
||||
public static long parseNumber(String numStr) {
|
||||
return parseNumber(numStr, Long.valueOf(0));
|
||||
}
|
||||
|
||||
public static Long parseNumber(String numStr, Long defaultValue) {
|
||||
|
||||
numStr = (numStr == null ? "" : numStr.trim());
|
||||
if (numStr.length() == 0) {
|
||||
/**
|
||||
* 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.
|
||||
* @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;
|
||||
}
|
||||
|
||||
long value = 0;
|
||||
try {
|
||||
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
||||
value = Integer.parseInt(numStr.substring(2), 16);
|
||||
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
||||
value = Integer.parseInt(s.substring(2), 16);
|
||||
}
|
||||
else {
|
||||
value = Integer.parseInt(numStr);
|
||||
value = Integer.parseInt(s);
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException exc) {
|
||||
catch (NumberFormatException e) {
|
||||
// do nothing special; use default value
|
||||
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.
|
||||
* @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) {
|
||||
String origStr = numStr;
|
||||
public static long parseLong(String s) {
|
||||
String origStr = s;
|
||||
long value = 0;
|
||||
long sign = 1;
|
||||
|
||||
numStr = (numStr == null ? "" : numStr.trim());
|
||||
if (numStr.length() == 0) {
|
||||
s = (s == null ? "" : s.trim());
|
||||
if (s.length() == 0) {
|
||||
return value;
|
||||
}
|
||||
if (numStr.startsWith("-")) {
|
||||
if (s.startsWith("-")) {
|
||||
sign = -1;
|
||||
numStr = numStr.substring(1);
|
||||
s = s.substring(1);
|
||||
}
|
||||
int radix = 10;
|
||||
|
||||
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
||||
if (numStr.length() > 18) {
|
||||
throw new NumberFormatException(numStr + " has too many digits.");
|
||||
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
||||
if (s.length() > 18) {
|
||||
throw new NumberFormatException(s + " has too many digits.");
|
||||
}
|
||||
numStr = numStr.substring(2);
|
||||
s = s.substring(2);
|
||||
radix = 16;
|
||||
}
|
||||
if (numStr.length() == 0) {
|
||||
if (s.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
BigInteger bi = new BigInteger(numStr, radix);
|
||||
BigInteger bi = new BigInteger(s, radix);
|
||||
return bi.longValue() * sign;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// This is a little hacky, but the message should be complete and report about the
|
||||
// original string
|
||||
// A little hacky, but the message should be complete and report the original string
|
||||
NumberFormatException e2 =
|
||||
new NumberFormatException("Cannot parse long from " + origStr);
|
||||
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.
|
||||
* @param s the string to parse
|
||||
* @return the long value
|
||||
* @throws NumberFormatException if the string is blank
|
||||
*/
|
||||
public static long parseOctLong(String numStr) {
|
||||
|
||||
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);
|
||||
public static long parseHexLong(String s) {
|
||||
return parseHexBigInteger(s).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
||||
* string.
|
||||
* Parses the given hex string as a BigIntge value, detecting whether or not it begins with a
|
||||
* 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
|
||||
* @return the string
|
||||
*/
|
||||
public final static String toHexString(long value) {
|
||||
return HEX_PREFIX_x + Long.toHexString(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
||||
* string.
|
||||
* 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 size number of bytes to be represented
|
||||
* @return the string
|
||||
*/
|
||||
public final static String toHexString(long value, int size) {
|
||||
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
|
||||
* HEX_PREFIX_x string.
|
||||
* {@link #HEX_PREFIX_x} string.
|
||||
*
|
||||
* @param value the long value to convert
|
||||
* @return the string
|
||||
*/
|
||||
public final static String toSignedHexString(long value) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue