1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.cst.items;
7   
8   import java.util.List;
9   
10  import dev.metaschema.core.metapath.DynamicContext;
11  import dev.metaschema.core.metapath.IExpression;
12  import dev.metaschema.core.metapath.cst.AbstractNAryExpression;
13  import dev.metaschema.core.metapath.cst.IExpressionVisitor;
14  import dev.metaschema.core.metapath.function.library.FnConcat;
15  import dev.metaschema.core.metapath.item.ISequence;
16  import dev.metaschema.core.metapath.item.atomic.IStringItem;
17  import dev.metaschema.core.util.ObjectUtils;
18  import edu.umd.cs.findbugs.annotations.NonNull;
19  
20  /**
21   * An XPath 3.1
22   * <a href="https://www.w3.org/TR/xpath-31/#id-string-concat-expr">string
23   * concatenation expression</a>.
24   *
25   * <p>
26   * Concatenates the string values of its operands in left to right order. Each
27   * operand is atomized and converted to a string value before concatenation.
28   *
29   * <p>
30   * Example Metapath usage:
31   * <p>
32   * {@code "abc" || "def"} -> {@code "abcdef"}
33   * <p>
34   * {@code 123 || "456"} -> {@code "123456"}
35   * <p>
36   * {@code () || "xyz" || "abc"} -> {@code "xyzabc"}
37   */
38  public class StringConcat
39      extends AbstractNAryExpression {
40  
41    /**
42     * Create a new expression that concatenates the results of evaluating the
43     * provided {@code expressions} as strings.
44     *
45     * @param text
46     *          the parsed text of the expression
47     * @param expressions
48     *          the expressions to evaluate
49     */
50    public StringConcat(@NonNull String text, @NonNull List<IExpression> expressions) {
51      super(text, expressions);
52    }
53  
54    @Override
55    public Class<IStringItem> getBaseResultType() {
56      return IStringItem.class;
57    }
58  
59    @Override
60    public Class<IStringItem> getStaticResultType() {
61      return getBaseResultType();
62    }
63  
64    @Override
65    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
66      return visitor.visitStringConcat(this, context);
67    }
68  
69    @Override
70    protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
71      return ISequence.of(FnConcat.concat(ObjectUtils.notNull(getChildren().stream()
72          .map(child -> child.accept(dynamicContext, focus))
73          .flatMap(result -> ObjectUtils.notNull(result).atomize()))));
74    }
75  }