1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.item.atomic;
7   
8   import java.time.temporal.TemporalAmount;
9   
10  import dev.metaschema.core.datatype.adapter.AbstractDurationAdapter;
11  import dev.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
12  import dev.metaschema.core.metapath.type.IAtomicOrUnionType;
13  import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
14  import dev.metaschema.core.metapath.type.impl.TypeConstants;
15  import edu.umd.cs.findbugs.annotations.NonNull;
16  
17  /**
18   * An atomic Metapath item representing a duration data value.
19   * <p>
20   * This interface supports both day-time and year-month duration formats
21   * following the ISO 8601 standard. Examples of valid durations include:
22   * <ul>
23   * <li>P1Y2M (1 year, 2 months)
24   * <li>P3DT4H5M (3 days, 4 hours, 5 minutes)
25   * </ul>
26   *
27   * @see IDayTimeDurationItem
28   * @see IYearMonthDurationItem
29   */
30  public interface IDurationItem extends IAnyAtomicItem {
31    /**
32     * Get the type information for this item.
33     *
34     * @return the type information
35     */
36    @NonNull
37    static IAtomicOrUnionType<IDurationItem> type() {
38      return TypeConstants.DURATION_TYPE;
39    }
40  
41    /**
42     * Construct a new duration item using the provided string {@code value}.
43     *
44     * @param value
45     *          a string representing a day time duration
46     * @return the new item
47     * @throws InvalidTypeMetapathException
48     *           if the provided string value is not a year/month or day/time
49     *           duration value according to ISO 8601
50     */
51    @NonNull
52    static IDurationItem valueOf(@NonNull String value) {
53      try {
54        return AbstractDurationAdapter.parseDuration(value);
55      } catch (IllegalArgumentException ex) {
56        throw new InvalidTypeMetapathException(
57            null,
58            String.format("Invalid year/month or day/time duration value '%s'.",
59                value),
60            ex);
61      }
62    }
63  
64    /**
65     * Cast the provided type to this item type.
66     *
67     * @param item
68     *          the item to cast
69     * @return the original item if it is already this type, otherwise a new item
70     *         cast to this type
71     * @throws InvalidValueForCastFunctionException
72     *           if the provided {@code item} cannot be cast to this type
73     */
74    @NonNull
75    static IDurationItem cast(@NonNull IAnyAtomicItem item) {
76      IDurationItem retval;
77      if (item instanceof IDurationItem) {
78        retval = (IDurationItem) item;
79      } else {
80        String value;
81        try {
82          value = item.asString();
83        } catch (IllegalStateException ex) {
84          // asString can throw IllegalStateException exception
85          throw new InvalidValueForCastFunctionException(ex);
86        }
87  
88        try {
89          retval = valueOf(value);
90        } catch (InvalidTypeMetapathException ex) {
91          throw new InvalidValueForCastFunctionException(
92              String.format("Value '%s' cannot be parsed as either a day-time or year-month duration", value),
93              ex);
94        }
95      }
96      return retval;
97    }
98  
99    @Override
100   TemporalAmount getValue();
101 
102   @Override
103   default IDurationItem castAsType(IAnyAtomicItem item) {
104     return cast(item);
105   }
106 
107   /**
108    * Compares this value with the argument.
109    *
110    * @param item
111    *          the item to compare with this value
112    * @return a negative integer, zero, or a positive integer if this value is less
113    *         than, equal to, or greater than the {@code item}.
114    */
115   int compareTo(@NonNull IDurationItem item);
116 }