Skip to content

Commit aa516cd

Browse files
Added support for the array:put Metapath function. Cleaned up some Javadocs. Added a utility function to convert a sequence into an array member.
1 parent 531f958 commit aa516cd

File tree

7 files changed

+222
-21
lines changed

7 files changed

+222
-21
lines changed

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,22 @@ default ITEM getFirstItem(boolean requireSingleton) {
153153
return getFirstItem(this, requireSingleton);
154154
}
155155

156+
@NonNull
157+
default IItem toArrayMember() {
158+
IItem retval;
159+
switch (size()) {
160+
case 0:
161+
retval = IArrayItem.empty();
162+
break;
163+
case 1:
164+
retval = stream().findFirst().get();
165+
break;
166+
default:
167+
retval = IArrayItem.ofCollection(this);
168+
}
169+
return retval;
170+
}
171+
156172
/**
157173
* Get a stream guaranteed to be backed by a list.
158174
*

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquare.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,7 @@ public List<? extends IExpression> getChildren() {
5252
public ISequence<? extends IItem> accept(DynamicContext dynamicContext, ISequence<?> focus) {
5353
return ISequence.of(getChildren().stream()
5454
.map(expr -> expr.accept(dynamicContext, focus))
55-
.map(sequence -> {
56-
57-
IItem retval;
58-
switch (sequence.size()) {
59-
case 0:
60-
retval = IArrayItem.empty();
61-
break;
62-
case 1:
63-
retval = sequence.iterator().next();
64-
break;
65-
default:
66-
retval = IArrayItem.ofCollection(sequence);
67-
}
68-
return retval;
69-
})
55+
.map(ISequence::toArrayMember)
7056
.collect(IArrayItem.toArrayItem()));
7157
}
7258

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,14 @@ private static ISequence<?> execute(@NonNull IFunction function,
7979
* "https://www.w3.org/TR/xpath-functions-31/#func-array-get">array:get</a>.
8080
*
8181
* @param <T>
82-
* the type for the given Metapath sequence
82+
* the type of items in the given Metapath array
8383
* @param target
84-
* the sequence of Metapath items that is the target of insertion
84+
* the array of Metapath items that is the target of retrieval
8585
* @param positionItem
86-
* the integer position of the item to insert before
87-
* @return the sequence of Metapath items with insertions
86+
* the integer position of the item to retrieve
87+
* @return the retrieved item
88+
* @throws IndexOutOfBoundsException
89+
* if the position is not in the range of 1 to array:size
8890
*/
8991
@SuppressWarnings("PMD.OnlyOneReturn")
9092
@NonNull
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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+
27+
package gov.nist.secauto.metaschema.core.metapath.function.library;
28+
29+
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
30+
import gov.nist.secauto.metaschema.core.metapath.ISequence;
31+
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
32+
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
33+
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
34+
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
35+
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
36+
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
37+
import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException;
38+
import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
39+
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
40+
41+
import java.util.ArrayList;
42+
import java.util.List;
43+
44+
import edu.umd.cs.findbugs.annotations.NonNull;
45+
46+
public class ArrayPut {
47+
@NonNull
48+
public static final IFunction SIGNATURE = IFunction.builder()
49+
.name("put")
50+
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY)
51+
.argument(IArgument.builder()
52+
.name("array")
53+
.type(IArrayItem.class)
54+
.one()
55+
.build())
56+
.argument(IArgument.builder()
57+
.name("position")
58+
.type(IIntegerItem.class)
59+
.one()
60+
.build())
61+
.argument(IArgument.builder()
62+
.name("member")
63+
.type(IItem.class)
64+
.zeroOrMore()
65+
.build())
66+
.returnType(IArrayItem.class)
67+
.returnOne()
68+
.functionHandler(ArrayPut::execute)
69+
.build();
70+
71+
@SuppressWarnings("unused")
72+
@NonNull
73+
private static <T extends IItem> ISequence<IArrayItem<T>> execute(@NonNull IFunction function,
74+
@NonNull List<ISequence<?>> arguments,
75+
@NonNull DynamicContext dynamicContext,
76+
IItem focus) {
77+
IArrayItem<T> array = FunctionUtils.asType(ObjectUtils.requireNonNull(
78+
arguments.get(0).getFirstItem(true)));
79+
IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
80+
T member = FunctionUtils.asType(arguments.get(2).toArrayMember());
81+
82+
return ISequence.of(put(array, position, member));
83+
}
84+
85+
/**
86+
* An implementation of XPath 3.1 <a href=
87+
* "https://www.w3.org/TR/xpath-functions-31/#func-array-put">array:put</a>.
88+
*
89+
* @param <T>
90+
* the type of items in the given Metapath array
91+
* @param array
92+
* the target Metapath array
93+
* @param positionItem
94+
* the integer position of the item to replace
95+
* @param member
96+
* the Metapath item to replace the identified array member with
97+
* @return a new array containing the modification
98+
* @throws IndexOutOfBoundsException
99+
* if the position is not in the range of 1 to array:size
100+
*/
101+
@SuppressWarnings("PMD.OnlyOneReturn")
102+
@NonNull
103+
public static <T extends IItem> IArrayItem<T> put(
104+
@NonNull IArrayItem<T> array,
105+
@NonNull IIntegerItem positionItem,
106+
@NonNull T member) {
107+
return put(array, positionItem.asInteger().intValue(), member);
108+
}
109+
110+
@NonNull
111+
public static <T extends IItem> IArrayItem<T> put(@NonNull IArrayItem<T> array, int position, @NonNull T member) {
112+
113+
List<T> copy = new ArrayList<>(array);
114+
try {
115+
copy.set(position - 1, member);
116+
} catch (IndexOutOfBoundsException ex) {
117+
throw new ArrayException(
118+
ArrayException.INDEX_OUT_OF_BOUNDS,
119+
String.format("The position %d is outside the range of values for the array of size '%d'.",
120+
position,
121+
copy.size()),
122+
ex);
123+
}
124+
125+
return IArrayItem.ofCollection(copy);
126+
}
127+
}

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ public DefaultFunctionLibrary() { // NOPMD - intentional
197197
registerFunction(ArrayGet.SIGNATURE);
198198
// https://www.w3.org/TR/xpath-functions-31/#func-array-size
199199
registerFunction(ArraySize.SIGNATURE);
200+
// https://www.w3.org/TR/xpath-functions-31/#func-array-put
201+
registerFunction(ArrayPut.SIGNATURE);
200202

201203
// xpath casting functions
202204
registerFunction(

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ArrayItemN.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ public class ArrayItemN<ITEM extends IItem>
4141

4242
@SafeVarargs
4343
public ArrayItemN(@NonNull ITEM... items) {
44-
this(CollectionUtil.unmodifiableList(ObjectUtils.notNull(List.of(items))));
44+
this(ObjectUtils.notNull(List.of(items)));
4545
}
4646

4747
public ArrayItemN(@NonNull List<ITEM> items) {
48-
this.items = items;
48+
this.items = CollectionUtil.unmodifiableList(items);
4949
}
5050

5151
@Override
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
27+
package gov.nist.secauto.metaschema.core.metapath.function.library;
28+
29+
import static gov.nist.secauto.metaschema.core.metapath.TestUtils.array;
30+
import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string;
31+
import static org.junit.jupiter.api.Assertions.assertEquals;
32+
33+
import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase;
34+
import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
35+
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
36+
37+
import org.junit.jupiter.params.ParameterizedTest;
38+
import org.junit.jupiter.params.provider.Arguments;
39+
import org.junit.jupiter.params.provider.MethodSource;
40+
41+
import java.util.stream.Stream;
42+
43+
import edu.umd.cs.findbugs.annotations.NonNull;
44+
45+
class ArrayPutTest
46+
extends ExpressionTestBase {
47+
private static Stream<Arguments> provideValues() { // NOPMD - false positive
48+
return Stream.of(
49+
Arguments.of(
50+
array(string("a"), string("d"), string("c")),
51+
"array:put([\"a\", \"b\", \"c\"], 2, \"d\")"),
52+
Arguments.of(
53+
array(string("a"), array(string("d"), string("e")), string("c")),
54+
"array:put([\"a\", \"b\", \"c\"], 2, (\"d\", \"e\"))"),
55+
Arguments.of(
56+
array(array(string("d"), string("e"))),
57+
"array:put([\"a\"], 1, [\"d\", \"e\"]) "));
58+
}
59+
60+
@ParameterizedTest
61+
@MethodSource("provideValues")
62+
void testExpression(@NonNull IItem expected, @NonNull String metapath) {
63+
64+
IItem result = MetapathExpression.compile(metapath)
65+
.evaluateAs(null, MetapathExpression.ResultType.NODE, newDynamicContext());
66+
assertEquals(expected, result);
67+
}
68+
}

0 commit comments

Comments
 (0)