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   import java.util.stream.Stream;
10  
11  import dev.metaschema.core.metapath.DynamicContext;
12  import dev.metaschema.core.metapath.IExpression;
13  import dev.metaschema.core.metapath.cst.AbstractNAryExpression;
14  import dev.metaschema.core.metapath.cst.ExpressionUtils;
15  import dev.metaschema.core.metapath.cst.IExpressionVisitor;
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   * The CST node for a Metapath
23   * <a href="https://www.w3.org/TR/xpath-31/#combining_seq">union expression</a>.
24   */
25  public class Union
26      extends AbstractNAryExpression {
27  
28    @NonNull
29    private final Class<? extends IItem> staticResultType;
30  
31    /**
32     * Create a new expression that gets the union of the results of evaluating the
33     * provided {@code expressions}.
34     *
35     * @param text
36     *          the parsed text of the expression
37     * @param expressions
38     *          the expressions to evaluate
39     */
40    public Union(@NonNull String text, @NonNull List<IExpression> expressions) {
41      super(text, expressions);
42      this.staticResultType = ExpressionUtils.analyzeStaticResultType(IItem.class, expressions);
43    }
44  
45    @Override
46    public Class<? extends IItem> getStaticResultType() {
47      return staticResultType;
48    }
49  
50    @Override
51    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
52      return visitor.visitUnion(this, context);
53    }
54  
55    @Override
56    protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
57      // now process the union
58      @NonNull
59      Stream<? extends IItem> retval = ObjectUtils.notNull(getChildren().stream()
60          .flatMap(child -> {
61            ISequence<?> result = child.accept(
62                dynamicContext,
63                // ensure the sequence is backed by a list
64                focus.reusable());
65            return result.stream();
66          }).distinct());
67      return ISequence.of(retval);
68    }
69  }