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.List;
9   import java.util.stream.Collectors;
10  import java.util.stream.Stream;
11  
12  import dev.metaschema.core.metapath.DynamicContext;
13  import dev.metaschema.core.metapath.IExpression;
14  import dev.metaschema.core.metapath.function.IFunction;
15  import dev.metaschema.core.metapath.item.IItem;
16  import dev.metaschema.core.metapath.item.ISequence;
17  import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
18  import dev.metaschema.core.util.ObjectUtils;
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  
21  /**
22   * An implementation of the XPATH 3.1
23   * <a href="https://www.w3.org/TR/xpath-31/#id-eval-function-call">function call
24   * accessor</a>.
25   */
26  public class FunctionCallAccessor
27      extends AbstractExpression {
28    @NonNull
29    private final IExpression base;
30    @NonNull
31    private final List<IExpression> arguments;
32  
33    /**
34     * Construct a new functional call accessor.
35     *
36     * @param text
37     *          the parsed text of the expression
38     * @param base
39     *          the expression whose result is used as the map or array to perform
40     *          the lookup on
41     * @param arguments
42     *          the function call argument expressions
43     */
44    public FunctionCallAccessor(@NonNull String text, @NonNull IExpression base, @NonNull List<IExpression> arguments) {
45      super(text);
46      this.base = base;
47      this.arguments = arguments;
48    }
49  
50    /**
51     * Get the base sub-expression.
52     *
53     * @return the sub-expression
54     */
55    @NonNull
56    public IExpression getBase() {
57      return base;
58    }
59  
60    /**
61     * Retrieve the argument to use for the lookup.
62     *
63     * @return the argument
64     */
65    @NonNull
66    public List<IExpression> getArguments() {
67      return arguments;
68    }
69  
70    @SuppressWarnings("null")
71    @Override
72    public List<IExpression> getChildren() {
73      return Stream.concat(Stream.of(getBase()), getArguments().stream())
74          .collect(Collectors.toUnmodifiableList());
75    }
76  
77    @Override
78    protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
79      ISequence<?> target = getBase().accept(dynamicContext, focus);
80      IItem collection = target.getFirstItem(true);
81  
82      if (!(collection instanceof IFunction)) {
83        throw new InvalidTypeMetapathException(
84            collection,
85            "The base expression did not evaluate to a function.")
86                .registerEvaluationContext(dynamicContext);
87      }
88  
89      return ((IFunction) collection).execute(ObjectUtils.notNull(getArguments().stream()
90          .map(expr -> expr.accept(dynamicContext, focus))
91          .collect(Collectors.toUnmodifiableList())), dynamicContext, focus);
92    }
93  
94    @Override
95    public <RESULT, CONTEXT> RESULT accept(@NonNull IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
96      return visitor.visitFunctionCallAccessor(this, context);
97    }
98  }