1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.cst.logic;
7   
8   import dev.metaschema.core.metapath.DynamicContext;
9   import dev.metaschema.core.metapath.IExpression;
10  import dev.metaschema.core.metapath.cst.IExpressionVisitor;
11  import dev.metaschema.core.metapath.function.ComparisonFunctions;
12  import dev.metaschema.core.metapath.item.ISequence;
13  import dev.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
14  import dev.metaschema.core.metapath.item.atomic.IBooleanItem;
15  import edu.umd.cs.findbugs.annotations.NonNull;
16  import edu.umd.cs.findbugs.annotations.Nullable;
17  
18  /**
19   * Supports Metapath
20   * <a href="https://www.w3.org/TR/xpath-31/#id-value-comparisons">value
21   * comparisons</a>.
22   */
23  public class ValueComparison
24      extends AbstractComparison {
25  
26    /**
27     * Create a new value comparison expression.
28     *
29     * @param text
30     *          the parsed text of the expression
31     * @param left
32     *          the expression to compare against
33     * @param operator
34     *          the comparison operator
35     * @param right
36     *          the expression to compare with
37     */
38    public ValueComparison(
39        @NonNull String text,
40        @NonNull IExpression left,
41        @NonNull ComparisonFunctions.Operator operator,
42        @NonNull IExpression right) {
43      super(text, left, operator, right);
44    }
45  
46    @Override
47    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
48      return visitor.visitValueComparison(this, context);
49    }
50  
51    @Override
52    protected ISequence<? extends IBooleanItem> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
53      IAnyAtomicItem left = ISequence.of(getLeft().accept(dynamicContext, focus).atomize()).getFirstItem(false);
54      IAnyAtomicItem right = ISequence.of(getRight().accept(dynamicContext, focus).atomize()).getFirstItem(false);
55      return resultOrEmpty(left, right, dynamicContext);
56    }
57  
58    /**
59     * Compare the two atomic items.
60     *
61     * @param leftItem
62     *          the first item to compare
63     * @param rightItem
64     *          the second item to compare
65     * @param dynamicContext
66     *          used to get the implicit timezone from the evaluation context
67     * @return a or an empty {@link ISequence} if either item is {@code null}
68     */
69    @NonNull
70    private ISequence<? extends IBooleanItem> resultOrEmpty(
71        @Nullable IAnyAtomicItem leftItem,
72        @Nullable IAnyAtomicItem rightItem,
73        @NonNull DynamicContext dynamicContext) {
74      ISequence<? extends IBooleanItem> retval;
75      if (leftItem == null || rightItem == null) {
76        retval = ISequence.empty();
77      } else {
78        IBooleanItem result = ComparisonFunctions.valueCompairison(leftItem, getOperator(), rightItem, dynamicContext);
79        retval = ISequence.of(result);
80      }
81      return retval;
82    }
83  }