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: *

    *
  1. {@link org.apache.commons.collections4.CollectionUtils}
  2. *
  3. {@link IterableUtils}
  4. *
  5. {@link IteratorUtils}
  6. *
  7. {@link StringUtils#join(Iterable, char)} - for pretty printing collections with newlines
  8. - *
  9. Apache CollectionUtils.collect(Collection, Transformer) - to turn a + *
  10. Apache CollectionUtils.collect(Collection, Transformer) - to turn a * collection in to collection of strings when the default toString() is lacking
  11. *
*/ @@ -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(); + } +}