IFunction.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;

  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.MetapathException;
  30. import gov.nist.secauto.metaschema.core.metapath.item.IItem;
  31. import gov.nist.secauto.metaschema.core.util.ObjectUtils;

  32. import java.net.URI;
  33. import java.util.ArrayList;
  34. import java.util.EnumSet;
  35. import java.util.LinkedList;
  36. import java.util.List;
  37. import java.util.Objects;
  38. import java.util.Set;

  39. import javax.xml.namespace.QName;

  40. import edu.umd.cs.findbugs.annotations.NonNull;

  41. public interface IFunction {
  42.   enum FunctionProperty {
  43.     /**
  44.      * Indicates that the function will produce identical results for the same
  45.      * arguments (see XPath 3.1 <a href=
  46.      * "https://www.w3.org/TR/xpath-functions-31/#dt-deterministic">deterministic</a>).
  47.      * If not assigned to a function definition, a function call with the same
  48.      * arguments is not guaranteed to produce the same results in the same order for
  49.      * subsequent calls within the same execution context.
  50.      */
  51.     DETERMINISTIC,
  52.     /**
  53.      * Indicates that the result of the function depends on property values within
  54.      * the static or dynamic context and the provided arguments (see XPath 3.1
  55.      * <a href=
  56.      * "https://www.w3.org/TR/xpath-functions-31/#dt-context-dependent">context-dependent</a>).
  57.      * If not assigned to a function definition, a call will not be affected by the
  58.      * property values within the static or dynamic context and will not have any
  59.      * arguments.
  60.      */
  61.     CONTEXT_DEPENDENT,
  62.     /**
  63.      * Indicates that the result of the function depends on the current focus (see
  64.      * XPath 3.1 <a href=
  65.      * "https://www.w3.org/TR/xpath-functions-31/#dt-focus-independent">focus-dependent</a>).
  66.      * If not assigned to a function definition, a call will not be affected by the
  67.      * current focus.
  68.      */
  69.     FOCUS_DEPENDENT,
  70.     /**
  71.      * The function allows the last argument to be repeated any number of times.
  72.      */
  73.     UNBOUNDED_ARITY;
  74.   }

  75.   /**
  76.    * Retrieve the name of the function.
  77.    *
  78.    * @return the function's name
  79.    */
  80.   @NonNull
  81.   String getName();

  82.   /**
  83.    * Retrieve the namespace of the function.
  84.    *
  85.    * @return the function's namespace
  86.    */
  87.   @NonNull
  88.   String getNamespace();

  89.   /**
  90.    * Retrieve the namespace qualified name of the function.
  91.    *
  92.    * @return the namespace qualified name
  93.    */
  94.   @NonNull
  95.   default QName getQName() {
  96.     return new QName(getNamespace(), getName());
  97.   }

  98.   /**
  99.    * Retrieve the set of assigned function properties.
  100.    *
  101.    * @return the set of properties or an empty set
  102.    */
  103.   @NonNull
  104.   Set<FunctionProperty> getProperties();

  105.   /**
  106.    * Retrieve the list of function arguments.
  107.    *
  108.    * @return the function arguments or an empty list if there are none
  109.    */
  110.   @NonNull
  111.   List<IArgument> getArguments();

  112.   /**
  113.    * Determine the number of arguments the function has.
  114.    *
  115.    * @return the number of function arguments
  116.    */
  117.   int arity();

  118.   /**
  119.    * Determines if the result of the function call will produce identical results
  120.    * when provided the same implicit or explicit arguments.
  121.    *
  122.    * @return {@code true} if function is deterministic or {@code false} otherwise
  123.    * @see FunctionProperty#DETERMINISTIC
  124.    */
  125.   default boolean isDeterministic() {
  126.     return getProperties().contains(FunctionProperty.DETERMINISTIC);
  127.   }

  128.   /**
  129.    * Determines if the result of the function call depends on property values
  130.    * within the static or dynamic context and the provided arguments.
  131.    *
  132.    * @return {@code true} if function is context dependent or {@code false}
  133.    *         otherwise
  134.    * @see FunctionProperty#CONTEXT_DEPENDENT
  135.    */
  136.   default boolean isContextDepenent() {
  137.     return getProperties().contains(FunctionProperty.CONTEXT_DEPENDENT);
  138.   }

  139.   /**
  140.    * Determines if the result of the function call depends on the current focus.
  141.    *
  142.    * @return {@code true} if function is focus dependent or {@code false}
  143.    *         otherwise
  144.    * @see FunctionProperty#FOCUS_DEPENDENT
  145.    */
  146.   default boolean isFocusDepenent() {
  147.     return getProperties().contains(FunctionProperty.FOCUS_DEPENDENT);
  148.   }

  149.   /**
  150.    * Determines if the final argument can be repeated.
  151.    *
  152.    * @return {@code true} if the final argument can be repeated or {@code false}
  153.    *         otherwise
  154.    * @see FunctionProperty#UNBOUNDED_ARITY
  155.    */
  156.   default boolean isArityUnbounded() {
  157.     return getProperties().contains(FunctionProperty.UNBOUNDED_ARITY);
  158.   }

  159.   /**
  160.    * Retrieve the function result sequence type.
  161.    *
  162.    * @return the function result sequence type
  163.    */
  164.   @NonNull
  165.   ISequenceType getResult();

  166.   // /**
  167.   // * Determines by static analysis if the function supports the expression
  168.   // arguments provided.
  169.   // *
  170.   // * @param arguments
  171.   // * the expression arguments to evaluate
  172.   // * @return {@code true} if the arguments are supported or {@code false}
  173.   // otherwise
  174.   // */
  175.   // boolean isSupported(List<IExpression<?>> arguments);

  176.   @NonNull
  177.   ISequence<?> execute(
  178.       @NonNull List<ISequence<?>> arguments,
  179.       @NonNull DynamicContext dynamicContext,
  180.       @NonNull ISequence<?> focus) throws MetapathException;

  181.   /**
  182.    * Get the signature of the function as a string.
  183.    *
  184.    * @return the signature
  185.    */
  186.   String toSignature();

  187.   @NonNull
  188.   static Builder builder() {
  189.     return new Builder();
  190.   }

  191.   @SuppressWarnings("PMD.LooseCoupling")
  192.   class Builder {
  193.     private String name;
  194.     private String namespace;
  195.     @SuppressWarnings("null")
  196.     @NonNull
  197.     private final EnumSet<FunctionProperty> properties = EnumSet.noneOf(FunctionProperty.class);
  198.     @NonNull
  199.     private final List<IArgument> arguments = new LinkedList<>();
  200.     private Class<? extends IItem> returnType = IItem.class;
  201.     private Occurrence returnOccurrence = Occurrence.ONE;
  202.     private IFunctionExecutor functionHandler;

  203.     @NonNull
  204.     public Builder name(@NonNull String name) {
  205.       Objects.requireNonNull(name, "name");
  206.       if (name.isBlank()) {
  207.         throw new IllegalArgumentException("the name must be non-blank");
  208.       }
  209.       this.name = name.trim();
  210.       return this;
  211.     }

  212.     @NonNull
  213.     public Builder namespace(@NonNull URI uri) {
  214.       return namespace(ObjectUtils.notNull(uri.toASCIIString()));
  215.     }

  216.     @NonNull
  217.     public Builder namespace(@NonNull String name) {
  218.       Objects.requireNonNull(name, "name");
  219.       if (name.isBlank()) {
  220.         throw new IllegalArgumentException("the name must be non-blank");
  221.       }
  222.       this.namespace = name.trim();
  223.       return this;
  224.     }

  225.     @NonNull
  226.     public Builder deterministic() {
  227.       properties.add(FunctionProperty.DETERMINISTIC);
  228.       return this;
  229.     }

  230.     @NonNull
  231.     public Builder nonDeterministic() {
  232.       properties.remove(FunctionProperty.DETERMINISTIC);
  233.       return this;
  234.     }

  235.     @NonNull
  236.     public Builder contextDependent() {
  237.       properties.add(FunctionProperty.CONTEXT_DEPENDENT);
  238.       return this;
  239.     }

  240.     @NonNull
  241.     public Builder contextIndependent() {
  242.       properties.remove(FunctionProperty.CONTEXT_DEPENDENT);
  243.       return this;
  244.     }

  245.     @NonNull
  246.     public Builder focusDependent() {
  247.       properties.add(FunctionProperty.FOCUS_DEPENDENT);
  248.       return this;
  249.     }

  250.     @NonNull
  251.     public Builder focusIndependent() {
  252.       properties.remove(FunctionProperty.FOCUS_DEPENDENT);
  253.       return this;
  254.     }

  255.     @NonNull
  256.     public Builder allowUnboundedArity(boolean allow) {
  257.       if (allow) {
  258.         properties.add(FunctionProperty.UNBOUNDED_ARITY);
  259.       } else {
  260.         properties.remove(FunctionProperty.UNBOUNDED_ARITY);
  261.       }
  262.       return this;
  263.     }

  264.     @NonNull
  265.     public Builder returnType(@NonNull Class<? extends IItem> type) {
  266.       Objects.requireNonNull(type, "type");
  267.       this.returnType = type;
  268.       return this;
  269.     }

  270.     @NonNull
  271.     public Builder returnZeroOrOne() {
  272.       return returnOccurrence(Occurrence.ZERO_OR_ONE);
  273.     }

  274.     @NonNull
  275.     public Builder returnOne() {
  276.       return returnOccurrence(Occurrence.ONE);
  277.     }

  278.     @NonNull
  279.     public Builder returnZeroOrMore() {
  280.       return returnOccurrence(Occurrence.ZERO_OR_MORE);
  281.     }

  282.     @NonNull
  283.     public Builder returnOneOrMore() {
  284.       return returnOccurrence(Occurrence.ONE_OR_MORE);
  285.     }

  286.     @NonNull
  287.     public Builder returnOccurrence(@NonNull Occurrence occurrence) {
  288.       Objects.requireNonNull(occurrence, "occurrence");
  289.       this.returnOccurrence = occurrence;
  290.       return this;
  291.     }

  292.     @NonNull
  293.     public Builder argument(@NonNull IArgument.Builder builder) {
  294.       return argument(builder.build());
  295.     }

  296.     @NonNull
  297.     public Builder argument(@NonNull IArgument argument) {
  298.       Objects.requireNonNull(argument, "argument");
  299.       this.arguments.add(argument);
  300.       return this;
  301.     }

  302.     @NonNull
  303.     public Builder functionHandler(@NonNull IFunctionExecutor handler) {
  304.       Objects.requireNonNull(handler, "handler");
  305.       this.functionHandler = handler;
  306.       return this;
  307.     }

  308.     @NonNull
  309.     public IFunction build() {
  310.       ISequenceType sequenceType;
  311.       if (returnType == null) {
  312.         sequenceType = ISequenceType.EMPTY;
  313.       } else {
  314.         sequenceType = new SequenceTypeImpl(
  315.             returnType,
  316.             ObjectUtils.requireNonNull(returnOccurrence, "the return occurrence must not be null"));
  317.       }

  318.       if (properties.contains(FunctionProperty.UNBOUNDED_ARITY) && arguments.isEmpty()) {
  319.         throw new IllegalStateException("to allow unbounded arity, at least one argument must be provided");
  320.       }

  321.       return new DefaultFunction(
  322.           ObjectUtils.requireNonNull(name, "the name must not be null"),
  323.           ObjectUtils.requireNonNull(namespace, "the namespace must not be null"),
  324.           properties,
  325.           new ArrayList<>(arguments),
  326.           sequenceType,
  327.           ObjectUtils.requireNonNull(functionHandler, "the function handler must not be null"));
  328.     }
  329.   }
  330. }