From cffe48760ff4577907ac46a1c42134a48d3d7370 Mon Sep 17 00:00:00 2001 From: David Waltermire Date: Tue, 21 May 2024 00:26:29 -0400 Subject: [PATCH] Added basic support for array construction. Cleaned up ISequence implementations, moving getFirstItem to be a method member. --- .../metaschema/core/metapath/ISequence.java | 59 ++++++++++++++- .../core/metapath/IStringValued.java | 39 ++++++++++ .../core/metapath/MetapathConstants.java | 6 +- .../core/metapath/MetapathExpression.java | 6 +- .../core/metapath/StaticContext.java | 3 + .../core/metapath/cst/AbstractExpression.java | 6 +- .../core/metapath/cst/BuildCSTVisitor.java | 5 ++ .../metapath/function/DefaultFunction.java | 43 +++++------ .../core/metapath/function/FunctionUtils.java | 74 +------------------ .../metapath/function/library/ArraySize.java | 69 +++++++++++++++++ .../function/library/CastFunction.java | 2 +- .../library/DefaultFunctionLibrary.java | 3 + .../core/metapath/function/library/FnAbs.java | 5 +- .../metapath/function/library/FnBaseUri.java | 2 +- .../metapath/function/library/FnBoolean.java | 3 +- .../metapath/function/library/FnCeiling.java | 2 +- .../metapath/function/library/FnCompare.java | 8 +- .../metapath/function/library/FnConcat.java | 3 +- .../metapath/function/library/FnData.java | 66 ++++++++++++++--- .../core/metapath/function/library/FnDoc.java | 2 +- .../function/library/FnDocumentUri.java | 2 +- .../function/library/FnInsertBefore.java | 5 +- .../metapath/function/library/FnPath.java | 2 +- .../metapath/function/library/FnRemove.java | 4 +- .../function/library/FnResolveUri.java | 6 +- .../metapath/function/library/FnRound.java | 9 +-- .../function/library/FnStartsWith.java | 8 +- .../core/metapath/function/library/FnSum.java | 8 +- .../function/library/MpRecurseDepth.java | 4 +- .../function/library/NumericFunction.java | 2 +- .../core/metapath/impl/AbstractArrayItem.java | 7 +- .../core/metapath/impl/AbstractSequence.java | 6 ++ .../metapath/impl/ImmutableCollections.java | 1 - .../metapath/item/atomic/IAnyAtomicItem.java | 4 +- .../metapath/item/atomic/IDecimalItem.java | 3 +- .../metapath/item/atomic/IIntegerItem.java | 14 ++++ .../metapath/item/function/IArrayItem.java | 3 +- .../core/metapath/ISequenceTest.java | 55 ++++++++++++++ .../metapath/cst/BuildCstVisitorTest.java | 5 +- .../function/library/ArraySizeTest.java | 71 ++++++++++++++++++ .../databind/metapath/function/Model.java | 2 +- 41 files changed, 458 insertions(+), 169 deletions(-) create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IStringValued.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySize.java create mode 100644 core/src/test/java/gov/nist/secauto/metaschema/core/metapath/ISequenceTest.java create mode 100644 core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySizeTest.java diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java index e46b22890..146dca0af 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java @@ -61,7 +61,7 @@ * the Java type of the items in a sequence */ @SuppressWarnings("PMD.ShortMethodName") -public interface ISequence extends List, IArrayMember { +public interface ISequence extends List, IArrayMember, IStringValued { /** * Get an empty sequence. * @@ -97,6 +97,63 @@ default Iterator iterator() { @NonNull Stream stream(); + /** + * Retrieves the first item in a sequence. If the sequence is empty, a + * {@code null} result is returned. If requireSingleton is {@code true} and the + * sequence contains more than one item, a {@link TypeMetapathException} is + * thrown. + * + * @param + * the item type to return derived from the provided sequence + * @param items + * the sequence to retrieve the first item from + * @param requireSingleton + * if {@code true} then a {@link TypeMetapathException} is thrown if + * the sequence contains more than one item + * @return {@code null} if the sequence is empty, or the item otherwise + * @throws TypeMetapathException + * if the sequence contains more than one item and requireSingleton is + * {@code true} + */ + static T getFirstItem(@NonNull ISequence items, boolean requireSingleton) { + return getFirstItem(items.stream(), requireSingleton); + } + + /** + * Retrieves the first item in a sequence. If the sequence is empty, a + * {@code null} result is returned. If requireSingleton is {@code true} and the + * sequence contains more than one item, a {@link TypeMetapathException} is + * thrown. + * + * @param + * the item type to return derived from the provided sequence + * @param items + * the sequence to retrieve the first item from + * @param requireSingleton + * if {@code true} then a {@link TypeMetapathException} is thrown if + * the sequence contains more than one item + * @return {@code null} if the sequence is empty, or the item otherwise + * @throws TypeMetapathException + * if the sequence contains more than one item and requireSingleton is + * {@code true} + */ + static T getFirstItem(@NonNull Stream items, boolean requireSingleton) { + return items.limit(2) + .reduce((t, u) -> { + if (requireSingleton) { + throw new InvalidTypeMetapathException( + null, + String.format("sequence expected to contain only one item, but found multiple")); + } + return t; + }).orElse(null); + } + + @Nullable + default ITEM getFirstItem(boolean requireSingleton) { + return getFirstItem(this, requireSingleton); + } + /** * Get a stream guaranteed to be backed by a list. * diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IStringValued.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IStringValued.java new file mode 100644 index 000000000..ec9b682db --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IStringValued.java @@ -0,0 +1,39 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public interface IStringValued { + /** + * Get the string value. + * + * @return the string value + */ + @NonNull + String asString(); +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java index 86b73dd12..115c0f63a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java @@ -52,6 +52,9 @@ public final class MetapathConstants { public static final URI NS_METAPATH_FUNCTIONS_MATH = ObjectUtils.requireNonNull( URI.create("http://csrc.nist.gov/ns/metaschema/metapath-functions/math")); @NonNull + public static final URI NS_METAPATH_FUNCTIONS_ARRAY = ObjectUtils.requireNonNull( + URI.create("http://csrc.nist.gov/ns/metaschema/metapath-functions/array")); + @NonNull public static final URI NS_METAPATH_FUNCTIONS_EXTENDED = NS_METAPATH; @NonNull @@ -62,9 +65,10 @@ public final class MetapathConstants { public static final String PREFIX_XPATH_FUNCTIONS = "mp"; @NonNull public static final String PREFIX_XPATH_FUNCTIONS_MATH = "math"; + @NonNull + public static final String PREFIX_XPATH_FUNCTIONS_ARRAY = "array"; private MetapathConstants() { // disable construction } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java index 2d09b2fbe..1e8b7ca17 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java @@ -346,7 +346,7 @@ protected T toResultType(@NonNull ISequence sequence, @NonNull ResultType result = FnBoolean.fnBoolean(sequence).toBoolean(); break; case NODE: - result = FunctionUtils.getFirstItem(sequence, true); + result = sequence.getFirstItem(true); break; case NUMBER: INumericItem numeric = FunctionUtils.toNumeric(sequence, true); @@ -356,8 +356,8 @@ protected T toResultType(@NonNull ISequence sequence, @NonNull ResultType result = sequence; break; case STRING: - IItem item = FunctionUtils.getFirstItem(sequence, true); - result = item == null ? "" : FnData.fnDataItem(item).asString(); + IAnyAtomicItem item = FnData.fnData(sequence).getFirstItem(true); + result = item == null ? "" : item.asString(); break; default: throw new InvalidTypeMetapathException(null, String.format("unsupported result type '%s'", resultType.name())); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java index 18f07fb07..1859059a7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java @@ -62,6 +62,9 @@ public final class StaticContext { knownNamespaces.put( MetapathConstants.PREFIX_XPATH_FUNCTIONS_MATH, MetapathConstants.NS_METAPATH_FUNCTIONS_MATH); + knownNamespaces.put( + MetapathConstants.PREFIX_XPATH_FUNCTIONS_ARRAY, + MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY); WELL_KNOWN_NAMESPACES = CollectionUtil.unmodifiableMap(knownNamespaces); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java index 967b413c2..b60654f2f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java @@ -28,9 +28,7 @@ import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.TypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.library.FnData; -import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import edu.umd.cs.findbugs.annotations.NonNull; @@ -54,9 +52,7 @@ public abstract class AbstractExpression implements IExpression { @Nullable public static IAnyAtomicItem getFirstDataItem(@NonNull ISequence sequence, boolean requireSingleton) { - IItem item = FunctionUtils.getFirstItem(sequence, requireSingleton); - - return item == null ? null : FnData.fnDataItem(item); + return FnData.fnData(sequence).getFirstItem(requireSingleton); } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java index 0db7d56c5..4fb564e00 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java @@ -266,6 +266,11 @@ protected IExpression handleLet(LetexprContext context) { @Override protected IExpression handleArrayConstructor(SquarearrayconstructorContext context) { + if (context.getChildCount() == 2) { + // empty + return new ArrayMembers(CollectionUtil.emptyList()); + } + return nAiryToCollection(context, 1, 2, (ctx, idx) -> { int pos = (idx - 1) / 2; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java index 159cf5207..27690d589 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java @@ -35,6 +35,7 @@ import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.ArrayList; import java.util.Collections; @@ -43,6 +44,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -205,7 +207,7 @@ public static List> convertArguments( String.format("a sequence of one expected, but found '%d'", size)); } - IItem item = FunctionUtils.getFirstItem(parameter, true); + IItem item = parameter.getFirstItem(true); parameter = item == null ? ISequence.empty() : ISequence.of(item); break; } @@ -216,7 +218,7 @@ public static List> convertArguments( String.format("a sequence of zero or one expected, but found '%d'", size)); } - IItem item = FunctionUtils.getFirstItem(parameter, false); + IItem item = parameter.getFirstItem(false); parameter = item == null ? ISequence.empty() : ISequence.of(item); break; } @@ -279,35 +281,34 @@ protected static ISequence convertSequence(@NonNull IArgument argument, @NonN ISequenceType requiredSequenceType = argument.getSequenceType(); Class requiredSequenceTypeClass = requiredSequenceType.getType(); - List result = new ArrayList<>(sequence.size()); + Stream stream = sequence.safeStream(); - boolean atomize = IAnyAtomicItem.class.isAssignableFrom(requiredSequenceTypeClass); + if (IAnyAtomicItem.class.isAssignableFrom(requiredSequenceTypeClass)) { + Stream atomicStream = stream.flatMap(item -> FnData.atomize(item)); - for (IItem item : sequence.getValue()) { - assert item != null; - if (atomize) { - item = FnData.fnDataItem(item); // NOPMD - intentional - - // if (IUntypedAtomicItem.class.isInstance(item)) { // NOPMD - // // TODO: apply cast to atomic type - // } + // if (IUntypedAtomicItem.class.isInstance(item)) { // NOPMD + // // TODO: apply cast to atomic type + // } + if (IStringItem.class.equals(requiredSequenceTypeClass)) { // promote URIs to strings if a string is required - if (IStringItem.class.equals(requiredSequenceTypeClass) && IAnyUriItem.class.isInstance(item)) { - item = IStringItem.cast((IAnyUriItem) item); // NOPMD - intentional - } + atomicStream = atomicStream.map(item -> IAnyUriItem.class.isInstance(item) ? IStringItem.cast(item) : item); } - // item = requiredSequenceType. + stream = atomicStream; + } + + stream = stream.peek(item -> { if (!requiredSequenceTypeClass.isInstance(item)) { throw new InvalidTypeMetapathException( item, - String.format("The type '%s' is not a subtype of '%s'", item.getClass().getName(), + String.format("The type '%s' is not a subtype of '%s'", + item.getClass().getName(), requiredSequenceTypeClass.getName())); } - result.add(item); - } - retval = ISequence.of(result); + }); + + retval = ISequence.of(stream); } return retval; } @@ -320,7 +321,7 @@ public ISequence execute( try { List> convertedArguments = convertArguments(this, arguments); - IItem contextItem = isFocusDepenent() ? FunctionUtils.requireFirstItem(focus, true) : null; + IItem contextItem = isFocusDepenent() ? ObjectUtils.requireNonNull(focus.getFirstItem(true)) : null; CallingContext callingContext = null; ISequence result = null; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java index 91ab3b8f4..73d7dca75 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java @@ -112,74 +112,6 @@ public static long asLong(@NonNull BigInteger value) { return value.longValueExact(); } - /** - * Retrieves the first item in a sequence. If the sequence is empty, a - * {@link TypeMetapathException} exception is thrown. If requireSingleton is - * {@code true} and the sequence contains more than one item, a - * {@link TypeMetapathException} is thrown. - * - * @param - * the item type to return derived from the provided sequence - * @param sequence - * the sequence to retrieve the first item from - * @param requireSingleton - * if {@code true} then a {@link TypeMetapathException} is thrown if - * the sequence contains more than one item - * @return {@code null} if the sequence is empty, or the item otherwise - * @throws TypeMetapathException - * if the sequence is empty, or contains more than one item and - * requireSingleton is {@code true} - */ - @NonNull - public static ITEM requireFirstItem(@NonNull ISequence sequence, - boolean requireSingleton) { - if (sequence.isEmpty()) { - throw new InvalidTypeMetapathException( - null, - "Expected a non-empty sequence, but sequence was empty."); - } - List items = sequence.getValue(); - if (requireSingleton && items.size() != 1) { - throw new InvalidTypeMetapathException( - null, - String.format("sequence expected to contain one item, but found '%d'", items.size())); - } - return ObjectUtils.notNull(items.iterator().next()); - } - - /** - * Retrieves the first item in a sequence. If the sequence is empty, a - * {@code null} result is returned. If requireSingleton is {@code true} and the - * sequence contains more than one item, a {@link TypeMetapathException} is - * thrown. - * - * @param - * the item type to return derived from the provided sequence - * @param sequence - * the sequence to retrieve the first item from - * @param requireSingleton - * if {@code true} then a {@link TypeMetapathException} is thrown if - * the sequence contains more than one item - * @return {@code null} if the sequence is empty, or the item otherwise - * @throws TypeMetapathException - * if the sequence contains more than one item and requireSingleton is - * {@code true} - */ - @Nullable - public static ITEM getFirstItem(@NonNull ISequence sequence, boolean requireSingleton) { - @Nullable ITEM retval = null; - if (!sequence.isEmpty()) { - List items = sequence.getValue(); - if (requireSingleton && items.size() != 1) { - throw new InvalidTypeMetapathException( - null, - String.format("sequence expected to contain one item, but found '%d'", items.size())); - } - retval = items.iterator().next(); - } - return retval; - } - /** * Gets the first item of the provided sequence as a {@link INumericItem} value. * If the sequence is empty, then a {@code null} value is returned. @@ -198,7 +130,7 @@ public static ITEM getFirstItem(@NonNull ISequence se */ @Nullable public static INumericItem toNumeric(@NonNull ISequence sequence, boolean requireSingleton) { - IItem item = getFirstItem(sequence, requireSingleton); + IItem item = sequence.getFirstItem(requireSingleton); return item == null ? null : toNumeric(item); } @@ -215,7 +147,7 @@ public static INumericItem toNumeric(@NonNull ISequence sequence, boolean req @NonNull public static INumericItem toNumeric(@NonNull IItem item) { // atomize - IAnyAtomicItem atomicItem = FnData.fnDataItem(item); + IAnyAtomicItem atomicItem = ISequence.getFirstItem(FnData.atomize(item), true); return toNumeric(atomicItem); } @@ -229,7 +161,7 @@ public static INumericItem toNumeric(@NonNull IItem item) { * if the item cannot be cast to a numeric value */ @NonNull - public static INumericItem toNumeric(@NonNull IAnyAtomicItem item) { + public static INumericItem toNumeric(@Nullable IAnyAtomicItem item) { try { return IDecimalItem.cast(item); } catch (InvalidValueForCastFunctionException ex) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySize.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySize.java new file mode 100644 index 000000000..bed9e9d69 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySize.java @@ -0,0 +1,69 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath.function.library; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class ArraySize { + @NonNull + static final IFunction SIGNATURE = IFunction.builder() + .name("size") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .returnType(IIntegerItem.class) + .returnOne() + .functionHandler(ArraySize::execute) + .build(); + + @SuppressWarnings("unused") + @NonNull + private static ISequence execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true))); + + return ISequence.of(IIntegerItem.valueOf(array.size())); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java index f6c45a1c8..6af3c8230 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java @@ -105,7 +105,7 @@ public ISequence execute(@NonNull IFunction function, ISequence arg = FunctionUtils.asType( ObjectUtils.notNull(arguments.get(0))); - IAnyAtomicItem item = FunctionUtils.getFirstItem(arg, true); + IAnyAtomicItem item = arg.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java index 271813910..d696e9840 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java @@ -193,6 +193,9 @@ public DefaultFunctionLibrary() { // NOPMD - intentional // https://www.w3.org/TR/xpath-functions-31/#func-years-from-duration // P2: https://www.w3.org/TR/xpath-functions-31/#func-zero-or-one + // https://www.w3.org/TR/xpath-functions-31/#func-array-size + registerFunction(ArraySize.SIGNATURE); + // xpath casting functions registerFunction( CastFunction.signature(MetapathConstants.NS_XML_SCHEMA, "boolean", IBooleanItem.class, IBooleanItem::cast)); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java index 0d0a999f8..98c99d588 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java @@ -76,10 +76,9 @@ private static ISequence execute( @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - ISequence sequence = FunctionUtils.asType( - ObjectUtils.requireNonNull(arguments.get(0))); + ISequence sequence = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java index 91486eda6..e193bbe9c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java @@ -101,7 +101,7 @@ private static ISequence executeOneArg(@NonNull IFunction function, ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - INodeItem item = FunctionUtils.getFirstItem(arg, true); + INodeItem item = arg.getFirstItem(true); return ISequence.of(fnBaseUri(item)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java index 4d044095a..bafdf43b4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java @@ -29,7 +29,6 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; -import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.IArgument; import gov.nist.secauto.metaschema.core.metapath.function.IFunction; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; @@ -109,7 +108,7 @@ public static IBooleanItem fnBoolean(@NonNull ISequence sequence) { */ public static boolean fnBooleanAsPrimitive(@NonNull ISequence sequence) { boolean retval = false; - IItem first = FunctionUtils.getFirstItem(sequence, false); + IItem first = sequence.getFirstItem(false); if (first != null) { if (first instanceof INodeItem) { retval = true; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java index 215ede52b..7d847da3e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java @@ -79,7 +79,7 @@ private static ISequence execute( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java index ad5fc26b6..f6fbf7516 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java @@ -35,7 +35,6 @@ import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -80,11 +79,8 @@ private static ISequence execute( @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - IStringItem comparand1 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))), true); - - IStringItem comparand2 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true); + IStringItem comparand1 = FunctionUtils.asTypeOrNull(arguments.get(0).getFirstItem(true)); + IStringItem comparand2 = FunctionUtils.asTypeOrNull(arguments.get(1).getFirstItem(true)); ISequence retval; if (comparand1 == null || comparand2 == null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java index 56eaf357f..42594b5f9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java @@ -29,7 +29,6 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; -import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.IArgument; import gov.nist.secauto.metaschema.core.metapath.function.IFunction; import gov.nist.secauto.metaschema.core.metapath.item.IItem; @@ -87,7 +86,7 @@ private static ISequence execute( return ISequence.of(concat(ObjectUtils.notNull(arguments.stream() .map(arg -> { assert arg != null; - return (IAnyAtomicItem) FunctionUtils.getFirstItem(arg, true); + return (IAnyAtomicItem) arg.getFirstItem(true); })))); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java index 69e292cc5..3ebb645e5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java @@ -36,6 +36,7 @@ import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAtomicValuedItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -126,11 +127,9 @@ private static ISequence executeOneArg( */ @SuppressWarnings("null") @NonNull - public static ISequence fnData(@NonNull ISequence sequence) { - @NonNull Stream stream = sequence.stream(); - return ISequence.of(stream.flatMap(x -> { - return Stream.of(fnDataItem(x)); - })); + public static ISequence fnData(@NonNull ISequence sequence) { + return ISequence.of(sequence.stream() + .flatMap(FnData::atomize)); } /** @@ -143,15 +142,62 @@ public static ISequence fnData(@NonNull ISequence sequence) { * @return the atomized result */ @NonNull - public static IAnyAtomicItem fnDataItem(@NonNull IItem item) { + public static IAnyAtomicItem fnDataItem(@NonNull INodeItem item) { IAnyAtomicItem retval = null; - if (item instanceof IAnyAtomicItem) { - retval = (IAnyAtomicItem) item; - } else if (item instanceof IAtomicValuedItem) { + if (item instanceof IAtomicValuedItem) { retval = ((IAtomicValuedItem) item).toAtomicItem(); } - if (retval == null) { + if (retval != null) { + return retval; + } + throw new InvalidTypeFunctionException(InvalidTypeFunctionException.NODE_HAS_NO_TYPED_VALUE, item); + } + + /** + * An implementation of + * item + * atomization. + * + * @param item + * the item to atomize + * @return the atomized result + */ + @NonNull + public static Stream fnDataItem(@NonNull IArrayItem item) { + return ObjectUtils.notNull(item.stream().flatMap(member -> { + Stream result; + if (member instanceof IItem) { + result = atomize((IItem) member); + } else if (member instanceof ISequence) { + result = ((ISequence) member).stream() + .flatMap(FnData::atomize); + } else { + throw new UnsupportedOperationException("array member not an item or sequence."); + } + return result; + })); + } + + /** + * An implementation of + * item + * atomization. + * + * @param item + * the item to atomize + * @return the atomized result + */ + @NonNull + public static Stream atomize(@NonNull IItem item) { + Stream retval; + if (item instanceof IAnyAtomicItem) { + retval = ObjectUtils.notNull(Stream.of((IAnyAtomicItem) item)); + } else if (item instanceof IAtomicValuedItem) { + retval = ObjectUtils.notNull(Stream.of(((IAtomicValuedItem) item).toAtomicItem())); + } else if (item instanceof IArrayItem) { + retval = fnDataItem((IArrayItem) item); + } else { throw new InvalidTypeFunctionException(InvalidTypeFunctionException.NODE_HAS_NO_TYPED_VALUE, item); } return retval; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java index efb74b3a0..049e4b58e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java @@ -76,7 +76,7 @@ private static ISequence execute(@NonNull IFunction function, IItem focus) { ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IStringItem item = FunctionUtils.getFirstItem(arg, true); + IStringItem item = arg.getFirstItem(true); return item == null ? ISequence.empty() : ISequence.of(fnDoc(item, dynamicContext)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java index 2e02b919e..1bb8c0c7e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java @@ -107,7 +107,7 @@ private static ISequence executeOneArg(@NonNull IFunction function, ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - INodeItem item = FunctionUtils.getFirstItem(arg, true); + INodeItem item = arg.getFirstItem(true); ISequence retval; if (item == null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java index 0189f99a0..b94c03f24 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java @@ -84,9 +84,8 @@ private static ISequence execute(@NonNull IFunction function, @NonNull DynamicContext dynamicContext, IItem focus) { ISequence target = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IIntegerItem position - = ObjectUtils.requireNonNull( - FunctionUtils.getFirstItem(FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true)); + + IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); ISequence inserts = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(2))); return ISequence.of(fnInsertBefore(target, position, inserts)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java index 1242f9bbc..bd06bdd36 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java @@ -121,7 +121,7 @@ private static ISequence executeOneArg(@NonNull IFunction function, */ @NonNull public static ISequence fnPath(@NonNull ISequence sequence) { - IItem item = FunctionUtils.getFirstItem(sequence, true); + IItem item = sequence.getFirstItem(true); ISequence retval; if (item == null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java index 9568d11d3..8e6d3956f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java @@ -79,9 +79,7 @@ private static ISequence execute(@NonNull IFunction function, @NonNull DynamicContext dynamicContext, IItem focus) { ISequence target = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IIntegerItem position - = ObjectUtils.requireNonNull( - FunctionUtils.getFirstItem(FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true)); + IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); return ISequence.of(fnRemove(target, position)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java index fbfe91ec3..210932977 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java @@ -102,7 +102,7 @@ private static ISequence executeOneArg( return ISequence.empty(); // NOPMD - readability } - IStringItem relativeString = FunctionUtils.getFirstItem(relativeSequence, true); + IStringItem relativeString = relativeSequence.getFirstItem(true); IAnyUriItem resolvedUri = null; if (relativeString != null) { resolvedUri = fnResolveUri(relativeString, null, dynamicContext); @@ -143,7 +143,7 @@ private static ISequence executeTwoArg( } ISequence baseSequence = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))); - IStringItem baseString = FunctionUtils.getFirstItem(baseSequence, true); + IStringItem baseString = baseSequence.getFirstItem(true); if (baseString == null) { throw new InvalidArgumentFunctionException( @@ -152,7 +152,7 @@ private static ISequence executeTwoArg( } IAnyUriItem baseUri = IAnyUriItem.cast(baseString); - IStringItem relativeString = FunctionUtils.getFirstItem(relativeSequence, true); + IStringItem relativeString = relativeSequence.getFirstItem(true); IAnyUriItem resolvedUri = null; if (relativeString != null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java index 5edef5cf5..7b39a3643 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java @@ -102,7 +102,7 @@ private static ISequence executeOneArg( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } @@ -120,15 +120,12 @@ private static ISequence executeTwoArg( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } - IIntegerItem precision = FunctionUtils.asType( - FunctionUtils.requireFirstItem( - ObjectUtils.requireNonNull(arguments.get(1)), - true)); + IIntegerItem precision = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); return ISequence.of(item.round(precision)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java index 33320a005..b07098495 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java @@ -35,7 +35,6 @@ import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -68,10 +67,9 @@ private static ISequence execute(@NonNull IFunction function, @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - IStringItem arg1 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))), true); - IStringItem arg2 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true); + IStringItem arg1 = FunctionUtils.asTypeOrNull(arguments.get(0).getFirstItem(true)); + + IStringItem arg2 = FunctionUtils.asTypeOrNull(arguments.get(1).getFirstItem(true)); return ISequence.of(fnStartsWith(arg1, arg2)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java index 0b07b68f8..2275c7ee4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java @@ -115,13 +115,9 @@ private static ISequence executeTwoArg( @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - ISequence sequence = FunctionUtils.asType( - ObjectUtils.requireNonNull(arguments.get(0))); + ISequence sequence = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IAnyAtomicItem zero = FunctionUtils.getFirstItem( - FunctionUtils.asType( - ObjectUtils.requireNonNull(arguments.get(1))), - true); + IAnyAtomicItem zero = FunctionUtils.asTypeOrNull(arguments.get(1).getFirstItem(true)); return ISequence.of(sum(sequence.getValue(), zero)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java index 8604dcb04..d8bfdd423 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java @@ -105,7 +105,7 @@ private static ISequence executeOneArg( ISequence initalContext = ISequence.of(FunctionUtils.requireType(INodeItem.class, focus)); ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IStringItem recursionPath = FunctionUtils.requireFirstItem(arg, true); + IStringItem recursionPath = ObjectUtils.requireNonNull(arg.getFirstItem(true)); return recurseDepth(initalContext, recursionPath, dynamicContext); } @@ -121,7 +121,7 @@ private static ISequence executeTwoArg( ISequence initalContext = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))); - IStringItem recursionPath = FunctionUtils.requireFirstItem(arg, true); + IStringItem recursionPath = ObjectUtils.requireNonNull(arg.getFirstItem(true)); return recurseDepth(initalContext, recursionPath, dynamicContext); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java index 09244ee9f..ddbb03df6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java @@ -103,7 +103,7 @@ public ISequence execute(@NonNull IFunction function, return ISequence.empty(); // NOPMD - readability } - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java index f132dd327..5169937f5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java @@ -78,7 +78,7 @@ public ISequence execute(List> arguments, DynamicContext dynamic ISequence arg = FunctionUtils.asType( ObjectUtils.notNull(arguments.get(0))); - IIntegerItem position = FunctionUtils.getFirstItem(arg, true); + IIntegerItem position = arg.getFirstItem(true); if (position == null) { return ISequence.empty(); // NOPMD - readability } @@ -100,4 +100,9 @@ public boolean equals(Object other) { return other == this || other instanceof IArrayItem && getValue().equals(((IArrayItem) other).getValue()); } + + @Override + public String asString() { + return ObjectUtils.notNull(toString()); + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractSequence.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractSequence.java index 8b87046de..fec7c18cd 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractSequence.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractSequence.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import edu.umd.cs.findbugs.annotations.NonNull; @@ -61,4 +62,9 @@ public boolean equals(Object other) { public int hashCode() { return getValue().hashCode(); } + + @Override + public String asString() { + return ObjectUtils.notNull(toString()); + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java index b567ec122..4102d577d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java @@ -166,6 +166,5 @@ public Stream stream() { public String toString() { return getValue().toString(); } - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java index 29e033264..a915a9ce7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java @@ -27,13 +27,14 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.IStringValued; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.Set; import edu.umd.cs.findbugs.annotations.NonNull; -public interface IAnyAtomicItem extends IAtomicValuedItem { +public interface IAnyAtomicItem extends IAtomicValuedItem, IStringValued { @NonNull Set> PRIMITIVE_ITEM_TYPES = ObjectUtils.notNull(Set.of( IStringItem.class, @@ -69,6 +70,7 @@ default IAnyAtomicItem toAtomicItem() { * * @return the string value value of the item */ + @Override @NonNull String asString(); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java index 408193a2f..d6d23bf64 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java @@ -34,6 +34,7 @@ import java.math.RoundingMode; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; public interface IDecimalItem extends INumericItem { @SuppressWarnings("null") @@ -104,7 +105,7 @@ static IDecimalItem valueOf(@NonNull BigDecimal value) { * if the provided {@code item} cannot be cast to this type */ @NonNull - static IDecimalItem cast(@NonNull IAnyAtomicItem item) { + static IDecimalItem cast(@Nullable IAnyAtomicItem item) { return MetaschemaDataTypeProvider.DECIMAL.cast(item); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java index 36216c291..ad72386c7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java @@ -67,6 +67,20 @@ static IIntegerItem valueOf(@NonNull String value) { } } + /** + * Construct a new integer item using the provided {@code value}. + * + * @param value + * a long value + * @return the new item + */ + @NonNull + static IIntegerItem valueOf(int value) { + @SuppressWarnings("null") + @NonNull BigInteger bigInteger = BigInteger.valueOf(value); + return valueOf(bigInteger); + } + /** * Construct a new integer item using the provided {@code value}. * diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java index e478d423f..328ecdda3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.IStringValued; import gov.nist.secauto.metaschema.core.metapath.function.IArgument; import gov.nist.secauto.metaschema.core.metapath.function.IFunction; import gov.nist.secauto.metaschema.core.metapath.function.ISequenceType; @@ -53,7 +54,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; @SuppressWarnings("PMD.ShortMethodName") -public interface IArrayItem extends IFunction, IItem, List { +public interface IArrayItem extends IFunction, IItem, List, IStringValued { @NonNull static IArrayItem empty() { return AbstractArrayItem.empty(); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/ISequenceTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/ISequenceTest.java new file mode 100644 index 000000000..437b7e645 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/ISequenceTest.java @@ -0,0 +1,55 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath; + +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class ISequenceTest { + + @Test + void testGetFirstNonSingleton() { + assertAll( + () -> assertEquals(integer(1), ISequence.of(integer(1), integer(2)).getFirstItem(false)), + () -> assertEquals(integer(3), ISequence.of(integer(3)).getFirstItem(false)), + () -> assertNull(ISequence.of().getFirstItem(false))); + } + + @Test + void testGetFirstSingleton() { + assertAll( + () -> assertThrows(InvalidTypeMetapathException.class, + () -> ISequence.of(integer(1), integer(2)).getFirstItem(true)), + () -> assertEquals(integer(3), ISequence.of(integer(3)).getFirstItem(true)), + () -> assertNull(ISequence.of().getFirstItem(true))); + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java index 3ef302d32..b941dece8 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java @@ -53,7 +53,6 @@ import gov.nist.secauto.metaschema.core.metapath.cst.comparison.GeneralComparison; import gov.nist.secauto.metaschema.core.metapath.cst.comparison.ValueComparison; import gov.nist.secauto.metaschema.core.metapath.function.ComparisonFunctions; -import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; @@ -316,7 +315,7 @@ void testAnd(@NonNull String metapath, @NonNull IBooleanItem expectedResult) { IDocumentNodeItem document = newTestDocument(); ISequence result = ast.accept(new DynamicContext(), ISequence.of(document)); - IItem resultItem = FunctionUtils.getFirstItem(result, false); + IItem resultItem = result.getFirstItem(false); assertAll( () -> assertEquals(And.class, ast.getClass()), () -> assertNotNull(resultItem), @@ -341,7 +340,7 @@ void testIf(@NonNull String metapath, @NonNull IBooleanItem expectedResult) { IDocumentNodeItem document = newTestDocument(); ISequence result = ast.accept(new DynamicContext(), ISequence.of(document)); - IItem resultItem = FunctionUtils.getFirstItem(result, false); + IItem resultItem = result.getFirstItem(false); assertAll( () -> assertEquals(If.class, ast.getClass()), () -> assertNotNull(resultItem), diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySizeTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySizeTest.java new file mode 100644 index 000000000..5d35f625e --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySizeTest.java @@ -0,0 +1,71 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath.function.library; + +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; +import gov.nist.secauto.metaschema.core.metapath.MetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; + +class ArraySizeTest + extends ExpressionTestBase { + private static Stream provideValues() { // NOPMD - false positive + return Stream.of( + Arguments.of( + integer(3), + "array:size([\"a\", \"b\", \"c\"])"), + Arguments.of( + integer(2), + "array:size([\"a\", [\"b\", \"c\"]])"), + Arguments.of( + integer(0), + "array:size([ ])"), + Arguments.of( + integer(1), + "array:size([[ ]])")); + } + + @ParameterizedTest + @MethodSource("provideValues") + void testExpression(@NonNull IIntegerItem expected, @NonNull String metapath) { + + IItem result = MetapathExpression.compile(metapath) + .evaluateAs(null, MetapathExpression.ResultType.NODE, newDynamicContext()); + assertEquals(expected, result); + } +} diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/metapath/function/Model.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/metapath/function/Model.java index e676430a3..aa19f9913 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/metapath/function/Model.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/metapath/function/Model.java @@ -82,7 +82,7 @@ public static ISequence execute( } // always not null, since the first item is required - INodeItem node = FunctionUtils.requireFirstItem(nodeSequence, true); + INodeItem node = nodeSequence.getFirstItem(true); if (!(node instanceof IDefinitionNodeItem)) { return ISequence.empty();