mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 14:50:08 +08:00
GT-2892 - Tables - updated default sorting to use the column's rendered
value when possible
This commit is contained in:
+5
@@ -26,4 +26,9 @@ public class IncomingReferenceEndpoint extends ReferenceEndpoint {
|
|||||||
public IncomingReferenceEndpoint(Reference r, boolean isOffcut) {
|
public IncomingReferenceEndpoint(Reference r, boolean isOffcut) {
|
||||||
super(r, r.getFromAddress(), r.getReferenceType(), isOffcut, r.getSource());
|
super(r, r.getFromAddress(), r.getReferenceType(), isOffcut, r.getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Incoming " + getReferenceType().getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -38,4 +38,9 @@ public class OutgoingReferenceEndpoint extends ReferenceEndpoint {
|
|||||||
public OutgoingReferenceEndpoint(Reference r, Address toAddress, boolean isOffcut) {
|
public OutgoingReferenceEndpoint(Reference r, Address toAddress, boolean isOffcut) {
|
||||||
super(r, toAddress, r.getReferenceType(), isOffcut, r.getSource());
|
super(r, toAddress, r.getReferenceType(), isOffcut, r.getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Outgoing " + getReferenceType().getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-38
@@ -20,7 +20,9 @@ import java.util.*;
|
|||||||
import javax.swing.event.TableModelEvent;
|
import javax.swing.event.TableModelEvent;
|
||||||
import javax.swing.table.TableModel;
|
import javax.swing.table.TableModel;
|
||||||
|
|
||||||
import ghidra.util.SystemUtilities;
|
import docking.widgets.table.sort.DefaultColumnComparator;
|
||||||
|
import docking.widgets.table.sort.RowToColumnComparator;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||||
import ghidra.util.datastruct.WeakSet;
|
import ghidra.util.datastruct.WeakSet;
|
||||||
|
|
||||||
@@ -178,8 +180,7 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||||||
|
|
||||||
isSortPending = true;
|
isSortPending = true;
|
||||||
pendingSortState = newSortState;
|
pendingSortState = newSortState;
|
||||||
SystemUtilities.runSwingLater(
|
Swing.runLater(() -> sort(getModelData(), createSortingContext(newSortState)));
|
||||||
() -> sort(getModelData(), createSortingContext(newSortState)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TableSortState getPendingSortState() {
|
public TableSortState getPendingSortState() {
|
||||||
@@ -223,7 +224,7 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||||||
hasEverSorted = true;
|
hasEverSorted = true;
|
||||||
isSortPending = true;
|
isSortPending = true;
|
||||||
pendingSortState = sortState;
|
pendingSortState = sortState;
|
||||||
SystemUtilities.runSwingLater(() -> sort(getModelData(), createSortingContext(sortState)));
|
Swing.runLater(() -> sort(getModelData(), createSortingContext(sortState)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -320,14 +321,14 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An extension point for subclasses to insert their own comparator objects for their data.
|
* An extension point for subclasses to insert their own comparator objects for their data.
|
||||||
* Subclasses can create comparators for a single or multiple columns, as desired. The
|
* Subclasses can create comparators for a single or multiple columns, as desired.
|
||||||
* {@link DefaultColumnComparator} is used as a, well, default comparator.
|
|
||||||
*
|
*
|
||||||
* @param columnIndex the column index for which a comparator is desired.
|
* @param columnIndex the column index
|
||||||
* @return a comparator for the given index.
|
* @return the comparator
|
||||||
*/
|
*/
|
||||||
protected Comparator<T> createSortComparator(int columnIndex) {
|
protected Comparator<T> createSortComparator(int columnIndex) {
|
||||||
return new DefaultColumnComparator(columnIndex);
|
return new RowToColumnComparator<>(this, columnIndex, new DefaultColumnComparator(),
|
||||||
|
new StringBasedBackupRowToColumnComparator(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Comparator<T> createLastResortComparator(ComparatorLink parentChain) {
|
private Comparator<T> createLastResortComparator(ComparatorLink parentChain) {
|
||||||
@@ -416,23 +417,6 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int size() {
|
|
||||||
int count = 0;
|
|
||||||
if (primaryComparator != null) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextComparator == null) {
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextComparator instanceof AbstractSortedTableModel.ComparatorLink) {
|
|
||||||
count += ((ComparatorLink) nextComparator).size();
|
|
||||||
}
|
|
||||||
|
|
||||||
return count + 1; // +1 for the non-null comparator
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(T t1, T t2) {
|
public int compare(T t1, T t2) {
|
||||||
int result = primaryComparator.compare(t1, t2);
|
int result = primaryComparator.compare(t1, t2);
|
||||||
@@ -451,18 +435,18 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||||||
* when we get to this comparator, then we have to make a decision about reasonable default
|
* when we get to this comparator, then we have to make a decision about reasonable default
|
||||||
* comparisons in order to maintain sorting consistency across sorts.
|
* comparisons in order to maintain sorting consistency across sorts.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked") // Comparable cast
|
||||||
// Comparable cast
|
|
||||||
private class EndOfChainComparator implements Comparator<T> {
|
private class EndOfChainComparator implements Comparator<T> {
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
public int compare(T t1, T t2) {
|
public int compare(T t1, T t2) {
|
||||||
|
|
||||||
// at this point we compare the rows, since all of the sorting columns are
|
// at this point we compare the rows, since all of the sorting column values are equal
|
||||||
// completely equal
|
|
||||||
if (t1 instanceof Comparable) {
|
if (t1 instanceof Comparable) {
|
||||||
return ((Comparable) t1).compareTo(t2);
|
return ((Comparable) t1).compareTo(t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use the identity hash to provide a consistent unique identifier within a JVM session
|
||||||
return System.identityHashCode(t1) - System.identityHashCode(t2);
|
return System.identityHashCode(t1) - System.identityHashCode(t2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,19 +464,35 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DefaultColumnComparator implements Comparator<T> {
|
private class StringBasedBackupRowToColumnComparator implements Comparator<T> {
|
||||||
private final int columnIndex;
|
|
||||||
|
|
||||||
public DefaultColumnComparator(int columnIndex) {
|
private int sortColumn;
|
||||||
this.columnIndex = columnIndex;
|
|
||||||
|
StringBasedBackupRowToColumnComparator(int sortColumn) {
|
||||||
|
this.sortColumn = sortColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(T t1, T t2) {
|
public int compare(T t1, T t2) {
|
||||||
Object value1 = getColumnValueForRow(t1, columnIndex);
|
if (t1 == t2) {
|
||||||
Object value2 = getColumnValueForRow(t2, columnIndex);
|
return 0;
|
||||||
return DEFAULT_COMPARATOR.compare(value1, value2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String s1 = getColumStringValue(t1);
|
||||||
|
String s2 = getColumStringValue(t2);
|
||||||
|
|
||||||
|
if (s1 == null || s2 == null) {
|
||||||
|
return TableComparators.compareWithNullValues(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s1.compareToIgnoreCase(s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getColumStringValue(T t) {
|
||||||
|
// just use the toString(), which may or may not produce a good value (this will
|
||||||
|
// catch the cases where the column value is itself a string)
|
||||||
|
Object o = getColumnValueForRow(t, sortColumn);
|
||||||
|
return o == null ? null : o.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -20,7 +20,8 @@ package docking.widgets.table;
|
|||||||
*
|
*
|
||||||
* @param <ROW_TYPE> the row type of the underlying table model
|
* @param <ROW_TYPE> the row type of the underlying table model
|
||||||
*/
|
*/
|
||||||
public interface DynamicColumnTableModel<ROW_TYPE> extends ConfigurableColumnTableModel {
|
public interface DynamicColumnTableModel<ROW_TYPE>
|
||||||
|
extends ConfigurableColumnTableModel, RowObjectTableModel<ROW_TYPE> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the column for the given model index
|
* Returns the column for the given model index
|
||||||
|
|||||||
+9
-46
@@ -22,6 +22,7 @@ import javax.swing.event.ChangeEvent;
|
|||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
|
||||||
|
import docking.widgets.table.sort.*;
|
||||||
import ghidra.docking.settings.*;
|
import ghidra.docking.settings.*;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
@@ -174,11 +175,14 @@ public abstract class GDynamicColumnTableModel<ROW_TYPE, DATA_SOURCE>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Comparator<ROW_TYPE> createSortComparator(int columnIndex) {
|
protected Comparator<ROW_TYPE> createSortComparator(int columnIndex) {
|
||||||
Comparator<Object> comparator = createSortComparatorForColumn(columnIndex);
|
Comparator<Object> columnComparator = createSortComparatorForColumn(columnIndex);
|
||||||
if (comparator != null) {
|
if (columnComparator != null) {
|
||||||
return new RowToColumnComparator(columnIndex, comparator);
|
// the given column has its own comparator; wrap and us that
|
||||||
|
return new RowToColumnComparator<>(this, columnIndex, columnComparator);
|
||||||
}
|
}
|
||||||
return super.createSortComparator(columnIndex);
|
|
||||||
|
return new RowToColumnComparator<>(this, columnIndex, new DefaultColumnComparator(),
|
||||||
|
new ColumnRenderedValueBackupRowComparator<>(this, columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,7 +191,7 @@ public abstract class GDynamicColumnTableModel<ROW_TYPE, DATA_SOURCE>
|
|||||||
* column values.
|
* column values.
|
||||||
*
|
*
|
||||||
* @param columnIndex the column index
|
* @param columnIndex the column index
|
||||||
* @return a comparator for the specific column values; may be null
|
* @return a comparator for the specific column values
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked") // the column provides the values itself; safe cast
|
@SuppressWarnings("unchecked") // the column provides the values itself; safe cast
|
||||||
protected Comparator<Object> createSortComparatorForColumn(int columnIndex) {
|
protected Comparator<Object> createSortComparatorForColumn(int columnIndex) {
|
||||||
@@ -545,45 +549,4 @@ public abstract class GDynamicColumnTableModel<ROW_TYPE, DATA_SOURCE>
|
|||||||
DynamicTableColumn<ROW_TYPE, ?, ?> column = tableColumns.get(index);
|
DynamicTableColumn<ROW_TYPE, ?, ?> column = tableColumns.get(index);
|
||||||
return column.getMaxLines(columnSettings.get(column));
|
return column.getMaxLines(columnSettings.get(column));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A comparator for a specific column that will take in a ROW_TYPE object, extract the value
|
|
||||||
* for the given column and then call the give comparator.
|
|
||||||
*/
|
|
||||||
private class RowToColumnComparator implements Comparator<ROW_TYPE> {
|
|
||||||
|
|
||||||
private int columnIndex;
|
|
||||||
private Comparator<Object> columnComparator;
|
|
||||||
|
|
||||||
RowToColumnComparator(int columnIndex, Comparator<Object> comparator) {
|
|
||||||
this.columnIndex = columnIndex;
|
|
||||||
this.columnComparator = comparator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(ROW_TYPE t1, ROW_TYPE t2) {
|
|
||||||
Object value1 = getColumnValueForRow(t1, columnIndex);
|
|
||||||
Object value2 = getColumnValueForRow(t2, columnIndex);
|
|
||||||
|
|
||||||
if (value1 == null || value2 == null) {
|
|
||||||
return handleNullValues(value1, value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return columnComparator.compare(value1, value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int handleNullValues(Object o1, Object o2) {
|
|
||||||
// If both values are null return 0
|
|
||||||
if (o1 == null && o2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o1 == null) { // Define null less than everything.
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1; // o2 is null, so the o1 comes after
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-56
@@ -15,17 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package docking.widgets.table;
|
package docking.widgets.table;
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import javax.swing.table.TableModel;
|
import javax.swing.table.TableModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A table model that allows for setting the sorted column and direction.
|
* A table model that allows for setting the sorted column(s) and direction
|
||||||
*/
|
*/
|
||||||
public interface SortedTableModel extends TableModel {
|
public interface SortedTableModel extends TableModel {
|
||||||
|
|
||||||
public static final Comparator<Object> DEFAULT_COMPARATOR = new DefaultComparator();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort order in ascending order.
|
* Sort order in ascending order.
|
||||||
*/
|
*/
|
||||||
@@ -43,10 +39,23 @@ public interface SortedTableModel extends TableModel {
|
|||||||
*/
|
*/
|
||||||
public boolean isSortable(int columnIndex);
|
public boolean isSortable(int columnIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the column index that is the primary sorted column
|
||||||
|
*
|
||||||
|
* @return the index
|
||||||
|
*/
|
||||||
public int getPrimarySortColumnIndex();
|
public int getPrimarySortColumnIndex();
|
||||||
|
|
||||||
public void setTableSortState(TableSortState tableSortState);
|
/**
|
||||||
|
* Sets the sort state for this table model
|
||||||
|
* @param state the sort state
|
||||||
|
*/
|
||||||
|
public void setTableSortState(TableSortState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sort state of this sorted model
|
||||||
|
* @return the current sort state
|
||||||
|
*/
|
||||||
public TableSortState getTableSortState();
|
public TableSortState getTableSortState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,54 +67,4 @@ public interface SortedTableModel extends TableModel {
|
|||||||
* @param l the listener
|
* @param l the listener
|
||||||
*/
|
*/
|
||||||
public void addSortListener(SortListener l);
|
public void addSortListener(SortListener l);
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// Inner Classes
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
public static class DefaultComparator implements Comparator<Object> {
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// we checked cast to be safe
|
|
||||||
public int compare(Object o1, Object o2) {
|
|
||||||
|
|
||||||
if (o1 == null || o2 == null) {
|
|
||||||
return handleNullValues(o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (String.class == o1.getClass() && String.class == o2.getClass()) {
|
|
||||||
return compareAsStrings(o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Comparable.class.isAssignableFrom(o1.getClass()) && o1.getClass() == o2.getClass()) {
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
Comparable comparable = (Comparable) o1;
|
|
||||||
int result = comparable.compareTo(o2);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// give up and use the toString()
|
|
||||||
return compareAsStrings(o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int handleNullValues(Object o1, Object o2) {
|
|
||||||
// If both values are null return 0
|
|
||||||
if (o1 == null && o2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o1 == null) { // Define null less than everything.
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1; // o2 is null, so the o1 comes after
|
|
||||||
}
|
|
||||||
|
|
||||||
private int compareAsStrings(Object o1, Object o2) {
|
|
||||||
String s1 = o1.toString();
|
|
||||||
String s2 = o2.toString();
|
|
||||||
return s1.compareToIgnoreCase(s2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 docking.widgets.table;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class for tables to use when sorting
|
||||||
|
*/
|
||||||
|
public class TableComparators {
|
||||||
|
|
||||||
|
private static final Comparator<Object> NO_SORT_COMPARATOR = (o1, o2) -> 0;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // we are casting to Object; safe since everything is an Object
|
||||||
|
public static <T> Comparator<T> getNoSortComparator() {
|
||||||
|
return (Comparator<T>) NO_SORT_COMPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int compareWithNullValues(Object o1, Object o2) {
|
||||||
|
// If both values are null return 0
|
||||||
|
if (o1 == null && o2 == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o1 == null) { // Define null less than everything.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1; // o2 is null, so the o1 comes after
|
||||||
|
}
|
||||||
|
}
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 docking.widgets.table.sort;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import docking.widgets.table.*;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.util.table.column.GColumnRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special version of the backup comparator that uses the column's rendered value for
|
||||||
|
* the backup sort, rather the just <code>toString</code>, which is what the default parent
|
||||||
|
* table model will do.
|
||||||
|
*
|
||||||
|
* @param <T> the row type
|
||||||
|
*/
|
||||||
|
public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T> {
|
||||||
|
|
||||||
|
protected int sortColumn;
|
||||||
|
protected DynamicColumnTableModel<T> model;
|
||||||
|
|
||||||
|
public ColumnRenderedValueBackupRowComparator(DynamicColumnTableModel<T> model,
|
||||||
|
int sortColumn) {
|
||||||
|
this.model = model;
|
||||||
|
this.sortColumn = sortColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(T t1, T t2) {
|
||||||
|
if (t1 == t2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String s1 = getRenderedColumnStringValue(t1);
|
||||||
|
String s2 = getRenderedColumnStringValue(t2);
|
||||||
|
|
||||||
|
if (s1 == null || s2 == null) {
|
||||||
|
return TableComparators.compareWithNullValues(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s1.compareToIgnoreCase(s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The case to Object is safe, but passing the object to the renderer below is potentially
|
||||||
|
// unsafe. We happen know that we retrieved the value from the column that we are passing
|
||||||
|
// it to, so the casting and usage is indeed safe.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private String getRenderedColumnStringValue(T t) {
|
||||||
|
|
||||||
|
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
||||||
|
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
||||||
|
Object o = model.getColumnValueForRow(t, sortColumn);
|
||||||
|
if (renderer == null) {
|
||||||
|
return o == null ? null : o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings settings = model.getColumnSettings(sortColumn);
|
||||||
|
return renderer.getFilterString(o, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getColumnValue(T t) {
|
||||||
|
return model.getColumnValueForRow(t, sortColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 docking.widgets.table.sort;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import docking.widgets.table.TableComparators;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A column comparator that is used when columns do not supply their own comparator. This
|
||||||
|
* comparator will use the natural sorting (i.e., the value implements Comparable),
|
||||||
|
* defaulting to the String representation for the given value.
|
||||||
|
*/
|
||||||
|
public class DefaultColumnComparator implements Comparator<Object> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked") // we checked cast to be safe
|
||||||
|
public int compare(Object o1, Object o2) {
|
||||||
|
|
||||||
|
if (o1 == null || o2 == null) {
|
||||||
|
return TableComparators.compareWithNullValues(o1, o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<? extends Object> c1 = o1.getClass();
|
||||||
|
Class<? extends Object> c2 = o2.getClass();
|
||||||
|
if (String.class == c1 && String.class == c2) {
|
||||||
|
return compareAsStrings(o1, o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Comparable.class.isAssignableFrom(c1) && c1 == c2) {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
Comparable comparable = (Comparable) o1;
|
||||||
|
int result = comparable.compareTo(o2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we do not know how to compare these items well. Return 0, which
|
||||||
|
// will signal to any further comparators that more comparing is needed.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compareAsStrings(Object o1, Object o2) {
|
||||||
|
String s1 = o1.toString();
|
||||||
|
String s2 = o2.toString();
|
||||||
|
return s1.compareToIgnoreCase(s2);
|
||||||
|
}
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 docking.widgets.table.sort;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import docking.widgets.table.RowObjectTableModel;
|
||||||
|
import docking.widgets.table.TableComparators;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comparator for a specific column that will take in a T row object, extract the value
|
||||||
|
* for the given column and then call the give comparator
|
||||||
|
*
|
||||||
|
* @param <T> the row type
|
||||||
|
*/
|
||||||
|
public class RowToColumnComparator<T> implements Comparator<T> {
|
||||||
|
|
||||||
|
protected RowObjectTableModel<T> model;
|
||||||
|
protected int sortColumn;
|
||||||
|
protected Comparator<Object> columnComparator;
|
||||||
|
protected Comparator<T> backupRowComparator = TableComparators.getNoSortComparator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs this class with the given column comparator that will get called after the
|
||||||
|
* given row is converted to the column value for the given sort column
|
||||||
|
*
|
||||||
|
* @param model the table model using this comparator
|
||||||
|
* @param sortColumn the column being sorted
|
||||||
|
* @param comparator the column comparator to use for sorting
|
||||||
|
*/
|
||||||
|
public RowToColumnComparator(RowObjectTableModel<T> model, int sortColumn,
|
||||||
|
Comparator<Object> comparator) {
|
||||||
|
this.model = model;
|
||||||
|
this.sortColumn = sortColumn;
|
||||||
|
this.columnComparator = Objects.requireNonNull(comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This version of the constructor is used for the default case where the client will
|
||||||
|
* supply a backup row comparator that will get called if the given column comparator returns
|
||||||
|
* a '0' value.
|
||||||
|
*
|
||||||
|
* @param model the table model using this comparator
|
||||||
|
* @param sortColumn the column being sorted
|
||||||
|
* @param comparator the column comparator to use for sorting
|
||||||
|
* @param backupRowComparator the backup row comparator
|
||||||
|
*/
|
||||||
|
public RowToColumnComparator(RowObjectTableModel<T> model, int sortColumn,
|
||||||
|
Comparator<Object> comparator, Comparator<T> backupRowComparator) {
|
||||||
|
this.model = model;
|
||||||
|
this.sortColumn = sortColumn;
|
||||||
|
this.columnComparator = Objects.requireNonNull(comparator);
|
||||||
|
this.backupRowComparator = Objects.requireNonNull(backupRowComparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(T t1, T t2) {
|
||||||
|
if (t1 == t2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value1 = getColumnValue(t1);
|
||||||
|
Object value2 = getColumnValue(t2);
|
||||||
|
|
||||||
|
if (value1 == null || value2 == null) {
|
||||||
|
return TableComparators.compareWithNullValues(value1, value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = columnComparator.compare(value1, value2);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// At this point we have one of two cases:
|
||||||
|
// 1) the column comparator is a non-default comparator that has returned 0, which means
|
||||||
|
// the column values should sort the same, or
|
||||||
|
// 2) the column comparator is a default/non-specific comparator, which means that the
|
||||||
|
// column values should sort the same, or *that the default comparator could not
|
||||||
|
// figure out how to sort them.
|
||||||
|
//
|
||||||
|
// In case 1, this backup comparator will be just a stub comparator; in case 2, this
|
||||||
|
// backup comparator is not a stub and will do something reasonable for the sort,
|
||||||
|
// depending upon how the model created this class.
|
||||||
|
//
|
||||||
|
return backupRowComparator.compare(t1, t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getColumnValue(T t) {
|
||||||
|
return model.getColumnValueForRow(t, sortColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
-92
@@ -1,92 +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 docking.widgets.table.threaded;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import docking.widgets.table.SortedTableModel;
|
|
||||||
|
|
||||||
public class TableColumnComparator<T> implements Comparator<T> {
|
|
||||||
private ThreadedTableModel<T, ?> model;
|
|
||||||
private final int sortColumn;
|
|
||||||
private Comparator<Object> columnComparator;
|
|
||||||
|
|
||||||
public TableColumnComparator(ThreadedTableModel<T, ?> model,
|
|
||||||
Comparator<Object> columnComparator, int sortColumn) {
|
|
||||||
this.model = model;
|
|
||||||
this.columnComparator = columnComparator;
|
|
||||||
this.sortColumn = sortColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(T t1, T t2) {
|
|
||||||
if (t1 == t2) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object o1 = model.getCachedColumnValueForRow(t1, sortColumn);
|
|
||||||
Object o2 = model.getCachedColumnValueForRow(t2, sortColumn);
|
|
||||||
|
|
||||||
if (o1 == null || o2 == null) {
|
|
||||||
return handleNullValues(o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (columnComparator != null) {
|
|
||||||
return columnComparator.compare(o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SortedTableModel.DEFAULT_COMPARATOR.compare(o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int handleNullValues(Object o1, Object o2) {
|
|
||||||
// If both values are null return 0
|
|
||||||
if (o1 == null && o2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o1 == null) { // Define null less than everything.
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1; // o2 is null, so the o1 comes after
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSortColumn() {
|
|
||||||
return sortColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (obj.getClass() != getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
TableColumnComparator other = (TableColumnComparator) obj;
|
|
||||||
return (sortColumn == other.sortColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return sortColumn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 docking.widgets.table.threaded;
|
||||||
|
|
||||||
|
import docking.widgets.table.sort.ColumnRenderedValueBackupRowComparator;
|
||||||
|
import docking.widgets.table.sort.RowToColumnComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of {@link ColumnRenderedValueBackupRowComparator} that uses the
|
||||||
|
* {@link ThreadedTableModel}'s cache for column lookups
|
||||||
|
*
|
||||||
|
* @param <T> the row type
|
||||||
|
*/
|
||||||
|
public class ThreadedBackupRowComparator<T> extends ColumnRenderedValueBackupRowComparator<T> {
|
||||||
|
|
||||||
|
private ThreadedTableModel<T, ?> threadedModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs this class with the given column comparator that will get called after the
|
||||||
|
* given row is converted to the column value for the given sort column
|
||||||
|
*
|
||||||
|
* @param model the table model using this comparator
|
||||||
|
* @param sortColumn the column being sorted
|
||||||
|
* @see RowToColumnComparator
|
||||||
|
*/
|
||||||
|
public ThreadedBackupRowComparator(ThreadedTableModel<T, ?> model, int sortColumn) {
|
||||||
|
super(model, sortColumn);
|
||||||
|
this.threadedModel = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object getColumnValue(T t) {
|
||||||
|
return threadedModel.getCachedColumnValueForRow(t, sortColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 docking.widgets.table.threaded;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import docking.widgets.table.sort.RowToColumnComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comparator for comparing table column values for threaded table models. This comparator
|
||||||
|
* uses the column cache of the {@link ThreadedTableModel}.
|
||||||
|
*
|
||||||
|
* @param <T> the row type
|
||||||
|
*/
|
||||||
|
public class ThreadedTableColumnComparator<T> extends RowToColumnComparator<T> {
|
||||||
|
private ThreadedTableModel<T, ?> threadedModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs this class with the given column comparator that will get called after the
|
||||||
|
* given row is converted to the column value for the given sort column
|
||||||
|
*
|
||||||
|
* @param model the table model using this comparator
|
||||||
|
* @param sortColumn the column being sorted
|
||||||
|
* @param comparator the column comparator to use for sorting
|
||||||
|
* @see RowToColumnComparator
|
||||||
|
*/
|
||||||
|
public ThreadedTableColumnComparator(ThreadedTableModel<T, ?> model, int sortColumn,
|
||||||
|
Comparator<Object> comparator) {
|
||||||
|
super(model, sortColumn, comparator);
|
||||||
|
this.threadedModel = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This version of the constructor is used for the default case where the client will
|
||||||
|
* supply a backup row comparator that will get called if the given column comparator returns
|
||||||
|
* a '0' value.
|
||||||
|
*
|
||||||
|
* @param model the table model using this comparator
|
||||||
|
* @param sortColumn the column being sorted
|
||||||
|
* @param comparator the column comparator to use for sorting
|
||||||
|
* @param backupRowComparator the backup row comparator
|
||||||
|
* @see RowToColumnComparator
|
||||||
|
*/
|
||||||
|
public ThreadedTableColumnComparator(ThreadedTableModel<T, ?> model, int sortColumn,
|
||||||
|
Comparator<Object> comparator, Comparator<T> backupRowComparator) {
|
||||||
|
super(model, sortColumn, comparator, backupRowComparator);
|
||||||
|
this.threadedModel = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object getColumnValue(T t) {
|
||||||
|
return threadedModel.getCachedColumnValueForRow(t, sortColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-1
@@ -21,6 +21,7 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.event.TableModelEvent;
|
import javax.swing.event.TableModelEvent;
|
||||||
|
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
|
import docking.widgets.table.sort.DefaultColumnComparator;
|
||||||
import generic.concurrent.ConcurrentListenerSet;
|
import generic.concurrent.ConcurrentListenerSet;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
@@ -290,8 +291,15 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Comparator<ROW_OBJECT> createSortComparator(int columnIndex) {
|
protected Comparator<ROW_OBJECT> createSortComparator(int columnIndex) {
|
||||||
|
|
||||||
Comparator<Object> columnComparator = createSortComparatorForColumn(columnIndex);
|
Comparator<Object> columnComparator = createSortComparatorForColumn(columnIndex);
|
||||||
return new TableColumnComparator<>(this, columnComparator, columnIndex);
|
if (columnComparator != null) {
|
||||||
|
// the given column has its own comparator; wrap and us that
|
||||||
|
return new ThreadedTableColumnComparator<>(this, columnIndex, columnComparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ThreadedTableColumnComparator<>(this, columnIndex, new DefaultColumnComparator(),
|
||||||
|
new ThreadedBackupRowComparator<>(this, columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user