From 801eb01f4b4c5cd42e48b3412000dd301a520472 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Wed, 8 Jun 2022 09:34:14 -0400 Subject: [PATCH] GP-2050 - Symbol Table - Removed old add/remove strategy --- .../SymbolTableAddRemoveStrategy.java | 270 --------- .../core/symtable/SymbolTableModel.java | 27 +- .../SymbolTableAddRemoveStrategyTest.java | 567 ------------------ 3 files changed, 8 insertions(+), 856 deletions(-) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategy.java delete mode 100644 Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategyTest.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategy.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategy.java deleted file mode 100644 index a39df41132..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategy.java +++ /dev/null @@ -1,270 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.symtable; - -import static docking.widgets.table.AddRemoveListItem.Type.*; - -import java.util.*; - -import docking.widgets.table.AddRemoveListItem; -import docking.widgets.table.TableSortingContext; -import docking.widgets.table.threaded.*; -import ghidra.program.model.symbol.Symbol; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - -/** - * The symbol table users a {@link ThreadedTableModel}. This model does not correctly function - * with data that can change outside of the table. The symbol table's row objects are the - * {@link Symbol} db objects. These db objects can be changed by the user and by analysis - * while table is loaded. The problem with this is that the table's sort can be broken when - * symbols are to be added, removed or re-inserted, as this process requires a binary search which - * will be broken if the criteria used to sort the data has changed. Effectively, a symbol - * change can break the binary search if that symbol stays in a previously sorted position, but - * has updated data that would put the symbol in a new position if sorted again. For example, - * if the table is sorted on name and the name of a symbol changes, then future uses of the - * binary search will be broken while that symbol is still in the position that matches its old - * name. - *

- * This issue has been around for quite some time. To completely fix this issue, each row object - * of the symbol table would need to be immutable, at least on the sort criteria. We could fix - * this in the future if the *mostly correct* sorting behavior is not good enough. For now, the - * client can trigger a re-sort (e.g., by opening and closing the table) to fix the slightly - * out-of-sort data. - *

- * The likelihood of the sort being inconsistent now relates directly to how many changed symbols - * are in the table at the time of an insert. The more changes symbols, the higher the chance - * of a stale/misplaced symbol being used during a binary search, thus producing an invalid insert - * position. - *

- * This strategy is setup to mitigate the number of invalid symbols in the table at the - * time the inserts are applied. The basic workflow of this algorithm is: - *

- * 1) condense the add / remove requests to remove duplicate efforts
- * 2) process all removes first
- *    --all pure removes
- *    --all removes as part of a re-insert
- * 3) process all items that failed to remove due to the sort data changing
- * 4) process all adds (this step will fail if the data contains mis-sorted items)
- *    --all adds as part of a re-insert
- *    --all pure adds
- * 
- * - * Step 3, processing failed removals, is done to avoid a brute force lookup at each removal - * request. - * - *

This strategy has knowledge of client proxy object usage. The proxy objects - * are coded such that the {@code hashCode()} and {@code equals()} methods will match those - * methods of the data's real objects. - */ -public class SymbolTableAddRemoveStrategy implements TableAddRemoveStrategy { - - @Override - public void process(List> addRemoveList, - TableData tableData, - TaskMonitor monitor) throws CancelledException { - - Set> items = coalesceAddRemoveItems(addRemoveList); - - // - // Hash map the existing values so that we can use any object inside the add/remove list - // as a key into this map to get the matching existing value. Using the existing value - // enables the binary search to work when the add/remove item is a proxy object, but the - // existing item still has the data used to sort it. If the sort data has changed, then - // even this step will not allow the TableData to find the item in a search. - // - Map hashed = new HashMap<>(); - for (SymbolRowObject rowObject : tableData) { - hashed.put(rowObject, rowObject); - } - - Set failedToRemove = new HashSet<>(); - - int n = items.size(); - monitor.setMessage("Removing " + n + " items..."); - monitor.initialize(n); - - Iterator> it = items.iterator(); - while (it.hasNext()) { - AddRemoveListItem item = it.next(); - SymbolRowObject value = item.getValue(); - if (item.isChange()) { - SymbolRowObject toRemove = hashed.remove(value); - remove(tableData, toRemove, failedToRemove); - monitor.incrementProgress(1); - } - else if (item.isRemove()) { - SymbolRowObject toRemove = hashed.remove(value); - remove(tableData, toRemove, failedToRemove); - it.remove(); - } - monitor.checkCanceled(); - } - - if (!failedToRemove.isEmpty()) { - int size = failedToRemove.size(); - String message = size == 1 ? "1 old symbol..." : size + " old symbols..."; - monitor.setMessage("Removing " + message); - - tableData.process((data, sortContext) -> { - return expungeLostItems(failedToRemove, data, sortContext); - }); - } - - n = items.size(); - monitor.setMessage("Adding " + n + " items..."); - it = items.iterator(); - while (it.hasNext()) { - AddRemoveListItem item = it.next(); - SymbolRowObject value = item.getValue(); - if (item.isChange()) { - tableData.insert(value); - hashed.put(value, value); - } - else if (item.isAdd()) { - tableData.insert(value); - hashed.put(value, value); - } - monitor.checkCanceled(); - monitor.incrementProgress(1); - } - - monitor.setMessage("Done adding/removing"); - } - - private Set> coalesceAddRemoveItems( - List> addRemoveList) { - - Map> map = new HashMap<>(); - - for (AddRemoveListItem item : addRemoveList) { - - if (item.isChange()) { - handleChange(item, map); - } - else if (item.isAdd()) { - handleAdd(item, map); - } - else { - handleRemove(item, map); - } - } - - return new HashSet<>(map.values()); - } - - private void handleAdd(AddRemoveListItem item, - Map> map) { - - long id = item.getValue().getID(); - AddRemoveListItem existing = map.get(id); - if (existing == null) { - map.put(id, item); - return; - } - - if (existing.isChange()) { - return; // change -> add; keep the change - } - if (existing.isAdd()) { - return; // already an add - } - - // remove -> add; make a change - map.put(id, new AddRemoveListItem<>(CHANGE, existing.getValue())); - } - - private void handleRemove(AddRemoveListItem item, - Map> map) { - - long id = item.getValue().getID(); - AddRemoveListItem existing = map.get(id); - if (existing == null) { - map.put(id, item); - return; - } - - if (existing.isChange()) { - map.put(id, item); // change -> remove; just do the remove - return; - } - if (existing.isRemove()) { - return; - } - - // add -> remove; do no work - map.remove(id); - } - - private void handleChange(AddRemoveListItem item, - Map> map) { - - long id = item.getValue().getID(); - AddRemoveListItem existing = map.get(id); - if (existing == null) { - map.put(id, item); - return; - } - - if (!existing.isChange()) { - // either add or remove followed by a change; keep the change - map.put(id, item); - } - - // otherwise, we had a change followed by a change; keep just 1 change - } - - private void remove(TableData tableData, SymbolRowObject symbol, - Set failedToRemove) { - if (symbol == null) { - return; - } - - if (!tableData.remove(symbol)) { - failedToRemove.add(symbol); - } - } - - /* - * Removes the given set of items that were unsuccessfully removed from the table as part of - * the add/remove process. These items could not be removed because some part of their - * state has changed such that the binary search performed during the normal remove process - * cannot locate the item in the table data. This algorithm will check the given set of - * items against the entire list of table data, locating the item to be removed. - */ - private List expungeLostItems(Set toRemove, - List data, - TableSortingContext sortContext) { - - if (sortContext.isUnsorted()) { - // this can happen if the data is unsorted and we were asked to remove an item that - // was never in the table for some reason - return data; - } - - // Copy to a new list those items that are not marked for removal. This saves the - // list move its items every time a remove takes place - List newList = new ArrayList<>(data.size() - toRemove.size()); - for (int i = 0; i < data.size(); i++) { - SymbolRowObject rowObject = data.get(i); - if (!toRemove.contains(rowObject)) { - newList.add(rowObject); - } - } - - return newList; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableModel.java index 5b71c4f4a8..319b75ad59 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTableModel.java @@ -4,9 +4,9 @@ * 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. @@ -18,7 +18,6 @@ package ghidra.app.plugin.core.symtable; import java.util.*; import docking.widgets.table.*; -import docking.widgets.table.threaded.TableAddRemoveStrategy; import ghidra.app.cmd.function.DeleteFunctionCmd; import ghidra.app.cmd.label.DeleteLabelCmd; import ghidra.app.cmd.label.RenameLabelCmd; @@ -61,9 +60,6 @@ class SymbolTableModel extends AddressBasedTableModel { private SymbolRowObject lastSymbol; private SymbolFilter filter; - private TableAddRemoveStrategy deletedDbObjectAddRemoveStrategy = - new SymbolTableAddRemoveStrategy(); - SymbolTableModel(SymbolProvider provider, PluginTool tool) { super("Symbols", tool, null, null); this.provider = provider; @@ -91,11 +87,6 @@ class SymbolTableModel extends AddressBasedTableModel { return descriptor; } - @Override - protected TableAddRemoveStrategy getAddRemoveStrategy() { - return deletedDbObjectAddRemoveStrategy; - } - void setFilter(SymbolFilter filter) { this.filter = filter; reload(); @@ -364,12 +355,12 @@ class SymbolTableModel extends AddressBasedTableModel { protected Comparator createSortComparator(int columnIndex) { DynamicTableColumn column = getColumn(columnIndex); if (column instanceof NameTableColumn) { - // note: we use our own name comparator to increase sorting speed for the name - // column. This works because this comparator is called for each *row object* - // allowing the comparator to compare the Symbols based on name instead of + // note: we use our own name comparator to increase sorting speed for the name + // column. This works because this comparator is called for each *row object* + // allowing the comparator to compare the Symbols based on name instead of // having to use the table model's code for getting a column value for the // row object. The code for retrieving a column value is slower than just - // working with the row object directly. See + // working with the row object directly. See // ThreadedTableModel.getCachedColumnValueForRow for more info. return NAME_COL_COMPARATOR; } @@ -436,16 +427,14 @@ class SymbolTableModel extends AddressBasedTableModel { @Override public AddressBasedLocation getValue(SymbolRowObject rowObject, Settings settings, - Program p, - ServiceProvider svcProvider) throws IllegalArgumentException { + Program p, ServiceProvider svcProvider) throws IllegalArgumentException { Symbol symbol = rowObject.getSymbol(); return getSymbolLocation(symbol); } @Override public ProgramLocation getProgramLocation(SymbolRowObject rowObject, Settings settings, - Program p, - ServiceProvider svcProvider) { + Program p, ServiceProvider svcProvider) { Symbol symbol = rowObject.getSymbol(); if (symbol == null || symbol.isDeleted()) { return null; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategyTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategyTest.java deleted file mode 100644 index 3c44514d00..0000000000 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTableAddRemoveStrategyTest.java +++ /dev/null @@ -1,567 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.symtable; - -import static docking.widgets.table.AddRemoveListItem.Type.*; -import static org.junit.Assert.*; - -import java.util.*; - -import org.junit.Before; -import org.junit.Test; - -import docking.widgets.table.*; -import docking.widgets.table.threaded.TestTableData; -import ghidra.program.model.ProgramTestDouble; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.TestAddress; -import ghidra.program.model.listing.Program; -import ghidra.program.model.symbol.*; -import ghidra.program.util.ProgramLocation; -import ghidra.util.task.TaskMonitor; - -public class SymbolTableAddRemoveStrategyTest { - - private static Program DUMMY_PROGRAM = new ProgramTestDouble(); - - private SymbolTableAddRemoveStrategy strategy; - private SpyTableData spyTableData; - private List modelData; - - @Before - public void setUp() throws Exception { - strategy = new SymbolTableAddRemoveStrategy(); - modelData = createModelData(); - spyTableData = createTableData(); - } - - private SpyTableData createTableData() { - Comparator comparator = (s1, s2) -> { - return s1.toString().compareTo(s2.toString()); // based on symbol name - }; - TableSortState sortState = TableSortState.createDefaultSortState(0); - TableSortingContext sortContext = - new TableSortingContext<>(sortState, comparator); - - modelData.sort(comparator); - - return new SpyTableData(modelData, sortContext); - } - - private List createModelData() { - List data = new ArrayList<>(); - data.add(new TestSymbolRowObject(new TestSymbol(1, new TestAddress(101)))); - data.add(new TestSymbolRowObject(new TestSymbol(2, new TestAddress(102)))); - data.add(new TestSymbolRowObject(new TestSymbol(3, new TestAddress(103)))); - data.add(new TestSymbolRowObject(new TestSymbol(4, new TestAddress(104)))); - return data; - } - - @Test - public void testRemove_DifferentInstance_SameId() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = new TestSymbolRowObject(new TestSymbol(1, new TestAddress(101))); - addRemoves.add(new AddRemoveListItem<>(REMOVE, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(0, spyTableData.getInsertCount()); - } - - @Test - public void testInsert_NewSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject newSymbol = - new TestSymbolRowObject(new TestSymbol(10, new TestAddress(1010))); - addRemoves.add(new AddRemoveListItem<>(ADD, newSymbol)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - assertEquals(0, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testInsertAndRemove_NewSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject newSymbol = - new TestSymbolRowObject(new TestSymbol(10, new TestAddress(1010))); - addRemoves.add(new AddRemoveListItem<>(ADD, newSymbol)); - addRemoves.add(new AddRemoveListItem<>(REMOVE, newSymbol)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // no work was done, since the insert was followed by a remove - assertEquals(0, spyTableData.getRemoveCount()); - assertEquals(0, spyTableData.getInsertCount()); - } - - @Test - public void testChange_NewSymbol() throws Exception { - List> addRemoves = new ArrayList<>(); - - SymbolRowObject newSymbol = - new TestSymbolRowObject(new TestSymbol(10, new TestAddress(1010))); - addRemoves.add(new AddRemoveListItem<>(CHANGE, newSymbol)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // no remove, since the symbol was not in the table - assertEquals(0, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testChange_ExisingSymbol() throws Exception { - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(CHANGE, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // no remove, since the symbol was not in the table - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testRemoveAndInsert_NewSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject newSymbol = - new TestSymbolRowObject(new TestSymbol(10, new TestAddress(1010))); - addRemoves.add(new AddRemoveListItem<>(REMOVE, newSymbol)); - addRemoves.add(new AddRemoveListItem<>(ADD, newSymbol)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the remove does not happen, since the time was not in the table - assertEquals(0, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testRemoveAndInsert_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(REMOVE, s)); - addRemoves.add(new AddRemoveListItem<>(ADD, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the remove does not happen, since the time was not in the table - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testChangeAndInsert_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(CHANGE, s)); - addRemoves.add(new AddRemoveListItem<>(ADD, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the insert portions get coalesced - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testChangeAndRemove_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(CHANGE, s)); - addRemoves.add(new AddRemoveListItem<>(REMOVE, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the remove portions get coalesced; no insert takes place - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(0, spyTableData.getInsertCount()); - } - - @Test - public void testChangeAndChange_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(CHANGE, s)); - addRemoves.add(new AddRemoveListItem<>(CHANGE, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the changes get coalesced - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testRemoveAndRemove_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(REMOVE, s)); - addRemoves.add(new AddRemoveListItem<>(REMOVE, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the removes get coalesced - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(0, spyTableData.getInsertCount()); - } - - @Test - public void testInsertAndInsert_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(ADD, s)); - addRemoves.add(new AddRemoveListItem<>(ADD, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the inserts get coalesced - assertEquals(0, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testInsertAndChange_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(ADD, s)); - addRemoves.add(new AddRemoveListItem<>(CHANGE, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the insert portions get coalesced - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testRemoveAndChange_ExistingSymbol() throws Exception { - - List> addRemoves = new ArrayList<>(); - - SymbolRowObject s = modelData.get(0); - addRemoves.add(new AddRemoveListItem<>(REMOVE, s)); - addRemoves.add(new AddRemoveListItem<>(CHANGE, s)); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the remove portions get coalesced - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - - @Test - public void testLostItems_Remove() throws Exception { - - // - // Test that symbols get removed when the data up on which they are sorted changes before - // the removal takes place - // - List> addRemoves = new ArrayList<>(); - - TestSymbol symbol = (TestSymbol) modelData.get(0).getSymbol(); - symbol.setName("UpdatedName"); - addRemoves.add(new AddRemoveListItem<>(REMOVE, new TestSymbolRowObject(symbol))); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the insert portions get coalesced - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(0, spyTableData.getInsertCount()); - } - - @Test - public void testLostItems_Change() throws Exception { - - // - // Test that symbols get removed when the data up on which they are sorted changes before - // the removal takes place - // - List> addRemoves = new ArrayList<>(); - - TestSymbol symbol = (TestSymbol) modelData.get(0).getSymbol(); - symbol.setName("UpdatedName"); - addRemoves.add(new AddRemoveListItem<>(CHANGE, new TestSymbolRowObject(symbol))); - - strategy.process(addRemoves, spyTableData, TaskMonitor.DUMMY); - - // the insert portions get coalesced - assertEquals(1, spyTableData.getRemoveCount()); - assertEquals(1, spyTableData.getInsertCount()); - } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class SpyTableData extends TestTableData { - - private int removeCount; - private int insertCount; - - SpyTableData(List data, TableSortingContext sortContext) { - super(data, sortContext); - } - - @Override - public boolean remove(SymbolRowObject t) { - removeCount++; - return super.remove(t); - } - - @Override - public void insert(SymbolRowObject value) { - insertCount++; - super.insert(value); - } - - int getRemoveCount() { - return removeCount; - } - - int getInsertCount() { - return insertCount; - } - } - - private class TestSymbolRowObject extends SymbolRowObject { - - private TestSymbol sym; - - public TestSymbolRowObject(TestSymbol s) { - super(s); - this.sym = s; - } - - @Override - public Symbol getSymbol() { - return sym; - } - } - - private class TestSymbol implements Symbol { - - private long id; - private Address address; - private String name; - - TestSymbol(long id, Address address) { - this.id = id; - this.address = address; - name = id + "@" + address; - } - - @Override - public long getID() { - return id; - } - - @Override - public Address getAddress() { - return address; - } - - void setName(String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean isDeleted() { - return false; - } - - @Override - public String toString() { - return name; - } - - @Override - public Program getProgram() { - return DUMMY_PROGRAM; - } - - @Override - public SymbolType getSymbolType() { - throw new UnsupportedOperationException(); - } - - @Override - public ProgramLocation getProgramLocation() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isExternal() { - throw new UnsupportedOperationException(); - } - - @Override - public Object getObject() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPrimary() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isValidParent(Namespace parent) { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getPath() { - throw new UnsupportedOperationException(); - } - - @Override - public String getName(boolean includeNamespace) { - throw new UnsupportedOperationException(); - } - - @Override - public Namespace getParentNamespace() { - throw new UnsupportedOperationException(); - } - - @Override - public Symbol getParentSymbol() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isDescendant(Namespace namespace) { - throw new UnsupportedOperationException(); - } - - @Override - public int getReferenceCount() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasMultipleReferences() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasReferences() { - throw new UnsupportedOperationException(); - } - - @Override - public Reference[] getReferences(TaskMonitor monitor) { - throw new UnsupportedOperationException(); - } - - @Override - public Reference[] getReferences() { - throw new UnsupportedOperationException(); - } - - @Override - public void setName(String newName, SourceType source) { - throw new UnsupportedOperationException(); - } - - @Override - public void setNamespace(Namespace newNamespace) { - throw new UnsupportedOperationException(); - } - - @Override - public void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean delete() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPinned() { - throw new UnsupportedOperationException(); - } - - @Override - public void setPinned(boolean pinned) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isDynamic() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setPrimary() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isExternalEntryPoint() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isGlobal() { - throw new UnsupportedOperationException(); - } - - @Override - public void setSource(SourceType source) { - throw new UnsupportedOperationException(); - } - - @Override - public SourceType getSource() { - throw new UnsupportedOperationException(); - } - } -}