未验证 提交 bc4db61e 编写于 作者: S Skylot

fix(gui): improve resources search (#1648)

上级 c7e6e288
...@@ -107,7 +107,7 @@ public class QuarkManager { ...@@ -107,7 +107,7 @@ public class QuarkManager {
private void loadReport() { private void loadReport() {
try { try {
QuarkReportNode quarkNode = new QuarkReportNode(reportFile); QuarkReportNode quarkNode = new QuarkReportNode(reportFile);
JRoot root = mainWindow.getCacheObject().getJRoot(); JRoot root = mainWindow.getTreeRoot();
root.replaceCustomNode(quarkNode); root.replaceCustomNode(quarkNode);
root.update(); root.update();
mainWindow.reloadTree(); mainWindow.reloadTree();
......
package jadx.gui.search.providers; package jadx.gui.search.providers;
import java.io.File; import java.util.ArrayDeque;
import java.io.IOException; import java.util.Deque;
import java.util.ArrayList; import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.tree.TreeNode; import javax.swing.tree.TreeNode;
...@@ -18,56 +15,55 @@ import org.slf4j.LoggerFactory; ...@@ -18,56 +15,55 @@ import org.slf4j.LoggerFactory;
import jadx.api.ICodeWriter; import jadx.api.ICodeWriter;
import jadx.api.ResourceFile; import jadx.api.ResourceFile;
import jadx.api.ResourceType; import jadx.api.ResourceType;
import jadx.core.utils.files.FileUtils; import jadx.api.plugins.utils.CommonFileUtils;
import jadx.gui.jobs.Cancelable; import jadx.gui.jobs.Cancelable;
import jadx.gui.search.ISearchProvider; import jadx.gui.search.ISearchProvider;
import jadx.gui.search.SearchSettings; import jadx.gui.search.SearchSettings;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResSearchNode; import jadx.gui.treemodel.JResSearchNode;
import jadx.gui.treemodel.JResource; import jadx.gui.treemodel.JResource;
import jadx.gui.treemodel.JRoot;
import jadx.gui.ui.MainWindow; import jadx.gui.ui.MainWindow;
import jadx.gui.utils.CacheObject;
public class ResourceSearchProvider implements ISearchProvider { public class ResourceSearchProvider implements ISearchProvider {
private static final Logger LOG = LoggerFactory.getLogger(ResourceSearchProvider.class); private static final Logger LOG = LoggerFactory.getLogger(ResourceSearchProvider.class);
private final CacheObject cache;
private final SearchSettings searchSettings; private final SearchSettings searchSettings;
private final Set<String> extSet = new HashSet<>(); private final Set<String> extSet;
private final int sizeLimit;
private List<JResource> resNodes;
private String fileExts;
private boolean anyExt; private boolean anyExt;
private int sizeLimit;
private int progress; /**
* Resources queue for process. Using UI nodes to reuse loading cache
*/
private final Deque<JResource> resQueue;
private int pos; private int pos;
public ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings) { public ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings) {
this.cache = mw.getCacheObject();
this.searchSettings = searchSettings; this.searchSettings = searchSettings;
this.sizeLimit = mw.getSettings().getSrhResourceSkipSize() * 1048576;
this.extSet = buildAllowedFilesExtensions(mw.getSettings().getSrhResourceFileExt());
this.resQueue = initResQueue(mw);
} }
@Override @Override
public @Nullable JNode next(Cancelable cancelable) { public @Nullable JNode next(Cancelable cancelable) {
if (resNodes == null) {
load();
}
if (resNodes.isEmpty()) {
return null;
}
while (true) { while (true) {
if (cancelable.isCanceled()) { if (cancelable.isCanceled()) {
return null; return null;
} }
JResource resNode = resNodes.get(progress); JResource resNode = getNextNode();
if (resNode == null) {
return null;
}
JNode newResult = search(resNode); JNode newResult = search(resNode);
if (newResult != null) { if (newResult != null) {
return newResult; return newResult;
} }
progress++;
pos = 0; pos = 0;
if (progress >= resNodes.size()) { resQueue.removeLast();
addChildren(resQueue, resNode);
if (resQueue.isEmpty()) {
return null; return null;
} }
} }
...@@ -94,133 +90,110 @@ public class ResourceSearchProvider implements ISearchProvider { ...@@ -94,133 +90,110 @@ public class ResourceSearchProvider implements ISearchProvider {
return new JResSearchNode(resNode, line.trim(), newPos); return new JResSearchNode(resNode, line.trim(), newPos);
} }
private synchronized void load() { private @Nullable JResource getNextNode() {
resNodes = new ArrayList<>(); JResource node = resQueue.peekLast();
sizeLimit = cache.getJadxSettings().getSrhResourceSkipSize() * 1048576; if (node == null) {
fileExts = cache.getJadxSettings().getSrhResourceFileExt(); return null;
for (String extStr : fileExts.split("\\|")) {
String ext = extStr.trim();
if (!ext.isEmpty()) {
anyExt = ext.equals("*");
if (anyExt) {
break;
}
extSet.add(ext);
}
}
try (ZipFile zipFile = getZipFile(cache.getJRoot())) {
traverseTree(cache.getJRoot(), zipFile); // reindex
} catch (Exception e) {
LOG.error("Failed to apply settings to resource index", e);
}
} }
private void traverseTree(TreeNode root, @Nullable ZipFile zip) {
for (int i = 0; i < root.getChildCount(); i++) {
TreeNode node = root.getChildAt(i);
if (node instanceof JResource) {
JResource resNode = (JResource) node;
try { try {
resNode.loadNode(); node.loadNode();
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error load resource node: {}", resNode, e); LOG.error("Error load resource node: {}", node, e);
return; resQueue.removeLast();
return getNextNode();
} }
ResourceFile resFile = resNode.getResFile(); if (node.getType() == JResource.JResType.FILE) {
if (resFile == null) { if (shouldProcess(node)) {
traverseTree(node, zip); return node;
} else {
if (resFile.getType() == ResourceType.ARSC && shouldSearchXML()) {
resFile.loadContent();
resNode.getFiles().forEach(t -> traverseTree(t, null));
} else {
filter(resNode, zip);
} }
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 boolean shouldSearchXML() {
return anyExt || fileExts.contains(".xml");
} }
@Nullable private static Deque<JResource> initResQueue(MainWindow mw) {
private ZipFile getZipFile(TreeNode res) { JRoot jRoot = mw.getTreeRoot();
for (int i = 0; i < res.getChildCount(); i++) { Deque<JResource> deque = new ArrayDeque<>(jRoot.getChildCount());
TreeNode node = res.getChildAt(i); Enumeration<TreeNode> children = jRoot.children();
while (children.hasMoreElements()) {
TreeNode node = children.nextElement();
if (node instanceof JResource) { if (node instanceof JResource) {
JResource resNode = (JResource) node; JResource resNode = (JResource) node;
try { deque.add(resNode);
resNode.loadNode();
} catch (Exception e) {
LOG.error("Error load resource node: {}", resNode, e);
return null;
} }
ResourceFile file = resNode.getResFile();
if (file == null) {
ZipFile zip = getZipFile(resNode);
if (zip != null) {
return zip;
}
} else {
ResourceFile.ZipRef zipRef = file.getZipRef();
if (zipRef != null) {
File zfile = zipRef.getZipFile();
if (FileUtils.isZipFile(zfile)) {
try {
return new ZipFile(zfile);
} catch (IOException ignore) {
} }
return deque;
} }
private Set<String> buildAllowedFilesExtensions(String srhResourceFileExt) {
Set<String> set = new HashSet<>();
for (String extStr : srhResourceFileExt.split("[|.]")) {
String ext = extStr.trim();
if (!ext.isEmpty()) {
anyExt = ext.equals("*");
if (anyExt) {
break;
} }
set.add(ext);
} }
} }
} return set;
return null;
} }
private void filter(JResource resNode, ZipFile zip) { private boolean shouldProcess(JResource resNode) {
if (!anyExt) {
String fileExt;
ResourceFile resFile = resNode.getResFile(); ResourceFile resFile = resNode.getResFile();
if (JResource.isSupportedForView(resFile.getType())) { if (resFile.getType() == ResourceType.ARSC) {
long size = -1; fileExt = "xml";
if (zip != null) { } else {
ZipEntry entry = zip.getEntry(resFile.getOriginalName()); fileExt = CommonFileUtils.getFileExtension(resFile.getOriginalName());
if (entry != null) { if (fileExt == null) {
size = entry.getSize(); return false;
}
}
if (size == -1) { // resource from ARSC is unknown size
try {
size = resNode.getCodeInfo().getCodeStr().length();
} catch (Exception ignore) {
return;
} }
} }
if (size <= sizeLimit) { if (!extSet.contains(fileExt)) {
if (!anyExt) { return false;
for (String ext : extSet) {
if (resFile.getOriginalName().endsWith(ext)) {
resNodes.add(resNode);
break;
} }
} }
} else { if (sizeLimit == 0) {
resNodes.add(resNode); return true;
} }
} else { try {
LOG.debug("Resource index skipped because of size limit: {} res size {} bytes", resNode, size); int charsCount = resNode.getCodeInfo().getCodeStr().length();
long size = charsCount * 8L;
if (size > sizeLimit) {
LOG.debug("Resource search skipped because of size limit: {} res size {} bytes", resNode, size);
return false;
} }
return true;
} catch (Exception e) {
LOG.warn("Resource load error: {}", resNode, e);
return false;
} }
} }
@Override @Override
public int progress() { public int progress() {
return progress; return 0;
} }
@Override @Override
public int total() { public int total() {
return resNodes == null ? 0 : resNodes.size(); return 0;
} }
} }
...@@ -92,7 +92,7 @@ public class JResource extends JLoadableNode { ...@@ -92,7 +92,7 @@ public class JResource extends JLoadableNode {
} }
@Override @Override
public void loadNode() { public synchronized void loadNode() {
getCodeInfo(); getCodeInfo();
update(); update();
} }
...@@ -102,6 +102,10 @@ public class JResource extends JLoadableNode { ...@@ -102,6 +102,10 @@ public class JResource extends JLoadableNode {
return name; return name;
} }
public JResType getType() {
return type;
}
public List<JResource> getFiles() { public List<JResource> getFiles() {
return files; return files;
} }
......
...@@ -596,8 +596,6 @@ public class MainWindow extends JFrame { ...@@ -596,8 +596,6 @@ public class MainWindow extends JFrame {
protected void resetCache() { protected void resetCache() {
cacheObject.reset(); cacheObject.reset();
cacheObject.setJRoot(treeRoot);
cacheObject.setJadxSettings(settings);
} }
synchronized void runInitialBackgroundJobs() { synchronized void runInitialBackgroundJobs() {
...@@ -680,13 +678,11 @@ public class MainWindow extends JFrame { ...@@ -680,13 +678,11 @@ public class MainWindow extends JFrame {
public void initTree() { public void initTree() {
treeRoot = new JRoot(wrapper); treeRoot = new JRoot(wrapper);
cacheObject.setJRoot(treeRoot);
treeRoot.setFlatPackages(isFlattenPackage); treeRoot.setFlatPackages(isFlattenPackage);
treeModel.setRoot(treeRoot); treeModel.setRoot(treeRoot);
addTreeCustomNodes(); addTreeCustomNodes();
treeRoot.update(); treeRoot.update();
reloadTree(); reloadTree();
cacheObject.setJadxSettings(settings);
} }
private void clearTree() { private void clearTree() {
......
...@@ -8,8 +8,6 @@ import java.util.Set; ...@@ -8,8 +8,6 @@ import java.util.Set;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import jadx.api.JavaClass; import jadx.api.JavaClass;
import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JRoot;
import jadx.gui.ui.dialog.SearchDialog; import jadx.gui.ui.dialog.SearchDialog;
public class CacheObject { public class CacheObject {
...@@ -18,9 +16,6 @@ public class CacheObject { ...@@ -18,9 +16,6 @@ public class CacheObject {
private JNodeCache jNodeCache; private JNodeCache jNodeCache;
private Map<SearchDialog.SearchPreset, Set<SearchDialog.SearchOptions>> lastSearchOptions; private Map<SearchDialog.SearchPreset, Set<SearchDialog.SearchOptions>> lastSearchOptions;
private JRoot jRoot;
private JadxSettings settings;
private List<List<JavaClass>> decompileBatches; private List<List<JavaClass>> decompileBatches;
public CacheObject() { public CacheObject() {
...@@ -28,8 +23,6 @@ public class CacheObject { ...@@ -28,8 +23,6 @@ public class CacheObject {
} }
public void reset() { public void reset() {
jRoot = null;
settings = null;
lastSearch = null; lastSearch = null;
jNodeCache = new JNodeCache(); jNodeCache = new JNodeCache();
lastSearchOptions = new HashMap<>(); lastSearchOptions = new HashMap<>();
...@@ -53,22 +46,6 @@ public class CacheObject { ...@@ -53,22 +46,6 @@ public class CacheObject {
return lastSearchOptions; return lastSearchOptions;
} }
public void setJadxSettings(JadxSettings settings) {
this.settings = settings;
}
public JadxSettings getJadxSettings() {
return this.settings;
}
public JRoot getJRoot() {
return jRoot;
}
public void setJRoot(JRoot jRoot) {
this.jRoot = jRoot;
}
public @Nullable List<List<JavaClass>> getDecompileBatches() { public @Nullable List<List<JavaClass>> getDecompileBatches() {
return decompileBatches; return decompileBatches;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册