Skip to content

Commit

Permalink
Added support for the array:put Metapath function. Cleaned up some Ja…
Browse files Browse the repository at this point in the history
…vadocs. Added a utility function to convert a sequence into an array member.
  • Loading branch information
david-waltermire committed May 25, 2024
1 parent 531f958 commit 3818ef9
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,22 @@ default ITEM getFirstItem(boolean requireSingleton) {
return getFirstItem(this, requireSingleton);
}

@NonNull
default IItem toArrayMember() {
IItem retval;
switch (size()) {
case 0:
retval = IArrayItem.empty();
break;
case 1:
retval = stream().findFirst().get();
break;
default:
retval = IArrayItem.ofCollection(this);
}
return retval;
}

/**
* Get a stream guaranteed to be backed by a list.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,7 @@ public List<? extends IExpression> getChildren() {
public ISequence<? extends IItem> accept(DynamicContext dynamicContext, ISequence<?> focus) {
return ISequence.of(getChildren().stream()
.map(expr -> expr.accept(dynamicContext, focus))
.map(sequence -> {

IItem retval;
switch (sequence.size()) {
case 0:
retval = IArrayItem.empty();
break;
case 1:
retval = sequence.iterator().next();
break;
default:
retval = IArrayItem.ofCollection(sequence);
}
return retval;
})
.map(ISequence::toArrayMember)
.collect(IArrayItem.toArrayItem()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@ private static ISequence<?> execute(@NonNull IFunction function,
* "https://www.w3.org/TR/xpath-functions-31/#func-array-get">array:get</a>.
*
* @param <T>
* the type for the given Metapath sequence
* the type of items in the given Metapath array
* @param target
* the sequence of Metapath items that is the target of insertion
* the array of Metapath items that is the target of retrieval
* @param positionItem
* the integer position of the item to insert before
* @return the sequence of Metapath items with insertions
* the integer position of the item to retrieve
* @return the retrieved item
* @throws IndexOutOfBoundsException
* if the position is not in the range of 1 to array:size
*/
@SuppressWarnings("PMD.OnlyOneReturn")
@NonNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* 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.ArrayException;
import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

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

public class ArrayPut {
@NonNull
public static final IFunction SIGNATURE = IFunction.builder()
.name("put")
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY)
.argument(IArgument.builder()
.name("array")
.type(IArrayItem.class)
.one()
.build())
.argument(IArgument.builder()
.name("position")
.type(IIntegerItem.class)
.one()
.build())
.argument(IArgument.builder()
.name("member")
.type(IItem.class)
.zeroOrMore()
.build())
.returnType(IArrayItem.class)
.returnOne()
.functionHandler(ArrayPut::execute)
.build();

@SuppressWarnings("unused")
@NonNull
private static <T extends IItem> ISequence<IArrayItem<T>> execute(@NonNull IFunction function,
@NonNull List<ISequence<?>> arguments,
@NonNull DynamicContext dynamicContext,
IItem focus) {
IArrayItem<T> array = FunctionUtils.asType(ObjectUtils.requireNonNull(
arguments.get(0).getFirstItem(true)));
IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
T member = FunctionUtils.asType(arguments.get(2).toArrayMember());

return ISequence.of(put(array, position, member));
}

/**
* An implementation of XPath 3.1 <a href=
* "https://www.w3.org/TR/xpath-functions-31/#func-array-get">array:get</a>.
*
* @param <T>
* the type of items in the given Metapath array
* @param array
* the target Metapath array
* @param positionItem
* the integer position of the item to replace
* @param member
* the Metapath item to replace the identified array member with
* @return a new array containing the modification
* @throws IndexOutOfBoundsException
* if the position is not in the range of 1 to array:size
*/
@SuppressWarnings("PMD.OnlyOneReturn")
@NonNull
public static <T extends IItem> IArrayItem<T> put(
@NonNull IArrayItem<T> array,
@NonNull IIntegerItem positionItem,
@NonNull T member) {
return put(array, positionItem.asInteger().intValue(), member);
}

@NonNull
public static <T extends IItem> IArrayItem<T> put(@NonNull IArrayItem<T> array, int position, @NonNull T member) {

List<T> copy = new ArrayList<>(array);
try {
copy.set(position - 1, member);
} catch (IndexOutOfBoundsException ex) {
throw new ArrayException(
ArrayException.INDEX_OUT_OF_BOUNDS,
String.format("The position %d is outside the range of values for the array of size '%d'.",
position,
copy.size()),
ex);
}

return IArrayItem.ofCollection(copy);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ public DefaultFunctionLibrary() { // NOPMD - intentional
registerFunction(ArrayGet.SIGNATURE);
// https://www.w3.org/TR/xpath-functions-31/#func-array-size
registerFunction(ArraySize.SIGNATURE);
// https://www.w3.org/TR/xpath-functions-31/#func-array-put
registerFunction(ArrayPut.SIGNATURE);

// xpath casting functions
registerFunction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ public class ArrayItemN<ITEM extends IItem>

@SafeVarargs
public ArrayItemN(@NonNull ITEM... items) {
this(CollectionUtil.unmodifiableList(ObjectUtils.notNull(List.of(items))));
this(ObjectUtils.notNull(List.of(items)));
}

public ArrayItemN(@NonNull List<ITEM> items) {
this.items = items;
this.items = CollectionUtil.unmodifiableList(items);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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.array;
import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string;
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 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 ArrayPutTest
extends ExpressionTestBase {
private static Stream<Arguments> provideValues() { // NOPMD - false positive
return Stream.of(
Arguments.of(
array(string("a"), string("d"), string("c")),
"array:put([\"a\", \"b\", \"c\"], 2, \"d\")"),
Arguments.of(
array(string("a"), array(string("d"), string("e")), string("c")),
"array:put([\"a\", \"b\", \"c\"], 2, (\"d\", \"e\"))"),
Arguments.of(
array(array(string("d"), string("e"))),
"array:put([\"a\"], 1, [\"d\", \"e\"]) "));
}

@ParameterizedTest
@MethodSource("provideValues")
void testExpression(@NonNull IItem expected, @NonNull String metapath) {

IItem result = MetapathExpression.compile(metapath)
.evaluateAs(null, MetapathExpression.ResultType.NODE, newDynamicContext());
assertEquals(expected, result);
}
}

0 comments on commit 3818ef9

Please sign in to comment.