Skip to content

Commit

Permalink
feat(gui): limit search to a package (PR #2284)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
andyjsmith authored Sep 20, 2024
1 parent 699ceb1 commit efa2f5d
Show file tree
Hide file tree
Showing 17 changed files with 145 additions and 11 deletions.
4 changes: 4 additions & 0 deletions jadx-core/src/main/java/jadx/api/JavaClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ public String getPackage() {
return cls.getPackage();
}

public JavaPackage getJavaPackage() {
return cls.getPackageNode().getJavaNode();
}

@Override
public JavaClass getDeclaringClass() {
return parent;
Expand Down
16 changes: 16 additions & 0 deletions jadx-core/src/main/java/jadx/api/JavaPackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ public boolean isParentRenamed() {
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;
Expand Down
9 changes: 8 additions & 1 deletion jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.jetbrains.annotations.Nullable;

import jadx.api.JavaPackage;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JResource;

Expand All @@ -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
Expand Down Expand Up @@ -50,6 +53,10 @@ public boolean isIgnoreCase() {
return this.ignoreCase;
}

public JavaPackage getSearchPackage() {
return this.searchPackage;
}

public String getSearchString() {
return this.searchString;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import jadx.api.JadxDecompiler;
import jadx.api.JavaClass;
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ public CommentSearchProvider(MainWindow mw, SearchSettings searchSettings) {

@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;
Expand All @@ -65,6 +62,7 @@ public CommentSearchProvider(MainWindow mw, SearchSettings searchSettings) {
return result;
}
}
return null;
}

@Nullable
Expand All @@ -73,6 +71,10 @@ private JNode isMatch(SearchSettings searchSettings, ICodeComment comment) {
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);
Expand Down
64 changes: 59 additions & 5 deletions jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
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;
Expand Down Expand Up @@ -96,6 +97,12 @@ public static void searchText(MainWindow window, String text) {
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) {
Expand Down Expand Up @@ -130,6 +137,7 @@ public enum SearchOptions {
private transient Color searchFieldDefaultBgColor;

private transient JTextField searchField;
private transient JTextField packageField;

private transient @Nullable SearchTask searchTask;
private transient JButton loadAllButton;
Expand All @@ -142,6 +150,7 @@ public enum SearchOptions {
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<>();
Expand Down Expand Up @@ -210,6 +219,10 @@ protected void openInit() {
searchField.setText(searchText);
searchField.selectAll();
}
String searchPackage = initSearchPackage != null ? initSearchPackage : cache.getLastSearchPackage();
if (searchPackage != null) {
packageField.setText(searchPackage);
}
searchField.requestFocus();
resultsTable.initColumnWidth();

Expand Down Expand Up @@ -278,10 +291,21 @@ private void initUI() {
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));
Expand Down Expand Up @@ -387,15 +411,22 @@ private void initSearchEvents() {
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) {
Expand Down Expand Up @@ -427,7 +458,29 @@ private SearchTask prepareSearch(String text) {
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)) {
Expand Down Expand Up @@ -597,6 +650,7 @@ private void updateTableHighlight() {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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;
Expand All @@ -41,6 +42,7 @@ public JPackagePopupMenu(MainWindow mainWindow, JPackage pkg) {
add(makeExcludeItem());
add(makeRenameMenuItem(pkg));
add(makeExportSubMenu(pkg));
add(makeSearchItem(pkg));
}

private JMenuItem makeRenameMenuItem(JPackage pkg) {
Expand Down Expand Up @@ -132,4 +134,14 @@ public void actionPerformed(ActionEvent e) {
}
});
}

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;
}
}
11 changes: 11 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +31,7 @@ public void reset() {
lastSearch = null;
jNodeCache = new JNodeCache();
lastSearchOptions = new HashMap<>();
lastSearchPackage = null;
decompileBatches = null;
packageHelper = null;
fullDecompilationFinished = false;
Expand All @@ -40,10 +42,19 @@ public String getLastSearch() {
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;
}
Expand Down
2 changes: 2 additions & 0 deletions jadx-gui/src/main/resources/i18n/Messages_de_DE.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions jadx-gui/src/main/resources/i18n/Messages_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions jadx-gui/src/main/resources/i18n/Messages_es_ES.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions jadx-gui/src/main/resources/i18n/Messages_id_ID.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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=다음의 사용 검색 결과:
Expand Down
2 changes: 2 additions & 0 deletions jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading

0 comments on commit efa2f5d

Please sign in to comment.