diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
index 7432ce4ce1..4c78312404 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
@@ -49,6 +49,7 @@ import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.program.util.*;
import ghidra.util.Msg;
+import ghidra.util.task.CancellableIterator;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
@@ -184,22 +185,22 @@ public class CodeBrowserClipboardProvider extends ByteCopier
public Transferable copySpecial(ClipboardType copyType, TaskMonitor monitor) {
if (copyType == ADDRESS_TEXT_TYPE) {
- return copyAddress();
+ return copyAddress(monitor);
}
else if (copyType == ADDRESS_TEXT_WITH_OFFSET_TYPE) {
- return copySymbolString();
+ return copySymbolString(monitor);
}
else if (copyType == CODE_TEXT_TYPE) {
return copyCode(monitor);
}
else if (copyType == LABELS_COMMENTS_TYPE) {
- return copyLabelsComments(true, true);
+ return copyLabelsComments(true, true, monitor);
}
else if (copyType == LABELS_TYPE) {
- return copyLabelsComments(true, false);
+ return copyLabelsComments(true, false, monitor);
}
else if (copyType == COMMENTS_TYPE) {
- return copyLabelsComments(false, true);
+ return copyLabelsComments(false, true, monitor);
}
return copyBytes(copyType, monitor);
@@ -308,17 +309,18 @@ public class CodeBrowserClipboardProvider extends ByteCopier
return new NonLabelStringTransferable(location.getOperandRepresentation());
}
- private Transferable copyAddress() {
+ private Transferable copyAddress(TaskMonitor monitor) {
AddressSetView addrs = getSelectedAddresses();
- Iterable
it = addrs.getAddresses(true);
+ Iterable iterable = addrs.getAddresses(true);
+ CancellableIterator it = new CancellableIterator<>(iterable.iterator(), monitor);
return createStringTransferable(StringUtils.join(it, "\n"));
}
- private Transferable copySymbolString() {
+ private Transferable copySymbolString(TaskMonitor monitor) {
Listing listing = currentProgram.getListing();
List strings = new ArrayList<>();
CodeUnitIterator codeUnits = listing.getCodeUnits(getSelectedAddresses(), true);
- while (codeUnits.hasNext()) {
+ while (codeUnits.hasNext() && !monitor.isCancelled()) {
CodeUnit cu = codeUnits.next();
Address addr = cu.getAddress();
Function function = listing.getFunctionContaining(addr);
@@ -379,11 +381,12 @@ public class CodeBrowserClipboardProvider extends ByteCopier
return createStringTransferable(copyBytesAsString(set, false, TaskMonitor.DUMMY));
}
- private CodeUnitInfoTransferable copyLabelsComments(boolean copyLabels, boolean copyComments) {
+ private CodeUnitInfoTransferable copyLabelsComments(boolean copyLabels, boolean copyComments,
+ TaskMonitor monitor) {
AddressSetView addressSet = getSelectedAddresses();
List list = new ArrayList<>();
Address startAddr = addressSet.getMinAddress();
- getCodeUnitInfo(addressSet, startAddr, list, copyLabels, copyComments);
+ getCodeUnitInfo(addressSet, startAddr, list, copyLabels, copyComments, monitor);
return new CodeUnitInfoTransferable(list);
}
@@ -489,23 +492,23 @@ public class CodeBrowserClipboardProvider extends ByteCopier
}
private void getCodeUnitInfo(AddressSetView set, Address startAddr, List list,
- boolean copyLabels, boolean copyComments) {
+ boolean copyLabels, boolean copyComments, TaskMonitor monitor) {
Map map = new HashMap<>();
if (copyLabels) {
- getFunctions(startAddr, set, list, map);
- getLabels(startAddr, set, list, map);
+ getFunctions(startAddr, set, list, map, monitor);
+ getLabels(startAddr, set, list, map, monitor);
}
if (copyComments) {
- getComments(startAddr, set, list, map);
+ getComments(startAddr, set, list, map, monitor);
}
}
private void getFunctions(Address startAddr, AddressSetView set, List list,
- Map map) {
+ Map map, TaskMonitor monitor) {
- FunctionIterator iter = currentProgram.getListing().getFunctions(set, true);
- while (iter.hasNext()) {
- Function function = iter.next();
+ FunctionIterator it = currentProgram.getListing().getFunctions(set, true);
+ while (it.hasNext() && !monitor.isCancelled()) {
+ Function function = it.next();
Address entry = function.getEntryPoint();
CodeUnitInfo info = getInfoFromMap(list, map, entry, startAddr);
info.setFunction(function);
@@ -513,13 +516,12 @@ public class CodeBrowserClipboardProvider extends ByteCopier
}
private void getComments(Address startAddr, AddressSetView set, List list,
- Map map) {
+ Map map, TaskMonitor monitor) {
- CodeUnitIterator iter =
- currentProgram.getListing().getCodeUnitIterator(CodeUnit.COMMENT_PROPERTY, set, true);
-
- while (iter.hasNext()) {
- CodeUnit cu = iter.next();
+ Listing listing = currentProgram.getListing();
+ CodeUnitIterator it = listing.getCodeUnitIterator(CodeUnit.COMMENT_PROPERTY, set, true);
+ while (it.hasNext() && !monitor.isCancelled()) {
+ CodeUnit cu = it.next();
Address minAddress = cu.getMinAddress();
CodeUnitInfo info = getInfoFromMap(list, map, minAddress, startAddr);
setCommentInfo(cu, info);
@@ -537,12 +539,11 @@ public class CodeBrowserClipboardProvider extends ByteCopier
}
private void getLabels(Address startAddr, AddressSetView set, List list,
- Map map) {
+ Map map, TaskMonitor monitor) {
- SymbolIterator iter = currentProgram.getSymbolTable().getPrimarySymbolIterator(set, true);
-
- while (iter.hasNext()) {
- Symbol symbol = iter.next();
+ SymbolIterator it = currentProgram.getSymbolTable().getPrimarySymbolIterator(set, true);
+ while (it.hasNext() && !monitor.isCancelled()) {
+ Symbol symbol = it.next();
Address minAddress = symbol.getAddress();
Symbol[] symbols = currentProgram.getSymbolTable().getSymbols(minAddress);
CodeUnitInfo info = getInfoFromMap(list, map, minAddress, startAddr);
diff --git a/Ghidra/Framework/Generic/src/main/java/util/CollectionUtils.java b/Ghidra/Framework/Generic/src/main/java/util/CollectionUtils.java
index 48205ef2f3..756368fadb 100644
--- a/Ghidra/Framework/Generic/src/main/java/util/CollectionUtils.java
+++ b/Ghidra/Framework/Generic/src/main/java/util/CollectionUtils.java
@@ -24,20 +24,23 @@ import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
+import ghidra.util.task.CancellableIterator;
+import ghidra.util.task.TaskMonitor;
+
/**
* A collection of utility methods that prevent you from having to do unsafe casts of
* {@link Collection} classes due to runtime type erasure.
- *
- * Be sure to check Apache collection utils before using this class, as it's a
+ *
+ *
Be sure to check Apache collection utils before using this class, as it's a
* standard utility and often more efficient.
- *
+ *
*
Some examples:
*
* - {@link org.apache.commons.collections4.CollectionUtils}
* - {@link IterableUtils}
* - {@link IteratorUtils}
* - {@link StringUtils#join(Iterable, char)} - for pretty printing collections with newlines
- * Apache CollectionUtils.collect(Collection, Transformer)
- to turn a
+ * Apache CollectionUtils.collect(Collection, Transformer)
- to turn a
* collection in to collection of strings when the default toString()
is lacking
*
*/
@@ -50,7 +53,7 @@ public class CollectionUtils {
/**
* Turns the given items into a set. If there is only a single item and it is null, then
* an empty set will be returned.
- *
+ *
* @param items the items to put in the set
* @return the list of items
*/
@@ -85,8 +88,8 @@ public class CollectionUtils {
/**
* Drains the given iterator into a new Set
- *
- * @param it the iterator
+ *
+ * @param it the iterator
* @return the set
*/
public static Set asSet(Iterator it) {
@@ -104,9 +107,9 @@ public class CollectionUtils {
/**
* Turns the given iterable into a new Set, returning it directly if it is a set, draining
- * it into a set if it is not already.
- *
- * @param iterable the iterable
+ * it into a set if it is not already.
+ *
+ * @param iterable the iterable
* @return the set
*/
public static Set asSet(Iterable iterable) {
@@ -129,9 +132,9 @@ public class CollectionUtils {
/**
* Similar to {@link Arrays#asList(Object...)}, except that this method will turn a single
- * null parameter into an empty list. Also, this method creates a new, mutable array,
+ * null parameter into an empty list. Also, this method creates a new, mutable array,
* whereas the former's array is not mutable.
- *
+ *
* @param items the items to add to the list
* @return the list
*/
@@ -361,7 +364,7 @@ public class CollectionUtils {
/**
* Returns true if the given array is null or has 0 length
- *
+ *
* @param c the collection to check
* @return true if blank
*/
@@ -371,7 +374,7 @@ public class CollectionUtils {
/**
* Returns true if the given array is null or has 0 length
- *
+ *
* @param t the items to check
* @return true if blank
*/
@@ -399,9 +402,9 @@ public class CollectionUtils {
}
/**
- * Combines all collections passed-in into a pass-through not creating a new collection)
+ * Combines all collections passed-in into a pass-through (not creating a new collection)
* Iterable.
- *
+ *
* @param iterables the iterables to combine
* @return the iterable
*/
@@ -411,9 +414,26 @@ public class CollectionUtils {
return asIterable(s.iterator());
}
+ /**
+ * Combines all collections passed-in into a pass-through (not creating a new collection)
+ * Iterable that uses the given task monitor.
+ *
+ * @param monitor a task monitor that allows for cancelling iteration
+ * @param iterables the iterables to combine
+ * @return the iterable
+ */
+ @SafeVarargs
+ public static Iterable asCancellableIterable(TaskMonitor monitor,
+ Iterable... iterables) {
+ Stream s = asStream(iterables);
+ Iterator it = s.iterator();
+ CancellableIterator cancellable = new CancellableIterator<>(it, monitor);
+ return asIterable(cancellable);
+ }
+
/**
* Turns the given iterator into a stream
- *
+ *
* @param iterator the iterator
* @return the stream
*/
@@ -422,15 +442,15 @@ public class CollectionUtils {
}
/**
- * Combines all iterables passed-in into a pass-through (not creating a new collection) Stream.
- *
- * @param iterables the iterables to combine
+ * Combines all iterables passed-in into a pass-through (not creating a new collection) Stream.
+ *
+ * @param iterables the iterables to combine
* @return the stream
*/
@SafeVarargs
public static Stream asStream(Iterable... iterables) {
- Stream s = Stream.of(iterables)
- .flatMap(e -> StreamSupport.stream(e.spliterator(), false));
+ Stream s =
+ Stream.of(iterables).flatMap(e -> StreamSupport.stream(e.spliterator(), false));
return s;
}
diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/util/task/CancellableIterator.java b/Ghidra/Framework/Utility/src/main/java/ghidra/util/task/CancellableIterator.java
new file mode 100644
index 0000000000..a2357309ed
--- /dev/null
+++ b/Ghidra/Framework/Utility/src/main/java/ghidra/util/task/CancellableIterator.java
@@ -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 ghidra.util.task;
+
+import java.util.Iterator;
+import java.util.Objects;
+
+/**
+ * An {@link Iterator} wrapper that allows clients to use a task monitor to cancel iteration
+ *
+ * @param the type
+ */
+public class CancellableIterator implements Iterator {
+
+ private Iterator delegate;
+ private TaskMonitor monitor;
+
+ public CancellableIterator(Iterator delegate, TaskMonitor monitor) {
+ this.delegate = Objects.requireNonNull(delegate);
+ this.monitor = Objects.requireNonNull(monitor);
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (monitor.isCancelled()) {
+ return false;
+ }
+ return delegate.hasNext();
+ }
+
+ @Override
+ public T next() {
+ return delegate.next();
+ }
+}