From 5484258e996576567ba47de6fb8823f0ccb45dfc Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 8 Dec 2024 03:12:53 -0500 Subject: [PATCH] Refactor WorkspaceProcessingService to allow registering custom processors --- .../transform/TransformationManager.java | 23 +++- .../workspace/WorkspaceProcessing.java | 47 -------- .../workspace/WorkspaceProcessingConfig.java | 20 ++++ .../workspace/WorkspaceProcessingService.java | 111 ++++++++++++++++++ .../workspace/WorkspaceProcessor.java | 8 +- .../ThrowablePropertyAssigningProcessor.java | 2 +- 6 files changed, 158 insertions(+), 53 deletions(-) delete mode 100644 recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessing.java create mode 100644 recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingConfig.java create mode 100644 recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingService.java diff --git a/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationManager.java b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationManager.java index ccc834892..209a26ad7 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationManager.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationManager.java @@ -15,7 +15,6 @@ import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; -import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -66,6 +65,28 @@ public TransformationManager(@Nonnull Map, this.config = new TransformationManagerConfig(); } + /** + * @param transformerClass + * Class of transformer to register. + * @param transformerSupplier + * Supplier of transformer instances. + * @param + * Transformer type. + */ + public void registerJvmClassTransformer(@Nonnull Class transformerClass, @Nonnull Supplier transformerSupplier) { + jvmTransformerSuppliers.put(Unchecked.cast(transformerClass), Unchecked.cast(transformerSupplier)); + } + + /** + * @param transformerClass + * Class of transformer to unregister. + * @param + * Transformer type. + */ + public void unregisterJvmClassTransformer(@Nonnull Class transformerClass) { + jvmTransformerSuppliers.remove(Unchecked.cast(transformerClass)); + } + @Nonnull @SuppressWarnings("unchecked") public T newJvmTransformer(@Nonnull Class type) throws TransformationException { diff --git a/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessing.java b/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessing.java deleted file mode 100644 index b700c0dcb..000000000 --- a/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessing.java +++ /dev/null @@ -1,47 +0,0 @@ -package software.coley.recaf.services.workspace; - -import jakarta.annotation.Nonnull; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; -import org.slf4j.Logger; -import software.coley.recaf.analytics.logging.Logging; -import software.coley.recaf.cdi.EagerInitialization; -import software.coley.recaf.cdi.WorkspaceScoped; -import software.coley.recaf.services.inheritance.InheritanceGraph; -import software.coley.recaf.workspace.model.Workspace; - -/** - * Applies all discovered {@link WorkspaceProcessor} instances to {@link Workspace} instances upon loading them via - * {@link WorkspaceManager#setCurrent(Workspace)}. - * - * @author Matt Coley - * @see WorkspaceProcessor Processor type to implement. - */ -@WorkspaceScoped -@EagerInitialization -public class WorkspaceProcessing { - private static final Logger logger = Logging.get(WorkspaceProcessing.class); - - /** - * Since this is an eager ({@link EagerInitialization}) {@link WorkspaceScoped} bean a new instance is created - * every time a workspace is opened. Since it takes in all discovered {@link WorkspaceProcessor} via the {@code processors} - * parameter, all processors are applied to the given workspace. - *
- * Any implementation of {@link WorkspaceProcessor} can thus be either a {@link ApplicationScoped} or {@link WorkspaceScoped} - * bean. The benefit of this is that {@link WorkspaceScoped} beans can {@link Inject} other {@link WorkspaceScoped} services - * such as {@link InheritanceGraph}. - * - * @param workspace - * Opened workspace. - * @param processors - * Discovered processors to apply. - */ - @Inject - public WorkspaceProcessing(@Nonnull Workspace workspace, @Nonnull Instance processors) { - for (WorkspaceProcessor processor : processors) { - logger.trace("Applying workspace processor: {}", processor.name()); - processor.onWorkspaceOpened(workspace); - } - } -} diff --git a/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingConfig.java b/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingConfig.java new file mode 100644 index 000000000..b21176041 --- /dev/null +++ b/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingConfig.java @@ -0,0 +1,20 @@ +package software.coley.recaf.services.workspace; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import software.coley.recaf.config.BasicConfigContainer; +import software.coley.recaf.config.ConfigGroups; +import software.coley.recaf.services.ServiceConfig; + +/** + * Config for {@link WorkspaceProcessingService}. + * + * @author Matt Coley + */ +@ApplicationScoped +public class WorkspaceProcessingConfig extends BasicConfigContainer implements ServiceConfig { + @Inject + public WorkspaceProcessingConfig() { + super(ConfigGroups.SERVICE_TRANSFORM, WorkspaceProcessingService.SERVICE_ID + CONFIG_SUFFIX); + } +} diff --git a/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingService.java b/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingService.java new file mode 100644 index 000000000..2f629de27 --- /dev/null +++ b/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessingService.java @@ -0,0 +1,111 @@ +package software.coley.recaf.services.workspace; + +import jakarta.annotation.Nonnull; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.inject.Inject; +import org.slf4j.Logger; +import software.coley.collections.Unchecked; +import software.coley.recaf.Bootstrap; +import software.coley.recaf.analytics.logging.Logging; +import software.coley.recaf.cdi.EagerInitialization; +import software.coley.recaf.services.Service; +import software.coley.recaf.workspace.model.Workspace; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * Applies all discovered {@link WorkspaceProcessor} instances to {@link Workspace} instances upon loading them via + * {@link WorkspaceManager#setCurrent(Workspace)}. + * + * @author Matt Coley + * @see WorkspaceProcessor Processor type to implement. + */ +@EagerInitialization +@ApplicationScoped +public class WorkspaceProcessingService implements Service { + public static final String SERVICE_ID = "workspace-processing"; + private static final Logger logger = Logging.get(WorkspaceProcessingService.class); + private final Map, Supplier> processorSuppliers = new IdentityHashMap<>(); + private final WorkspaceProcessingConfig config; + + /** + * @param workspaceManager + * Manager to facilitate listening to new opened workspaces. + * @param config + * Service config. + * @param processors + * Discovered processors to apply. + */ + @Inject + public WorkspaceProcessingService(@Nonnull WorkspaceManager workspaceManager, + @Nonnull WorkspaceProcessingConfig config, + @Nonnull Instance processors) { + this.config = config; + for (Instance.Handle handle : processors.handles()) { + Bean bean = handle.getBean(); + Class processorClass = Unchecked.cast(bean.getBeanClass()); + processorSuppliers.put(processorClass, () -> { + // Even though our processors may be @Dependent scoped, we need to do a new lookup each time we want + // a new instance to get our desired scope behavior. If we re-use the instance handle that is injected + // here then even @Dependent scoped beans will yield the same instance again and again. + return Bootstrap.get().get(processorClass); + }); + } + + // Apply processors when new workspace is opened + workspaceManager.addWorkspaceOpenListener(this::processWorkspace); + } + + /** + * @param processorClass + * Class of processor to register. + * @param processorSupplier + * Supplier of processor instances. + * @param + * Processor type. + */ + public void register(@Nonnull Class processorClass, @Nonnull Supplier processorSupplier) { + processorSuppliers.put(Unchecked.cast(processorClass), Unchecked.cast(processorSupplier)); + } + + /** + * @param processorClass + * Class of processor to unregister. + * @param + * Processor type. + */ + public void unregister(@Nonnull Class processorClass) { + processorSuppliers.remove(Unchecked.cast(processorClass)); + } + + /** + * Applies all processors to the given workspace. + * + * @param workspace + * Workspace to process. + */ + public void processWorkspace(@Nonnull Workspace workspace) { + for (Supplier processorSupplier : processorSuppliers.values()) { + WorkspaceProcessor processor = processorSupplier.get(); + + logger.trace("Applying workspace processor: {}", processor.name()); + processor.processWorkspace(workspace); + } + } + + @Nonnull + @Override + public String getServiceId() { + return SERVICE_ID; + } + + @Nonnull + @Override + public WorkspaceProcessingConfig getServiceConfig() { + return config; + } +} diff --git a/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessor.java b/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessor.java index 5b9a20073..a058d3e56 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessor.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/workspace/WorkspaceProcessor.java @@ -4,19 +4,19 @@ import software.coley.recaf.workspace.model.Workspace; /** - * Subtype of {@link WorkspaceOpenListener} for use in {@link WorkspaceProcessing}. + * Generic processor for use in {@link WorkspaceProcessingService}. * * @author Matt Coley - * @see WorkspaceProcessing Manages calling implementations of this type. + * @see WorkspaceProcessingService Manages calling implementations of this type. */ public interface WorkspaceProcessor { /** * Called when {@link WorkspaceManager#setCurrent(Workspace)} passes. * * @param workspace - * New workspace assigned. + * Workspace to process. */ - void onWorkspaceOpened(@Nonnull Workspace workspace); + void processWorkspace(@Nonnull Workspace workspace); /** * @return Post processing task name. diff --git a/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java b/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java index ed7cf3391..7a619dad9 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java @@ -39,7 +39,7 @@ public ThrowablePropertyAssigningProcessor(@Nonnull InheritanceGraphService grap } @Override - public void onWorkspaceOpened(@Nonnull Workspace workspace) { + public void processWorkspace(@Nonnull Workspace workspace) { inheritanceGraph.getVertex(THROWABLE).allChildren().forEach(vertex -> { ClassInfo classInfo = vertex.getValue(); ThrowableProperty.set(classInfo);