ClosedSourceRecommendationRefiner.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.analysis;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.sandbox.jdt.container.api.ContainerFlowComponent;
import org.sandbox.jdt.container.api.ContainerFlowGraph.ClosureStatus;
import org.sandbox.jdt.container.api.ContainerFlowGraph.EdgeKind;
import org.sandbox.jdt.container.api.ContainerFlowGraph.NodeKind;
import org.sandbox.jdt.container.api.ContainerRecommendation;
import org.sandbox.jdt.container.api.ContainerRecommendation.Confidence;
import org.sandbox.jdt.container.api.ContainerRecommendation.ContractAssessment;
import org.sandbox.jdt.container.api.ContainerRecommendation.ContractProperty;
import org.sandbox.jdt.container.api.ContainerRecommendation.Preservation;
import org.sandbox.jdt.container.api.ContainerUsageProfile.AnalysisCompleteness;
/**
* Replaces the provisional signature assessment after a complete caller-to-parameter
* source closure has been proven.
*/
public final class ClosedSourceRecommendationRefiner {
/**
* Returns a recommendation whose signature property reflects the coordinated
* source migration, or empty when the supplied component is not the supported
* closed direct-call topology.
*/
public Optional<ContainerRecommendation> refine(
ContainerFlowComponent component,
ContainerRecommendation recommendation) {
Objects.requireNonNull(component, "component"); //$NON-NLS-1$
Objects.requireNonNull(recommendation, "recommendation"); //$NON-NLS-1$
if (!supported(component, recommendation)) {
return Optional.empty();
}
Map<ContractProperty, ContractAssessment> assessments=
new EnumMap<>(ContractProperty.class);
for (ContractAssessment assessment : recommendation.assessments()) {
ContractAssessment previous= assessments.putIfAbsent(
assessment.property(), assessment);
if (previous != null && !previous.equals(assessment)) {
return Optional.empty();
}
}
if (!preserved(assessments, ContractProperty.ALIASING)
|| !preserved(assessments, ContractProperty.CONCURRENCY)) {
return Optional.empty();
}
assessments.put(
ContractProperty.SIGNATURES,
new ContractAssessment(
ContractProperty.SIGNATURES,
Preservation.PRESERVED,
"The local declaration, exact source parameter and all known callers form one closed atomic migration component.")); //$NON-NLS-1$
List<ContractAssessment> ordered= new ArrayList<>(assessments.values());
ordered.sort(Comparator.comparingInt(assessment -> assessment.property().ordinal()));
return Optional.of(new ContainerRecommendation(
recommendation.sourceProfile(),
recommendation.targetContract(),
recommendation.rule(),
Confidence.HIGH,
recommendation.automationLevel(),
ordered));
}
private static boolean supported(
ContainerFlowComponent component,
ContainerRecommendation recommendation) {
if (component.closureStatus() != ClosureStatus.LOCAL_CLOSED
|| !component.diagnostics().isEmpty()
|| component.nodes().size() != 2
|| component.edges().size() != 1
|| recommendation.sourceProfile().completeness()
!= AnalysisCompleteness.FLOW_COMPLETE) {
return false;
}
var edge= component.edges().get(0);
return edge.kind() == EdgeKind.ARGUMENT_TO_PARAMETER
&& component.node(edge.sourceNodeId())
.filter(node -> node.kind() == NodeKind.LOCAL_VARIABLE)
.filter(node -> node.bindingKey().equals(
recommendation.sourceProfile().identity().bindingKey()))
.isPresent()
&& component.node(edge.targetNodeId())
.filter(node -> node.kind() == NodeKind.PARAMETER)
.isPresent();
}
private static boolean preserved(
Map<ContractProperty, ContractAssessment> assessments,
ContractProperty property) {
ContractAssessment assessment= assessments.get(property);
return assessment != null && assessment.preservation() == Preservation.PRESERVED;
}
}