mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-2892 - Tables - updated default sorting to use the column's rendered
value when possible
This commit is contained in:
parent
e883885687
commit
2df2963f09
14 changed files with 486 additions and 234 deletions
|
@ -20,7 +20,9 @@ import java.util.*;
|
|||
import javax.swing.event.TableModelEvent;
|
||||
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.WeakSet;
|
||||
|
||||
|
@ -178,8 +180,7 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||
|
||||
isSortPending = true;
|
||||
pendingSortState = newSortState;
|
||||
SystemUtilities.runSwingLater(
|
||||
() -> sort(getModelData(), createSortingContext(newSortState)));
|
||||
Swing.runLater(() -> sort(getModelData(), createSortingContext(newSortState)));
|
||||
}
|
||||
|
||||
public TableSortState getPendingSortState() {
|
||||
|
@ -223,7 +224,7 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||
hasEverSorted = true;
|
||||
isSortPending = true;
|
||||
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.
|
||||
* Subclasses can create comparators for a single or multiple columns, as desired. The
|
||||
* {@link DefaultColumnComparator} is used as a, well, default comparator.
|
||||
* Subclasses can create comparators for a single or multiple columns, as desired.
|
||||
*
|
||||
* @param columnIndex the column index for which a comparator is desired.
|
||||
* @return a comparator for the given index.
|
||||
* @param columnIndex the column index
|
||||
* @return the comparator
|
||||
*/
|
||||
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) {
|
||||
|
@ -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
|
||||
public int compare(T t1, T 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
|
||||
* comparisons in order to maintain sorting consistency across sorts.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// Comparable cast
|
||||
@SuppressWarnings("unchecked") // Comparable cast
|
||||
private class EndOfChainComparator implements Comparator<T> {
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public int compare(T t1, T t2) {
|
||||
|
||||
// at this point we compare the rows, since all of the sorting columns are
|
||||
// completely equal
|
||||
// at this point we compare the rows, since all of the sorting column values are equal
|
||||
if (t1 instanceof Comparable) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -480,19 +464,35 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||
}
|
||||
}
|
||||
|
||||
private class DefaultColumnComparator implements Comparator<T> {
|
||||
private final int columnIndex;
|
||||
private class StringBasedBackupRowToColumnComparator implements Comparator<T> {
|
||||
|
||||
public DefaultColumnComparator(int columnIndex) {
|
||||
this.columnIndex = columnIndex;
|
||||
private int sortColumn;
|
||||
|
||||
StringBasedBackupRowToColumnComparator(int sortColumn) {
|
||||
this.sortColumn = sortColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(T t1, T t2) {
|
||||
Object value1 = getColumnValueForRow(t1, columnIndex);
|
||||
Object value2 = getColumnValueForRow(t2, columnIndex);
|
||||
return DEFAULT_COMPARATOR.compare(value1, value2);
|
||||
if (t1 == t2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ package docking.widgets.table;
|
|||
*
|
||||
* @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
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.swing.event.ChangeEvent;
|
|||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
import docking.widgets.table.sort.*;
|
||||
import ghidra.docking.settings.*;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.util.Msg;
|
||||
|
@ -174,11 +175,14 @@ public abstract class GDynamicColumnTableModel<ROW_TYPE, DATA_SOURCE>
|
|||
|
||||
@Override
|
||||
protected Comparator<ROW_TYPE> createSortComparator(int columnIndex) {
|
||||
Comparator<Object> comparator = createSortComparatorForColumn(columnIndex);
|
||||
if (comparator != null) {
|
||||
return new RowToColumnComparator(columnIndex, comparator);
|
||||
Comparator<Object> columnComparator = createSortComparatorForColumn(columnIndex);
|
||||
if (columnComparator != null) {
|
||||
// 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.
|
||||
*
|
||||
* @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
|
||||
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);
|
||||
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,17 +15,13 @@
|
|||
*/
|
||||
package docking.widgets.table;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
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 static final Comparator<Object> DEFAULT_COMPARATOR = new DefaultComparator();
|
||||
|
||||
/**
|
||||
* Sort order in ascending order.
|
||||
*/
|
||||
|
@ -43,10 +39,23 @@ public interface SortedTableModel extends TableModel {
|
|||
*/
|
||||
public boolean isSortable(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the column index that is the primary sorted column
|
||||
*
|
||||
* @return the index
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
|
@ -58,54 +67,4 @@ public interface SortedTableModel extends TableModel {
|
|||
* @param l the listener
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import javax.swing.SwingUtilities;
|
|||
import javax.swing.event.TableModelEvent;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.sort.DefaultColumnComparator;
|
||||
import generic.concurrent.ConcurrentListenerSet;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
@ -290,8 +291,15 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
|||
|
||||
@Override
|
||||
protected Comparator<ROW_OBJECT> createSortComparator(int 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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue