fix(gui): use new resource class for files in arsc (#2771)
This commit is contained in:
@@ -12,8 +12,10 @@ import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JInputScript;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
import jadx.gui.treemodel.JSubResource;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class TabStateViewAdapter {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TabStateViewAdapter.class);
|
||||
@@ -21,11 +23,13 @@ public class TabStateViewAdapter {
|
||||
@Nullable
|
||||
public static TabViewState build(EditorViewState viewState) {
|
||||
TabViewState tvs = new TabViewState();
|
||||
tvs.setSubPath(viewState.getSubPath());
|
||||
if (!saveJNode(tvs, viewState.getNode())) {
|
||||
LOG.debug("Can't save view state: " + viewState);
|
||||
if (UiUtils.JADX_GUI_DEBUG) {
|
||||
LOG.warn("Can't save view state: {}", viewState);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
tvs.setSubPath(viewState.getSubPath());
|
||||
tvs.setCaret(viewState.getCaretPos());
|
||||
tvs.setView(new ViewPoint(viewState.getViewPoint()));
|
||||
tvs.setActive(viewState.isActive());
|
||||
@@ -41,6 +45,9 @@ public class TabStateViewAdapter {
|
||||
try {
|
||||
JNode node = loadJNode(mw, tvs);
|
||||
if (node == null) {
|
||||
if (UiUtils.JADX_GUI_DEBUG) {
|
||||
LOG.warn("Can't restore view for {}", tvs);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
EditorViewState viewState = new EditorViewState(node, tvs.getSubPath(), tvs.getCaret(), tvs.getView().toPoint());
|
||||
@@ -67,8 +74,16 @@ public class TabStateViewAdapter {
|
||||
break;
|
||||
|
||||
case "resource":
|
||||
JResource tmpNode = new JResource(null, tvs.getTabPath(), JResource.JResType.FILE);
|
||||
return mw.getTreeRoot().searchNode(tmpNode); // equals method in JResource check only name
|
||||
return mw.getTreeRoot().searchResourceByName(tvs.getTabPath());
|
||||
|
||||
case "sub-resource":
|
||||
String[] parts = tvs.getTabPath().split(JSubResource.SUB_RES_PREFIX);
|
||||
JResource baseRes = mw.getTreeRoot().searchResourceByName(parts[0]);
|
||||
if (baseRes != null) {
|
||||
String subName = parts[1];
|
||||
return baseRes.searchDepthNode(n -> n.getName().equals(subName)); // will load node before search
|
||||
}
|
||||
return null;
|
||||
|
||||
case "script":
|
||||
return mw.getTreeRoot()
|
||||
@@ -87,6 +102,12 @@ public class TabStateViewAdapter {
|
||||
tvs.setTabPath(((JClass) node).getCls().getRawName());
|
||||
return true;
|
||||
}
|
||||
if (node instanceof JSubResource) {
|
||||
JSubResource subRes = (JSubResource) node;
|
||||
tvs.setType("sub-resource");
|
||||
tvs.setTabPath(subRes.getBaseRes().getName() + JSubResource.SUB_RES_PREFIX + subRes.getName());
|
||||
return true;
|
||||
}
|
||||
if (node instanceof JResource) {
|
||||
tvs.setType("resource");
|
||||
tvs.setTabPath(node.getName());
|
||||
|
||||
@@ -91,4 +91,9 @@ public class TabViewState {
|
||||
public void setPreviewTab(boolean previewTab) {
|
||||
this.previewTab = previewTab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TabViewState{type=" + type + ", tabPath=" + tabPath + ", subPath=" + subPath + '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,12 @@ public abstract class JLoadableNode extends JNode {
|
||||
return super.searchNode(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JNode searchDepthNode(Predicate<JNode> filter) {
|
||||
loadNode();
|
||||
return super.searchDepthNode(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JNode removeNode(Predicate<JNode> filter) {
|
||||
loadNode();
|
||||
|
||||
@@ -140,6 +140,17 @@ public abstract class JNode extends DefaultMutableTreeNode implements ITreeNode,
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable JNode searchDepthNode(Predicate<JNode> filter) {
|
||||
Enumeration<?> en = this.breadthFirstEnumeration();
|
||||
while (en.hasMoreElements()) {
|
||||
JNode node = (JNode) en.nextElement();
|
||||
if (filter.test(node)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return first found node
|
||||
*/
|
||||
|
||||
@@ -34,16 +34,6 @@ public class JResSearchNode extends JNode {
|
||||
return resNode.getJParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeLongStringHtml() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return resNode.getIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return resNode.getName();
|
||||
@@ -54,6 +44,31 @@ public class JResSearchNode extends JNode {
|
||||
return resNode.makeString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeLongString() {
|
||||
return resNode.makeLongString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeLongStringHtml() {
|
||||
return resNode.makeLongStringHtml();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltip() {
|
||||
return resNode.getTooltip();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableHtml() {
|
||||
return resNode.disableHtml();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return resNode.getIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDescString() {
|
||||
return !StringUtils.isEmpty(text);
|
||||
|
||||
@@ -77,11 +77,14 @@ public class JResource extends JLoadableNode {
|
||||
private transient List<JResource> subNodes = Collections.emptyList();
|
||||
private transient ICodeInfo content = ICodeInfo.EMPTY;
|
||||
|
||||
public JResource(ResourceFile resFile, String name, JResType type) {
|
||||
public JResource(@Nullable ResourceFile resFile, String name, JResType type) {
|
||||
this(resFile, name, name, type);
|
||||
}
|
||||
|
||||
public JResource(ResourceFile resFile, String name, String shortName, JResType type) {
|
||||
public JResource(@Nullable ResourceFile resFile, String name, String shortName, JResType type) {
|
||||
if (resFile == null && type == JResType.FILE) {
|
||||
throw new IllegalArgumentException("Null resource file");
|
||||
}
|
||||
this.resFile = resFile;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -217,7 +220,7 @@ public class JResource extends JLoadableNode {
|
||||
}
|
||||
if (rc.getDataType() == ResContainer.DataType.RES_TABLE) {
|
||||
ICodeInfo codeInfo = loadCurrentSingleRes(rc);
|
||||
List<JResource> nodes = ResTableHelper.buildTree(rc);
|
||||
List<JResource> nodes = ResTableHelper.buildTree(this, rc);
|
||||
sortResNodes(nodes);
|
||||
subNodes = nodes;
|
||||
UiUtils.uiRun(this::update);
|
||||
|
||||
@@ -66,7 +66,7 @@ public class JRoot extends JNode {
|
||||
int count = parts.length;
|
||||
for (int i = 0; i < count - 1; i++) {
|
||||
String name = parts[i];
|
||||
JResource subRF = getResourceByName(curRf, name);
|
||||
JResource subRF = getSubNodeByName(curRf, name);
|
||||
if (subRF == null) {
|
||||
subRF = new JResource(null, name, JResType.DIR);
|
||||
curRf.addSubNode(subRF);
|
||||
@@ -81,7 +81,7 @@ public class JRoot extends JNode {
|
||||
return root;
|
||||
}
|
||||
|
||||
private JResource getResourceByName(JResource rf, String name) {
|
||||
public static JResource getSubNodeByName(JResource rf, String name) {
|
||||
for (JResource sub : rf.getSubNodes()) {
|
||||
if (sub.getName().equals(name)) {
|
||||
return sub;
|
||||
@@ -90,6 +90,20 @@ public class JRoot extends JNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable JResource searchResourceByName(String name) {
|
||||
Enumeration<?> en = this.breadthFirstEnumeration();
|
||||
while (en.hasMoreElements()) {
|
||||
Object obj = en.nextElement();
|
||||
if (obj instanceof JResource) {
|
||||
JResource res = (JResource) obj;
|
||||
if (res.getName().equals(name)) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable JNode searchNode(JNode node) {
|
||||
Enumeration<?> en = this.breadthFirstEnumeration();
|
||||
while (en.hasMoreElements()) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package jadx.gui.treemodel;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
|
||||
/**
|
||||
* Resource inside resource file.
|
||||
* Add base file prefix to distinguish from other files.
|
||||
*/
|
||||
public class JSubResource extends JResource {
|
||||
public static final String SUB_RES_PREFIX = ":/";
|
||||
|
||||
public JResource baseRes;
|
||||
|
||||
public JSubResource(JResource baseRes, @Nullable ResourceFile resFile, String name, String shortName, JResType type) {
|
||||
super(resFile, name, shortName, type);
|
||||
this.baseRes = Objects.requireNonNull(baseRes);
|
||||
}
|
||||
|
||||
public JResource getBaseRes() {
|
||||
return baseRes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeLongString() {
|
||||
return baseRes.makeLongString() + SUB_RES_PREFIX + super.makeLongString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof JSubResource)) {
|
||||
return false;
|
||||
}
|
||||
JSubResource other = (JSubResource) o;
|
||||
return baseRes.equals(other.baseRes)
|
||||
&& getName().equals(other.getName())
|
||||
&& getType().equals(other.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return baseRes.hashCode() + 31 * super.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import jadx.api.ResourceFileContent;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
import jadx.gui.treemodel.JSubResource;
|
||||
|
||||
public class ResTableHelper {
|
||||
|
||||
@@ -20,18 +21,18 @@ public class ResTableHelper {
|
||||
*
|
||||
* @return root nodes
|
||||
*/
|
||||
public static List<JResource> buildTree(ResContainer resTable) {
|
||||
ResTableHelper resTableHelper = new ResTableHelper(resTable.getFileName());
|
||||
public static List<JResource> buildTree(JResource resTableRes, ResContainer resTable) {
|
||||
ResTableHelper resTableHelper = new ResTableHelper(resTableRes);
|
||||
resTableHelper.process(resTable);
|
||||
return resTableHelper.roots;
|
||||
}
|
||||
|
||||
private final List<JResource> roots = new ArrayList<>();
|
||||
private final Map<String, JResource> dirs = new HashMap<>();
|
||||
private final String resNamePrefix;
|
||||
private final JResource resTableRes;
|
||||
|
||||
private ResTableHelper(String resTableFileName) {
|
||||
this.resNamePrefix = resTableFileName + "/";
|
||||
private ResTableHelper(JResource resTableRes) {
|
||||
this.resTableRes = resTableRes;
|
||||
}
|
||||
|
||||
private void process(ResContainer resTable) {
|
||||
@@ -54,7 +55,7 @@ public class ResTableHelper {
|
||||
}
|
||||
ICodeInfo code = rc.getText();
|
||||
ResourceFileContent fileContent = new ResourceFileContent(name, ResourceType.XML, code);
|
||||
JResource resFile = new JResource(fileContent, resNamePrefix + resName, name, JResource.JResType.FILE);
|
||||
JResource resFile = new JSubResource(resTableRes, fileContent, resName, name, JResource.JResType.FILE);
|
||||
addResFile(dir, resFile);
|
||||
|
||||
for (ResContainer subFile : rc.getSubFiles()) {
|
||||
@@ -82,7 +83,7 @@ public class ResTableHelper {
|
||||
JResource curDir = dirs.get(path);
|
||||
if (curDir == null) {
|
||||
String dirName = last ? dir.substring(prevStart) : dir.substring(prevStart, splitPos);
|
||||
curDir = new JResource(null, resNamePrefix + path, dirName, JResource.JResType.DIR);
|
||||
curDir = new JSubResource(resTableRes, null, path, dirName, JResource.JResType.DIR);
|
||||
dirs.put(path, curDir);
|
||||
if (parentDir == null) {
|
||||
roots.add(curDir);
|
||||
|
||||
Reference in New Issue
Block a user