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 org.apache.commons.lang3.StringUtils;
9   
10  import java.util.List;
11  
12  import dev.metaschema.core.metapath.DynamicContext;
13  import dev.metaschema.core.metapath.MetapathConstants;
14  import dev.metaschema.core.metapath.function.FunctionUtils;
15  import dev.metaschema.core.metapath.function.IArgument;
16  import dev.metaschema.core.metapath.function.IFunction;
17  import dev.metaschema.core.metapath.item.IItem;
18  import dev.metaschema.core.metapath.item.ISequence;
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  
23  // CPD-OFF
24  /**
25   * Implements the XPath 3.1 <a href=
26   * "https://www.w3.org/TR/xpath-functions-31/#func-substring-after">fn:substring-after</a>
27   * function.
28   */
29  public final class FnSubstringAfter {
30    private static final String NAME = "substring-after";
31    @NonNull
32    static final IFunction SIGNATURE_TWO_ARG = IFunction.builder()
33        .name(NAME)
34        .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
35        .deterministic()
36        .contextIndependent()
37        .focusIndependent()
38        .argument(IArgument.builder()
39            .name("arg1")
40            .type(IStringItem.type())
41            .zeroOrOne()
42            .build())
43        .argument(IArgument.builder()
44            .name("arg2")
45            .type(IStringItem.type())
46            .zeroOrOne()
47            .build())
48        .returnType(IStringItem.type())
49        .returnOne()
50        .functionHandler(FnSubstringAfter::executeTwoArg)
51        .build();
52    // CPD-ON
53  
54    private FnSubstringAfter() {
55      // disable construction
56    }
57  
58    @SuppressWarnings("unused")
59    @NonNull
60    private static ISequence<IStringItem> executeTwoArg(
61        @NonNull IFunction function,
62        @NonNull List<ISequence<?>> arguments,
63        @NonNull DynamicContext dynamicContext,
64        IItem focus) {
65  
66      // From the XPath 3.1 specification:
67      // If the value of $arg1 or $arg2 is the empty sequence, or contains only
68      // ignorable collation units, it is interpreted as the zero-length string.
69      IStringItem arg1 = arguments.get(0).isEmpty()
70          ? IStringItem.valueOf("")
71          : FunctionUtils.asType(ObjectUtils.notNull(arguments.get(0).getFirstItem(true)));
72      IStringItem arg2 = arguments.get(1).isEmpty()
73          ? IStringItem.valueOf("")
74          : FunctionUtils.asType(ObjectUtils.notNull(arguments.get(1).getFirstItem(true)));
75  
76      return ISequence.of(IStringItem.valueOf(fnSubstringAfter(arg1.asString(), arg2.asString())));
77    }
78  
79    /**
80     * An implementation of XPath 3.1 <a href=
81     * "https://www.w3.org/TR/xpath-functions-31/#func-substring-after">fn:substring-after</a>.
82     *
83     * @param arg1
84     *          the source string to get a substring from
85     * @param arg2
86     *          the substring to match and find the substring to return after the
87     *          match
88     * @return the substring
89     */
90    @NonNull
91    public static String fnSubstringAfter(
92        @NonNull String arg1,
93        @NonNull String arg2) {
94      return ObjectUtils.notNull(StringUtils.substringAfter(arg1, arg2));
95    }
96  }