Skip to content

Commit

Permalink
Add removed item key tracking to bundles
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Aug 22, 2024
1 parent a4c7e91 commit 2dee74f
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public static GeneratedPhantomWorkspaceResource wrap(@Nonnull Map<String, byte[]
JvmClassInfo phantomClassInfo = new JvmClassInfoBuilder(phantom).build();
bundle.initialPut(phantomClassInfo);
});
bundle.markInitialState();
return new GeneratedPhantomWorkspaceResource(new WorkspaceResourceBuilder()
.withJvmClassBundle(bundle));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import software.coley.collections.Unchecked;
import software.coley.recaf.analytics.logging.Logging;
import software.coley.recaf.info.Info;
import software.coley.recaf.workspace.model.resource.BasicWorkspaceResource;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -23,6 +24,8 @@ public class BasicBundle<I extends Info> implements Bundle<I> {
private final Map<String, Stack<I>> history = new ConcurrentHashMap<>();
private final List<BundleListener<I>> listeners = new CopyOnWriteArrayList<>();
private final Map<String, I> backing = new ConcurrentHashMap<>();
private final Set<String> initialKeys = ConcurrentHashMap.newKeySet();
private final NavigableSet<String> removed = Collections.synchronizedNavigableSet(new TreeSet<>());

/**
* Create initial history item.
Expand All @@ -41,12 +44,24 @@ private void initHistory(@Nonnull I info) {
*
* @param info
* Item to put.
*
* @see #markInitialState()
*/
public void initialPut(@Nonnull I info) {
backing.put(info.getName(), info);
initHistory(info);
}

/**
* Mark the current snapshot of items as the initial state of the bundle.
* <p>
* Called when {@link BasicWorkspaceResource} is constructed, so any bundle assigned to a resource should be
* automatically marked.
*/
public void markInitialState() {
initialKeys.addAll(backing.keySet());
}

/**
* Utility call for {@link #put(String, Info)}
*
Expand Down Expand Up @@ -88,6 +103,12 @@ public Set<String> getDirtyKeys() {
return dirty;
}

@Nonnull
@Override
public Set<String> getRemovedKeys() {
return Collections.unmodifiableNavigableSet(removed);
}

@Override
public boolean hasHistory(@Nonnull String key) {
return history.get(key) != null;
Expand Down Expand Up @@ -171,6 +192,10 @@ public I get(@Nonnull Object key) {
@Override
public I put(@Nonnull String key, @Nonnull I newValue) {
I oldValue = backing.put(key, newValue);

// Ensure we don't track entries by this name as 'removed'
removed.remove(key);

// Notify listeners
Unchecked.checkedForEach(listeners, listener -> {
if (oldValue == null) {
Expand All @@ -193,8 +218,15 @@ public I put(@Nonnull String key, @Nonnull I newValue) {
public I remove(@Nonnull Object key) {
I info = backing.remove(key);
if (info != null) {
String keyStr = (String) key;

// Mark the entry key as being removed, but only if it was in the initial key-set.
// Adding a file and removing it should not be tracked as a net-removal.
if (initialKeys.contains(keyStr))
removed.add(keyStr);

// Notify listeners
Unchecked.checkedForEach(listeners, listener -> listener.onRemoveItem((String) key, info),
Unchecked.checkedForEach(listeners, listener -> listener.onRemoveItem(keyStr, info),
(listener, t) -> logger.error("Exception thrown when removing bundle item", t));

// Update history
Expand All @@ -210,6 +242,7 @@ public void putAll(@Nonnull Map<? extends String, ? extends I> map) {

@Override
public void clear() {
removed.addAll(initialKeys);
backing.clear();
history.clear();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public interface Bundle<I extends Info> extends Map<String, I>, Iterable<I>, Clo
@Nonnull
Set<String> getDirtyKeys();

/**
* @return Keys of items that were part of the initial bundle contents but have since been removed.
*/
@Nonnull
Set<String> getRemovedKeys();

/**
* @param info
* Item to write.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
import software.coley.recaf.behavior.Closing;
import software.coley.recaf.info.AndroidClassInfo;
import software.coley.recaf.info.FileInfo;
import software.coley.recaf.info.Info;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.info.properties.BasicPropertyContainer;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.AndroidClassBundle;
import software.coley.recaf.workspace.model.bundle.BundleListener;
import software.coley.recaf.workspace.model.bundle.FileBundle;
import software.coley.recaf.workspace.model.bundle.JvmClassBundle;
import software.coley.recaf.workspace.model.bundle.*;

import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -87,6 +85,7 @@ public BasicWorkspaceResource(JvmClassBundle jvmClassBundle,
protected void setup() {
setupListenerDelegation();
linkToEmbedded();
markInitialBundleStates();
}

/**
Expand Down Expand Up @@ -204,6 +203,19 @@ private void linkToEmbedded() {
embeddedResources.values().forEach(resource -> resource.setContainingResource(this));
}

/**
* Mark the bundle as being in its initial state.
*/
private void markInitialBundleStates() {
// Since the bundles have been passed to our resource, we're going to assume that its fully constructed.
// Any changes after this point will be tracked as deviations from the state at this point.
bundleStream().forEach(bundle -> {
if (bundle instanceof BasicBundle<Info> basicBundle) {
basicBundle.markInitialState();
}
});
}

@Nonnull
@Override
public JvmClassBundle getJvmClassBundle() {
Expand Down

0 comments on commit 2dee74f

Please sign in to comment.