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.math.BigInteger;
9   import java.util.ArrayList;
10  import java.util.List;
11  
12  import dev.metaschema.core.metapath.DynamicContext;
13  import dev.metaschema.core.metapath.IExpression;
14  import dev.metaschema.core.metapath.cst.AbstractBinaryExpression;
15  import dev.metaschema.core.metapath.cst.IExpressionVisitor;
16  import dev.metaschema.core.metapath.item.ISequence;
17  import dev.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
18  import dev.metaschema.core.metapath.item.atomic.IIntegerItem;
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/#doc-xpath31-RangeExpr">range
24   * expression</a>.
25   */
26  public class Range
27      extends AbstractBinaryExpression<IExpression, IExpression> {
28  
29    /**
30     * Construct a new range expression.
31     *
32     * @param text
33     *          the parsed text of the expression
34     * @param start
35     *          the expressions representing the start of the range
36     * @param end
37     *          the expressions representing the end of the range
38     *
39     */
40    public Range(
41        @NonNull String text,
42        @NonNull IExpression start,
43        @NonNull IExpression end) {
44      super(text, start, end);
45    }
46  
47    @Override
48    public Class<IIntegerItem> getBaseResultType() {
49      return IIntegerItem.class;
50    }
51  
52    @Override
53    protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
54      IAnyAtomicItem leftItem = ISequence.of(getLeft().accept(dynamicContext, focus).atomize()).getFirstItem(true);
55      IAnyAtomicItem rightItem = ISequence.of(getRight().accept(dynamicContext, focus).atomize()).getFirstItem(true);
56  
57      IIntegerItem left = leftItem == null ? null : IIntegerItem.cast(leftItem);
58      IIntegerItem right = rightItem == null ? null : IIntegerItem.cast(rightItem);
59  
60      ISequence<IIntegerItem> retval;
61      if (left == null || right == null || left.compareTo(right) > 0) {
62        retval = ISequence.empty();
63      } else {
64  
65        BigInteger min = left.asInteger();
66        BigInteger max = right.asInteger();
67  
68        List<IIntegerItem> range = new ArrayList<>(max.subtract(min).add(BigInteger.ONE).intValueExact());
69        for (BigInteger val = left.asInteger(); val.compareTo(max) <= 0; val = val.add(BigInteger.ONE)) {
70          range.add(IIntegerItem.valueOf(val));
71        }
72  
73        retval = ISequence.ofCollection(range);
74      }
75      return retval;
76    }
77  
78    @Override
79    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
80      return visitor.visitRange(this, context);
81    }
82  }