mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-2763 - Table Sorting - fixed bug that triggered filtering to take
place for individual add/remove operations; fixed bug that caused loss of added/removed items
This commit is contained in:
parent
da5f009c71
commit
445c7ca03e
32 changed files with 720 additions and 143 deletions
|
@ -15,12 +15,13 @@
|
|||
*/
|
||||
package docking.widgets.filter;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class AbstractPatternTextFilter implements TextFilter {
|
||||
|
||||
protected final String filterText;
|
||||
private Pattern filterPattern;
|
||||
protected Pattern filterPattern;
|
||||
|
||||
protected AbstractPatternTextFilter(String filterText) {
|
||||
this.filterText = filterText;
|
||||
|
@ -67,6 +68,45 @@ public abstract class AbstractPatternTextFilter implements TextFilter {
|
|||
return filterPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((filterPattern == null) ? 0 : filterPattern.hashCode());
|
||||
result = prime * result + ((filterText == null) ? 0 : filterText.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AbstractPatternTextFilter other = (AbstractPatternTextFilter) obj;
|
||||
|
||||
String myPattern = getPatternString();
|
||||
String otherPattern = other.getPatternString();
|
||||
if (!myPattern.equals(otherPattern)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(filterText, other.filterText)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getPatternString() {
|
||||
return filterPattern == null ? "" : filterPattern.pattern();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
//@formatter:off
|
||||
|
|
|
@ -24,13 +24,8 @@ import ghidra.util.UserSearchUtils;
|
|||
*/
|
||||
public class ContainsTextFilter extends MatchesPatternTextFilter {
|
||||
|
||||
private boolean caseSensitive;
|
||||
private boolean allowGlobbing;
|
||||
|
||||
public ContainsTextFilter(String filterText, boolean caseSensitive, boolean allowGlobbing) {
|
||||
super(filterText);
|
||||
this.caseSensitive = caseSensitive;
|
||||
this.allowGlobbing = allowGlobbing;
|
||||
super(filterText, caseSensitive, allowGlobbing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package docking.widgets.filter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class InvertedTextFilter implements TextFilter {
|
||||
|
||||
private final TextFilter filter;
|
||||
|
@ -39,4 +41,30 @@ public class InvertedTextFilter implements TextFilter {
|
|||
return filter.getFilterText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((filter == null) ? 0 : filter.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InvertedTextFilter other = (InvertedTextFilter) obj;
|
||||
if (!Objects.equals(filter, other.filter)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,14 +24,9 @@ import ghidra.util.UserSearchUtils;
|
|||
*/
|
||||
public class MatchesExactlyTextFilter extends MatchesPatternTextFilter {
|
||||
|
||||
private boolean caseSensitive;
|
||||
private boolean allowGlobbing;
|
||||
|
||||
public MatchesExactlyTextFilter(String filterText, boolean caseSensitive,
|
||||
boolean allowGlobbing) {
|
||||
super(filterText);
|
||||
this.caseSensitive = caseSensitive;
|
||||
this.allowGlobbing = allowGlobbing;
|
||||
super(filterText, caseSensitive, allowGlobbing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,12 +22,55 @@ import java.util.regex.Pattern;
|
|||
*/
|
||||
public abstract class MatchesPatternTextFilter extends AbstractPatternTextFilter {
|
||||
|
||||
public MatchesPatternTextFilter(String filterText) {
|
||||
protected boolean caseSensitive;
|
||||
protected boolean allowGlobbing;
|
||||
|
||||
public MatchesPatternTextFilter(String filterText, boolean caseSensitive,
|
||||
boolean allowGlobbing) {
|
||||
super(filterText);
|
||||
|
||||
this.caseSensitive = caseSensitive;
|
||||
this.allowGlobbing = allowGlobbing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String text, Pattern pattern) {
|
||||
return pattern.matcher(text).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + (allowGlobbing ? 1231 : 1237);
|
||||
result = prime * result + (caseSensitive ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MatchesPatternTextFilter other = (MatchesPatternTextFilter) obj;
|
||||
if (allowGlobbing != other.allowGlobbing) {
|
||||
return false;
|
||||
}
|
||||
if (caseSensitive != other.caseSensitive) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,13 +24,8 @@ import ghidra.util.UserSearchUtils;
|
|||
*/
|
||||
public class StartsWithTextFilter extends MatchesPatternTextFilter {
|
||||
|
||||
private boolean caseSensitive;
|
||||
private boolean allowGlobbing;
|
||||
|
||||
public StartsWithTextFilter(String filterText, boolean caseSensitive, boolean allowGlobbing) {
|
||||
super(filterText);
|
||||
this.caseSensitive = caseSensitive;
|
||||
this.allowGlobbing = allowGlobbing;
|
||||
super(filterText, caseSensitive, allowGlobbing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -232,9 +232,6 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
|||
* fact that the data searched is retrieved from {@link #getModelData()}, which may be
|
||||
* filtered.
|
||||
*
|
||||
* <p>Warning: if the this model has no sort applied, then -1 will be returned. You can call
|
||||
* {@link #isSorted()} to know when this will happen.
|
||||
*
|
||||
* @param rowObject The object for which to search.
|
||||
* @return the index of the item in the data returned by
|
||||
*/
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package docking.widgets.table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Combines multiple Table Filters into a single TableFilter that can be applied. All contained
|
||||
|
@ -101,4 +100,32 @@ public class CombinedTableFilter<T> implements TableFilter<T> {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((filters == null) ? 0 : filters.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombinedTableFilter<?> other = (CombinedTableFilter<?>) obj;
|
||||
if (!Objects.equals(filters, other.filters)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package docking.widgets.table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
@ -27,7 +26,7 @@ import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
|
|||
|
||||
public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransformer<ROW_OBJECT> {
|
||||
|
||||
private List<String> columnData = new ArrayList<String>();
|
||||
private List<String> columnData = new ArrayList<>();
|
||||
private TableColumnModel columnModel;
|
||||
private final RowObjectTableModel<ROW_OBJECT> model;
|
||||
|
||||
|
@ -129,4 +128,41 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
|||
(GColumnRenderer<Object>) column.getColumnRenderer();
|
||||
return columnRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((columnData == null) ? 0 : columnData.hashCode());
|
||||
result = prime * result + ((columnModel == null) ? 0 : columnModel.hashCode());
|
||||
result = prime * result + ((model == null) ? 0 : model.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultRowFilterTransformer<?> other = (DefaultRowFilterTransformer<?>) obj;
|
||||
if (!Objects.equals(columnData, other.columnData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(columnModel, other.columnModel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(model, other.model)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ import java.util.List;
|
|||
|
||||
import docking.widgets.filter.*;
|
||||
|
||||
public class DefaultTableTextFilterFactory<ROW_OBJECT> implements
|
||||
TableTextFilterFactory<ROW_OBJECT> {
|
||||
public class DefaultTableTextFilterFactory<ROW_OBJECT>
|
||||
implements TableTextFilterFactory<ROW_OBJECT> {
|
||||
|
||||
private final TextFilterFactory textFilterFactory;
|
||||
private final boolean inverted;
|
||||
|
@ -40,7 +40,7 @@ public class DefaultTableTextFilterFactory<ROW_OBJECT> implements
|
|||
TableFilter<ROW_OBJECT> tableFilter = getBaseFilter(text, transformer);
|
||||
|
||||
if (inverted && tableFilter != null) {
|
||||
tableFilter = new InvertedTableFilter<ROW_OBJECT>(tableFilter);
|
||||
tableFilter = new InvertedTableFilter<>(tableFilter);
|
||||
}
|
||||
return tableFilter;
|
||||
}
|
||||
|
@ -55,14 +55,14 @@ public class DefaultTableTextFilterFactory<ROW_OBJECT> implements
|
|||
if (textFilter == null) {
|
||||
return null;
|
||||
}
|
||||
return new TableTextFilter<ROW_OBJECT>(textFilter, transformer);
|
||||
return new TableTextFilter<>(textFilter, transformer);
|
||||
|
||||
}
|
||||
|
||||
private TableFilter<ROW_OBJECT> getMultiWordTableFilter(String text,
|
||||
RowFilterTransformer<ROW_OBJECT> transformer) {
|
||||
|
||||
List<TextFilter> filters = new ArrayList<TextFilter>();
|
||||
List<TextFilter> filters = new ArrayList<>();
|
||||
TermSplitter splitter = filterOptions.getTermSplitter();
|
||||
for (String term : splitter.split(text)) {
|
||||
TextFilter textFilter = textFilterFactory.getTextFilter(term);
|
||||
|
@ -70,7 +70,7 @@ public class DefaultTableTextFilterFactory<ROW_OBJECT> implements
|
|||
filters.add(textFilter);
|
||||
}
|
||||
}
|
||||
return new MultiTextFilterTableFilter<ROW_OBJECT>(text, filters, transformer,
|
||||
return new MultiTextFilterTableFilter<>(filters, transformer,
|
||||
filterOptions.getMultitermEvaluationMode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package docking.widgets.table;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class InvertedTableFilter<ROW_OBJECT> implements TableFilter<ROW_OBJECT> {
|
||||
|
||||
private final TableFilter<ROW_OBJECT> filter;
|
||||
|
@ -34,4 +36,31 @@ public class InvertedTableFilter<ROW_OBJECT> implements TableFilter<ROW_OBJECT>
|
|||
return !filter.acceptsRow(rowObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((filter == null) ? 0 : filter.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InvertedTableFilter<?> other = (InvertedTableFilter<?>) obj;
|
||||
if (!Objects.equals(filter, other.filter)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package docking.widgets.table;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import docking.widgets.filter.MultitermEvaluationMode;
|
||||
import docking.widgets.filter.TextFilter;
|
||||
|
@ -23,13 +24,11 @@ import docking.widgets.filter.TextFilter;
|
|||
public class MultiTextFilterTableFilter<ROW_OBJECT> implements TableFilter<ROW_OBJECT> {
|
||||
|
||||
private final List<TextFilter> filters;
|
||||
private final String text;
|
||||
private final RowFilterTransformer<ROW_OBJECT> transformer;
|
||||
private final MultitermEvaluationMode evalMode;
|
||||
|
||||
public MultiTextFilterTableFilter(String text, List<TextFilter> filters,
|
||||
public MultiTextFilterTableFilter(List<TextFilter> filters,
|
||||
RowFilterTransformer<ROW_OBJECT> transformer, MultitermEvaluationMode evalMode) {
|
||||
this.text = text;
|
||||
this.filters = filters;
|
||||
this.transformer = transformer;
|
||||
this.evalMode = evalMode;
|
||||
|
@ -90,4 +89,41 @@ public class MultiTextFilterTableFilter<ROW_OBJECT> implements TableFilter<ROW_O
|
|||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((evalMode == null) ? 0 : evalMode.hashCode());
|
||||
result = prime * result + ((filters == null) ? 0 : filters.hashCode());
|
||||
result = prime * result + ((transformer == null) ? 0 : transformer.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MultiTextFilterTableFilter<?> other = (MultiTextFilterTableFilter<?>) obj;
|
||||
if (evalMode != other.evalMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(filters, other.filters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(transformer, other.transformer)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,9 +64,6 @@ public interface RowObjectFilterModel<ROW_OBJECT> extends RowObjectTableModel<RO
|
|||
* <p>This operation will be O(n) unless the implementation is sorted, in which case the
|
||||
* operation is O(log n), as it uses a binary search.
|
||||
*
|
||||
* <p>Note: if a sorted implementation is moved to an unsorted state, then -1 will be returned
|
||||
* from this method.
|
||||
*
|
||||
* @param t the item
|
||||
* @return the view index
|
||||
*/
|
||||
|
@ -80,9 +77,6 @@ public interface RowObjectFilterModel<ROW_OBJECT> extends RowObjectTableModel<RO
|
|||
* <p>This operation will be O(n) unless the implementation is sorted, in which case the
|
||||
* operation is O(log n), as it uses a binary search.
|
||||
*
|
||||
* <p>Note: if a sorted implementation is moved to an unsorted state, then -1 will be returned
|
||||
* from this method.
|
||||
*
|
||||
* @param t the item
|
||||
* @return the model index
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package docking.widgets.table;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import docking.widgets.filter.TextFilter;
|
||||
|
||||
|
@ -56,6 +57,38 @@ public class TableTextFilter<ROW_OBJECT> implements TableFilter<ROW_OBJECT> {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((textFilter == null) ? 0 : textFilter.hashCode());
|
||||
result = prime * result + ((transformer == null) ? 0 : transformer.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TableTextFilter<?> other = (TableTextFilter<?>) obj;
|
||||
if (!Objects.equals(textFilter, other.textFilter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(transformer, other.transformer)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " - filter='" + textFilter.getFilterText() + "'";
|
||||
|
|
|
@ -318,10 +318,6 @@ public class ColumnBasedTableFilter<R> implements TableFilter<R> {
|
|||
list.add(constraintSet);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
boolean acceptsRow(R rowObject) {
|
||||
for (ColumnConstraintSet<R, ?> constraintSet : list) {
|
||||
if (!constraintSet.accepts(rowObject, tableFilterContext)) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,17 +15,30 @@
|
|||
*/
|
||||
package docking.widgets.table.threaded;
|
||||
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.table.AddRemoveListItem;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class AddRemoveJob<T> extends TableUpdateJob<T> {
|
||||
AddRemoveJob(ThreadedTableModel<T, ?> model, List<AddRemoveListItem<T>> addRemoveList,
|
||||
TaskMonitor monitor) {
|
||||
super(model, monitor);
|
||||
setForceFilter(false); // the item will do its own sorting and filtering
|
||||
this.addRemoveList = addRemoveList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean filter() {
|
||||
//
|
||||
// This is a request to fully filter the table's data (like when the filter changes).
|
||||
// In this case, we had disabled 'force filter', as the sorting did not need it.
|
||||
// However, when the client asks to filter, make sure we filter.
|
||||
//
|
||||
boolean jobIsStillRunning = super.filter();
|
||||
if (jobIsStillRunning) {
|
||||
setForceFilter(true); // reset, since we had turned it off above; now we have to filter
|
||||
}
|
||||
return jobIsStillRunning;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,4 +38,23 @@ public class NullTableFilter<ROW_OBJECT> implements TableFilter<ROW_OBJECT> {
|
|||
// to filter, which doesn't make sense if this is meant to only be used by itself.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,8 +15,8 @@
|
|||
*/
|
||||
package docking.widgets.table.threaded;
|
||||
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import docking.widgets.table.TableSortingContext;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class SortJob<T> extends TableUpdateJob<T> {
|
||||
|
||||
|
@ -30,10 +29,15 @@ public class SortJob<T> extends TableUpdateJob<T> {
|
|||
|
||||
@Override
|
||||
public synchronized boolean filter() {
|
||||
boolean canFilter = super.filter();
|
||||
if (canFilter) {
|
||||
//
|
||||
// This is a request to fully filter the table's data (like when the filter changes).
|
||||
// In this case, we had disabled 'force filter', as the sorting did not need it.
|
||||
// However, when the client asks to filter, make sure we filter.
|
||||
//
|
||||
boolean jobIsStillRunning = super.filter();
|
||||
if (jobIsStillRunning) {
|
||||
setForceFilter(true); // reset, since we had turned it off above; now we have to filter
|
||||
}
|
||||
return canFilter;
|
||||
return jobIsStillRunning;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,9 +83,13 @@ public class TableData<ROW_OBJECT> implements Iterable<ROW_OBJECT> {
|
|||
}
|
||||
|
||||
TableData<ROW_OBJECT> copy() {
|
||||
return copy(source);
|
||||
}
|
||||
|
||||
TableData<ROW_OBJECT> copy(TableData<ROW_OBJECT> newSource) {
|
||||
List<ROW_OBJECT> dataCopy = new ArrayList<>(data);
|
||||
TableData<ROW_OBJECT> newData = new TableData<>(dataCopy, sortContext);
|
||||
newData.source = source;
|
||||
newData.source = newSource;
|
||||
newData.tableFilter = tableFilter;
|
||||
newData.ID = ID; // it is a copy, but represents the same data
|
||||
return newData;
|
||||
|
@ -128,7 +132,8 @@ public class TableData<ROW_OBJECT> implements Iterable<ROW_OBJECT> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Uses the current sort to perform a fast lookup of the given item in the given list
|
||||
* Uses the current sort to perform a fast lookup of the given item in the given list when
|
||||
* sorted; a brute-force lookup when not sorted
|
||||
* @param t the item
|
||||
* @return the index
|
||||
*/
|
||||
|
@ -258,9 +263,11 @@ public class TableData<ROW_OBJECT> implements Iterable<ROW_OBJECT> {
|
|||
* @return true if the source data nor the filter are different that what is used by this object.
|
||||
*/
|
||||
boolean matchesFilter(TableFilter<ROW_OBJECT> filter) {
|
||||
|
||||
// O.K., we are derived from the same source data, if the filter is the same, then there
|
||||
// is no need to refilter
|
||||
// is no need to refilter.
|
||||
//
|
||||
// Note: if a given filter does not override equals(), then this really means that they
|
||||
// must be the same filter for this method to return true
|
||||
return SystemUtilities.isEqual(tableFilter, filter);
|
||||
}
|
||||
|
||||
|
@ -287,6 +294,16 @@ public class TableData<ROW_OBJECT> implements Iterable<ROW_OBJECT> {
|
|||
return source.isUnrelatedTo(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of this table data. It is possible that two data instances of this class
|
||||
* that have the same ID are considered to be the same data.
|
||||
*
|
||||
* @return the ID
|
||||
*/
|
||||
int getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root dataset for this data and all its ancestors.
|
||||
* @return the root dataset for this data and all its ancestors.
|
||||
|
|
|
@ -166,7 +166,8 @@ public class TableUpdateJob<T> {
|
|||
*
|
||||
* @param item the add/remove item to add to the list of items to be processed in the add/remove
|
||||
* phase of this job.
|
||||
* @param the maximum number of add/remove jobs to queue before performing a full reload
|
||||
* @param maxAddRemoveCount the maximum number of add/remove jobs to queue before performing
|
||||
* a full reload
|
||||
*/
|
||||
public synchronized void addRemove(AddRemoveListItem<T> item, int maxAddRemoveCount) {
|
||||
if (currentState != NOT_RUNNING) {
|
||||
|
@ -219,17 +220,17 @@ public class TableUpdateJob<T> {
|
|||
|
||||
/**
|
||||
* Tells the job that the filter criteria has changed. This method can be called on
|
||||
* the currently running job as well as the pending job. If called on the running job, the effect
|
||||
* depends on the running job's state:
|
||||
* the currently running job as well as the pending job. If called on the running job, the
|
||||
* effect depends on the running job's state:
|
||||
* <ul>
|
||||
* <li>If the filter state hasn't happened yet, then nothing needs to be done as this job
|
||||
* will filter later anyway.
|
||||
* will filter later anyway.
|
||||
* <li>If the filter state has already been started or completed, then this method
|
||||
* attempts to stop the current process phase and cause the state machine to return to the
|
||||
* filter phase.
|
||||
* attempts to stop the current process phase and cause the state machine to
|
||||
* return to the filter phase.
|
||||
* <li>If the current job has already entered the DONE state, then the filter cannot take
|
||||
* effect in this job and a false value is returned to indicate the
|
||||
* filter was not handled by this job.
|
||||
* effect in this job and a false value is returned to indicate the filter was
|
||||
* not handled by this job.
|
||||
* </ul>
|
||||
* @return true if the filter can be processed by this job, false if this job is essentially already
|
||||
* completed and therefor cannot perform the filter job.
|
||||
|
@ -239,7 +240,7 @@ public class TableUpdateJob<T> {
|
|||
return false;
|
||||
}
|
||||
if (hasFiltered()) {
|
||||
// the user has requested a new filter, and we've already filtered, so we need to filter again
|
||||
// the user has requested a new filter; we've already filtered, so filter again
|
||||
monitor.cancel();
|
||||
pendingRequestedState = FILTERING;
|
||||
}
|
||||
|
@ -457,6 +458,11 @@ public class TableUpdateJob<T> {
|
|||
|
||||
/** True if the sort applied to the table is not the same as that in the source dataset */
|
||||
private boolean tableSortDiffersFromSourceData() {
|
||||
// Note: at this point in time we do not check to see if the table is user-unsorted. It
|
||||
// doesn't seem to hurt to leave the original source data sorted, even if the
|
||||
// current context is 'unsorted'. In that case, this method will return true,
|
||||
// that the sorts are different. But, later in this job, we check the new sort and
|
||||
// do not perform sorting when 'unsorted'
|
||||
return !SystemUtilities.isEqual(sourceData.getSortContext(), model.getSortingContext());
|
||||
}
|
||||
|
||||
|
@ -600,23 +606,24 @@ public class TableUpdateJob<T> {
|
|||
|
||||
List<T> list = filterSourceData.getData();
|
||||
List<T> result = model.doFilter(list, lastSortContext, monitor);
|
||||
if (result != list) { // yes, '=='
|
||||
if (result == list) { // yes, '=='
|
||||
// no filtering took place
|
||||
updatedData = filterSourceData;
|
||||
}
|
||||
else {
|
||||
// the derived data is sorted the same as the source data
|
||||
TableSortingContext<T> sortContext = filterSourceData.getSortContext();
|
||||
updatedData = TableData.createSubDataset(filterSourceData, result, sortContext);
|
||||
updatedData.setTableFilter(model.getTableFilter());
|
||||
}
|
||||
else {
|
||||
// no filtering took place
|
||||
updatedData = filterSourceData;
|
||||
}
|
||||
|
||||
monitor.setMessage(
|
||||
"Done filtering " + model.getName() + " (" + updatedData.size() + " rows)");
|
||||
}
|
||||
|
||||
private void copyCurrentFilterData() {
|
||||
TableData<T> currentFilteredData = getCurrentFilteredData();
|
||||
updatedData = currentFilteredData.copy(); // copy so we don't modify the UIs version
|
||||
updatedData = currentFilteredData.copy(sourceData); // copy; don't modify the UI's version
|
||||
|
||||
// We are re-using the filtered data, so use too its sort
|
||||
lastSortContext = updatedData.getSortContext();
|
||||
|
|
|
@ -486,7 +486,11 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
|||
|
||||
SystemUtilities.assertThisIsTheSwingThread("Must be called on the Swing thread");
|
||||
|
||||
boolean dataChanged = (this.filteredData.size() != filteredData.size());
|
||||
//@formatter:off
|
||||
// The data is changed when it is filtered OR when an item has been added or removed
|
||||
boolean dataChanged = this.filteredData.getId() != filteredData.getId() ||
|
||||
this.filteredData.size() != filteredData.size();
|
||||
//@formatter:on
|
||||
this.allData = allData;
|
||||
this.filteredData = filteredData;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue