1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.function.library;
7   
8   import java.util.List;
9   import java.util.Set;
10  
11  import dev.metaschema.core.metapath.DynamicContext;
12  import dev.metaschema.core.metapath.MetapathConstants;
13  import dev.metaschema.core.metapath.function.FunctionUtils;
14  import dev.metaschema.core.metapath.function.IArgument;
15  import dev.metaschema.core.metapath.function.IFunction;
16  import dev.metaschema.core.metapath.item.IItem;
17  import dev.metaschema.core.metapath.item.ISequence;
18  import dev.metaschema.core.metapath.item.atomic.IDateTimeItem;
19  import dev.metaschema.core.metapath.item.atomic.IStringItem;
20  import dev.metaschema.core.util.ObjectUtils;
21  import edu.umd.cs.findbugs.annotations.NonNull;
22  import edu.umd.cs.findbugs.annotations.Nullable;
23  
24  /**
25   * Implements the XPath 3.1 <a href=
26   * "https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime">fn:format-dateTime</a>
27   * functions.
28   *
29   * @see <a href=
30   *      "https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime">XPath
31   *      3.1 fn:format-dateTime</a>
32   */
33  public final class FnFormatDateTime {
34    private static final String NAME = "format-dateTime";
35  
36    /**
37     * The set of component specifiers allowed for dateTime values, which includes
38     * all date and time markers.
39     */
40    @NonNull
41    static final Set<Character> ALL_MARKERS = Set.of(
42        'Y', 'M', 'D', 'd', 'F', 'W', 'w',
43        'H', 'h', 'P', 'm', 's', 'f',
44        'Z', 'z', 'C', 'E');
45  
46    @NonNull
47    static final IFunction SIGNATURE_TWO_ARG = IFunction.builder()
48        .name(NAME)
49        .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
50        .deterministic()
51        .contextDependent()
52        .focusIndependent()
53        .argument(IArgument.builder()
54            .name("value")
55            .type(IDateTimeItem.type())
56            .zeroOrOne()
57            .build())
58        .argument(IArgument.builder()
59            .name("picture")
60            .type(IStringItem.type())
61            .one()
62            .build())
63        .returnType(IStringItem.type())
64        .returnZeroOrOne()
65        .functionHandler(FnFormatDateTime::executeTwoArg)
66        .build();
67  
68    @NonNull
69    static final IFunction SIGNATURE_FIVE_ARG = IFunction.builder()
70        .name(NAME)
71        .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
72        .deterministic()
73        .contextDependent()
74        .focusIndependent()
75        .argument(IArgument.builder()
76            .name("value")
77            .type(IDateTimeItem.type())
78            .zeroOrOne()
79            .build())
80        .argument(IArgument.builder()
81            .name("picture")
82            .type(IStringItem.type())
83            .one()
84            .build())
85        .argument(IArgument.builder()
86            .name("language")
87            .type(IStringItem.type())
88            .zeroOrOne()
89            .build())
90        .argument(IArgument.builder()
91            .name("calendar")
92            .type(IStringItem.type())
93            .zeroOrOne()
94            .build())
95        .argument(IArgument.builder()
96            .name("place")
97            .type(IStringItem.type())
98            .zeroOrOne()
99            .build())
100       .returnType(IStringItem.type())
101       .returnZeroOrOne()
102       .functionHandler(FnFormatDateTime::executeFiveArg)
103       .build();
104 
105   private FnFormatDateTime() {
106     // disable construction
107   }
108 
109   @SuppressWarnings("unused")
110   @NonNull
111   private static ISequence<IStringItem> executeTwoArg(
112       @NonNull IFunction function,
113       @NonNull List<ISequence<?>> arguments,
114       @NonNull DynamicContext dynamicContext,
115       IItem focus) {
116 
117     IDateTimeItem value = FunctionUtils.asTypeOrNull(arguments.get(0).getFirstItem(true));
118     if (value == null) {
119       return ISequence.empty();
120     }
121 
122     IStringItem picture = FunctionUtils.asType(
123         ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
124 
125     String lang = dynamicContext.getStaticContext().getDefaultLanguage();
126 
127     return ISequence.of(IStringItem.valueOf(
128         formatDateTime(value, picture.asString(), lang, null, null)));
129   }
130 
131   @SuppressWarnings("unused")
132   @NonNull
133   private static ISequence<IStringItem> executeFiveArg(
134       @NonNull IFunction function,
135       @NonNull List<ISequence<?>> arguments,
136       @NonNull DynamicContext dynamicContext,
137       IItem focus) {
138 
139     IDateTimeItem value = FunctionUtils.asTypeOrNull(arguments.get(0).getFirstItem(true));
140     if (value == null) {
141       return ISequence.empty();
142     }
143 
144     IStringItem picture = FunctionUtils.asType(
145         ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
146     IStringItem language = FunctionUtils.asTypeOrNull(arguments.get(2).getFirstItem(true));
147     IStringItem calendar = FunctionUtils.asTypeOrNull(arguments.get(3).getFirstItem(true));
148     IStringItem place = FunctionUtils.asTypeOrNull(arguments.get(4).getFirstItem(true));
149 
150     return ISequence.of(IStringItem.valueOf(
151         formatDateTime(
152             value,
153             picture.asString(),
154             language == null ? dynamicContext.getStaticContext().getDefaultLanguage() : language.asString(),
155             calendar == null ? null : calendar.asString(),
156             place == null ? null : place.asString())));
157   }
158 
159   /**
160    * Format a dateTime value using a picture string per the XPath 3.1 <a href=
161    * "https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime">fn:format-dateTime</a>
162    * specification.
163    *
164    * @param value
165    *          the dateTime value to format
166    * @param picture
167    *          the picture string
168    * @param language
169    *          the language, or {@code null}
170    * @param calendar
171    *          the calendar, or {@code null}
172    * @param place
173    *          the place, or {@code null}
174    * @return the formatted string
175    */
176   @NonNull
177   public static String formatDateTime(
178       @NonNull IDateTimeItem value,
179       @NonNull String picture,
180       @Nullable String language,
181       @Nullable String calendar,
182       @Nullable String place) {
183     return DateTimeFormatUtil.formatDateTime(value, picture, language, calendar, place, ALL_MARKERS);
184   }
185 }