diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index 8a373fae2..c1978e422 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -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; diff --git a/jadx-core/src/main/java/jadx/api/JavaPackage.java b/jadx-core/src/main/java/jadx/api/JavaPackage.java index d333d5981..4fa16f1d9 100644 --- a/jadx-core/src/main/java/jadx/api/JavaPackage.java +++ b/jadx-core/src/main/java/jadx/api/JavaPackage.java @@ -76,6 +76,22 @@ public final class JavaPackage implements JavaNode, Comparable { 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; diff --git a/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java b/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java index b2d4bcf25..a5574ec78 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java @@ -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; } diff --git a/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java b/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java index 6ed549535..74436af26 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java +++ b/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java @@ -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 classes; + protected final SearchSettings searchSettings; public BaseSearchProvider(MainWindow mw, SearchSettings searchSettings, List 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) { diff --git a/jadx-gui/src/main/java/jadx/gui/search/providers/CommentSearchProvider.java b/jadx-gui/src/main/java/jadx/gui/search/providers/CommentSearchProvider.java index ce3e57db3..363c42ecf 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/providers/CommentSearchProvider.java +++ b/jadx-gui/src/main/java/jadx/gui/search/providers/CommentSearchProvider.java @@ -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 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); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java index 4550176b9..bad2629cf 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java @@ -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 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 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); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JPackagePopupMenu.java b/jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JPackagePopupMenu.java index c4eb78742..66cddbfd3 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JPackagePopupMenu.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/popupmenu/JPackagePopupMenu.java @@ -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; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java b/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java index 8875710b8..6e45dc221 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java @@ -16,6 +16,7 @@ public class CacheObject { private String lastSearch; private JNodeCache jNodeCache; private Map> lastSearchOptions; + private String lastSearchPackage; private List> 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; } diff --git a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties index 9a3d75086..08d9c7b14 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -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: diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index 922bf2249..2af3ba065 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -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: diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 39e0c23b0..4bfc85386 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -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: diff --git a/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties b/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties index 15b32ef15..57a48e546 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties @@ -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: diff --git a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties index 6f51ae66c..0272d8663 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -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=다음의 사용 검색 결과: diff --git a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties index 00940d852..c1ba3e677 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties @@ -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: diff --git a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties index 58f449271..3ed839545 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties @@ -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=Использования: diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index c40ad83c4..751bb484b 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -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=查找用例: diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties index 6381f386b..4317d0556 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties @@ -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=使用情況: