FnResolveUri.java

  1. /*
  2.  * Portions of this software was developed by employees of the National Institute
  3.  * of Standards and Technology (NIST), an agency of the Federal Government and is
  4.  * being made available as a public service. Pursuant to title 17 United States
  5.  * Code Section 105, works of NIST employees are not subject to copyright
  6.  * protection in the United States. This software may be subject to foreign
  7.  * copyright. Permission in the United States and in foreign countries, to the
  8.  * extent that NIST may hold copyright, to use, copy, modify, create derivative
  9.  * works, and distribute this software and its documentation without fee is hereby
  10.  * granted on a non-exclusive basis, provided that this notice and disclaimer
  11.  * of warranty appears in all copies.
  12.  *
  13.  * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
  14.  * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
  15.  * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
  16.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
  17.  * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
  18.  * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
  19.  * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
  20.  * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
  21.  * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
  22.  * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
  23.  * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
  24.  * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
  25.  */

  26. package gov.nist.secauto.metaschema.core.metapath.function.library;

  27. import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
  28. import gov.nist.secauto.metaschema.core.metapath.ISequence;
  29. import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
  30. import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
  31. import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
  32. import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
  33. import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException;
  34. import gov.nist.secauto.metaschema.core.metapath.function.UriFunctionException;
  35. import gov.nist.secauto.metaschema.core.metapath.item.IItem;
  36. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem;
  37. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
  38. import gov.nist.secauto.metaschema.core.util.ObjectUtils;

  39. import java.net.URI;
  40. import java.util.List;

  41. import edu.umd.cs.findbugs.annotations.NonNull;
  42. import edu.umd.cs.findbugs.annotations.Nullable;

  43. public final class FnResolveUri {
  44.   @NonNull
  45.   static final IFunction SIGNATURE_ONE_ARG = IFunction.builder()
  46.       .name("resolve-uri")
  47.       .namespace(MetapathConstants.NS_XPATH_FUNCTIONS)
  48.       .deterministic()
  49.       .contextDependent()
  50.       .focusIndependent()
  51.       .argument(IArgument.newBuilder()
  52.           .name("relative")
  53.           .type(IStringItem.class)
  54.           .zeroOrOne()
  55.           .build())
  56.       .returnType(IAnyUriItem.class)
  57.       .returnZeroOrOne()
  58.       .functionHandler(FnResolveUri::executeOneArg)
  59.       .build();

  60.   @NonNull
  61.   static final IFunction SIGNATURE_TWO_ARG = IFunction.builder()
  62.       .name("resolve-uri")
  63.       .namespace(MetapathConstants.NS_XPATH_FUNCTIONS)
  64.       .deterministic()
  65.       .contextIndependent()
  66.       .focusIndependent()
  67.       .argument(IArgument.newBuilder()
  68.           .name("relative")
  69.           .type(IStringItem.class)
  70.           .zeroOrOne()
  71.           .build())
  72.       .argument(IArgument.newBuilder()
  73.           .name("base")
  74.           .type(IStringItem.class)
  75.           .one()
  76.           .build())
  77.       .returnType(IAnyUriItem.class)
  78.       .returnZeroOrOne()
  79.       .functionHandler(FnResolveUri::executeTwoArg)
  80.       .build();

  81.   private FnResolveUri() {
  82.     // disable construction
  83.   }

  84.   @SuppressWarnings("unused")
  85.   @NonNull
  86.   private static ISequence<IAnyUriItem> executeOneArg(
  87.       @NonNull IFunction function,
  88.       @NonNull List<ISequence<?>> arguments,
  89.       @NonNull DynamicContext dynamicContext,
  90.       IItem focus) {

  91.     ISequence<? extends IStringItem> relativeSequence
  92.         = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
  93.     if (relativeSequence.isEmpty()) {
  94.       return ISequence.empty(); // NOPMD - readability
  95.     }

  96.     IAnyUriItem baseUri = FnStaticBaseUri.fnStaticBaseUri(dynamicContext);
  97.     if (baseUri == null) {
  98.       throw new UriFunctionException(UriFunctionException.BASE_URI_NOT_DEFINED_IN_STATIC_CONTEXT,
  99.           "The base-uri is not defined in the static context");
  100.     }

  101.     IStringItem relativeString = FunctionUtils.getFirstItem(relativeSequence, true);

  102.     IAnyUriItem resolvedUri = fnResolveUri(relativeString, baseUri);
  103.     return resolvedUri == null ? ISequence.empty() : ISequence.of(resolvedUri);
  104.   }

  105.   /**
  106.    * Implements the two argument version of the XPath 3.1 function <a href=
  107.    * "https://www.w3.org/TR/xpath-functions-31/#func-resolve-uri">resolve-uri</a>.
  108.    *
  109.    * @param function
  110.    *          the function definition
  111.    * @param arguments
  112.    *          a list of sequence arguments with an expected size of 2
  113.    * @param dynamicContext
  114.    *          the evaluation context
  115.    * @param focus
  116.    *          the current focus item
  117.    * @return a sequence containing the resolved URI or and empty sequence if
  118.    *         either the base or relative URI is {@code null}
  119.    */
  120.   @SuppressWarnings("PMD.UnusedPrivateMethod") // used in lambda
  121.   @NonNull
  122.   private static ISequence<IAnyUriItem> executeTwoArg(
  123.       @NonNull IFunction function, // NOPMD - ok
  124.       @NonNull List<ISequence<?>> arguments,
  125.       @NonNull DynamicContext dynamicContext, // NOPMD - ok
  126.       IItem focus) { // NOPMD - ok

  127.     /* there will always be two arguments */
  128.     assert arguments.size() == 2;

  129.     ISequence<? extends IStringItem> relativeSequence = FunctionUtils.asType(
  130.         ObjectUtils.requireNonNull(arguments.get(0)));
  131.     if (relativeSequence.isEmpty()) {
  132.       return ISequence.empty(); // NOPMD - readability
  133.     }

  134.     ISequence<? extends IStringItem> baseSequence = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1)));
  135.     IStringItem baseString = FunctionUtils.getFirstItem(baseSequence, true);

  136.     if (baseString == null) {
  137.       throw new InvalidArgumentFunctionException(
  138.           InvalidArgumentFunctionException.INVALID_ARGUMENT_TO_RESOLVE_URI,
  139.           "Invalid argument to fn:resolve-uri().");
  140.     }
  141.     IAnyUriItem baseUri = IAnyUriItem.cast(baseString);

  142.     IStringItem relativeString = FunctionUtils.getFirstItem(relativeSequence, true);

  143.     IAnyUriItem resolvedUri = fnResolveUri(relativeString, baseUri);
  144.     return resolvedUri == null ? ISequence.empty() : ISequence.of(resolvedUri);
  145.   }

  146.   /**
  147.    * Resolve the {@code relative} URI against the provided {@code base} URI.
  148.    *
  149.    * @param relative
  150.    *          the relative URI to resolve
  151.    * @param base
  152.    *          the base URI to resolve against
  153.    * @return the resolved URI or {@code null} if the {@code relative} URI in
  154.    *         {@code null}
  155.    */
  156.   @Nullable
  157.   public static IAnyUriItem fnResolveUri(@Nullable IStringItem relative, @NonNull IAnyUriItem base) {
  158.     IAnyUriItem relativeUri = relative == null ? null : IAnyUriItem.cast(relative);

  159.     return fnResolveUri(relativeUri, base);
  160.   }

  161.   /**
  162.    * Resolve the {@code relative} URI against the provided {@code base} URI.
  163.    *
  164.    * @param relative
  165.    *          the relative URI to resolve
  166.    * @param base
  167.    *          the base URI to resolve against
  168.    * @return the resolved URI or {@code null} if the {@code relative} URI in
  169.    *         {@code null}
  170.    */
  171.   @Nullable
  172.   public static IAnyUriItem fnResolveUri(@Nullable IAnyUriItem relative, @NonNull IAnyUriItem base) {
  173.     if (relative == null) {
  174.       return null; // NOPMD - readability
  175.     }

  176.     @SuppressWarnings("null")
  177.     @NonNull URI resolvedUri = base.getValue().resolve(relative.getValue());
  178.     return IAnyUriItem.valueOf(resolvedUri);
  179.   }
  180. }