refactor: load resource table nodes in one change (#1648)
This commit is contained in:
@@ -52,7 +52,7 @@ public class ResourceSearchProvider implements ISearchProvider {
|
||||
if (cancelable.isCanceled()) {
|
||||
return null;
|
||||
}
|
||||
JResource resNode = getNextNode();
|
||||
JResource resNode = getNextResFile(cancelable);
|
||||
if (resNode == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class ResourceSearchProvider implements ISearchProvider {
|
||||
}
|
||||
pos = 0;
|
||||
resQueue.removeLast();
|
||||
addChildren(resQueue, resNode);
|
||||
addChildren(resNode);
|
||||
if (resQueue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -90,39 +90,37 @@ public class ResourceSearchProvider implements ISearchProvider {
|
||||
return new JResSearchNode(resNode, line.trim(), newPos);
|
||||
}
|
||||
|
||||
private @Nullable JResource getNextNode() {
|
||||
JResource node = resQueue.peekLast();
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
node.loadNode();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error load resource node: {}", node, e);
|
||||
resQueue.removeLast();
|
||||
return getNextNode();
|
||||
}
|
||||
if (node.getType() == JResource.JResType.FILE) {
|
||||
if (shouldProcess(node)) {
|
||||
return node;
|
||||
private @Nullable JResource getNextResFile(Cancelable cancelable) {
|
||||
while (true) {
|
||||
JResource node = resQueue.peekLast();
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
node.loadNode();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error load resource node: {}", node, e);
|
||||
resQueue.removeLast();
|
||||
continue;
|
||||
}
|
||||
if (cancelable.isCanceled()) {
|
||||
return null;
|
||||
}
|
||||
if (node.getType() == JResource.JResType.FILE) {
|
||||
if (shouldProcess(node)) {
|
||||
return node;
|
||||
}
|
||||
resQueue.removeLast();
|
||||
} else {
|
||||
// dir
|
||||
resQueue.removeLast();
|
||||
addChildren(node);
|
||||
}
|
||||
resQueue.removeLast();
|
||||
return getNextNode();
|
||||
}
|
||||
// dit or root
|
||||
resQueue.removeLast();
|
||||
addChildren(resQueue, node);
|
||||
return getNextNode();
|
||||
}
|
||||
|
||||
private void addChildren(Deque<JResource> deque, JResource resNode) {
|
||||
Enumeration<TreeNode> children = resNode.children();
|
||||
while (children.hasMoreElements()) {
|
||||
TreeNode node = children.nextElement();
|
||||
if (node instanceof JResource) {
|
||||
deque.add((JResource) node);
|
||||
}
|
||||
}
|
||||
private void addChildren(JResource resNode) {
|
||||
resQueue.addAll(resNode.getSubNodes());
|
||||
}
|
||||
|
||||
private static Deque<JResource> initResQueue(MainWindow mw) {
|
||||
@@ -155,16 +153,15 @@ public class ResourceSearchProvider implements ISearchProvider {
|
||||
}
|
||||
|
||||
private boolean shouldProcess(JResource resNode) {
|
||||
ResourceFile resFile = resNode.getResFile();
|
||||
if (resFile.getType() == ResourceType.ARSC) {
|
||||
// don't check size of generated resource table, it will also skip all sub files
|
||||
return anyExt || extSet.contains("xml");
|
||||
}
|
||||
if (!anyExt) {
|
||||
String fileExt;
|
||||
ResourceFile resFile = resNode.getResFile();
|
||||
if (resFile.getType() == ResourceType.ARSC) {
|
||||
fileExt = "xml";
|
||||
} else {
|
||||
fileExt = CommonFileUtils.getFileExtension(resFile.getOriginalName());
|
||||
if (fileExt == null) {
|
||||
return false;
|
||||
}
|
||||
String fileExt = CommonFileUtils.getFileExtension(resFile.getOriginalName());
|
||||
if (fileExt == null) {
|
||||
return false;
|
||||
}
|
||||
if (!extSet.contains(fileExt)) {
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.gui.treemodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -14,10 +14,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.ICodeWriter;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceFileContent;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
@@ -26,6 +26,7 @@ import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.panel.ImagePanel;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.res.ResTableHelper;
|
||||
|
||||
public class JResource extends JLoadableNode {
|
||||
private static final long serialVersionUID = -201018424302612434L;
|
||||
@@ -41,6 +42,10 @@ public class JResource extends JLoadableNode {
|
||||
private static final ImageIcon JAVA_ICON = UiUtils.openSvgIcon("nodes/java");
|
||||
private static final ImageIcon UNKNOWN_ICON = UiUtils.openSvgIcon("nodes/unknown");
|
||||
|
||||
public static final Comparator<JResource> RESOURCES_COMPARATOR =
|
||||
Comparator.<JResource>comparingInt(r -> r.type.ordinal())
|
||||
.thenComparing(JResource::getName, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
public enum JResType {
|
||||
ROOT,
|
||||
DIR,
|
||||
@@ -49,11 +54,11 @@ public class JResource extends JLoadableNode {
|
||||
|
||||
private final transient String name;
|
||||
private final transient String shortName;
|
||||
private final transient List<JResource> files = new ArrayList<>(1);
|
||||
private final transient JResType type;
|
||||
private final transient ResourceFile resFile;
|
||||
|
||||
private transient boolean loaded;
|
||||
private transient volatile boolean loaded;
|
||||
private transient List<JResource> subNodes = Collections.emptyList();
|
||||
private transient ICodeInfo content;
|
||||
|
||||
public JResource(ResourceFile resFile, String name, JResType type) {
|
||||
@@ -69,7 +74,8 @@ public class JResource extends JLoadableNode {
|
||||
}
|
||||
|
||||
public final void update() {
|
||||
if (files.isEmpty()) {
|
||||
removeAllChildren();
|
||||
if (Utils.isEmpty(subNodes)) {
|
||||
if (type == JResType.DIR || type == JResType.ROOT
|
||||
|| resFile.getType() == ResourceType.ARSC) {
|
||||
// fake leaf to force show expand button
|
||||
@@ -77,14 +83,7 @@ public class JResource extends JLoadableNode {
|
||||
add(new TextNode(NLS.str("tree.loading")));
|
||||
}
|
||||
} else {
|
||||
removeAllChildren();
|
||||
|
||||
Comparator<JResource> typeComparator = Comparator.comparingInt(r -> r.type.ordinal());
|
||||
Comparator<JResource> nameComparator = Comparator.comparing(JResource::getName, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
files.sort(typeComparator.thenComparing(nameComparator));
|
||||
|
||||
for (JResource res : files) {
|
||||
for (JResource res : subNodes) {
|
||||
res.update();
|
||||
add(res);
|
||||
}
|
||||
@@ -106,8 +105,23 @@ public class JResource extends JLoadableNode {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<JResource> getFiles() {
|
||||
return files;
|
||||
public List<JResource> getSubNodes() {
|
||||
return subNodes;
|
||||
}
|
||||
|
||||
public void addSubNode(JResource node) {
|
||||
subNodes = ListUtils.safeAdd(subNodes, node);
|
||||
}
|
||||
|
||||
public void sortSubNodes() {
|
||||
sortResNodes(subNodes);
|
||||
}
|
||||
|
||||
private static void sortResNodes(List<JResource> nodes) {
|
||||
if (Utils.notEmpty(nodes)) {
|
||||
nodes.forEach(JResource::sortSubNodes);
|
||||
nodes.sort(RESOURCES_COMPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,9 +159,9 @@ public class JResource extends JLoadableNode {
|
||||
}
|
||||
if (rc.getDataType() == ResContainer.DataType.RES_TABLE) {
|
||||
ICodeInfo codeInfo = loadCurrentSingleRes(rc);
|
||||
for (ResContainer subFile : rc.getSubFiles()) {
|
||||
loadSubNodes(this, subFile, 1);
|
||||
}
|
||||
List<JResource> nodes = ResTableHelper.buildTree(rc);
|
||||
sortResNodes(nodes);
|
||||
subNodes = nodes;
|
||||
return codeInfo;
|
||||
}
|
||||
// single node
|
||||
@@ -178,47 +192,6 @@ public class JResource extends JLoadableNode {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSubNodes(JResource root, ResContainer rc, int depth) {
|
||||
String resName = rc.getName();
|
||||
String[] path = resName.split("/");
|
||||
String resShortName = path.length == 0 ? resName : path[path.length - 1];
|
||||
ICodeInfo code = rc.getText();
|
||||
ResourceFileContent fileContent = new ResourceFileContent(resShortName, ResourceType.XML, code);
|
||||
addPath(path, root, new JResource(fileContent, resName, resShortName, JResType.FILE));
|
||||
|
||||
for (ResContainer subFile : rc.getSubFiles()) {
|
||||
loadSubNodes(root, subFile, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addPath(String[] path, JResource root, JResource jResource) {
|
||||
if (path.length == 1) {
|
||||
root.getFiles().add(jResource);
|
||||
return;
|
||||
}
|
||||
JResource currentRoot = root;
|
||||
int last = path.length - 1;
|
||||
for (int i = 0; i <= last; i++) {
|
||||
String f = path[i];
|
||||
if (i == last) {
|
||||
currentRoot.getFiles().add(jResource);
|
||||
} else {
|
||||
currentRoot = getResDir(currentRoot, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static JResource getResDir(JResource root, String dirName) {
|
||||
for (JResource file : root.getFiles()) {
|
||||
if (file.getName().equals(dirName)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
JResource resDir = new JResource(null, dirName, JResType.DIR);
|
||||
root.getFiles().add(resDir);
|
||||
return resDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntaxName() {
|
||||
if (resFile == null) {
|
||||
|
||||
@@ -68,17 +68,18 @@ public class JRoot extends JNode {
|
||||
} else {
|
||||
subRF = new JResource(rf, rf.getDeobfName(), name, JResType.FILE);
|
||||
}
|
||||
curRf.getFiles().add(subRF);
|
||||
curRf.addSubNode(subRF);
|
||||
}
|
||||
curRf = subRF;
|
||||
}
|
||||
}
|
||||
root.sortSubNodes();
|
||||
root.update();
|
||||
return root;
|
||||
}
|
||||
|
||||
private JResource getResourceByName(JResource rf, String name) {
|
||||
for (JResource sub : rf.getFiles()) {
|
||||
for (JResource sub : rf.getSubNodes()) {
|
||||
if (sub.getName().equals(name)) {
|
||||
return sub;
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ public class UiUtils {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(runnable);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warn("UI thread interrupted", e);
|
||||
LOG.warn("UI thread interrupted, runnable: {}", runnable, e);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package jadx.gui.utils.res;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.ResourceFileContent;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
|
||||
public class ResTableHelper {
|
||||
|
||||
/**
|
||||
* Build UI tree for resource table container.
|
||||
*
|
||||
* @return root nodes
|
||||
*/
|
||||
public static List<JResource> buildTree(ResContainer resTable) {
|
||||
ResTableHelper resTableHelper = new ResTableHelper();
|
||||
resTableHelper.process(resTable);
|
||||
return resTableHelper.roots;
|
||||
}
|
||||
|
||||
private final List<JResource> roots = new ArrayList<>();
|
||||
private final Map<String, JResource> dirs = new HashMap<>();
|
||||
|
||||
private ResTableHelper() {
|
||||
}
|
||||
|
||||
private void process(ResContainer resTable) {
|
||||
for (ResContainer subFile : resTable.getSubFiles()) {
|
||||
loadSubNodes(subFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSubNodes(ResContainer rc) {
|
||||
String resName = rc.getName();
|
||||
int split = resName.lastIndexOf('/');
|
||||
String dir;
|
||||
String name;
|
||||
if (split == -1) {
|
||||
dir = null;
|
||||
name = resName;
|
||||
} else {
|
||||
dir = resName.substring(0, split);
|
||||
name = resName.substring(split + 1);
|
||||
}
|
||||
ICodeInfo code = rc.getText();
|
||||
ResourceFileContent fileContent = new ResourceFileContent(name, ResourceType.XML, code);
|
||||
JResource resFile = new JResource(fileContent, resName, name, JResource.JResType.FILE);
|
||||
addResFile(dir, resFile);
|
||||
|
||||
for (ResContainer subFile : rc.getSubFiles()) {
|
||||
loadSubNodes(subFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void addResFile(@Nullable String dir, JResource resFile) {
|
||||
if (dir == null) {
|
||||
roots.add(resFile);
|
||||
return;
|
||||
}
|
||||
JResource dirRes = dirs.get(dir);
|
||||
if (dirRes != null) {
|
||||
dirRes.addSubNode(resFile);
|
||||
return;
|
||||
}
|
||||
JResource parentDir = null;
|
||||
int splitPos = -1;
|
||||
while (true) {
|
||||
int prevStart = splitPos + 1;
|
||||
splitPos = dir.indexOf('/', prevStart);
|
||||
boolean last = splitPos == -1;
|
||||
String path = last ? dir : dir.substring(0, splitPos);
|
||||
JResource curDir = dirs.get(path);
|
||||
if (curDir == null) {
|
||||
String dirName = last ? dir.substring(prevStart) : dir.substring(prevStart, splitPos);
|
||||
curDir = new JResource(null, dirName, JResource.JResType.DIR);
|
||||
dirs.put(path, curDir);
|
||||
if (parentDir == null) {
|
||||
roots.add(curDir);
|
||||
} else {
|
||||
parentDir.addSubNode(curDir);
|
||||
}
|
||||
}
|
||||
if (last) {
|
||||
curDir.addSubNode(resFile);
|
||||
return;
|
||||
}
|
||||
parentDir = curDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user