Scripts

What is a script?

A script is a single Java source file that is executed by users whenever they choose. They can be written as a full class to support similar capabilities to plugins such as service injection, or in a short-hand that offers automatic imports of most common utility classes, but no access to services.

Full class script

A full class script is just a regular class that defines a non-static void run(). The run() method is called whenever the user executes the script.

// ==Metadata==
// @name Hello world
// @description Prints hello world
// @version 1.0.0
// @author Author
// ==/Metadata==

class MyScript {
    // must define 'void run()'
    void run() {
        System.out.println("hello");
    }
}

You can access any of Recaf's services by declaring a constructor annotated with @Inject. More information on this is located further down the page.

Shorthand script

A shorthand script lets you write your logic without needing to declare a class and run() method. These shorthand scripts are given a variable reference to the current workspace, and a SLF4J logger. You can access the current workspace as workspace and the logger as log.

// ==Metadata== 
// @name What is open?
// @description Prints what kinda workspace is open
// @version 1.0.0
// @author Author
// ==/Metadata==

String name = "(empty)";
if (workspace != null)
    name = workspace.getClass().getSimpleName();
log.info("Workspace = {}", name);

Another example working with the provided workspace:

// Print out all enum names in the current workspace, if one is open.
if (workspace == null) return;
workspace.findClasses(Accessed::hasEnumModifier).stream()
    .map(c -> c.getValue().getName())
    .forEach(name -> log.info("Enum: {}", name));

Using services

Scripts in the simple form do not use services. Scripts using the full class form will be able to use services.

// ==Metadata==
// @name Content loader
// @description Script to load content from a pre-set path.
// @version 1.0.0
// @author Col-E
// ==/Metadata==

import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
import org.slf4j.Logger;
import software.coley.recaf.analytics.logging.Logging;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.services.workspace.WorkspaceManager;
import software.coley.recaf.services.workspace.io.ResourceImporter;
import software.coley.recaf.workspace.model.BasicWorkspace;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.resource.WorkspaceResource;
import java.nio.file.Paths;

@Dependent
public class LoadContentScript {
    private static final Logger logger = Logging.get("load-script");
    private final ResourceImporter importer;
    private final WorkspaceManager workspaceManager;

    // We're injecting the importer to load 'WorkspaceResource' instances from paths on our system
    // then we use the workspace manager to set the current workspace to the loaded content.
    @Inject
    public LoadContentScript(ResourceImporter importer, WorkspaceManager workspaceManager) {
        this.importer = importer;
        this.workspaceManager = workspaceManager;
    }

    // Scripts following the class model must define a 'void run()'
    public void run() {
        String path = "C:/Samples/Test.jar";
        try {
            // Load resource from path, wrap it in a basic workspace
            WorkspaceResource resource = importer.importResource(Paths.get(path));
            Workspace workspace = new BasicWorkspace(resource);

            // Assign the workspace so the UI displays its content
            workspaceManager.setCurrent(workspace);
        } catch (Exception ex) {
            logger.error("Failed to read content from '{}' - {}", path, ex.getMessage());
        }
    }
}

For the list of available services, see the service lists.