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.LinkedHashMap;
9   import java.util.Map;
10  
11  import dev.metaschema.core.metapath.IExpression;
12  import dev.metaschema.core.metapath.cst.IExpressionVisitor;
13  import dev.metaschema.core.metapath.function.impl.OperationFunctions;
14  import dev.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
15  import dev.metaschema.core.metapath.item.atomic.IDayTimeDurationItem;
16  import dev.metaschema.core.metapath.item.atomic.INumericItem;
17  import dev.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;
18  import dev.metaschema.core.util.CollectionUtil;
19  import dev.metaschema.core.util.ObjectUtils;
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 division.
26   */
27  public class Division
28      extends AbstractBasicArithmeticExpression {
29    @NonNull
30    private static final Map<Class<? extends IAnyAtomicItem>,
31        Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> DIVISION_STRATEGIES = generateStrategies();
32  
33    /**
34     * An expression that gets the quotient result by dividing the dividend by the
35     * divisor.
36     *
37     * @param text
38     *          the parsed text of the expression
39     * @param dividend
40     *          the expression whose result is to be divided
41     * @param divisor
42     *          the expression whose result is to divide by
43     */
44    public Division(
45        @NonNull String text,
46        @NonNull IExpression dividend,
47        @NonNull IExpression divisor) {
48      super(text, dividend, divisor);
49    }
50  
51    @Override
52    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
53      return visitor.visitDivision(this, context);
54    }
55  
56    @Override
57    protected Map<
58        Class<? extends IAnyAtomicItem>,
59        Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> getStrategies() {
60      return DIVISION_STRATEGIES;
61    }
62  
63    @Override
64    protected String unsupportedMessage(String dividend, String divisor) {
65      return ObjectUtils.notNull(String.format("Division of '%s' by '%s' is not supported.", dividend, divisor));
66    }
67  
68    @Override
69    protected INumericItem operationAsNumeric(INumericItem dividend, INumericItem divisor) {
70      // Default to numeric division
71      return OperationFunctions.opNumericDivide(dividend, divisor);
72    }
73  
74    @SuppressWarnings("PMD.UseConcurrentHashMap")
75    @NonNull
76    private static Map<
77        Class<? extends IAnyAtomicItem>,
78        Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> generateStrategies() {
79      Map<Class<? extends IAnyAtomicItem>, Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> strategies
80          = new LinkedHashMap<>();
81  
82      // IYearMonthDurationItem strategies
83      Map<Class<? extends IAnyAtomicItem>, OperationStrategy> typeStrategies = new LinkedHashMap<>();
84      typeStrategies.put(INumericItem.class,
85          (dividend, divisor, dynamicContext) -> OperationFunctions.opDivideYearMonthDuration(
86              (IYearMonthDurationItem) dividend,
87              (INumericItem) divisor));
88      typeStrategies.put(IYearMonthDurationItem.class,
89          (dividend, divisor, dynamicContext) -> OperationFunctions.opDivideYearMonthDurationByYearMonthDuration(
90              (IYearMonthDurationItem) dividend,
91              (IYearMonthDurationItem) divisor));
92      strategies.put(IYearMonthDurationItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
93  
94      // IDayTimeDurationItem strategies
95      typeStrategies = new LinkedHashMap<>();
96      typeStrategies.put(INumericItem.class,
97          (dividend, divisor, dynamicContext) -> OperationFunctions.opDivideDayTimeDuration(
98              (IDayTimeDurationItem) dividend,
99              (INumericItem) divisor));
100     typeStrategies.put(IDayTimeDurationItem.class,
101         (dividend, divisor, dynamicContext) -> OperationFunctions.opDivideDayTimeDurationByDayTimeDuration(
102             (IDayTimeDurationItem) dividend,
103             (IDayTimeDurationItem) divisor));
104     strategies.put(IDayTimeDurationItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
105 
106     // INumericItem strategies
107     typeStrategies = new LinkedHashMap<>();
108     typeStrategies.put(INumericItem.class,
109         (dividend, divisor, dynamicContext) -> OperationFunctions.opNumericDivide(
110             (INumericItem) dividend,
111             (INumericItem) divisor));
112     strategies.put(INumericItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
113 
114     return CollectionUtil.unmodifiableMap(strategies);
115   }
116 }