Skip to content

Commit bc4db61

Browse files
committed
fix(gui): improve resources search (#1648)
1 parent c7e6e28 commit bc4db61

5 files changed

Lines changed: 102 additions & 152 deletions

File tree

jadx-gui/src/main/java/jadx/gui/plugins/quark/QuarkManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ private void startAnalysis() {
107107
private void loadReport() {
108108
try {
109109
QuarkReportNode quarkNode = new QuarkReportNode(reportFile);
110-
JRoot root = mainWindow.getCacheObject().getJRoot();
110+
JRoot root = mainWindow.getTreeRoot();
111111
root.replaceCustomNode(quarkNode);
112112
root.update();
113113
mainWindow.reloadTree();
Lines changed: 96 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package jadx.gui.search.providers;
22

3-
import java.io.File;
4-
import java.io.IOException;
5-
import java.util.ArrayList;
3+
import java.util.ArrayDeque;
4+
import java.util.Deque;
5+
import java.util.Enumeration;
66
import java.util.HashSet;
7-
import java.util.List;
87
import java.util.Set;
9-
import java.util.zip.ZipEntry;
10-
import java.util.zip.ZipFile;
118

129
import javax.swing.tree.TreeNode;
1310

@@ -18,56 +15,55 @@
1815
import jadx.api.ICodeWriter;
1916
import jadx.api.ResourceFile;
2017
import jadx.api.ResourceType;
21-
import jadx.core.utils.files.FileUtils;
18+
import jadx.api.plugins.utils.CommonFileUtils;
2219
import jadx.gui.jobs.Cancelable;
2320
import jadx.gui.search.ISearchProvider;
2421
import jadx.gui.search.SearchSettings;
2522
import jadx.gui.treemodel.JNode;
2623
import jadx.gui.treemodel.JResSearchNode;
2724
import jadx.gui.treemodel.JResource;
25+
import jadx.gui.treemodel.JRoot;
2826
import jadx.gui.ui.MainWindow;
29-
import jadx.gui.utils.CacheObject;
3027

3128
public class ResourceSearchProvider implements ISearchProvider {
3229
private static final Logger LOG = LoggerFactory.getLogger(ResourceSearchProvider.class);
3330

34-
private final CacheObject cache;
3531
private final SearchSettings searchSettings;
36-
private final Set<String> extSet = new HashSet<>();
37-
38-
private List<JResource> resNodes;
39-
private String fileExts;
32+
private final Set<String> extSet;
33+
private final int sizeLimit;
4034
private boolean anyExt;
41-
private int sizeLimit;
4235

43-
private int progress;
36+
/**
37+
* Resources queue for process. Using UI nodes to reuse loading cache
38+
*/
39+
private final Deque<JResource> resQueue;
4440
private int pos;
4541

4642
public ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings) {
47-
this.cache = mw.getCacheObject();
4843
this.searchSettings = searchSettings;
44+
this.sizeLimit = mw.getSettings().getSrhResourceSkipSize() * 1048576;
45+
this.extSet = buildAllowedFilesExtensions(mw.getSettings().getSrhResourceFileExt());
46+
this.resQueue = initResQueue(mw);
4947
}
5048

5149
@Override
5250
public @Nullable JNode next(Cancelable cancelable) {
53-
if (resNodes == null) {
54-
load();
55-
}
56-
if (resNodes.isEmpty()) {
57-
return null;
58-
}
5951
while (true) {
6052
if (cancelable.isCanceled()) {
6153
return null;
6254
}
63-
JResource resNode = resNodes.get(progress);
55+
JResource resNode = getNextNode();
56+
if (resNode == null) {
57+
return null;
58+
}
6459
JNode newResult = search(resNode);
6560
if (newResult != null) {
6661
return newResult;
6762
}
68-
progress++;
6963
pos = 0;
70-
if (progress >= resNodes.size()) {
64+
resQueue.removeLast();
65+
addChildren(resQueue, resNode);
66+
if (resQueue.isEmpty()) {
7167
return null;
7268
}
7369
}
@@ -94,133 +90,110 @@ private JNode search(JResource resNode) {
9490
return new JResSearchNode(resNode, line.trim(), newPos);
9591
}
9692

97-
private synchronized void load() {
98-
resNodes = new ArrayList<>();
99-
sizeLimit = cache.getJadxSettings().getSrhResourceSkipSize() * 1048576;
100-
fileExts = cache.getJadxSettings().getSrhResourceFileExt();
101-
for (String extStr : fileExts.split("\\|")) {
102-
String ext = extStr.trim();
103-
if (!ext.isEmpty()) {
104-
anyExt = ext.equals("*");
105-
if (anyExt) {
106-
break;
107-
}
108-
extSet.add(ext);
109-
}
93+
private @Nullable JResource getNextNode() {
94+
JResource node = resQueue.peekLast();
95+
if (node == null) {
96+
return null;
11097
}
111-
try (ZipFile zipFile = getZipFile(cache.getJRoot())) {
112-
traverseTree(cache.getJRoot(), zipFile); // reindex
98+
try {
99+
node.loadNode();
113100
} catch (Exception e) {
114-
LOG.error("Failed to apply settings to resource index", e);
101+
LOG.error("Error load resource node: {}", node, e);
102+
resQueue.removeLast();
103+
return getNextNode();
115104
}
105+
if (node.getType() == JResource.JResType.FILE) {
106+
if (shouldProcess(node)) {
107+
return node;
108+
}
109+
resQueue.removeLast();
110+
return getNextNode();
111+
}
112+
// dit or root
113+
resQueue.removeLast();
114+
addChildren(resQueue, node);
115+
return getNextNode();
116116
}
117117

118-
private void traverseTree(TreeNode root, @Nullable ZipFile zip) {
119-
for (int i = 0; i < root.getChildCount(); i++) {
120-
TreeNode node = root.getChildAt(i);
118+
private void addChildren(Deque<JResource> deque, JResource resNode) {
119+
Enumeration<TreeNode> children = resNode.children();
120+
while (children.hasMoreElements()) {
121+
TreeNode node = children.nextElement();
121122
if (node instanceof JResource) {
122-
JResource resNode = (JResource) node;
123-
try {
124-
resNode.loadNode();
125-
} catch (Exception e) {
126-
LOG.error("Error load resource node: {}", resNode, e);
127-
return;
128-
}
129-
ResourceFile resFile = resNode.getResFile();
130-
if (resFile == null) {
131-
traverseTree(node, zip);
132-
} else {
133-
if (resFile.getType() == ResourceType.ARSC && shouldSearchXML()) {
134-
resFile.loadContent();
135-
resNode.getFiles().forEach(t -> traverseTree(t, null));
136-
} else {
137-
filter(resNode, zip);
138-
}
139-
}
123+
deque.add((JResource) node);
140124
}
141125
}
142126
}
143127

144-
private boolean shouldSearchXML() {
145-
return anyExt || fileExts.contains(".xml");
146-
}
147-
148-
@Nullable
149-
private ZipFile getZipFile(TreeNode res) {
150-
for (int i = 0; i < res.getChildCount(); i++) {
151-
TreeNode node = res.getChildAt(i);
128+
private static Deque<JResource> initResQueue(MainWindow mw) {
129+
JRoot jRoot = mw.getTreeRoot();
130+
Deque<JResource> deque = new ArrayDeque<>(jRoot.getChildCount());
131+
Enumeration<TreeNode> children = jRoot.children();
132+
while (children.hasMoreElements()) {
133+
TreeNode node = children.nextElement();
152134
if (node instanceof JResource) {
153135
JResource resNode = (JResource) node;
154-
try {
155-
resNode.loadNode();
156-
} catch (Exception e) {
157-
LOG.error("Error load resource node: {}", resNode, e);
158-
return null;
159-
}
160-
ResourceFile file = resNode.getResFile();
161-
if (file == null) {
162-
ZipFile zip = getZipFile(resNode);
163-
if (zip != null) {
164-
return zip;
165-
}
166-
} else {
167-
ResourceFile.ZipRef zipRef = file.getZipRef();
168-
if (zipRef != null) {
169-
File zfile = zipRef.getZipFile();
170-
if (FileUtils.isZipFile(zfile)) {
171-
try {
172-
return new ZipFile(zfile);
173-
} catch (IOException ignore) {
174-
}
175-
}
176-
}
177-
}
136+
deque.add(resNode);
178137
}
179138
}
180-
return null;
139+
return deque;
181140
}
182141

183-
private void filter(JResource resNode, ZipFile zip) {
184-
ResourceFile resFile = resNode.getResFile();
185-
if (JResource.isSupportedForView(resFile.getType())) {
186-
long size = -1;
187-
if (zip != null) {
188-
ZipEntry entry = zip.getEntry(resFile.getOriginalName());
189-
if (entry != null) {
190-
size = entry.getSize();
142+
private Set<String> buildAllowedFilesExtensions(String srhResourceFileExt) {
143+
Set<String> set = new HashSet<>();
144+
for (String extStr : srhResourceFileExt.split("[|.]")) {
145+
String ext = extStr.trim();
146+
if (!ext.isEmpty()) {
147+
anyExt = ext.equals("*");
148+
if (anyExt) {
149+
break;
191150
}
151+
set.add(ext);
192152
}
193-
if (size == -1) { // resource from ARSC is unknown size
194-
try {
195-
size = resNode.getCodeInfo().getCodeStr().length();
196-
} catch (Exception ignore) {
197-
return;
153+
}
154+
return set;
155+
}
156+
157+
private boolean shouldProcess(JResource resNode) {
158+
if (!anyExt) {
159+
String fileExt;
160+
ResourceFile resFile = resNode.getResFile();
161+
if (resFile.getType() == ResourceType.ARSC) {
162+
fileExt = "xml";
163+
} else {
164+
fileExt = CommonFileUtils.getFileExtension(resFile.getOriginalName());
165+
if (fileExt == null) {
166+
return false;
198167
}
199168
}
200-
if (size <= sizeLimit) {
201-
if (!anyExt) {
202-
for (String ext : extSet) {
203-
if (resFile.getOriginalName().endsWith(ext)) {
204-
resNodes.add(resNode);
205-
break;
206-
}
207-
}
208-
} else {
209-
resNodes.add(resNode);
210-
}
211-
} else {
212-
LOG.debug("Resource index skipped because of size limit: {} res size {} bytes", resNode, size);
169+
if (!extSet.contains(fileExt)) {
170+
return false;
213171
}
214172
}
173+
if (sizeLimit == 0) {
174+
return true;
175+
}
176+
try {
177+
int charsCount = resNode.getCodeInfo().getCodeStr().length();
178+
long size = charsCount * 8L;
179+
if (size > sizeLimit) {
180+
LOG.debug("Resource search skipped because of size limit: {} res size {} bytes", resNode, size);
181+
return false;
182+
}
183+
return true;
184+
} catch (Exception e) {
185+
LOG.warn("Resource load error: {}", resNode, e);
186+
return false;
187+
}
215188
}
216189

217190
@Override
218191
public int progress() {
219-
return progress;
192+
return 0;
220193
}
221194

222195
@Override
223196
public int total() {
224-
return resNodes == null ? 0 : resNodes.size();
197+
return 0;
225198
}
226199
}

jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public final void update() {
9292
}
9393

9494
@Override
95-
public void loadNode() {
95+
public synchronized void loadNode() {
9696
getCodeInfo();
9797
update();
9898
}
@@ -102,6 +102,10 @@ public String getName() {
102102
return name;
103103
}
104104

105+
public JResType getType() {
106+
return type;
107+
}
108+
105109
public List<JResource> getFiles() {
106110
return files;
107111
}

jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,6 @@ private void update() {
596596

597597
protected void resetCache() {
598598
cacheObject.reset();
599-
cacheObject.setJRoot(treeRoot);
600-
cacheObject.setJadxSettings(settings);
601599
}
602600

603601
synchronized void runInitialBackgroundJobs() {
@@ -680,13 +678,11 @@ private void saveAll(boolean export) {
680678

681679
public void initTree() {
682680
treeRoot = new JRoot(wrapper);
683-
cacheObject.setJRoot(treeRoot);
684681
treeRoot.setFlatPackages(isFlattenPackage);
685682
treeModel.setRoot(treeRoot);
686683
addTreeCustomNodes();
687684
treeRoot.update();
688685
reloadTree();
689-
cacheObject.setJadxSettings(settings);
690686
}
691687

692688
private void clearTree() {

0 commit comments

Comments
 (0)