refactor: use package nodes in api and ui
This commit is contained in:
@@ -6,7 +6,6 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -45,6 +44,7 @@ import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.export.ExportGradleProject;
|
||||
@@ -444,25 +444,7 @@ public final class JadxDecompiler implements IJadxDecompiler, Closeable {
|
||||
}
|
||||
|
||||
public List<JavaPackage> getPackages() {
|
||||
List<JavaClass> classList = getClasses();
|
||||
if (classList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<String, List<JavaClass>> map = new HashMap<>();
|
||||
for (JavaClass javaClass : classList) {
|
||||
String pkg = javaClass.getPackage();
|
||||
List<JavaClass> clsList = map.computeIfAbsent(pkg, k -> new ArrayList<>());
|
||||
clsList.add(javaClass);
|
||||
}
|
||||
List<JavaPackage> packages = new ArrayList<>(map.size());
|
||||
for (Map.Entry<String, List<JavaClass>> entry : map.entrySet()) {
|
||||
packages.add(new JavaPackage(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
Collections.sort(packages);
|
||||
for (JavaPackage pkg : packages) {
|
||||
pkg.getClasses().sort(Comparator.comparing(JavaClass::getName, String.CASE_INSENSITIVE_ORDER));
|
||||
}
|
||||
return Collections.unmodifiableList(packages);
|
||||
return Utils.collectionMap(root.getPackages(), this::convertPackageNode);
|
||||
}
|
||||
|
||||
public int getErrorsCount() {
|
||||
@@ -545,6 +527,26 @@ public final class JadxDecompiler implements IJadxDecompiler, Closeable {
|
||||
return javaMethod;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
synchronized JavaPackage convertPackageNode(PackageNode pkg) {
|
||||
JavaPackage foundPkg = pkg.getJavaNode();
|
||||
if (foundPkg != null) {
|
||||
return foundPkg;
|
||||
}
|
||||
List<JavaClass> clsList = Utils.collectionMap(pkg.getClasses(), this::convertClassNode);
|
||||
int subPkgsCount = pkg.getSubPackages().size();
|
||||
List<JavaPackage> subPkgs = subPkgsCount == 0 ? Collections.emptyList() : new ArrayList<>(subPkgsCount);
|
||||
JavaPackage javaPkg = new JavaPackage(pkg, clsList, subPkgs);
|
||||
if (subPkgsCount != 0) {
|
||||
// add subpackages after parent to avoid endless recursion
|
||||
for (PackageNode subPackage : pkg.getSubPackages()) {
|
||||
subPkgs.add(convertPackageNode(subPackage));
|
||||
}
|
||||
}
|
||||
pkg.setJavaNode(javaPkg);
|
||||
return javaPkg;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JavaClass searchJavaClassByOrigFullName(String fullName) {
|
||||
return getRoot().getClasses().stream()
|
||||
|
||||
@@ -18,8 +18,7 @@ public interface JavaNode {
|
||||
|
||||
List<JavaNode> getUseIn();
|
||||
|
||||
default void removeAlias() {
|
||||
}
|
||||
void removeAlias();
|
||||
|
||||
boolean isOwnCodeAnnotation(ICodeAnnotation ann);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,85 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import jadx.api.metadata.ICodeAnnotation;
|
||||
import jadx.core.dex.info.PackageInfo;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
|
||||
public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||
private final String name;
|
||||
private final PackageNode pkgNode;
|
||||
private final List<JavaClass> classes;
|
||||
private final List<JavaPackage> subPkgs;
|
||||
|
||||
JavaPackage(String name, List<JavaClass> classes) {
|
||||
this.name = name;
|
||||
JavaPackage(PackageNode pkgNode, List<JavaClass> classes, List<JavaPackage> subPkgs) {
|
||||
this.pkgNode = pkgNode;
|
||||
this.classes = classes;
|
||||
this.subPkgs = subPkgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
return pkgNode.getAliasPkgInfo().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
// TODO: store full package name
|
||||
return name;
|
||||
return pkgNode.getAliasPkgInfo().getFullName();
|
||||
}
|
||||
|
||||
public String getRawName() {
|
||||
return pkgNode.getPkgInfo().getName();
|
||||
}
|
||||
|
||||
public String getRawFullName() {
|
||||
return pkgNode.getPkgInfo().getFullName();
|
||||
}
|
||||
|
||||
public List<JavaPackage> getSubPackages() {
|
||||
return subPkgs;
|
||||
}
|
||||
|
||||
public List<JavaClass> getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return pkgNode.isRoot();
|
||||
}
|
||||
|
||||
public boolean isLeaf() {
|
||||
return pkgNode.isLeaf();
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return getFullName().isEmpty();
|
||||
}
|
||||
|
||||
public void rename(String alias) {
|
||||
pkgNode.rename(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAlias() {
|
||||
pkgNode.removeAlias();
|
||||
}
|
||||
|
||||
public boolean isParentRenamed() {
|
||||
PackageInfo parent = pkgNode.getPkgInfo().getParentPkg();
|
||||
PackageInfo aliasParent = pkgNode.getAliasPkgInfo().getParentPkg();
|
||||
return !Objects.equals(parent, aliasParent);
|
||||
}
|
||||
|
||||
@Internal
|
||||
public PackageNode getPkgNode() {
|
||||
return pkgNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return null;
|
||||
@@ -48,7 +97,16 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||
|
||||
@Override
|
||||
public List<JavaNode> getUseIn() {
|
||||
return Collections.emptyList();
|
||||
List<JavaNode> list = new ArrayList<>();
|
||||
addUseIn(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public void addUseIn(List<JavaNode> list) {
|
||||
list.addAll(classes);
|
||||
for (JavaPackage subPkg : subPkgs) {
|
||||
subPkg.addUseIn(list);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,7 +116,7 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull JavaPackage o) {
|
||||
return name.compareTo(o.name);
|
||||
return pkgNode.compareTo(o.pkgNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,16 +128,16 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||
return false;
|
||||
}
|
||||
JavaPackage that = (JavaPackage) o;
|
||||
return name.equals(that.name);
|
||||
return pkgNode.equals(that.pkgNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
return pkgNode.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
return pkgNode.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,10 @@ public class JavaVariable implements JavaNode {
|
||||
return Collections.singletonList(mth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAlias() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
|
||||
if (ann.getAnnType() == ICodeAnnotation.AnnType.VAR_REF) {
|
||||
|
||||
@@ -85,4 +85,12 @@ public class JadxCodeRename implements ICodeRename {
|
||||
public int hashCode() {
|
||||
return 31 * getNodeRef().hashCode() + Objects.hashCode(getCodeRef());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JadxCodeRename{" + nodeRef
|
||||
+ ", codeRef=" + codeRef
|
||||
+ ", newName='" + newName + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import static jadx.core.utils.StringUtils.notEmpty;
|
||||
|
||||
public class NameMapper {
|
||||
|
||||
private static final Pattern VALID_JAVA_IDENTIFIER = Pattern.compile(
|
||||
public static final Pattern VALID_JAVA_IDENTIFIER = Pattern.compile(
|
||||
"\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");
|
||||
|
||||
private static final Pattern VALID_JAVA_FULL_IDENTIFIER = Pattern.compile(
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Objects;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.core.dex.info.PackageInfo;
|
||||
|
||||
public class PackageNode implements IPackageUpdate, IDexNode, Comparable<PackageNode> {
|
||||
@@ -19,6 +20,8 @@ public class PackageNode implements IPackageUpdate, IDexNode, Comparable<Package
|
||||
|
||||
private PackageInfo aliasPkgInfo;
|
||||
|
||||
private JavaPackage javaNode;
|
||||
|
||||
public static PackageNode getForClass(RootNode root, String fullPkg, ClassNode cls) {
|
||||
PackageNode pkg = getOrBuild(root, fullPkg);
|
||||
pkg.getClasses().add(cls);
|
||||
@@ -144,6 +147,10 @@ public class PackageNode implements IPackageUpdate, IDexNode, Comparable<Package
|
||||
return !Objects.equals(pkgInfo.getParentPkg(), aliasPkgInfo.getParentPkg());
|
||||
}
|
||||
|
||||
public void removeAlias() {
|
||||
aliasPkgInfo = pkgInfo;
|
||||
}
|
||||
|
||||
public PackageNode getParentPkg() {
|
||||
return parentPkg;
|
||||
}
|
||||
@@ -168,6 +175,14 @@ public class PackageNode implements IPackageUpdate, IDexNode, Comparable<Package
|
||||
return classes;
|
||||
}
|
||||
|
||||
public JavaPackage getJavaNode() {
|
||||
return javaNode;
|
||||
}
|
||||
|
||||
public void setJavaNode(JavaPackage javaNode) {
|
||||
this.javaNode = javaNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "package";
|
||||
|
||||
@@ -302,6 +302,9 @@ public class RootNode implements IRootNode {
|
||||
}
|
||||
}
|
||||
classes.forEach(ClassNode::updateParentClass);
|
||||
for (PackageNode pkg : packages) {
|
||||
pkg.getClasses().removeIf(ClassNode::isInner);
|
||||
}
|
||||
}
|
||||
|
||||
public void mergePasses(Map<JadxPassType, List<JadxPass>> customPasses) {
|
||||
|
||||
@@ -225,7 +225,7 @@ public class DebugUtils {
|
||||
}
|
||||
|
||||
public static void printStackTrace(String label) {
|
||||
LOG.debug("StackTrace: {}\n{}", label, Utils.getStackTrace(new Exception()));
|
||||
LOG.debug("StackTrace: {}\n{}", label, Utils.getFullStackTrace(new Exception()));
|
||||
}
|
||||
|
||||
public static void printMethodOverrideTop(RootNode root) {
|
||||
|
||||
@@ -3,10 +3,12 @@ package jadx.core.utils;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -14,6 +16,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -116,13 +119,23 @@ public class Utils {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getFullStackTrace(Throwable throwable) {
|
||||
return getStackTrace(throwable, false);
|
||||
}
|
||||
|
||||
public static String getStackTrace(Throwable throwable) {
|
||||
return getStackTrace(throwable, true);
|
||||
}
|
||||
|
||||
private static String getStackTrace(Throwable throwable, boolean filter) {
|
||||
if (throwable == null) {
|
||||
return "";
|
||||
}
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw, true);
|
||||
filterRecursive(throwable);
|
||||
if (filter) {
|
||||
filterRecursive(throwable);
|
||||
}
|
||||
throwable.printStackTrace(pw);
|
||||
return sw.getBuffer().toString();
|
||||
}
|
||||
@@ -345,6 +358,27 @@ public class Utils {
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple DFS visit for tree (cycles not allowed)
|
||||
*/
|
||||
public static <T> void treeDfsVisit(T root, Function<T, List<T>> childrenProvider, Consumer<T> visitor) {
|
||||
multiRootTreeDfsVisit(Collections.singletonList(root), childrenProvider, visitor);
|
||||
}
|
||||
|
||||
public static <T> void multiRootTreeDfsVisit(List<T> roots, Function<T, List<T>> childrenProvider, Consumer<T> visitor) {
|
||||
Deque<T> queue = new ArrayDeque<>(roots);
|
||||
while (true) {
|
||||
T current = queue.pollLast();
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
visitor.accept(current);
|
||||
for (T child : childrenProvider.apply(current)) {
|
||||
queue.addLast(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T> T getOne(@Nullable List<T> list) {
|
||||
if (list == null || list.size() != 1) {
|
||||
|
||||
Reference in New Issue
Block a user