1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.cst.math;
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.AbstractUnaryExpression;
13  import dev.metaschema.core.metapath.cst.ExpressionUtils;
14  import dev.metaschema.core.metapath.cst.IExpressionVisitor;
15  import dev.metaschema.core.metapath.function.FunctionUtils;
16  import dev.metaschema.core.metapath.function.impl.OperationFunctions;
17  import dev.metaschema.core.metapath.item.ISequence;
18  import dev.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
19  import dev.metaschema.core.metapath.item.atomic.INumericItem;
20  import edu.umd.cs.findbugs.annotations.NonNull;
21  
22  /**
23   * An XPath 3.1
24   * <a href="https://www.w3.org/TR/xpath-31/#id-arithmetic">arithmetic
25   * expression</a> supporting negation.
26   */
27  public class Negate
28      extends AbstractUnaryExpression {
29  
30    @NonNull
31    private final Class<? extends INumericItem> staticResultType;
32  
33    /**
34     * Create an expression that gets the complement of a number.
35     *
36     * @param text
37     *          the parsed text of the expression
38     * @param expr
39     *          the expression whose item result will be complemented
40     */
41    @SuppressWarnings("null")
42    public Negate(@NonNull String text, @NonNull IExpression expr) {
43      super(text, expr);
44      this.staticResultType = ExpressionUtils.analyzeStaticResultType(INumericItem.class, List.of(expr));
45    }
46  
47    @Override
48    public Class<INumericItem> getBaseResultType() {
49      return INumericItem.class;
50    }
51  
52    @Override
53    public Class<? extends INumericItem> getStaticResultType() {
54      return staticResultType;
55    }
56  
57    @Override
58    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
59      return visitor.visitNegate(this, context);
60    }
61  
62    @Override
63    protected ISequence<? extends INumericItem> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
64      IAnyAtomicItem atomicItem = ISequence.of(getChild().accept(dynamicContext, focus).atomize()).getFirstItem(true);
65      INumericItem item = atomicItem == null ? null : FunctionUtils.castToNumeric(atomicItem);
66      if (item != null) {
67        item = OperationFunctions.opNumericUnaryMinus(item);
68      }
69      return ISequence.of(item);
70    }
71  }