Skip to content

Commit

Permalink
Fix case where tree removal would fail, resulting in ghost paths bein…
Browse files Browse the repository at this point in the history
…g accessible in the UI
  • Loading branch information
Col-E committed Sep 23, 2023
1 parent aa0dfd3 commit ebedb30
Show file tree
Hide file tree
Showing 21 changed files with 597 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public abstract class AbstractPathNode<P, V> implements PathNode<V> {
* @param value
* Value instance.
*/
protected AbstractPathNode(@Nullable String id, @Nullable PathNode<P> parent, @Nonnull Class<V> valueType, @Nonnull V value) {
protected AbstractPathNode(@Nonnull String id, @Nullable PathNode<P> parent, @Nonnull Class<V> valueType, @Nonnull V value) {
this.id = id;
this.parent = parent;
this.valueType = valueType;
Expand Down Expand Up @@ -58,15 +58,13 @@ protected P parentValue() {
protected int cmpHierarchy(@Nonnull PathNode<?> path) {
if (getValueType() != path.getValueType()) {
// Check direct parent (quicker validation) and then if that does not pass, a multi-level descendant test.
if ((parent != null && parent.idMatch(path)) ||
isDescendantOf(path)) {
if ((parent != null && parent.typeIdMatch(path)) || isDescendantOf(path)) {
// We are the child type, show last.
return 1;
}

// Check direct parent (quicker validation) and then if that does not pass, a multi-level descendant test.
if ((path.getParent() != null && idMatch(path.getParent())) ||
path.isDescendantOf(this)) {
if ((path.getParent() != null && typeIdMatch(path.getParent())) || path.isDescendantOf(this)) {
// We are the parent type, show first.
return -1;
}
Expand Down Expand Up @@ -105,7 +103,7 @@ public <T, I extends PathNode<T>> I getParentOfType(@Nonnull Class<T> type) {

@Nonnull
@Override
public String id() {
public String typeId() {
return id;
}

Expand All @@ -131,22 +129,6 @@ public V getValue() {
return value;
}

@Override
public boolean isDescendantOf(@Nonnull PathNode<?> other) {
if (idMatch(other)) {
// Same id as other, so must be same type. Compare local values.
return localCompare(other) >= 0;
}
if (parent != null) {
if (parent.idMatch(other)) {
// Parent has same id to the other, compare the parent's local values.
return parent.localCompare(other) >= 0;
}
return parent.isDescendantOf(other);
}
return false;
}

@Override
public int compareTo(@Nonnull PathNode<?> o) {
if (this == o) return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@
import software.coley.recaf.info.annotation.Annotated;
import software.coley.recaf.info.annotation.AnnotationInfo;

import java.util.Set;

/**
* Path node for annotations on {@link Annotated} types such as classes, fields, and methods..
*
* @author Matt Coley
*/
public class AnnotationPathNode extends AbstractPathNode<Object, AnnotationInfo> {
/**
* Type identifier for annotation nodes.
*/
public static final String TYPE_ID = "annotation";

/**
* Node without parent.
*
Expand All @@ -36,7 +43,7 @@ public AnnotationPathNode(@Nonnull AnnotationInfo annotation) {
*/
@SuppressWarnings("unchecked")
public AnnotationPathNode(@Nullable PathNode<?> parent, @Nonnull AnnotationInfo annotation) {
super("annotation", (PathNode<Object>) parent, AnnotationInfo.class, annotation);
super(TYPE_ID, (PathNode<Object>) parent, AnnotationInfo.class, annotation);
}

/**
Expand All @@ -50,6 +57,12 @@ public AnnotationPathNode child(@Nonnull AnnotationInfo annotation) {
return new AnnotationPathNode(this, annotation);
}

@Nonnull
@Override
public Set<String> directParentTypeIds() {
return Set.of(ClassPathNode.TYPE_ID, ClassMemberPathNode.TYPE_ID, AnnotationPathNode.TYPE_ID);
}

@Override
public int localCompare(PathNode<?> o) {
if (o instanceof AnnotationPathNode node) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
*/
@SuppressWarnings("rawtypes")
public class BundlePathNode extends AbstractPathNode<WorkspaceResource, Bundle> {
/**
* Type identifier for bundle nodes.
*/
public static final String TYPE_ID = "bundle";

/**
* Node without parent.
*
Expand All @@ -38,7 +43,7 @@ public BundlePathNode(@Nonnull Bundle<?> bundle) {
* @see ResourcePathNode#child(Bundle)
*/
public BundlePathNode(@Nullable ResourcePathNode parent, @Nonnull Bundle<?> bundle) {
super("bundle", parent, Bundle.class, bundle);
super(TYPE_ID, parent, Bundle.class, bundle);
}

/**
Expand Down Expand Up @@ -102,6 +107,12 @@ public ResourcePathNode getParent() {
return (ResourcePathNode) super.getParent();
}

@Nonnull
@Override
public Set<String> directParentTypeIds() {
return Set.of(ResourcePathNode.TYPE_ID);
}

@Override
public int localCompare(PathNode<?> o) {
if (o instanceof BundlePathNode bundlePathNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
import software.coley.recaf.info.member.ClassMember;
import software.coley.recaf.info.member.MethodMember;

import java.util.Set;

/**
* Path node for {@code catch(Exception)} within {@link MethodMember} instances.
*
* @author Matt Coley
*/
public class CatchPathNode extends AbstractPathNode<ClassMember, String> {
/**
* Type identifier for catch nodes.
*/
public static final String TYPE_ID = "catch";

/**
* Node without parent.
*
Expand All @@ -32,14 +39,20 @@ public CatchPathNode(@Nonnull String type) {
* @see ClassMemberPathNode#childCatch(String)
*/
public CatchPathNode(@Nullable ClassMemberPathNode parent, @Nonnull String type) {
super("catch", parent, String.class, type);
super(TYPE_ID, parent, String.class, type);
}

@Override
public ClassMemberPathNode getParent() {
return (ClassMemberPathNode) super.getParent();
}

@Nonnull
@Override
public Set<String> directParentTypeIds() {
return Set.of(ClassMemberPathNode.TYPE_ID);
}

@Override
public int localCompare(PathNode<?> o) {
if (o instanceof CatchPathNode node) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@
import software.coley.recaf.info.member.LocalVariable;

import java.util.List;
import java.util.Set;

/**
* Path node for {@link ClassMember} types.
*
* @author Matt Coley
*/
public class ClassMemberPathNode extends AbstractPathNode<ClassInfo, ClassMember> {
/**
* Type identifier for member nodes.
*/
public static final String TYPE_ID = "member";

/**
* Node without parent.
*
Expand All @@ -38,7 +44,7 @@ public ClassMemberPathNode(@Nonnull ClassMember member) {
* @see ClassPathNode#child(ClassMember)
*/
public ClassMemberPathNode(@Nullable ClassPathNode parent, @Nonnull ClassMember member) {
super("member", parent, ClassMember.class, member);
super(TYPE_ID, parent, ClassMember.class, member);
}

/**
Expand Down Expand Up @@ -123,6 +129,12 @@ public ClassPathNode getParent() {
return (ClassPathNode) super.getParent();
}

@Nonnull
@Override
public Set<String> directParentTypeIds() {
return Set.of(ClassPathNode.TYPE_ID);
}

@Override
public int localCompare(PathNode<?> o) {
if (o instanceof ClassMemberPathNode classMemberNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
import software.coley.recaf.info.annotation.AnnotationInfo;
import software.coley.recaf.info.member.ClassMember;

import java.util.Set;

/**
* Path node for {@link ClassInfo} types.
*
* @author Matt Coley
*/
public class ClassPathNode extends AbstractPathNode<String, ClassInfo> {
/**
* Type identifier for class nodes.
*/
public static final String TYPE_ID = "class";

/**
* Node without parent.
*
Expand All @@ -34,7 +41,7 @@ public ClassPathNode(@Nonnull ClassInfo info) {
* @see DirectoryPathNode#child(ClassInfo)
*/
public ClassPathNode(@Nullable DirectoryPathNode parent, @Nonnull ClassInfo info) {
super("class", parent, ClassInfo.class, info);
super(TYPE_ID, parent, ClassInfo.class, info);
}

/**
Expand Down Expand Up @@ -75,6 +82,12 @@ public DirectoryPathNode getParent() {
return (DirectoryPathNode) super.getParent();
}

@Nonnull
@Override
public Set<String> directParentTypeIds() {
return Set.of(DirectoryPathNode.TYPE_ID);
}

@Override
public int localCompare(PathNode<?> o) {
if (o instanceof ClassPathNode classPathNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@
import software.coley.recaf.info.FileInfo;
import software.coley.recaf.workspace.model.bundle.Bundle;

import java.util.Set;

/**
* Path node for packages of {@link ClassInfo} and directories of {@link FileInfo} types.
*
* @author Matt Coley
*/
@SuppressWarnings("rawtypes")
public class DirectoryPathNode extends AbstractPathNode<Bundle, String> {
/**
* Type identifier for directory nodes.
*/
public static final String TYPE_ID = "directory";

/**
* Node without parent.
*
Expand All @@ -34,7 +41,7 @@ public DirectoryPathNode(@Nonnull String directory) {
* @see BundlePathNode#child(String)
*/
public DirectoryPathNode(@Nullable BundlePathNode parent, @Nonnull String directory) {
super("directory", parent, String.class, directory);
super(TYPE_ID, parent, String.class, directory);
}

/**
Expand Down Expand Up @@ -76,11 +83,38 @@ public BundlePathNode getParent() {
return (BundlePathNode) super.getParent();
}

@Nonnull
@Override
public Set<String> directParentTypeIds() {
return Set.of(BundlePathNode.TYPE_ID, DirectoryPathNode.TYPE_ID);
}

@Override
public boolean hasEqualOrChildValue(@Nonnull PathNode<?> other) {
if (other instanceof DirectoryPathNode otherDirectory) {
String dir = getValue();
String maybeParentDir = otherDirectory.getValue();
return dir.startsWith(maybeParentDir);
}

return super.hasEqualOrChildValue(other);
}

@Override
public boolean isDescendantOf(@Nonnull PathNode<?> other) {
// Descendant check comparing between directories will check for containment within the local value's path.
// This way 'a/b/c' is seen as a descendant of 'a/b'.
if (typeId().equals(other.typeId()))
return hasEqualOrChildValue(other) && allParentsMatch(other);

return super.isDescendantOf(other);
}

@Override
public int localCompare(PathNode<?> o) {
if (o instanceof DirectoryPathNode classNode) {
if (o instanceof DirectoryPathNode pathNode) {
String name = getValue();
String otherName = classNode.getValue();
String otherName = pathNode.getValue();
return String.CASE_INSENSITIVE_ORDER.compare(name, otherName);
}
return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@
import jakarta.annotation.Nullable;
import software.coley.recaf.info.FileInfo;

import java.util.Set;

/**
* Path node for {@link FileInfo} types.
*
* @author Matt Coley
*/
public class FilePathNode extends AbstractPathNode<String, FileInfo> {
/**
* Type identifier for file nodes.
*/
public static final String TYPE_ID = "file";

/**
* Node without parent.
*
Expand All @@ -31,7 +38,7 @@ public FilePathNode(@Nonnull FileInfo info) {
* @see DirectoryPathNode#child(FileInfo)
*/
public FilePathNode(@Nullable DirectoryPathNode parent, @Nonnull FileInfo info) {
super("file", parent, FileInfo.class, info);
super(TYPE_ID, parent, FileInfo.class, info);
}

/**
Expand All @@ -50,6 +57,12 @@ public DirectoryPathNode getParent() {
return (DirectoryPathNode) super.getParent();
}

@Nonnull
@Override
public Set<String> directParentTypeIds() {
return Set.of(DirectoryPathNode.TYPE_ID);
}

@Override
public int localCompare(PathNode<?> o) {
if (o instanceof FilePathNode fileNode) {
Expand Down
Loading

0 comments on commit ebedb30

Please sign in to comment.