Skip to content

Commit 1deb3f0

Browse files
Added support for the array:reverse and array:flatten Metapath functions.
1 parent 5e0b678 commit 1deb3f0

File tree

9 files changed

+361
-1
lines changed

9 files changed

+361
-1
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,7 @@ public interface ICollectionValue {
4242
static Stream<? extends IItem> normalizeAsItems(@NonNull ICollectionValue value) {
4343
return value instanceof IItem ? ObjectUtils.notNull(Stream.of((IItem) value)) : value.asSequence().stream();
4444
}
45+
46+
@NonNull
47+
Stream<? extends IItem> flatten();
4548
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,13 @@ default Stream<ITEM> safeStream() {
179179
return ObjectUtils.notNull(getValue().stream());
180180
}
181181

182+
@SuppressWarnings("null")
183+
@Override
184+
default Stream<? extends IItem> flatten() {
185+
// TODO: Is a safe stream needed here?
186+
return safeStream();
187+
}
188+
182189
/**
183190
* A {@link Collector} implementation to generates a sequence from a stream of
184191
* Metapath items.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.IArgument;
33+
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
34+
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
35+
import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
36+
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
37+
38+
import java.util.List;
39+
import java.util.stream.Stream;
40+
41+
import edu.umd.cs.findbugs.annotations.NonNull;
42+
43+
public class ArrayFlatten {
44+
@NonNull
45+
static final IFunction SIGNATURE = IFunction.builder()
46+
.name("flatten")
47+
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY)
48+
.argument(IArgument.builder()
49+
.name("input")
50+
.type(IItem.class)
51+
.zeroOrMore()
52+
.build())
53+
.returnType(IItem.class)
54+
.returnZeroOrMore()
55+
.functionHandler(ArrayFlatten::execute)
56+
.build();
57+
58+
@SuppressWarnings("unused")
59+
@NonNull
60+
private static ISequence<?> execute(@NonNull IFunction function,
61+
@NonNull List<ISequence<?>> arguments,
62+
@NonNull DynamicContext dynamicContext,
63+
IItem focus) {
64+
ISequence<?> input = ObjectUtils.requireNonNull(arguments.get(0));
65+
66+
return ISequence.of(flatten(input));
67+
}
68+
69+
/**
70+
* An implementation of XPath 3.1 <a href=
71+
* "https://www.w3.org/TR/xpath-functions-31/#func-array-flatten">array:flatten</a>.
72+
*
73+
* @param input
74+
* the items to flatten
75+
* @return the flattened items
76+
*/
77+
@SuppressWarnings("null")
78+
@NonNull
79+
public static Stream<IItem> flatten(@NonNull List<? extends IItem> input) {
80+
return input.stream()
81+
.flatMap(ArrayFlatten::flatten);
82+
}
83+
84+
@SuppressWarnings("null")
85+
@NonNull
86+
public static Stream<IItem> flatten(@NonNull IItem item) {
87+
return item instanceof IArrayItem
88+
// flatten the array members
89+
? ((IArrayItem<?>) item).stream()
90+
.flatMap(member -> member.asSequence().stream()
91+
.flatMap(ArrayFlatten::flatten))
92+
// use the item
93+
: ObjectUtils.notNull(Stream.of(item));
94+
}
95+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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.ICollectionValue;
31+
import gov.nist.secauto.metaschema.core.metapath.ISequence;
32+
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
33+
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
34+
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
35+
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
36+
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
37+
import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
38+
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
39+
40+
import java.util.ArrayList;
41+
import java.util.Collections;
42+
import java.util.List;
43+
44+
import edu.umd.cs.findbugs.annotations.NonNull;
45+
46+
public class ArrayReverse {
47+
@NonNull
48+
public static final IFunction SIGNATURE = IFunction.builder()
49+
.name("reverse")
50+
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY)
51+
.argument(IArgument.builder()
52+
.name("array")
53+
.type(IArrayItem.class)
54+
.one()
55+
.build())
56+
.returnType(IArrayItem.class)
57+
.returnOne()
58+
.functionHandler(ArrayReverse::execute)
59+
.build();
60+
61+
@SuppressWarnings("unused")
62+
@NonNull
63+
private static <T extends ICollectionValue> ISequence<IArrayItem<T>> execute(@NonNull IFunction function,
64+
@NonNull List<ISequence<?>> arguments,
65+
@NonNull DynamicContext dynamicContext,
66+
IItem focus) {
67+
IArrayItem<T> array = FunctionUtils.asType(ObjectUtils.requireNonNull(
68+
arguments.get(0).getFirstItem(true)));
69+
70+
return ISequence.of(reverse(array));
71+
}
72+
73+
/**
74+
* An implementation of XPath 3.1 <a href=
75+
* "https://www.w3.org/TR/xpath-functions-31/#func-array-reverse">array:reverse</a>.
76+
*
77+
* @param <T>
78+
* the type of items in the given Metapath array
79+
* @param array
80+
* the target Metapath array
81+
* @return a new array containing the modification
82+
*/
83+
@NonNull
84+
public static <T extends ICollectionValue> IArrayItem<T> reverse(
85+
@NonNull IArrayItem<T> array) {
86+
List<T> copy = new ArrayList<>(array);
87+
Collections.reverse(copy);
88+
return IArrayItem.ofCollection(copy);
89+
}
90+
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,18 @@ public DefaultFunctionLibrary() { // NOPMD - intentional
212212
registerFunction(ArrayHead.SIGNATURE);
213213
// https://www.w3.org/TR/xpath-functions-31/#func-array-tail
214214
registerFunction(ArrayTail.SIGNATURE);
215-
215+
// https://www.w3.org/TR/xpath-functions-31/#func-array-reverse
216+
registerFunction(ArrayReverse.SIGNATURE);
216217
// https://www.w3.org/TR/xpath-functions-31/#func-array-join
217218
registerFunction(ArrayJoin.SIGNATURE);
219+
// P3: https://www.w3.org/TR/xpath-functions-31/#func-array-for-each
220+
// P3: https://www.w3.org/TR/xpath-functions-31/#func-array-filter
221+
// P3: https://www.w3.org/TR/xpath-functions-31/#func-array-fold-left
222+
// P3: https://www.w3.org/TR/xpath-functions-31/#func-array-fold-right
223+
// P3: https://www.w3.org/TR/xpath-functions-31/#func-array-for-each-pair
224+
// P3: https://www.w3.org/TR/xpath-functions-31/#func-array-sort
225+
// https://www.w3.org/TR/xpath-functions-31/#func-array-flatten
226+
registerFunction(ArrayFlatten.SIGNATURE);
218227

219228
// xpath casting functions
220229
registerFunction(

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
3131
import gov.nist.secauto.metaschema.core.metapath.ISequence;
3232

33+
import java.util.stream.Stream;
34+
3335
public interface IItem extends ICollectionValue {
3436
/**
3537
* Get the item's "wrapped" value. This "wrapped" value may be:
@@ -61,4 +63,10 @@ default boolean hasValue() {
6163
default ISequence<?> asSequence() {
6264
return ISequence.of(this);
6365
}
66+
67+
@SuppressWarnings("null")
68+
@Override
69+
default Stream<? extends IItem> flatten() {
70+
return Stream.of(this);
71+
}
6472
}

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import java.util.function.Function;
5050
import java.util.function.Supplier;
5151
import java.util.stream.Collector;
52+
import java.util.stream.Stream;
5253

5354
import javax.xml.namespace.QName;
5455

@@ -241,6 +242,13 @@ default ISequence<? extends IArrayItem<ITEM>> asSequence() {
241242
return ISequence.of(this);
242243
}
243244

245+
@SuppressWarnings("null")
246+
@Override
247+
default Stream<? extends IItem> flatten() {
248+
return stream()
249+
.flatMap(ICollectionValue::flatten);
250+
}
251+
244252
@SuppressWarnings("unchecked")
245253
@NonNull
246254
static <T extends ICollectionValue> IArrayItem<T> ofCollection( // NOPMD - intentional
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.integer;
30+
import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence;
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.ISequence;
35+
import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
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 ArrayFlattenTest
46+
extends ExpressionTestBase {
47+
private static Stream<Arguments> provideValues() { // NOPMD - false positive
48+
return Stream.of(
49+
Arguments.of(
50+
sequence(integer(1), integer(4), integer(6), integer(5), integer(3)),
51+
"array:flatten([1, 4, 6, 5, 3])"),
52+
Arguments.of(
53+
sequence(integer(1), integer(2), integer(5), integer(10), integer(11), integer(12), integer(13)),
54+
"array:flatten(([1, 2, 5], [[10, 11], 12], [], 13))"),
55+
Arguments.of(
56+
sequence(integer(1), integer(0), integer(1), integer(1), integer(0), integer(1), integer(0), integer(0)),
57+
"array:flatten([(1,0), (1,1), (0,1), (0,0)])"));
58+
}
59+
60+
@ParameterizedTest
61+
@MethodSource("provideValues")
62+
void testExpression(@NonNull ISequence<?> expected, @NonNull String metapath) {
63+
64+
ISequence<?> result = MetapathExpression.compile(metapath)
65+
.evaluateAs(null, MetapathExpression.ResultType.SEQUENCE, newDynamicContext());
66+
assertEquals(expected, result);
67+
}
68+
}

0 commit comments

Comments
 (0)