GuardContext.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
*
* Contributors:
* Carsten Hammer - initial API and implementation
*******************************************************************************/
package org.sandbox.jdt.triggerpattern.api;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
/** Context for guard evaluation. */
public final class GuardContext {
/** One semantic input a guard could not resolve. */
public record UnknownSemanticRequirement(String guardName, String detail) {
public UnknownSemanticRequirement {
guardName= guardName == null || guardName.isBlank() ? "unknown" : guardName.trim(); //$NON-NLS-1$
detail= detail == null || detail.isBlank() ? "semantic binding unavailable" : detail.trim(); //$NON-NLS-1$
}
}
private final Match match;
private final CompilationUnit cu;
private final Map<String, String> compilerOptions;
private final SemanticRewritePlan semanticPlan;
private final Set<UnknownSemanticRequirement> unknownSemanticRequirements= new LinkedHashSet<>();
private int unknownSemanticObservationCount;
private GuardContext(Match match, CompilationUnit cu, Map<String, String> compilerOptions,
SemanticRewritePlan semanticPlan) {
this.match= Objects.requireNonNull(match, "Match cannot be null"); //$NON-NLS-1$
this.cu= cu;
this.compilerOptions= compilerOptions != null
? Collections.unmodifiableMap(compilerOptions)
: Collections.emptyMap();
this.semanticPlan= semanticPlan == null ? SemanticRewritePlan.empty() : semanticPlan;
}
/** Creates a context without compiler options or semantic plan. */
public static GuardContext fromMatch(Match match, CompilationUnit cu) {
return new GuardContext(match, cu, Collections.emptyMap(), SemanticRewritePlan.empty());
}
/** Creates a context with compiler options and no semantic plan. */
public static GuardContext fromMatch(Match match, CompilationUnit cu, Map<String, String> compilerOptions) {
return new GuardContext(match, cu, compilerOptions, SemanticRewritePlan.empty());
}
/** Creates a context with explicit compiler options and semantic authorization plan. */
public static GuardContext fromMatch(Match match, CompilationUnit cu, Map<String, String> compilerOptions,
SemanticRewritePlan semanticPlan) {
return new GuardContext(match, cu, compilerOptions, semanticPlan);
}
public Match getMatch() {
return match;
}
public CompilationUnit getCompilationUnit() {
return cu;
}
public Map<String, String> getCompilerOptions() {
return compilerOptions;
}
/** Returns the immutable semantic plan; empty for ordinary hint execution. */
public SemanticRewritePlan getSemanticPlan() {
return semanticPlan;
}
public ASTNode getBinding(String name) {
return match.getBinding(name);
}
public List<ASTNode> getListBinding(String name) {
return match.getListBinding(name);
}
public String getSourceVersion() {
String version= compilerOptions.get("org.eclipse.jdt.core.compiler.source"); //$NON-NLS-1$
return version != null ? version : "1.8"; //$NON-NLS-1$
}
public ASTNode getMatchedNode() {
return match.getMatchedNode();
}
/** Records one binding-dependent guard input whose meaning is unavailable. */
public void markUnknown(String guardName, String detail) {
unknownSemanticObservationCount++;
unknownSemanticRequirements.add(new UnknownSemanticRequirement(guardName, detail));
}
/**
* Returns how many unresolved semantic observations guards reported.
*
* <p>This count intentionally includes duplicate observations. The diagnostic
* list is deduplicated, but detailed expression evaluation must still notice
* every repeated unresolved function call.</p>
*/
public int unknownSemanticRequirementCount() {
return unknownSemanticObservationCount;
}
/** Returns whether any binding-dependent guard reported unknown semantics. */
public boolean hasUnknownSemanticRequirements() {
return unknownSemanticObservationCount > 0;
}
/** Returns deduplicated unknown semantic requirements in evaluation order. */
public List<UnknownSemanticRequirement> getUnknownSemanticRequirements() {
return List.copyOf(unknownSemanticRequirements);
}
}