WorkflowOperationLog.java

package org.hammer.audio.workflow;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/** Operation log that applies semantic workflow operations, supports replay and undo. */
public final class WorkflowOperationLog {

  private Workflow initialWorkflow;
  private Workflow current;
  private final List<WorkflowOperation> operationHistory;

  public WorkflowOperationLog(Workflow initialWorkflow) {
    this.initialWorkflow = Objects.requireNonNull(initialWorkflow, "initialWorkflow");
    this.current = initialWorkflow;
    this.operationHistory = new ArrayList<>();
  }

  public Workflow currentWorkflow() {
    return current;
  }

  public List<WorkflowOperation> operations() {
    return List.copyOf(operationHistory);
  }

  public Workflow apply(WorkflowOperation operation) {
    Objects.requireNonNull(operation, "operation");
    current = operation.apply(current);
    operationHistory.add(operation);
    return current;
  }

  public Workflow replay() {
    Workflow replayedWorkflow = initialWorkflow;
    for (WorkflowOperation operation : operationHistory) {
      replayedWorkflow = operation.apply(replayedWorkflow);
    }
    return replayedWorkflow;
  }

  public Workflow undoLast() {
    if (operationHistory.isEmpty()) {
      throw new IllegalStateException("No operation available to undo");
    }
    WorkflowOperation last = operationHistory.remove(operationHistory.size() - 1);
    WorkflowOperation inverse =
        last.inverseOperation()
            .orElseThrow(
                () ->
                    new UnsupportedOperationException(
                        "Operation has no inverse: " + last.getClass().getSimpleName()));
    current = inverse.apply(current);
    return current;
  }

  /**
   * Replaces the log state with a new workflow baseline and clears operation history.
   *
   * @param workflow new current and replay baseline workflow
   */
  public void reset(Workflow workflow) {
    this.initialWorkflow = Objects.requireNonNull(workflow, "workflow");
    this.current = workflow;
    this.operationHistory.clear();
  }
}