-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for the fn:has-children Metapath function.
- Loading branch information
1 parent
8f88271
commit d36a50d
Showing
7 changed files
with
221 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
...c/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildren.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* | ||
* SPDX-FileCopyrightText: none | ||
* SPDX-License-Identifier: CC0-1.0 | ||
*/ | ||
|
||
package gov.nist.secauto.metaschema.core.metapath.function.library; | ||
|
||
import gov.nist.secauto.metaschema.core.metapath.DynamicContext; | ||
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.ISequence; | ||
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.metapath.item.node.INodeItem; | ||
import gov.nist.secauto.metaschema.core.util.ObjectUtils; | ||
|
||
import java.util.List; | ||
|
||
import edu.umd.cs.findbugs.annotations.NonNull; | ||
|
||
/** | ||
* /** Implements | ||
* <a href= "https://www.w3.org/TR/xpath-functions-31/#func-root">fn:root</a> | ||
* functions. | ||
*/ | ||
public final class FnHasChildren { | ||
@NonNull | ||
private static final String NAME = "has-children"; | ||
@NonNull | ||
static final IFunction SIGNATURE_NO_ARG = IFunction.builder() | ||
.name(NAME) | ||
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) | ||
.deterministic() | ||
.contextDependent() | ||
.focusDependent() | ||
.returnType(IStringItem.type()) | ||
.returnOne() | ||
.functionHandler(FnHasChildren::executeNoArg) | ||
.build(); | ||
@NonNull | ||
static final IFunction SIGNATURE_ONE_ARG = IFunction.builder() | ||
.name(NAME) | ||
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) | ||
.deterministic() | ||
.contextIndependent() | ||
.focusIndependent() | ||
.argument(IArgument.builder() | ||
.name("arg") | ||
.type(INodeItem.type()) | ||
.zeroOrOne() | ||
.build()) | ||
.returnType(IBooleanItem.type()) | ||
.returnOne() | ||
.functionHandler(FnHasChildren::executeOneArg) | ||
.build(); | ||
|
||
@SuppressWarnings("unused") | ||
@NonNull | ||
private static ISequence<IBooleanItem> executeNoArg(@NonNull IFunction function, | ||
@NonNull List<ISequence<?>> arguments, | ||
@NonNull DynamicContext dynamicContext, | ||
IItem focus) { | ||
|
||
INodeItem arg = FunctionUtils.asType( | ||
// test that the focus is an INodeItem | ||
INodeItem.type().test(ObjectUtils.requireNonNull(focus))); | ||
|
||
return ISequence.of(IBooleanItem.valueOf(fnHasChildren(arg))); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
@NonNull | ||
private static ISequence<IBooleanItem> executeOneArg(@NonNull IFunction function, | ||
@NonNull List<ISequence<?>> arguments, | ||
@NonNull DynamicContext dynamicContext, | ||
IItem focus) { | ||
INodeItem arg = FunctionUtils.asTypeOrNull(ObjectUtils.requireNonNull(arguments.get(0)).getFirstItem(true)); | ||
|
||
return arg == null ? ISequence.empty() : ISequence.of(IBooleanItem.valueOf(fnHasChildren(arg))); | ||
} | ||
|
||
/** | ||
* Determine if the provided node argument has model item children. | ||
* <p> | ||
* Based on the XPath 3.1 <a href= | ||
* "https://www.w3.org/TR/xpath-functions-31/#func-has-children">fn:has-children</a> | ||
* function. | ||
* | ||
* @param arg | ||
* the node item to check for children | ||
* @return {@code true} if the provided node has model item children, or | ||
* {@code false} otherwise | ||
*/ | ||
public static boolean fnHasChildren(@NonNull INodeItem arg) { | ||
return arg.modelItems().findFirst().isPresent(); | ||
} | ||
|
||
private FnHasChildren() { | ||
// disable construction | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
...st/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* SPDX-FileCopyrightText: none | ||
* SPDX-License-Identifier: CC0-1.0 | ||
*/ | ||
|
||
package gov.nist.secauto.metaschema.core.metapath.function.library; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertAll; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
import gov.nist.secauto.metaschema.core.metapath.DynamicContext; | ||
import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; | ||
import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; | ||
import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; | ||
import gov.nist.secauto.metaschema.core.metapath.MetapathException; | ||
import gov.nist.secauto.metaschema.core.metapath.function.library.impl.MockedDocumentGenerator; | ||
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; | ||
import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; | ||
import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; | ||
import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; | ||
|
||
import org.junit.jupiter.api.Test; | ||
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 FnHasChildrenTest | ||
extends ExpressionTestBase { | ||
|
||
private static Stream<Arguments> provideValues() { // NOPMD - false positive | ||
return Stream.of( | ||
Arguments.of( | ||
true, | ||
"has-children()"), | ||
Arguments.of( | ||
true, | ||
"has-children(.)"), | ||
Arguments.of( | ||
true, | ||
"has-children(/root)"), | ||
Arguments.of( | ||
false, | ||
"has-children(/root/assembly)"), | ||
Arguments.of( | ||
false, | ||
"has-children(/root/assembly/@assembly-flag)"), | ||
Arguments.of( | ||
false, | ||
"has-children(/root/field)"), | ||
Arguments.of( | ||
false, | ||
"has-children(/root/field/@field-flag)")); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("provideValues") | ||
void test(boolean expected, @NonNull String metapath) { | ||
DynamicContext dynamicContext = newDynamicContext(); | ||
|
||
INodeItem node = MockedDocumentGenerator.generateDocumentNodeItem(getContext()); | ||
Boolean result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext()) | ||
.evaluateAs(node, IMetapathExpression.ResultType.BOOLEAN, dynamicContext); | ||
assertEquals(expected, result); | ||
} | ||
|
||
@Test | ||
void testContextAbsent() { | ||
DynamicContext dynamicContext = newDynamicContext(); | ||
|
||
MetapathException ex = assertThrows(MetapathException.class, () -> { | ||
IMetapathExpression.compile("has-children()", dynamicContext.getStaticContext()) | ||
.evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext); | ||
}); | ||
Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; | ||
|
||
assertAll( | ||
() -> assertEquals(DynamicMetapathException.class, cause == null | ||
? null | ||
: cause.getClass()), | ||
() -> assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, cause instanceof DynamicMetapathException | ||
? ((DynamicMetapathException) cause).getCode() | ||
: null)); | ||
} | ||
|
||
@Test | ||
void testNotANode() { | ||
DynamicContext dynamicContext = newDynamicContext(); | ||
|
||
MetapathException ex = assertThrows(MetapathException.class, () -> { | ||
IMetapathExpression.compile("has-children()", dynamicContext.getStaticContext()) | ||
.evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); | ||
}); | ||
Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; | ||
|
||
assertAll( | ||
() -> assertEquals(InvalidTypeMetapathException.class, cause == null | ||
? null | ||
: cause.getClass()), | ||
() -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException | ||
? ((TypeMetapathException) cause).getCode() | ||
: null)); | ||
} | ||
} |