Skip to content

Commit 22f8701

Browse files
Added support for the array:subarray Metapath function.
1 parent 5b5e434 commit 22f8701

File tree

6 files changed

+276
-3
lines changed

6 files changed

+276
-3
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private static ISequence<?> execute(@NonNull IFunction function,
8585
* @param positionItem
8686
* the integer position of the item to retrieve
8787
* @return the retrieved item
88-
* @throws IndexOutOfBoundsException
88+
* @throws ArrayException
8989
* if the position is not in the range of 1 to array:size
9090
*/
9191
@SuppressWarnings("PMD.OnlyOneReturn")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private static <T extends IItem> ISequence<IArrayItem<T>> execute(@NonNull IFunc
9595
* @param member
9696
* the Metapath item to replace the identified array member with
9797
* @return a new array containing the modification
98-
* @throws IndexOutOfBoundsException
98+
* @throws ArrayException
9999
* if the position is not in the range of 1 to array:size
100100
*/
101101
@SuppressWarnings("PMD.OnlyOneReturn")
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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 ArraySubarray {
47+
@NonNull
48+
public static final IFunction SIGNATURE_TWO_ARG = IFunction.builder()
49+
.name("subarray")
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("start")
58+
.type(IIntegerItem.class)
59+
.one()
60+
.build())
61+
.returnType(IArrayItem.class)
62+
.returnOne()
63+
.functionHandler(ArraySubarray::executeTwoArg)
64+
.build();
65+
@NonNull
66+
public static final IFunction SIGNATURE_THREE_ARG = IFunction.builder()
67+
.name("subarray")
68+
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY)
69+
.argument(IArgument.builder()
70+
.name("array")
71+
.type(IArrayItem.class)
72+
.one()
73+
.build())
74+
.argument(IArgument.builder()
75+
.name("start")
76+
.type(IIntegerItem.class)
77+
.one()
78+
.build())
79+
.argument(IArgument.builder()
80+
.name("length")
81+
.type(IIntegerItem.class)
82+
.one()
83+
.build())
84+
.returnType(IArrayItem.class)
85+
.returnOne()
86+
.functionHandler(ArraySubarray::executeThreeArg)
87+
.build();
88+
89+
@SuppressWarnings("unused")
90+
@NonNull
91+
private static <T extends IItem> ISequence<IArrayItem<T>> executeTwoArg(@NonNull IFunction function,
92+
@NonNull List<ISequence<?>> arguments,
93+
@NonNull DynamicContext dynamicContext,
94+
IItem focus) {
95+
IArrayItem<T> array = FunctionUtils.asType(ObjectUtils.requireNonNull(
96+
arguments.get(0).getFirstItem(true)));
97+
IIntegerItem start = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
98+
99+
return ISequence.of(subarray(array, start));
100+
}
101+
102+
@SuppressWarnings("unused")
103+
@NonNull
104+
private static <T extends IItem> ISequence<IArrayItem<T>> executeThreeArg(@NonNull IFunction function,
105+
@NonNull List<ISequence<?>> arguments,
106+
@NonNull DynamicContext dynamicContext,
107+
IItem focus) {
108+
IArrayItem<T> array = FunctionUtils.asType(ObjectUtils.requireNonNull(
109+
arguments.get(0).getFirstItem(true)));
110+
IIntegerItem start = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
111+
IIntegerItem length = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(2).getFirstItem(true)));
112+
113+
return ISequence.of(subarray(array, start, length));
114+
}
115+
116+
/**
117+
* An implementation of XPath 3.1 <a href=
118+
* "https://www.w3.org/TR/xpath-functions-31/#func-array-subarray">array:subarray</a>.
119+
*
120+
* @param <T>
121+
* the type of items in the given Metapath array
122+
* @param array
123+
* the target Metapath array
124+
* @param startItem
125+
* the integer position of the item to start with (inclusive)
126+
* @return a new array consisting of the items in the identified range
127+
* @throws ArrayException
128+
* if the position is not in the range of 1 to array:size
129+
*/
130+
@SuppressWarnings("PMD.OnlyOneReturn")
131+
@NonNull
132+
public static <T extends IItem> IArrayItem<T> subarray(
133+
@NonNull IArrayItem<T> array,
134+
@NonNull IIntegerItem startItem) {
135+
return subarray(array, startItem.asInteger().intValueExact());
136+
}
137+
138+
/**
139+
* An implementation of XPath 3.1 <a href=
140+
* "https://www.w3.org/TR/xpath-functions-31/#func-array-subarray">array:subarray</a>.
141+
*
142+
* @param <T>
143+
* the type of items in the given Metapath array
144+
* @param array
145+
* the target Metapath array
146+
* @param startItem
147+
* the integer position of the item to start with (inclusive)
148+
* @param lengthItem
149+
* the integer count of items to include starting with the item at the
150+
* start position
151+
* @return a new array consisting of the items in the identified range
152+
* @throws ArrayException
153+
* if the length is negative or the position is not in the range of 1
154+
* to array:size
155+
*/
156+
@SuppressWarnings("PMD.OnlyOneReturn")
157+
@NonNull
158+
public static <T extends IItem> IArrayItem<T> subarray(
159+
@NonNull IArrayItem<T> array,
160+
@NonNull IIntegerItem startItem,
161+
@NonNull IIntegerItem lengthItem) {
162+
return subarray(array, startItem.asInteger().intValueExact(), lengthItem.asInteger().intValueExact());
163+
}
164+
165+
@NonNull
166+
public static <T extends IItem> IArrayItem<T> subarray(@NonNull IArrayItem<T> array, int start) {
167+
return subarray(array, start, array.size() - start + 1);
168+
}
169+
170+
@NonNull
171+
public static <T extends IItem> IArrayItem<T> subarray(@NonNull IArrayItem<T> array, int start, int length) {
172+
if (length < 0) {
173+
throw new ArrayException(
174+
ArrayException.NEGATIVE_ARRAY_LENGTH, String.format("The length '%d' is negative.", length));
175+
}
176+
177+
List<T> copy;
178+
try {
179+
copy = array.subList(start - 1, start - 1 + length);
180+
} catch (IndexOutOfBoundsException ex) {
181+
throw new ArrayException(
182+
ArrayException.INDEX_OUT_OF_BOUNDS,
183+
String.format("The start + length (%d + %d) exceeds the array length '%d'.",
184+
start,
185+
length,
186+
array.size()),
187+
ex);
188+
}
189+
190+
return IArrayItem.ofCollection(new ArrayList<>(copy));
191+
}
192+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ public DefaultFunctionLibrary() { // NOPMD - intentional
201201
registerFunction(ArrayPut.SIGNATURE);
202202
// https://www.w3.org/TR/xpath-functions-31/#func-array-append
203203
registerFunction(ArrayAppend.SIGNATURE);
204+
// https://www.w3.org/TR/xpath-functions-31/#func-array-subarray
205+
registerFunction(ArraySubarray.SIGNATURE_TWO_ARG);
206+
registerFunction(ArraySubarray.SIGNATURE_THREE_ARG);
204207

205208
// xpath casting functions
206209
registerFunction(

core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayAppendTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ private static Stream<Arguments> provideValues() { // NOPMD - false positive
5757
@ParameterizedTest
5858
@MethodSource("provideValues")
5959
void testExpression(@NonNull IItem expected, @NonNull String metapath) {
60-
6160
IItem result = MetapathExpression.compile(metapath)
6261
.evaluateAs(null, MetapathExpression.ResultType.NODE, newDynamicContext());
6362
assertEquals(expected, result);
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 ArraySubarrayTest
46+
extends ExpressionTestBase {
47+
private static Stream<Arguments> provideValues() { // NOPMD - false positive
48+
return Stream.of(
49+
Arguments.of(
50+
array(string("b"), string("c"), string("d")),
51+
"array:subarray([\"a\", \"b\", \"c\", \"d\"], 2)"),
52+
Arguments.of(
53+
array(),
54+
"array:subarray([\"a\", \"b\", \"c\", \"d\"], 5)"),
55+
Arguments.of(
56+
array(),
57+
"array:subarray([\"a\", \"b\", \"c\", \"d\"], 2, 0)"),
58+
Arguments.of(
59+
array(string("b")),
60+
"array:subarray([\"a\", \"b\", \"c\", \"d\"], 2, 1)"),
61+
Arguments.of(
62+
array(string("b"), string("c")),
63+
"array:subarray([\"a\", \"b\", \"c\", \"d\"], 2, 2)"),
64+
Arguments.of(
65+
array(),
66+
"array:subarray([\"a\", \"b\", \"c\", \"d\"], 5, 0)"),
67+
Arguments.of(
68+
array(),
69+
"array:subarray([ ], 1, 0)"));
70+
}
71+
72+
@ParameterizedTest
73+
@MethodSource("provideValues")
74+
void testExpression(@NonNull IItem expected, @NonNull String metapath) {
75+
IItem result = MetapathExpression.compile(metapath)
76+
.evaluateAs(null, MetapathExpression.ResultType.NODE, newDynamicContext());
77+
assertEquals(expected, result);
78+
}
79+
}

0 commit comments

Comments
 (0)