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.IDateItem;
16  import dev.metaschema.core.metapath.item.atomic.IDateTimeItem;
17  import dev.metaschema.core.metapath.item.atomic.IDayTimeDurationItem;
18  import dev.metaschema.core.metapath.item.atomic.INumericItem;
19  import dev.metaschema.core.metapath.item.atomic.ITimeItem;
20  import dev.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;
21  import dev.metaschema.core.util.CollectionUtil;
22  import dev.metaschema.core.util.ObjectUtils;
23  import edu.umd.cs.findbugs.annotations.NonNull;
24  
25  /**
26   * Implements the '+' operator for Metapath addition operations.
27   * <p>
28   * An XPath 3.1
29   * <a href="https://www.w3.org/TR/xpath-31/#id-arithmetic">arithmetic
30   * expression</a> supporting addition.
31   * <p>
32   * Supports addition operations between:
33   * <ul>
34   * <li>Numeric values
35   * <li>Date/DateTime + {@link IYearMonthDurationItem}
36   * <li>Date/DateTime/Time + {@link IDayTimeDurationItem}
37   * <li>Date/Time arithmetic (adding durations to dates/times)
38   * <li>{@link IYearMonthDurationItem} + {@link IYearMonthDurationItem}
39   * <li>{@link IDayTimeDurationItem} + {@link IDayTimeDurationItem}
40   * </ul>
41   * <p>
42   * Example Metapath usage:
43   *
44   * <pre>
45   * // Numeric addition
46   * 1 + 2 → 3
47   * // Date/Time arithmetic
48   * date + yearMonthDuration
49   * dateTime + dayTimeDuration
50   * </pre>
51   */
52  public class Addition
53      extends AbstractBasicArithmeticExpression {
54    @NonNull
55    private static final Map<Class<? extends IAnyAtomicItem>,
56        Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> ADDITION_STRATEGIES = generateStrategies();
57  
58    /**
59     * An expression that sums two atomic data items.
60     *
61     * @param text
62     *          the parsed text of the expression
63     * @param left
64     *          an expression whose result is summed
65     * @param right
66     *          an expression whose result is summed
67     */
68    public Addition(
69        @NonNull String text,
70        @NonNull IExpression left,
71        @NonNull IExpression right) {
72      super(text, left, right);
73    }
74  
75    @Override
76    public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
77      return visitor.visitAddition(this, context);
78    }
79  
80    @Override
81    protected Map<
82        Class<? extends IAnyAtomicItem>,
83        Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> getStrategies() {
84      return ADDITION_STRATEGIES;
85    }
86  
87    @Override
88    protected String unsupportedMessage(String left, String right) {
89      return ObjectUtils.notNull(String.format("Addition of '%s' and '%s' is not supported.", left, right));
90    }
91  
92    @Override
93    protected INumericItem operationAsNumeric(INumericItem left, INumericItem right) {
94      // Default to numeric addition
95      return OperationFunctions.opNumericAdd(left, right);
96    }
97  
98    @SuppressWarnings("PMD.UseConcurrentHashMap")
99    @NonNull
100   private static Map<
101       Class<? extends IAnyAtomicItem>,
102       Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> generateStrategies() {
103     Map<Class<? extends IAnyAtomicItem>, Map<Class<? extends IAnyAtomicItem>, OperationStrategy>> strategies
104         = new LinkedHashMap<>();
105 
106     // Date strategies
107     Map<Class<? extends IAnyAtomicItem>, OperationStrategy> typeStrategies = new LinkedHashMap<>();
108     typeStrategies.put(IYearMonthDurationItem.class,
109         (left, right, dynamicContext) -> OperationFunctions.opAddYearMonthDurationToDate(
110             (IDateItem) left,
111             (IYearMonthDurationItem) right));
112     typeStrategies.put(IDayTimeDurationItem.class,
113         (left, right, dynamicContext) -> OperationFunctions.opAddDayTimeDurationToDate(
114             (IDateItem) left,
115             (IDayTimeDurationItem) right));
116     strategies.put(IDateItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
117 
118     // DateTime strategies
119     typeStrategies = new LinkedHashMap<>();
120     typeStrategies.put(IYearMonthDurationItem.class,
121         (left, right, dynamicContext) -> OperationFunctions.opAddYearMonthDurationToDateTime(
122             (IDateTimeItem) left,
123             (IYearMonthDurationItem) right));
124     typeStrategies.put(IDayTimeDurationItem.class,
125         (left, right, dynamicContext) -> OperationFunctions.opAddDayTimeDurationToDateTime(
126             (IDateTimeItem) left,
127             (IDayTimeDurationItem) right));
128     strategies.put(IDateTimeItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
129 
130     // time strategies
131     typeStrategies = new LinkedHashMap<>();
132     typeStrategies.put(IDayTimeDurationItem.class,
133         (left, right, dynamicContext) -> OperationFunctions.opAddDayTimeDurationToTime(
134             (ITimeItem) left,
135             (IDayTimeDurationItem) right));
136     strategies.put(ITimeItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
137 
138     // YearMonthDuration strategies
139     typeStrategies = new LinkedHashMap<>();
140     typeStrategies.put(IDateItem.class,
141         (left, right, dynamicContext) -> OperationFunctions.opAddYearMonthDurationToDate(
142             (IDateItem) right,
143             (IYearMonthDurationItem) left));
144     typeStrategies.put(IDateTimeItem.class,
145         (left, right, dynamicContext) -> OperationFunctions.opAddYearMonthDurationToDateTime(
146             (IDateTimeItem) right,
147             (IYearMonthDurationItem) left));
148     typeStrategies.put(IYearMonthDurationItem.class,
149         (left, right, dynamicContext) -> OperationFunctions.opAddYearMonthDurations(
150             (IYearMonthDurationItem) left,
151             (IYearMonthDurationItem) right));
152     strategies.put(IYearMonthDurationItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
153 
154     // DayTimeDuration strategies
155     typeStrategies = new LinkedHashMap<>();
156     typeStrategies.put(IDateItem.class,
157         (left, right, dynamicContext) -> OperationFunctions.opAddDayTimeDurationToDate(
158             (IDateItem) right,
159             (IDayTimeDurationItem) left));
160     typeStrategies.put(IDateTimeItem.class,
161         (left, right, dynamicContext) -> OperationFunctions.opAddDayTimeDurationToDateTime(
162             (IDateTimeItem) right,
163             (IDayTimeDurationItem) left));
164     typeStrategies.put(ITimeItem.class,
165         (left, right, dynamicContext) -> OperationFunctions.opAddDayTimeDurationToTime(
166             (ITimeItem) right,
167             (IDayTimeDurationItem) left));
168     typeStrategies.put(IDayTimeDurationItem.class,
169         (left, right, dynamicContext) -> OperationFunctions.opAddDayTimeDurations(
170             (IDayTimeDurationItem) left,
171             (IDayTimeDurationItem) right));
172     strategies.put(IDayTimeDurationItem.class, CollectionUtil.unmodifiableMap(typeStrategies));
173 
174     return CollectionUtil.unmodifiableMap(strategies);
175   }
176 }