feat(gui): limit search to a package (PR #2284)
* Add isDescendantOf and getJavaPackage helper functions * Add i18n strings for search package * Added search package to options in SearchSettings * Add package limiting to each search provider * Add package search to dialog and logic to get package by string. * Added search option to package context menu * Fix spotlessJavaCheck complaints * Revert changes to individual search providers and add filter to base provider
This commit is contained in:
@@ -252,6 +252,10 @@ public final class JavaClass implements JavaNode {
|
||||
return cls.getPackage();
|
||||
}
|
||||
|
||||
public JavaPackage getJavaPackage() {
|
||||
return cls.getPackageNode().getJavaNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return parent;
|
||||
|
||||
@@ -76,6 +76,22 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||
return !Objects.equals(parent, aliasParent);
|
||||
}
|
||||
|
||||
public boolean isDescendantOf(JavaPackage ancestor) {
|
||||
JavaPackage current = this;
|
||||
while (current != null) {
|
||||
if (ancestor.equals(current)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (current.getPkgNode().getParentPkg() == null) {
|
||||
current = null;
|
||||
} else {
|
||||
current = current.getPkgNode().getParentPkg().getJavaNode();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICodeNodeRef getCodeNodeRef() {
|
||||
return pkgNode;
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
|
||||
@@ -12,16 +13,18 @@ public class SearchSettings {
|
||||
private final String searchString;
|
||||
private final boolean useRegex;
|
||||
private final boolean ignoreCase;
|
||||
private final JavaPackage searchPackage;
|
||||
|
||||
private JClass activeCls;
|
||||
private JResource activeResource;
|
||||
private Pattern regexPattern;
|
||||
private ISearchMethod searchMethod;
|
||||
|
||||
public SearchSettings(String searchString, boolean ignoreCase, boolean useRegex) {
|
||||
public SearchSettings(String searchString, boolean ignoreCase, boolean useRegex, JavaPackage searchPackage) {
|
||||
this.searchString = searchString;
|
||||
this.useRegex = useRegex;
|
||||
this.ignoreCase = ignoreCase;
|
||||
this.searchPackage = searchPackage;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -50,6 +53,10 @@ public class SearchSettings {
|
||||
return this.ignoreCase;
|
||||
}
|
||||
|
||||
public JavaPackage getSearchPackage() {
|
||||
return this.searchPackage;
|
||||
}
|
||||
|
||||
public String getSearchString() {
|
||||
return this.searchString;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package jadx.gui.search.providers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
@@ -22,13 +23,22 @@ public abstract class BaseSearchProvider implements ISearchProvider {
|
||||
protected final ISearchMethod searchMth;
|
||||
protected final String searchStr;
|
||||
protected final List<JavaClass> classes;
|
||||
protected final SearchSettings searchSettings;
|
||||
|
||||
public BaseSearchProvider(MainWindow mw, SearchSettings searchSettings, List<JavaClass> classes) {
|
||||
this.nodeCache = mw.getCacheObject().getNodeCache();
|
||||
this.decompiler = mw.getWrapper().getDecompiler();
|
||||
this.searchMth = searchSettings.getSearchMethod();
|
||||
this.searchStr = searchSettings.getSearchString();
|
||||
this.classes = classes;
|
||||
if (searchSettings.getSearchPackage() != null) {
|
||||
this.classes = classes
|
||||
.stream()
|
||||
.filter(c -> c.getJavaPackage().isDescendantOf(searchSettings.getSearchPackage()))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
this.classes = classes;
|
||||
}
|
||||
this.searchSettings = searchSettings;
|
||||
}
|
||||
|
||||
protected boolean isMatch(String str) {
|
||||
|
||||
@@ -51,10 +51,7 @@ public class CommentSearchProvider implements ISearchProvider {
|
||||
|
||||
@Override
|
||||
public @Nullable JNode next(Cancelable cancelable) {
|
||||
while (true) {
|
||||
if (cancelable.isCanceled()) {
|
||||
return null;
|
||||
}
|
||||
while (!cancelable.isCanceled()) {
|
||||
List<ICodeComment> comments = project.getCodeData().getComments();
|
||||
if (progress >= comments.size()) {
|
||||
return null;
|
||||
@@ -65,6 +62,7 @@ public class CommentSearchProvider implements ISearchProvider {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -73,6 +71,10 @@ public class CommentSearchProvider implements ISearchProvider {
|
||||
if (all || searchSettings.isMatch(comment.getComment())) {
|
||||
JNode refNode = getRefNode(comment);
|
||||
if (refNode != null) {
|
||||
if (searchSettings.getSearchPackage() != null
|
||||
&& !refNode.getRootClass().getCls().getJavaPackage().isDescendantOf(searchSettings.getSearchPackage())) {
|
||||
return null;
|
||||
}
|
||||
JClass activeCls = searchSettings.getActiveCls();
|
||||
if (activeCls == null || Objects.equals(activeCls, refNode.getRootClass())) {
|
||||
return getCommentNode(comment, refNode);
|
||||
|
||||
@@ -41,6 +41,7 @@ import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.gui.jobs.ITaskInfo;
|
||||
import jadx.gui.jobs.ITaskProgress;
|
||||
@@ -96,6 +97,12 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
show(searchDialog, window);
|
||||
}
|
||||
|
||||
public static void searchPackage(MainWindow window, String packageName) {
|
||||
SearchDialog searchDialog = new SearchDialog(window, SearchPreset.TEXT, Collections.emptySet());
|
||||
searchDialog.initSearchPackage = packageName;
|
||||
show(searchDialog, window);
|
||||
}
|
||||
|
||||
private static void show(SearchDialog searchDialog, MainWindow mw) {
|
||||
mw.addLoadListener(loaded -> {
|
||||
if (!loaded) {
|
||||
@@ -130,6 +137,7 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
private transient Color searchFieldDefaultBgColor;
|
||||
|
||||
private transient JTextField searchField;
|
||||
private transient JTextField packageField;
|
||||
|
||||
private transient @Nullable SearchTask searchTask;
|
||||
private transient JButton loadAllButton;
|
||||
@@ -142,6 +150,7 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
private transient ChangeListener activeTabListener;
|
||||
|
||||
private transient String initSearchText = null;
|
||||
private transient String initSearchPackage = null;
|
||||
|
||||
// temporal list for pending results
|
||||
private final List<JNode> pendingResults = new ArrayList<>();
|
||||
@@ -210,6 +219,10 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
searchField.setText(searchText);
|
||||
searchField.selectAll();
|
||||
}
|
||||
String searchPackage = initSearchPackage != null ? initSearchPackage : cache.getLastSearchPackage();
|
||||
if (searchPackage != null) {
|
||||
packageField.setText(searchPackage);
|
||||
}
|
||||
searchField.requestFocus();
|
||||
resultsTable.initColumnWidth();
|
||||
|
||||
@@ -278,10 +291,21 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
searchOptions.add(makeOptionsCheckBox(NLS.str("search_dialog.regex"), USE_REGEX));
|
||||
searchOptions.add(makeOptionsCheckBox(NLS.str("search_dialog.active_tab"), SearchOptions.ACTIVE_TAB));
|
||||
|
||||
packageField = new JTextField();
|
||||
packageField.setAlignmentX(LEFT_ALIGNMENT);
|
||||
packageField.setPreferredSize(new Dimension(300, packageField.getPreferredSize().height));
|
||||
TextStandardActions.attach(packageField);
|
||||
packageField.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);
|
||||
|
||||
JPanel searchPackageOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
searchPackageOptions.setBorder(BorderFactory.createTitledBorder(NLS.str("search_dialog.limit_package")));
|
||||
searchPackageOptions.add(packageField);
|
||||
|
||||
JPanel optionsPanel = new JPanel(new WrapLayout(WrapLayout.LEFT, 0, 0));
|
||||
optionsPanel.setAlignmentX(LEFT_ALIGNMENT);
|
||||
optionsPanel.add(searchInPanel);
|
||||
optionsPanel.add(searchOptions);
|
||||
optionsPanel.add(searchPackageOptions);
|
||||
|
||||
JPanel searchPane = new JPanel();
|
||||
searchPane.setLayout(new BoxLayout(searchPane, BoxLayout.PAGE_AXIS));
|
||||
@@ -387,15 +411,22 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
searchEmitter = new SearchEventEmitter();
|
||||
Flowable<String> searchEvents;
|
||||
if (mainWindow.getSettings().isUseAutoSearch()) {
|
||||
searchEvents = Flowable.merge(RxUtils.textFieldChanges(searchField),
|
||||
RxUtils.textFieldEnterPress(searchField), searchEmitter.getFlowable());
|
||||
searchEvents = Flowable.merge(List.of(
|
||||
RxUtils.textFieldChanges(searchField),
|
||||
RxUtils.textFieldEnterPress(searchField),
|
||||
RxUtils.textFieldChanges(packageField),
|
||||
RxUtils.textFieldEnterPress(packageField),
|
||||
searchEmitter.getFlowable()));
|
||||
} else {
|
||||
searchEvents = Flowable.merge(RxUtils.textFieldEnterPress(searchField), searchEmitter.getFlowable());
|
||||
searchEvents = Flowable.merge(
|
||||
RxUtils.textFieldEnterPress(searchField),
|
||||
RxUtils.textFieldEnterPress(packageField),
|
||||
searchEmitter.getFlowable());
|
||||
}
|
||||
searchDisposable = searchEvents
|
||||
.debounce(50, TimeUnit.MILLISECONDS)
|
||||
.observeOn(Schedulers.from(searchBackgroundExecutor))
|
||||
.subscribe(this::search);
|
||||
.subscribe(t -> this.search(searchField.getText()));
|
||||
}
|
||||
|
||||
private void search(String text) {
|
||||
@@ -427,7 +458,29 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
LOG.debug("Building search for '{}', options: {}", text, options);
|
||||
boolean ignoreCase = options.contains(IGNORE_CASE);
|
||||
boolean useRegex = options.contains(USE_REGEX);
|
||||
SearchSettings searchSettings = new SearchSettings(text, ignoreCase, useRegex);
|
||||
|
||||
// Find the JavaPackage for the searched package string
|
||||
String packageText = packageField.getText();
|
||||
JavaPackage searchPackage = null;
|
||||
if (!packageText.isBlank()) {
|
||||
searchPackage = mainWindow
|
||||
.getWrapper()
|
||||
.getPackages()
|
||||
.stream()
|
||||
.filter(p -> p.getFullName().equals(packageText))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (searchPackage == null) {
|
||||
resultsInfoLabel.setText(NLS.str("search_dialog.package_not_found"));
|
||||
packageField.setBackground(SEARCH_FIELD_ERROR_COLOR);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (Objects.equals(packageField.getBackground(), SEARCH_FIELD_ERROR_COLOR)) {
|
||||
packageField.setBackground(searchFieldDefaultBgColor);
|
||||
}
|
||||
|
||||
SearchSettings searchSettings = new SearchSettings(text, ignoreCase, useRegex, searchPackage);
|
||||
String error = searchSettings.prepare();
|
||||
if (error == null) {
|
||||
if (Objects.equals(searchField.getBackground(), SEARCH_FIELD_ERROR_COLOR)) {
|
||||
@@ -597,6 +650,7 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
String text = searchField.getText();
|
||||
updateHighlightContext(text, !options.contains(IGNORE_CASE), options.contains(USE_REGEX), false);
|
||||
cache.setLastSearch(text);
|
||||
cache.setLastSearchPackage(packageField.getText());
|
||||
cache.getLastSearchOptions().put(searchPreset, options);
|
||||
if (!mainWindow.getSettings().isUseAutoSearch()) {
|
||||
mainWindow.getProject().addToSearchHistory(text);
|
||||
|
||||
@@ -21,6 +21,7 @@ import jadx.gui.treemodel.JPackage;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.dialog.ExcludePkgDialog;
|
||||
import jadx.gui.ui.dialog.RenameDialog;
|
||||
import jadx.gui.ui.dialog.SearchDialog;
|
||||
import jadx.gui.ui.filedialog.FileDialogWrapper;
|
||||
import jadx.gui.ui.filedialog.FileOpenMode;
|
||||
import jadx.gui.utils.NLS;
|
||||
@@ -41,6 +42,7 @@ public class JPackagePopupMenu extends JPopupMenu {
|
||||
add(makeExcludeItem());
|
||||
add(makeRenameMenuItem(pkg));
|
||||
add(makeExportSubMenu(pkg));
|
||||
add(makeSearchItem(pkg));
|
||||
}
|
||||
|
||||
private JMenuItem makeRenameMenuItem(JPackage pkg) {
|
||||
@@ -132,4 +134,14 @@ public class JPackagePopupMenu extends JPopupMenu {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private JMenuItem makeSearchItem(JPackage pkg) {
|
||||
JMenuItem searchItem = new JMenuItem(NLS.str("menu.text_search"));
|
||||
searchItem.addActionListener(e -> {
|
||||
String fullName = pkg.getPkg().getFullName();
|
||||
LOG.debug("Searching package: {}", fullName);
|
||||
SearchDialog.searchPackage(mainWindow, fullName);
|
||||
});
|
||||
return searchItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ public class CacheObject {
|
||||
private String lastSearch;
|
||||
private JNodeCache jNodeCache;
|
||||
private Map<SearchDialog.SearchPreset, Set<SearchDialog.SearchOptions>> lastSearchOptions;
|
||||
private String lastSearchPackage;
|
||||
|
||||
private List<List<JavaClass>> decompileBatches;
|
||||
private PackageHelper packageHelper;
|
||||
@@ -30,6 +31,7 @@ public class CacheObject {
|
||||
lastSearch = null;
|
||||
jNodeCache = new JNodeCache();
|
||||
lastSearchOptions = new HashMap<>();
|
||||
lastSearchPackage = null;
|
||||
decompileBatches = null;
|
||||
packageHelper = null;
|
||||
fullDecompilationFinished = false;
|
||||
@@ -40,10 +42,19 @@ public class CacheObject {
|
||||
return lastSearch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLastSearchPackage() {
|
||||
return lastSearchPackage;
|
||||
}
|
||||
|
||||
public void setLastSearch(String lastSearch) {
|
||||
this.lastSearch = lastSearch;
|
||||
}
|
||||
|
||||
public void setLastSearchPackage(String lastSearchPackage) {
|
||||
this.lastSearchPackage = lastSearchPackage;
|
||||
}
|
||||
|
||||
public JNodeCache getNodeCache() {
|
||||
return jNodeCache;
|
||||
}
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=Kommentare
|
||||
search_dialog.resource=Ressourcen
|
||||
search_dialog.keep_open=Offen halten
|
||||
search_dialog.tip_searching=Suchen…
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=Verwendungssuche
|
||||
usage_dialog.label=Verwendungen von:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=Comments
|
||||
search_dialog.resource=Resource
|
||||
search_dialog.keep_open=Keep open
|
||||
search_dialog.tip_searching=Searching
|
||||
search_dialog.limit_package=Limit to package:
|
||||
search_dialog.package_not_found=No matching package found
|
||||
|
||||
usage_dialog.title=Usage search
|
||||
usage_dialog.label=Usage for:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.regex=Regex
|
||||
#search_dialog.resource=
|
||||
#search_dialog.keep_open=
|
||||
#search_dialog.tip_searching=
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=Usage search
|
||||
usage_dialog.label=Usage for:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=Komentar
|
||||
search_dialog.resource=Sumber daya
|
||||
search_dialog.keep_open=Tetap terbuka
|
||||
search_dialog.tip_searching=Mencari
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=Pencarian penggunaan
|
||||
usage_dialog.label=Penggunaan untuk:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=주석
|
||||
search_dialog.resource=리소스
|
||||
search_dialog.keep_open=열어 두기
|
||||
search_dialog.tip_searching=검색 중...
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=사용 검색
|
||||
usage_dialog.label=다음의 사용 검색 결과:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=Comentários
|
||||
search_dialog.resource=Recursos
|
||||
search_dialog.keep_open=Manter aberto
|
||||
search_dialog.tip_searching=Buscando
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=Busca por utilização
|
||||
usage_dialog.label=Usado por:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=Комментарии
|
||||
search_dialog.resource=Ресурсы
|
||||
search_dialog.keep_open=Оставлять поиск открытым
|
||||
search_dialog.tip_searching=Поиск...
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=Поиск использований
|
||||
usage_dialog.label=Использования:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=注释
|
||||
search_dialog.resource=资源
|
||||
search_dialog.keep_open=保持窗口
|
||||
search_dialog.tip_searching=搜索中…
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=查找
|
||||
usage_dialog.label=查找用例:
|
||||
|
||||
@@ -152,6 +152,8 @@ search_dialog.comments=註解
|
||||
search_dialog.resource=資源
|
||||
search_dialog.keep_open=保持開啟
|
||||
search_dialog.tip_searching=正在搜尋
|
||||
#search_dialog.limit_package=
|
||||
#search_dialog.package_not_found=
|
||||
|
||||
usage_dialog.title=使用情況搜尋
|
||||
usage_dialog.label=使用情況:
|
||||
|
||||
Reference in New Issue
Block a user