Skip to content

Commit 63a5713

Browse files
committed
refactor: load resource table nodes in one change (#1648)
1 parent bc4db61 commit 63a5713

5 files changed

Lines changed: 171 additions & 102 deletions

File tree

jadx-gui/src/main/java/jadx/gui/search/providers/ResourceSearchProvider.java

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings) {
5252
if (cancelable.isCanceled()) {
5353
return null;
5454
}
55-
JResource resNode = getNextNode();
55+
JResource resNode = getNextResFile(cancelable);
5656
if (resNode == null) {
5757
return null;
5858
}
@@ -62,7 +62,7 @@ public ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings) {
6262
}
6363
pos = 0;
6464
resQueue.removeLast();
65-
addChildren(resQueue, resNode);
65+
addChildren(resNode);
6666
if (resQueue.isEmpty()) {
6767
return null;
6868
}
@@ -90,39 +90,37 @@ private JNode search(JResource resNode) {
9090
return new JResSearchNode(resNode, line.trim(), newPos);
9191
}
9292

93-
private @Nullable JResource getNextNode() {
94-
JResource node = resQueue.peekLast();
95-
if (node == null) {
96-
return null;
97-
}
98-
try {
99-
node.loadNode();
100-
} catch (Exception e) {
101-
LOG.error("Error load resource node: {}", node, e);
102-
resQueue.removeLast();
103-
return getNextNode();
104-
}
105-
if (node.getType() == JResource.JResType.FILE) {
106-
if (shouldProcess(node)) {
107-
return node;
93+
private @Nullable JResource getNextResFile(Cancelable cancelable) {
94+
while (true) {
95+
JResource node = resQueue.peekLast();
96+
if (node == null) {
97+
return null;
98+
}
99+
try {
100+
node.loadNode();
101+
} catch (Exception e) {
102+
LOG.error("Error load resource node: {}", node, e);
103+
resQueue.removeLast();
104+
continue;
105+
}
106+
if (cancelable.isCanceled()) {
107+
return null;
108+
}
109+
if (node.getType() == JResource.JResType.FILE) {
110+
if (shouldProcess(node)) {
111+
return node;
112+
}
113+
resQueue.removeLast();
114+
} else {
115+
// dir
116+
resQueue.removeLast();
117+
addChildren(node);
108118
}
109-
resQueue.removeLast();
110-
return getNextNode();
111119
}
112-
// dit or root
113-
resQueue.removeLast();
114-
addChildren(resQueue, node);
115-
return getNextNode();
116120
}
117121

118-
private void addChildren(Deque<JResource> deque, JResource resNode) {
119-
Enumeration<TreeNode> children = resNode.children();
120-
while (children.hasMoreElements()) {
121-
TreeNode node = children.nextElement();
122-
if (node instanceof JResource) {
123-
deque.add((JResource) node);
124-
}
125-
}
122+
private void addChildren(JResource resNode) {
123+
resQueue.addAll(resNode.getSubNodes());
126124
}
127125

128126
private static Deque<JResource> initResQueue(MainWindow mw) {
@@ -155,16 +153,15 @@ private Set<String> buildAllowedFilesExtensions(String srhResourceFileExt) {
155153
}
156154

157155
private boolean shouldProcess(JResource resNode) {
156+
ResourceFile resFile = resNode.getResFile();
157+
if (resFile.getType() == ResourceType.ARSC) {
158+
// don't check size of generated resource table, it will also skip all sub files
159+
return anyExt || extSet.contains("xml");
160+
}
158161
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;
167-
}
162+
String fileExt = CommonFileUtils.getFileExtension(resFile.getOriginalName());
163+
if (fileExt == null) {
164+
return false;
168165
}
169166
if (!extSet.contains(fileExt)) {
170167
return false;

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

Lines changed: 32 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package jadx.gui.treemodel;
22

3-
import java.util.ArrayList;
3+
import java.util.Collections;
44
import java.util.Comparator;
55
import java.util.List;
66
import java.util.Map;
@@ -14,10 +14,10 @@
1414
import jadx.api.ICodeInfo;
1515
import jadx.api.ICodeWriter;
1616
import jadx.api.ResourceFile;
17-
import jadx.api.ResourceFileContent;
1817
import jadx.api.ResourceType;
1918
import jadx.api.ResourcesLoader;
2019
import jadx.api.impl.SimpleCodeInfo;
20+
import jadx.core.utils.ListUtils;
2121
import jadx.core.utils.Utils;
2222
import jadx.core.xmlgen.ResContainer;
2323
import jadx.gui.ui.TabbedPane;
@@ -26,6 +26,7 @@
2626
import jadx.gui.ui.panel.ImagePanel;
2727
import jadx.gui.utils.NLS;
2828
import jadx.gui.utils.UiUtils;
29+
import jadx.gui.utils.res.ResTableHelper;
2930

3031
public class JResource extends JLoadableNode {
3132
private static final long serialVersionUID = -201018424302612434L;
@@ -41,6 +42,10 @@ public class JResource extends JLoadableNode {
4142
private static final ImageIcon JAVA_ICON = UiUtils.openSvgIcon("nodes/java");
4243
private static final ImageIcon UNKNOWN_ICON = UiUtils.openSvgIcon("nodes/unknown");
4344

45+
public static final Comparator<JResource> RESOURCES_COMPARATOR =
46+
Comparator.<JResource>comparingInt(r -> r.type.ordinal())
47+
.thenComparing(JResource::getName, String.CASE_INSENSITIVE_ORDER);
48+
4449
public enum JResType {
4550
ROOT,
4651
DIR,
@@ -49,11 +54,11 @@ public enum JResType {
4954

5055
private final transient String name;
5156
private final transient String shortName;
52-
private final transient List<JResource> files = new ArrayList<>(1);
5357
private final transient JResType type;
5458
private final transient ResourceFile resFile;
5559

56-
private transient boolean loaded;
60+
private transient volatile boolean loaded;
61+
private transient List<JResource> subNodes = Collections.emptyList();
5762
private transient ICodeInfo content;
5863

5964
public JResource(ResourceFile resFile, String name, JResType type) {
@@ -69,22 +74,16 @@ public JResource(ResourceFile resFile, String name, String shortName, JResType t
6974
}
7075

7176
public final void update() {
72-
if (files.isEmpty()) {
77+
removeAllChildren();
78+
if (Utils.isEmpty(subNodes)) {
7379
if (type == JResType.DIR || type == JResType.ROOT
7480
|| resFile.getType() == ResourceType.ARSC) {
7581
// fake leaf to force show expand button
7682
// real sub nodes will load on expand in loadNode() method
7783
add(new TextNode(NLS.str("tree.loading")));
7884
}
7985
} else {
80-
removeAllChildren();
81-
82-
Comparator<JResource> typeComparator = Comparator.comparingInt(r -> r.type.ordinal());
83-
Comparator<JResource> nameComparator = Comparator.comparing(JResource::getName, String.CASE_INSENSITIVE_ORDER);
84-
85-
files.sort(typeComparator.thenComparing(nameComparator));
86-
87-
for (JResource res : files) {
86+
for (JResource res : subNodes) {
8887
res.update();
8988
add(res);
9089
}
@@ -106,8 +105,23 @@ public JResType getType() {
106105
return type;
107106
}
108107

109-
public List<JResource> getFiles() {
110-
return files;
108+
public List<JResource> getSubNodes() {
109+
return subNodes;
110+
}
111+
112+
public void addSubNode(JResource node) {
113+
subNodes = ListUtils.safeAdd(subNodes, node);
114+
}
115+
116+
public void sortSubNodes() {
117+
sortResNodes(subNodes);
118+
}
119+
120+
private static void sortResNodes(List<JResource> nodes) {
121+
if (Utils.notEmpty(nodes)) {
122+
nodes.forEach(JResource::sortSubNodes);
123+
nodes.sort(RESOURCES_COMPARATOR);
124+
}
111125
}
112126

113127
@Override
@@ -145,9 +159,9 @@ private ICodeInfo loadContent() {
145159
}
146160
if (rc.getDataType() == ResContainer.DataType.RES_TABLE) {
147161
ICodeInfo codeInfo = loadCurrentSingleRes(rc);
148-
for (ResContainer subFile : rc.getSubFiles()) {
149-
loadSubNodes(this, subFile, 1);
150-
}
162+
List<JResource> nodes = ResTableHelper.buildTree(rc);
163+
sortResNodes(nodes);
164+
subNodes = nodes;
151165
return codeInfo;
152166
}
153167
// single node
@@ -178,47 +192,6 @@ private ICodeInfo loadCurrentSingleRes(ResContainer rc) {
178192
}
179193
}
180194

181-
private void loadSubNodes(JResource root, ResContainer rc, int depth) {
182-
String resName = rc.getName();
183-
String[] path = resName.split("/");
184-
String resShortName = path.length == 0 ? resName : path[path.length - 1];
185-
ICodeInfo code = rc.getText();
186-
ResourceFileContent fileContent = new ResourceFileContent(resShortName, ResourceType.XML, code);
187-
addPath(path, root, new JResource(fileContent, resName, resShortName, JResType.FILE));
188-
189-
for (ResContainer subFile : rc.getSubFiles()) {
190-
loadSubNodes(root, subFile, depth + 1);
191-
}
192-
}
193-
194-
private static void addPath(String[] path, JResource root, JResource jResource) {
195-
if (path.length == 1) {
196-
root.getFiles().add(jResource);
197-
return;
198-
}
199-
JResource currentRoot = root;
200-
int last = path.length - 1;
201-
for (int i = 0; i <= last; i++) {
202-
String f = path[i];
203-
if (i == last) {
204-
currentRoot.getFiles().add(jResource);
205-
} else {
206-
currentRoot = getResDir(currentRoot, f);
207-
}
208-
}
209-
}
210-
211-
private static JResource getResDir(JResource root, String dirName) {
212-
for (JResource file : root.getFiles()) {
213-
if (file.getName().equals(dirName)) {
214-
return file;
215-
}
216-
}
217-
JResource resDir = new JResource(null, dirName, JResType.DIR);
218-
root.getFiles().add(resDir);
219-
return resDir;
220-
}
221-
222195
@Override
223196
public String getSyntaxName() {
224197
if (resFile == null) {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,18 @@ private JResource getHierarchyResources(List<ResourceFile> resources) {
6868
} else {
6969
subRF = new JResource(rf, rf.getDeobfName(), name, JResType.FILE);
7070
}
71-
curRf.getFiles().add(subRF);
71+
curRf.addSubNode(subRF);
7272
}
7373
curRf = subRF;
7474
}
7575
}
76+
root.sortSubNodes();
7677
root.update();
7778
return root;
7879
}
7980

8081
private JResource getResourceByName(JResource rf, String name) {
81-
for (JResource sub : rf.getFiles()) {
82+
for (JResource sub : rf.getSubNodes()) {
8283
if (sub.getName().equals(name)) {
8384
return sub;
8485
}

jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ public static void uiRunAndWait(Runnable runnable) {
375375
try {
376376
SwingUtilities.invokeAndWait(runnable);
377377
} catch (InterruptedException e) {
378-
LOG.warn("UI thread interrupted", e);
378+
LOG.warn("UI thread interrupted, runnable: {}", runnable, e);
379379
} catch (Exception e) {
380380
throw new RuntimeException(e);
381381
}

0 commit comments

Comments
 (0)