WorkflowChange.java
package org.hammer.audio.workflow.history;
import java.util.Objects;
import org.hammer.audio.workflow.Edge;
import org.hammer.audio.workflow.Node;
/**
* Sealed hierarchy of domain-level change atoms produced by a semantic workflow diff.
*
* <p>Each variant represents a single indivisible change between two {@link
* org.hammer.audio.workflow.Workflow} snapshots. Callers can switch exhaustively over all variants
* using a {@code switch} expression on the sealed type.
*
* <p>Owned by the semantic-analysis/history-projection layer. Must not contain UI, JGit or
* execution dependencies.
*/
public sealed interface WorkflowChange
permits WorkflowChange.NodeAdded,
WorkflowChange.NodeRemoved,
WorkflowChange.EdgeAdded,
WorkflowChange.EdgeRemoved,
WorkflowChange.ParameterChanged,
WorkflowChange.FieldChanged {
/** Semantic workflow element owning a field change. */
enum ElementKind {
WORKFLOW,
NODE,
EDGE
}
/**
* A node that is present in the <em>after</em> snapshot but absent in the <em>before</em>
* snapshot.
*
* @param node the added node
*/
record NodeAdded(Node node) implements WorkflowChange {
public NodeAdded {
Objects.requireNonNull(node, "node");
}
}
/**
* A node that is present in the <em>before</em> snapshot but absent in the <em>after</em>
* snapshot.
*
* @param node the removed node
*/
record NodeRemoved(Node node) implements WorkflowChange {
public NodeRemoved {
Objects.requireNonNull(node, "node");
}
}
/**
* An edge that is present in the <em>after</em> snapshot but absent in the <em>before</em>
* snapshot.
*
* @param edge the added edge
*/
record EdgeAdded(Edge edge) implements WorkflowChange {
public EdgeAdded {
Objects.requireNonNull(edge, "edge");
}
}
/**
* An edge that is present in the <em>before</em> snapshot but absent in the <em>after</em>
* snapshot.
*
* @param edge the removed edge
*/
record EdgeRemoved(Edge edge) implements WorkflowChange {
public EdgeRemoved {
Objects.requireNonNull(edge, "edge");
}
}
/**
* A metadata property on a node or edge whose value changed between the two snapshots.
*
* <p>If the property was absent before the change {@code oldValue} is {@code null}. If it was
* removed by the change {@code newValue} is {@code null}.
*
* @param targetId stable identifier of the node or edge that owns the property
* @param propertyKey stable metadata key
* @param oldValue value before the change, or {@code null} if the property was newly added
* @param newValue value after the change, or {@code null} if the property was removed
*/
record ParameterChanged(String targetId, String propertyKey, String oldValue, String newValue)
implements WorkflowChange {
public ParameterChanged {
if (targetId == null || targetId.isBlank()) {
throw new IllegalArgumentException("targetId must not be blank");
}
if (propertyKey == null || propertyKey.isBlank()) {
throw new IllegalArgumentException("propertyKey must not be blank");
}
if (Objects.equals(oldValue, newValue)) {
throw new IllegalArgumentException("A workflow change requires different values");
}
}
}
/**
* A non-legacy semantic field whose canonical value changed.
*
* <p>This variant extends the original metadata-focused diff without replacing the established
* {@link ParameterChanged} contract. It covers workflow name/metadata, node type/label/ports and
* edge endpoints.
*
* @param elementKind kind of workflow element owning the field
* @param targetId stable workflow, node or edge identifier
* @param fieldPath stable semantic field path
* @param oldValue canonical previous value, or {@code null} when added
* @param newValue canonical next value, or {@code null} when removed
*/
record FieldChanged(
ElementKind elementKind, String targetId, String fieldPath, String oldValue, String newValue)
implements WorkflowChange {
public FieldChanged {
Objects.requireNonNull(elementKind, "elementKind");
if (targetId == null || targetId.isBlank()) {
throw new IllegalArgumentException("targetId must not be blank");
}
if (fieldPath == null || fieldPath.isBlank()) {
throw new IllegalArgumentException("fieldPath must not be blank");
}
if (Objects.equals(oldValue, newValue)) {
throw new IllegalArgumentException("A workflow change requires different values");
}
}
}
}