ContainerParameterRewritePlan.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.sandbox.jdt.container.api;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/**
* Immutable, AST-free rewrite description for one closed-source array parameter.
*
* <p>The first executable slice changes a one-dimensional reference array parameter
* to a list and translates only {@code length} reads. Enhanced-for iteration is
* retained and revalidated. Every other parameter use rejects the complete plan.</p>
*/
public record ContainerParameterRewritePlan(
String compilationUnitHandle,
String methodJavaElementHandle,
String parameterBindingKey,
int parameterIndex,
String targetInterfaceType,
TargetContainerContract targetContract,
List<ParameterEdit> edits) {
public ContainerParameterRewritePlan {
compilationUnitHandle= requiredText(
compilationUnitHandle, "compilationUnitHandle"); //$NON-NLS-1$
methodJavaElementHandle= requiredText(
methodJavaElementHandle, "methodJavaElementHandle"); //$NON-NLS-1$
parameterBindingKey= requiredText(
parameterBindingKey, "parameterBindingKey"); //$NON-NLS-1$
if (parameterIndex < 0) {
throw new IllegalArgumentException("parameterIndex must not be negative"); //$NON-NLS-1$
}
targetInterfaceType= requiredText(targetInterfaceType, "targetInterfaceType"); //$NON-NLS-1$
Objects.requireNonNull(targetContract, "targetContract"); //$NON-NLS-1$
edits= List.copyOf(Objects.requireNonNull(edits, "edits")); //$NON-NLS-1$
validateEdits(edits);
}
/** One parameter rewrite or verification anchored to immutable source evidence. */
public record ParameterEdit(EditKind kind, int sourceStart, int sourceLength) {
public ParameterEdit {
Objects.requireNonNull(kind, "kind"); //$NON-NLS-1$
if (sourceStart < 0 || sourceLength < 0) {
throw new IllegalArgumentException("Source range must not be negative"); //$NON-NLS-1$
}
}
}
/** Planning result retaining complete fail-closed diagnostics. */
public record PlanningResult(
Optional<ContainerParameterRewritePlan> plan,
List<PlanningDiagnostic> diagnostics) {
public PlanningResult {
plan= Objects.requireNonNull(plan, "plan"); //$NON-NLS-1$
diagnostics= List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics")); //$NON-NLS-1$
if (plan.isPresent() == !diagnostics.isEmpty()) {
throw new IllegalArgumentException(
"A parameter rewrite result must contain either one plan or diagnostics"); //$NON-NLS-1$
}
}
public static PlanningResult accepted(ContainerParameterRewritePlan plan) {
return new PlanningResult(Optional.of(plan), List.of());
}
public static PlanningResult rejected(List<PlanningDiagnostic> diagnostics) {
if (diagnostics.isEmpty()) {
throw new IllegalArgumentException(
"A rejected parameter rewrite requires diagnostics"); //$NON-NLS-1$
}
return new PlanningResult(Optional.empty(), diagnostics);
}
public boolean ready() {
return plan.isPresent();
}
}
/** One eligibility diagnostic. */
public record PlanningDiagnostic(DiagnosticKind kind, String message) {
public PlanningDiagnostic {
Objects.requireNonNull(kind, "kind"); //$NON-NLS-1$
message= requiredText(message, "message"); //$NON-NLS-1$
}
}
public enum EditKind {
CHANGE_PARAMETER_DECLARATION,
REPLACE_LENGTH_WITH_SIZE,
VERIFY_ENCOUNTER_ITERATION
}
public enum DiagnosticKind {
NOT_AUTOMATIC,
SIGNATURE_PLAN_NOT_CLOSED_SOURCE,
UNSUPPORTED_SIGNATURE_GROUP,
SIGNATURE_MEMBER_MISMATCH,
FLOW_NODE_MISMATCH,
UNSUPPORTED_TARGET,
INCOMPLETE_PARAMETER_PROFILE,
UNSUPPORTED_PARAMETER_USAGE,
UNSUPPORTED_EVIDENCE
}
private static void validateEdits(List<ParameterEdit> edits) {
if (edits.isEmpty()) {
throw new IllegalArgumentException("A parameter rewrite plan requires edits"); //$NON-NLS-1$
}
Set<EditKind> kinds= EnumSet.noneOf(EditKind.class);
for (ParameterEdit edit : edits) {
kinds.add(edit.kind());
}
if (!kinds.contains(EditKind.CHANGE_PARAMETER_DECLARATION)) {
throw new IllegalArgumentException(
"A parameter rewrite requires a declaration edit"); //$NON-NLS-1$
}
}
private static String requiredText(String value, String fieldName) {
String text= Objects.requireNonNull(value, fieldName).strip();
if (text.isEmpty()) {
throw new IllegalArgumentException(fieldName + " must not be empty"); //$NON-NLS-1$
}
return text;
}
}