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.StaticMetapathException;
15  import dev.metaschema.core.metapath.function.IFunction;
16  import dev.metaschema.core.metapath.item.IItem;
17  import dev.metaschema.core.metapath.item.ISequence;
18  import dev.metaschema.core.util.ObjectUtils;
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  
21  /**
22   * Executes a function call based on a specifier expression that is used to
23   * determine the function and multiple argument expressions that are used to
24   * determine the function arguments.
25   */
26  public class DynamicFunctionCall
27      extends AbstractExpression {
28    @NonNull
29    private final IExpression functionIdentifier;
30    @NonNull
31    private final List<IExpression> arguments;
32  
33    /**
34     * Construct a new function call expression.
35     *
36     * @param text
37     *          the parsed text of the expression
38     * @param functionIdentifier
39     *          the function expression, identifying either a function or function
40     *          name
41     * @param arguments
42     *          the expressions used to provide arguments to the function call
43     */
44    public DynamicFunctionCall(
45        @NonNull String text,
46        @NonNull IExpression functionIdentifier,
47        @NonNull List<IExpression> arguments) {
48      super(text);
49      this.functionIdentifier = functionIdentifier;
50      this.arguments = arguments;
51    }
52  
53    @Override
54    public List<IExpression> getChildren() {
55      return ObjectUtils.notNull(Stream.concat(
56          Stream.of(functionIdentifier),
57          arguments.stream())
58          .collect(Collectors.toUnmodifiableList()));
59    }
60  
61    @Override
62    public Class<? extends IItem> getBaseResultType() {
63      return IItem.class;
64    }
65  
66    @Override
67    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
68      return visitor.visitDynamicFunctionCall(this, context);
69    }
70  
71    @Override
72    protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
73      List<ISequence<?>> arguments = ObjectUtils.notNull(this.arguments.stream()
74          .map(expression -> expression.accept(dynamicContext, focus)).collect(Collectors.toList()));
75  
76      IItem specifier = functionIdentifier.accept(dynamicContext, focus).getFirstItem(true);
77      IFunction function;
78      if (specifier instanceof IFunction) {
79        function = (IFunction) specifier;
80      } else if (specifier != null) {
81        function = dynamicContext.getStaticContext().lookupFunction(
82            specifier.toAtomicItem().asString(),
83            arguments.size());
84      } else {
85        throw new StaticMetapathException(
86            StaticMetapathException.NO_FUNCTION_MATCH,
87            "Unable to get function name. The error specifier is an empty sequence.");
88      }
89      return function.execute(arguments, dynamicContext, focus);
90    }
91  }