FunctionUtils.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.ISequence;
  28. import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException;
  29. import gov.nist.secauto.metaschema.core.metapath.TypeMetapathException;
  30. import gov.nist.secauto.metaschema.core.metapath.function.library.FnData;
  31. import gov.nist.secauto.metaschema.core.metapath.item.IItem;
  32. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
  33. import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem;
  34. import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
  35. import gov.nist.secauto.metaschema.core.util.ObjectUtils;

  36. import java.math.BigInteger;
  37. import java.math.MathContext;
  38. import java.util.List;

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

  41. public final class FunctionUtils {
  42.   public static final MathContext MATH_CONTEXT = MathContext.DECIMAL64;

  43.   private FunctionUtils() {
  44.     // disable
  45.   }

  46.   /**
  47.    * Converts a {@link INumericItem} value to an integer value.
  48.    *
  49.    * @param value
  50.    *          the value to convert
  51.    * @return the integer value
  52.    * @throws ArithmeticException
  53.    *           if the provided value will not exactly fit in an {@code int}
  54.    */
  55.   public static int asInteger(@NonNull INumericItem value) {
  56.     return asInteger(value.asInteger());
  57.   }

  58.   /**
  59.    * Converts a {@link BigInteger} value to an integer value.
  60.    *
  61.    * @param value
  62.    *          the value to convert
  63.    * @return the integer value
  64.    * @throws ArithmeticException
  65.    *           if the provided value will not exactly fit in an {@code int}
  66.    */
  67.   public static int asInteger(@NonNull BigInteger value) {
  68.     return value.intValueExact();
  69.   }

  70.   /**
  71.    * Converts a {@link INumericItem} value to a long value.
  72.    *
  73.    * @param value
  74.    *          the value to convert
  75.    * @return the long value
  76.    * @throws ArithmeticException
  77.    *           if the provided value will not exactly fit in an {@code long}
  78.    */
  79.   public static long asLong(@NonNull INumericItem value) {
  80.     return asLong(value.asInteger());
  81.   }

  82.   /**
  83.    * Converts a {@link BigInteger} value to a long value.
  84.    *
  85.    * @param value
  86.    *          the value to convert
  87.    * @return the long value
  88.    * @throws ArithmeticException
  89.    *           if the provided value will not exactly fit in an {@code long}
  90.    */
  91.   public static long asLong(@NonNull BigInteger value) {
  92.     return value.longValueExact();
  93.   }

  94.   /**
  95.    * Retrieves the first item in a sequence. If the sequence is empty, a
  96.    * {@link TypeMetapathException} exception is thrown. If requireSingleton is
  97.    * {@code true} and the sequence contains more than one item, a
  98.    * {@link TypeMetapathException} is thrown.
  99.    *
  100.    * @param <ITEM>
  101.    *          the item type to return derived from the provided sequence
  102.    * @param sequence
  103.    *          the sequence to retrieve the first item from
  104.    * @param requireSingleton
  105.    *          if {@code true} then a {@link TypeMetapathException} is thrown if
  106.    *          the sequence contains more than one item
  107.    * @return {@code null} if the sequence is empty, or the item otherwise
  108.    * @throws TypeMetapathException
  109.    *           if the sequence is empty, or contains more than one item and
  110.    *           requireSingleton is {@code true}
  111.    */
  112.   @NonNull
  113.   public static <ITEM extends IItem> ITEM requireFirstItem(@NonNull ISequence<ITEM> sequence,
  114.       boolean requireSingleton) {
  115.     if (sequence.isEmpty()) {
  116.       throw new InvalidTypeMetapathException(
  117.           null,
  118.           "Expected a non-empty sequence, but sequence was empty.");
  119.     }
  120.     List<ITEM> items = sequence.asList();
  121.     if (requireSingleton && items.size() != 1) {
  122.       throw new InvalidTypeMetapathException(
  123.           null,
  124.           String.format("sequence expected to contain one item, but found '%d'", items.size()));
  125.     }
  126.     return ObjectUtils.notNull(items.iterator().next());
  127.   }

  128.   /**
  129.    * Retrieves the first item in a sequence. If the sequence is empty, a
  130.    * {@code null} result is returned. If requireSingleton is {@code true} and the
  131.    * sequence contains more than one item, a {@link TypeMetapathException} is
  132.    * thrown.
  133.    *
  134.    * @param <ITEM>
  135.    *          the item type to return derived from the provided sequence
  136.    * @param sequence
  137.    *          the sequence to retrieve the first item from
  138.    * @param requireSingleton
  139.    *          if {@code true} then a {@link TypeMetapathException} is thrown if
  140.    *          the sequence contains more than one item
  141.    * @return {@code null} if the sequence is empty, or the item otherwise
  142.    * @throws TypeMetapathException
  143.    *           if the sequence contains more than one item and requireSingleton is
  144.    *           {@code true}
  145.    */
  146.   @Nullable
  147.   public static <ITEM extends IItem> ITEM getFirstItem(@NonNull ISequence<ITEM> sequence, boolean requireSingleton) {
  148.     @Nullable ITEM retval = null;
  149.     if (!sequence.isEmpty()) {
  150.       List<ITEM> items = sequence.asList();
  151.       if (requireSingleton && items.size() != 1) {
  152.         throw new InvalidTypeMetapathException(
  153.             null,
  154.             String.format("sequence expected to contain one item, but found '%d'", items.size()));
  155.       }
  156.       retval = items.iterator().next();
  157.     }
  158.     return retval;
  159.   }

  160.   /**
  161.    * Gets the first item of the provided sequence as a {@link INumericItem} value.
  162.    * If the sequence is empty, then a {@code null} value is returned.
  163.    *
  164.    * @param sequence
  165.    *          a Metapath sequence containing the value to convert
  166.    * @param requireSingleton
  167.    *          if {@code true} then a {@link TypeMetapathException} is thrown if
  168.    *          the sequence contains more than one item
  169.    * @return the numeric item value, or {@code null} if the result is an empty
  170.    *         sequence
  171.    * @throws TypeMetapathException
  172.    *           if the sequence contains more than one item, or the item cannot be
  173.    *           cast to a numeric value
  174.    *
  175.    */
  176.   @Nullable
  177.   public static INumericItem toNumeric(@NonNull ISequence<?> sequence, boolean requireSingleton) {
  178.     IItem item = getFirstItem(sequence, requireSingleton);
  179.     return item == null ? null : toNumeric(item);
  180.   }

  181.   /**
  182.    * Gets the provided item value as a {@link INumericItem} value.
  183.    *
  184.    * @param item
  185.    *          the value to convert
  186.    * @return the numeric item value
  187.    * @throws TypeMetapathException
  188.    *           if the sequence contains more than one item, or the item cannot be
  189.    *           cast to a numeric value
  190.    */
  191.   @NonNull
  192.   public static INumericItem toNumeric(@NonNull IItem item) {
  193.     // atomize
  194.     IAnyAtomicItem atomicItem = FnData.fnDataItem(item);
  195.     return toNumeric(atomicItem);
  196.   }

  197.   /**
  198.    * Gets the provided item value as a {@link INumericItem} value.
  199.    *
  200.    * @param item
  201.    *          the value to convert
  202.    * @return the numeric item value
  203.    * @throws TypeMetapathException
  204.    *           if the item cannot be cast to a numeric value
  205.    */
  206.   @NonNull
  207.   public static INumericItem toNumeric(@NonNull IAnyAtomicItem item) {
  208.     try {
  209.       return IDecimalItem.cast(item);
  210.     } catch (InvalidValueForCastFunctionException ex) {
  211.       throw new InvalidTypeMetapathException(item, ex.getLocalizedMessage(), ex);
  212.     }
  213.   }

  214.   /**
  215.    * Gets the provided item value as a {@link INumericItem} value. If the item is
  216.    * {@code null}, then a {@code null} value is returned.
  217.    *
  218.    * @param item
  219.    *          the value to convert
  220.    * @return the numeric item value
  221.    * @throws TypeMetapathException
  222.    *           if the item cannot be cast to a numeric value
  223.    */
  224.   @Nullable
  225.   public static INumericItem toNumericOrNull(@Nullable IAnyAtomicItem item) {
  226.     return item == null ? null : toNumeric(item);
  227.   }

  228.   @SuppressWarnings("unchecked")
  229.   @NonNull
  230.   public static <TYPE extends IItem> TYPE asType(@NonNull IItem item) {
  231.     return (TYPE) item;
  232.   }

  233.   @SuppressWarnings("unchecked")
  234.   @NonNull
  235.   public static <TYPE extends IItem> ISequence<TYPE> asType(@NonNull ISequence<?> sequence) {
  236.     return (ISequence<TYPE>) sequence;
  237.   }

  238.   @SuppressWarnings("unchecked")
  239.   @Nullable
  240.   public static <TYPE extends IItem> TYPE asTypeOrNull(@Nullable IItem item) {
  241.     return (TYPE) item;
  242.   }

  243.   @NonNull
  244.   public static <TYPE extends IItem> TYPE requireType(Class<TYPE> clazz, IItem item) {
  245.     if (item == null) {
  246.       throw new InvalidTypeMetapathException(
  247.           null,
  248.           String.format("Expected non-null type '%s', but the node was null.",
  249.               clazz.getName()));
  250.     } else if (!clazz.isInstance(item)) {
  251.       throw new InvalidTypeMetapathException(
  252.           item,
  253.           String.format("Expected type '%s', but the node was type '%s'.",
  254.               clazz.getName(),
  255.               item.getClass().getName()));
  256.     }
  257.     return FunctionUtils.asType(item);
  258.   }

  259.   @Nullable
  260.   public static <TYPE extends IItem> TYPE requireTypeOrNull(Class<TYPE> clazz, @Nullable IItem item) {
  261.     if (item == null || clazz.isInstance(item)) {
  262.       return FunctionUtils.asTypeOrNull(item);
  263.     }
  264.     throw new InvalidTypeMetapathException(
  265.         item,
  266.         String.format("Expected type '%s', but the node was type '%s'.",
  267.             clazz.getName(),
  268.             item.getClass().getName()));
  269.   }
  270. }