1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.cst;
7   
8   import java.util.LinkedList;
9   import java.util.List;
10  
11  import dev.metaschema.core.metapath.DynamicContext;
12  import dev.metaschema.core.metapath.IExpression;
13  import dev.metaschema.core.metapath.cst.Let.VariableDeclaration;
14  import dev.metaschema.core.metapath.item.IItem;
15  import dev.metaschema.core.metapath.item.ISequence;
16  import dev.metaschema.core.util.ObjectUtils;
17  import edu.umd.cs.findbugs.annotations.NonNull;
18  
19  /**
20   * An implementation of the
21   * <a href="https://www.w3.org/TR/xpath-31/#id-for-expressions">For
22   * expression</a> supporting variable-based iteration.
23   */
24  public class For
25      extends AbstractExpression {
26    @NonNull
27    private final Let.VariableDeclaration variable;
28    @NonNull
29    private final IExpression returnExpression;
30  
31    /**
32     * Construct a new for expression using the provided variable and return clause.
33     *
34     * @param text
35     *          the parsed text of the expression
36     * @param variable
37     *          the variable declaration
38     * @param returnExpr
39     *          the return clause that makes use of variables for evaluation
40     */
41    public For(
42        @NonNull String text,
43        @NonNull VariableDeclaration variable,
44        @NonNull IExpression returnExpr) {
45      super(text);
46      this.variable = variable;
47      this.returnExpression = returnExpr;
48    }
49  
50    /**
51     * Get the variable declaration.
52     *
53     * @return the variable declaration expression
54     */
55    @NonNull
56    protected Let.VariableDeclaration getVariable() {
57      return variable;
58    }
59  
60    /**
61     * Get the return expression.
62     *
63     * @return the return expression
64     */
65    @NonNull
66    protected IExpression getReturnExpression() {
67      return returnExpression;
68    }
69  
70    @Override
71    public List<? extends IExpression> getChildren() {
72      return ObjectUtils.notNull(
73          List.of(getVariable().getBoundExpression(), returnExpression));
74    }
75  
76    @Override
77    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
78      return visitor.visitFor(this, context);
79    }
80  
81    @Override
82    protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
83      Let.VariableDeclaration variable = getVariable();
84      ISequence<?> variableResult = variable.getBoundExpression().accept(dynamicContext, focus);
85  
86      DynamicContext subDynamicContext = dynamicContext.subContext();
87  
88      List<IItem> retval = new LinkedList<>();
89      for (IItem item : variableResult) {
90        subDynamicContext.bindVariableValue(variable.getName(), ISequence.of(item));
91        retval.addAll(getReturnExpression().accept(subDynamicContext, focus));
92      }
93      return ISequence.ofCollection(retval);
94    }
95  
96    @SuppressWarnings("null")
97    @Override
98    public String toCSTString() {
99      return String.format("%s[variable=%s]", getClass().getName(), getVariable().getName());
100   }
101 }