Let.java
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/
package gov.nist.secauto.metaschema.core.metapath.cst;
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import java.util.List;
import javax.xml.namespace.QName;
import edu.umd.cs.findbugs.annotations.NonNull;
/**
* An implementation of
* <a href="https://www.w3.org/TR/xpath-31/#id-let-expressions">Let
* expression</a> supporting variable value binding.
*/
@SuppressWarnings("PMD.ShortClassName")
public class Let implements IExpression {
@NonNull
private final VariableDeclaration variable;
@NonNull
private final IExpression returnExpression;
/**
* Construct a new Let CST expression.
*
* @param name
* the variable name
* @param boundExpression
* the expression bound to the variable
* @param returnExpression
* the inner expression to evaluate with the variable in-scope
*/
public Let(@NonNull QName name, @NonNull IExpression boundExpression, @NonNull IExpression returnExpression) {
this.variable = new VariableDeclaration(name, boundExpression);
this.returnExpression = returnExpression;
}
/**
* Get the variable to evaluate with the variable in-scope.
*
* @return the inner expression
*/
@NonNull
public VariableDeclaration getVariable() {
return variable;
}
/**
* Get the inner expression to evaluate with the variable in-scope.
*
* @return the inner expression
*/
@NonNull
public IExpression getReturnExpression() {
return returnExpression;
}
@Override
public List<? extends IExpression> getChildren() {
return ObjectUtils.notNull(
List.of(returnExpression));
}
@Override
public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
return visitor.visitLet(this, context);
}
@Override
public ISequence<? extends IItem> accept(DynamicContext dynamicContext, ISequence<?> focus) {
DynamicContext subDynamicContext = dynamicContext.subContext();
getVariable().bind(dynamicContext, focus, subDynamicContext);
return getReturnExpression().accept(subDynamicContext, focus);
}
/**
* A Metapath expression that binds a variable name to an expresssion.
*/
public static class VariableDeclaration {
@NonNull
private final QName name;
@NonNull
private final IExpression boundExpression;
/**
* Construct a new variable declaration, binding the provided variable name to
* the bound expression.
*
* @param name
* trhe variable name
* @param boundExpression
* the bound expression
*/
public VariableDeclaration(@NonNull QName name, @NonNull IExpression boundExpression) {
this.name = name;
this.boundExpression = boundExpression;
}
/**
* Get the variable name.
*
* @return the variable name
*/
@NonNull
public QName getName() {
return name;
}
/**
* Get the expression bound to the variable.
*
* @return the bound expression
*/
@NonNull
public IExpression getBoundExpression() {
return boundExpression;
}
/**
* Bind the variable name to the evaluation result of the bound expression.
*
* @param evaluationDynamicContext
* the {@link DynamicContext} used to evaluate the bound expression
* @param focus
* the evaluation focus to use to evaluate the bound expression
* @param boundDynamicContext
* the {@link DynamicContext} the variable is bound to
*/
public void bind(
@NonNull DynamicContext evaluationDynamicContext,
@NonNull ISequence<?> focus,
@NonNull DynamicContext boundDynamicContext) {
ISequence<?> result = getBoundExpression().accept(evaluationDynamicContext, focus);
// ensure this sequence is list backed
result.getValue();
boundDynamicContext.bindVariableValue(getName(), result);
}
}
}