Skip to content

Commit d2fa78e

Browse files
Merge branch 'develop' into feature/metapath-distinct-values
2 parents d919727 + bcfee31 commit d2fa78e

30 files changed

Lines changed: 733 additions & 38 deletions

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visit
9999
@Override
100100
public ISequence<?> accept(DynamicContext dynamicContext, ISequence<?> focus) {
101101
List<ISequence<?>> arguments = ObjectUtils.notNull(this.arguments.stream()
102-
.map(expression -> expression.accept(dynamicContext, focus)).collect(Collectors.toList()));
102+
.map(expression -> expression.accept(dynamicContext, focus).contentsAsSequence())
103+
.collect(Collectors.toList()));
103104

104105
IFunction function = getFunction();
105106
return function.execute(arguments, dynamicContext, focus);

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

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,97 @@
2121

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

24-
@SuppressWarnings("PMD.ShortClassName") // intentional
24+
/**
25+
* An implementation of <a href="https://www.w3.org/TR/xpath-31/#axes">Metapath
26+
* axes</a>.
27+
*/
28+
@SuppressWarnings("PMD.ShortClassName")
2529
public enum Axis implements IExpression {
30+
/**
31+
* The {@code self::} axis, referring to the current context node.
32+
*/
2633
SELF(Stream::of),
34+
/**
35+
* The {@code parent::} axis, referring to the current context node's parent.
36+
*
37+
* @see INodeItem#getParentNodeItem()
38+
*/
2739
PARENT(focus -> Stream.ofNullable(focus.getParentNodeItem())),
40+
/**
41+
* The {@code flag::} axis, referring to the current context node's flags.
42+
*
43+
* @see INodeItem#getFlags()
44+
*/
2845
FLAG(INodeItem::flags),
46+
/**
47+
* The {@code ancestor::} axis, referring to the current context node's
48+
* parentage.
49+
*
50+
* @see INodeItem#ancestor()
51+
*/
2952
ANCESTOR(INodeItem::ancestor),
53+
/**
54+
* The {@code ancestor-or-self::} axis, referring to the current context node
55+
* and its parentage.
56+
*
57+
* @see INodeItem#ancestorOrSelf()
58+
*/
3059
ANCESTOR_OR_SELF(INodeItem::ancestorOrSelf),
60+
/**
61+
* The {@code children::} axis, referring to the current context node's direct
62+
* children.
63+
*
64+
* @see INodeItem#modelItems()
65+
*/
3166
CHILDREN(INodeItem::modelItems),
67+
/**
68+
* The {@code descendant::} axis, referring to all of the current context node's
69+
* descendants (i.e., the children, the children of the children, etc).
70+
*
71+
* @see INodeItem#descendant()
72+
*/
3273
DESCENDANT(INodeItem::descendant),
74+
/**
75+
* The {@code descendant-or-self::} axis, referring to the current context node
76+
* and all of the current context node's descendants (i.e., the children, the
77+
* children of the children, etc).
78+
*
79+
* @see INodeItem#descendantOrSelf()
80+
*/
3381
DESCENDANT_OR_SELF(INodeItem::descendantOrSelf),
82+
/**
83+
* The {@code following-sibling::} axis, referring to those children of the
84+
* context node's parent that occur after the context node in
85+
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
86+
* order</a>.
87+
*/
3488
FOLLOWING_SIBLING(INodeItem::followingSibling),
89+
/**
90+
* The {@code preceding-sibling::} axis, referring to those children of the
91+
* context node's parent that occur before the context node in
92+
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
93+
* order</a>.
94+
*/
3595
PRECEDING_SIBLING(INodeItem::precedingSibling),
96+
/**
97+
* The {@code preceding-sibling::} axis, referring to all nodes that are
98+
* descendants of the root of the tree in which the context node is found, are
99+
* not descendants of the context node, and occur after the context node in
100+
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
101+
* order</a>.
102+
*/
36103
FOLLOWING(INodeItem::following),
104+
/**
105+
* The {@code preceding-sibling::} axis, referring to all nodes that are
106+
* descendants of the root of the tree in which the context node is found, are
107+
* not ancestors of the context node, and occur before the context node in
108+
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
109+
* order</a>.
110+
*/
37111
PRECEDING(INodeItem::preceding),
112+
/**
113+
* This axis is not supported.
114+
*/
38115
NAMESPACE(focus -> {
39116
throw new StaticMetapathException(
40117
StaticMetapathException.AXIS_NAMESPACE_UNSUPPORTED,

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import gov.nist.secauto.metaschema.core.metapath.MetapathException;
1010
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
1111
import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException;
12+
import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue;
1213
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
1314
import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
1415
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
@@ -180,6 +181,12 @@ default boolean isArityUnbounded() {
180181
// */
181182
// boolean isSupported(List<IExpression<?>> arguments);
182183

184+
@Override
185+
default boolean deepEquals(ICollectionValue other) {
186+
// this is the expected result
187+
return false;
188+
}
189+
183190
/**
184191
* Execute the function with the provided {@code arguments}, using the provided
185192
* {@code DynamicContext} and {@code focus}.

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

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
99
import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException;
1010
import gov.nist.secauto.metaschema.core.metapath.MetapathException;
11-
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
1211
import gov.nist.secauto.metaschema.core.metapath.function.CalledContext;
1312
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
1413
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
@@ -19,6 +18,7 @@
1918
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem;
2019
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
2120
import gov.nist.secauto.metaschema.core.metapath.type.IItemType;
21+
import gov.nist.secauto.metaschema.core.metapath.type.ISequenceType;
2222
import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
2323
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
2424
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
@@ -32,19 +32,53 @@
3232
import edu.umd.cs.findbugs.annotations.NonNull;
3333
import edu.umd.cs.findbugs.annotations.Nullable;
3434

35+
/**
36+
* This abstract implementation provides common functionality shared by all
37+
* functions.
38+
*/
3539
public abstract class AbstractFunction implements IFunction {
3640
@NonNull
3741
private final IEnhancedQName qname;
3842
@NonNull
3943
private final List<IArgument> arguments;
4044

45+
/**
46+
* Construct a new function using the provided name and namespace, used together
47+
* to form the function's qualified name, and the provided arguments.
48+
* <p>
49+
* This constructor is equivalent to calling:
50+
*
51+
* <pre>
52+
* {@code
53+
* String name = ...;
54+
* String namespace = ...;
55+
* List<IArgument> arguments = ...;
56+
* new AbstractFunction(IEnhancedQName.of(namespace, name), arguments);
57+
* }
58+
* </pre>
59+
*
60+
* @param name
61+
* the function's name
62+
* @param namespace
63+
* the function's namespace
64+
* @param arguments
65+
* the function's arguments
66+
*/
4167
protected AbstractFunction(
4268
@NonNull String name,
4369
@NonNull String namespace,
4470
@NonNull List<IArgument> arguments) {
4571
this(IEnhancedQName.of(namespace, name), arguments);
4672
}
4773

74+
/**
75+
* Construct a new function using the provided qualified name and arguments.
76+
*
77+
* @param qname
78+
* the function's qualified name
79+
* @param arguments
80+
* the function's arguments
81+
*/
4882
protected AbstractFunction(
4983
@NonNull IEnhancedQName qname,
5084
@NonNull List<IArgument> arguments) {
@@ -118,26 +152,24 @@ public static List<ISequence<?>> convertArguments(
118152
private static ISequence<?> convertArgument(
119153
@NonNull IArgument argument,
120154
@NonNull ISequence<?> parameter) {
155+
ISequenceType sequenceType = argument.getSequenceType();
156+
121157
// apply occurrence
122-
ISequence<?> retval = argument.getSequenceType().getOccurrence().getSequenceHandler().handle(parameter);
158+
ISequence<?> retval = sequenceType.getOccurrence().getSequenceHandler().handle(parameter);
123159

124160
// apply function conversion and type promotion to the parameter
125161
if (!retval.isEmpty()) {
126-
IItemType type = argument.getSequenceType().getType();
162+
IItemType type = sequenceType.getType();
127163
// this is not required to be an empty sequence
128164
retval = convertSequence(argument, retval, type);
129165

130166
// verify resulting values
131-
Class<? extends IItem> argumentClass = type.getItemClass();
132-
for (IItem item : retval.getValue()) {
133-
Class<? extends IItem> itemClass = item.getClass();
134-
if (!argumentClass.isAssignableFrom(itemClass)) {
135-
throw new InvalidTypeMetapathException(
136-
item,
137-
String.format("The type '%s' is not a subtype of '%s'",
138-
StaticContext.lookupItemType(itemClass),
139-
type));
140-
}
167+
if (!sequenceType.matches(retval)) {
168+
throw new InvalidTypeMetapathException(
169+
null,
170+
String.format("The argument '%s' is not a '%s'",
171+
retval.toSignature(),
172+
sequenceType.toSignature()));
141173
}
142174
}
143175
return retval;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import gov.nist.secauto.metaschema.core.metapath.function.DateTimeFunctionException;
1010
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
1111
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
12+
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem;
1213
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem;
1314
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
1415
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem;
@@ -20,6 +21,8 @@
2021
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem;
2122
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
2223
import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
24+
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
25+
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUntypedAtomicItem;
2326
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;
2427
import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
2528
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
@@ -1066,4 +1069,17 @@ public static IBooleanItem opBooleanLessThan(@Nullable IBooleanItem arg1, @Nulla
10661069

10671070
return IBooleanItem.valueOf(!left && right);
10681071
}
1072+
1073+
public static boolean opSameKey(@NonNull IAnyAtomicItem k1, @NonNull IAnyAtomicItem k2) {
1074+
boolean retval;
1075+
if ((k1 instanceof IStringItem || k1 instanceof IAnyUriItem || k1 instanceof IUntypedAtomicItem)
1076+
&& (k2 instanceof IStringItem || k2 instanceof IAnyUriItem || k2 instanceof IUntypedAtomicItem)) {
1077+
retval = k1.asString().equals(k2.asString());
1078+
} else if (k1 instanceof IDecimalItem && k2 instanceof IDecimalItem) {
1079+
retval = ((IDecimalItem) k1).asDecimal().equals(((IDecimalItem) k2).asDecimal());
1080+
} else {
1081+
retval = k1.deepEquals(k2);
1082+
}
1083+
return retval;
1084+
}
10691085
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ public DefaultFunctionLibrary() { // NOPMD - intentional
6262
// https://www.w3.org/TR/xpath-functions-31/#func-day-from-date
6363
// https://www.w3.org/TR/xpath-functions-31/#func-day-from-dateTime
6464
// https://www.w3.org/TR/xpath-functions-31/#func-days-from-duration
65-
// P1: https://www.w3.org/TR/xpath-functions-31/#func-deep-equal
65+
// https://www.w3.org/TR/xpath-functions-31/#func-deep-equal
66+
registerFunction(FnDeepEqual.SIGNATURE_TWO_ARG);
6667
// https://www.w3.org/TR/xpath-functions-31/#func-distinct-values
6768
registerFunction(FnDistinctValues.SIGNATURE_ONE_ARG);
6869
// https://www.w3.org/TR/xpath-functions-31/#func-doc
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* SPDX-FileCopyrightText: none
3+
* SPDX-License-Identifier: CC0-1.0
4+
*/
5+
6+
package gov.nist.secauto.metaschema.core.metapath.function.library;
7+
8+
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
9+
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
10+
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
11+
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
12+
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
13+
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
14+
import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
15+
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
16+
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
17+
18+
import java.util.List;
19+
20+
import edu.umd.cs.findbugs.annotations.NonNull;
21+
22+
/**
23+
* /** Implements <a href=
24+
* "https://www.w3.org/TR/xpath-functions-31/#func-deep-equal">fn:deep-equal</a>
25+
* functions.
26+
* <p>
27+
* This implementation does not implement the three-arg variant with collation
28+
* at this time.
29+
*/
30+
public final class FnDeepEqual {
31+
@NonNull
32+
private static final String NAME = "deep-equal";
33+
@NonNull
34+
static final IFunction SIGNATURE_TWO_ARG = IFunction.builder()
35+
.name(NAME)
36+
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
37+
.deterministic()
38+
.contextDependent()
39+
.focusIndependent()
40+
.argument(IArgument.builder()
41+
.name("parameter1")
42+
.type(IItem.type())
43+
.zeroOrMore()
44+
.build())
45+
.argument(IArgument.builder()
46+
.name("parameter2")
47+
.type(IItem.type())
48+
.zeroOrMore()
49+
.build())
50+
.returnType(IBooleanItem.type())
51+
.returnOne()
52+
.functionHandler(FnDeepEqual::executeTwoArg)
53+
.build();
54+
55+
@SuppressWarnings("unused")
56+
@NonNull
57+
private static ISequence<IBooleanItem> executeTwoArg(@NonNull IFunction function,
58+
@NonNull List<ISequence<?>> arguments,
59+
@NonNull DynamicContext dynamicContext,
60+
IItem focus) {
61+
ISequence<?> parameter1 = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
62+
ISequence<?> parameter2 = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1)));
63+
64+
return ISequence.of(IBooleanItem.valueOf(parameter1.deepEquals(parameter2)));
65+
}
66+
67+
private FnDeepEqual() {
68+
// disable construction
69+
}
70+
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,7 @@ private static ISequence<IIntegerItem> executeTwoArg(@NonNull IFunction function
6464
ISequence<IAnyAtomicItem> seq = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
6565
IAnyAtomicItem search = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
6666

67-
if (seq.size() == 0) {
68-
return ISequence.empty();
69-
}
70-
return fnIndexOf(seq, search);
67+
return seq.isEmpty() ? ISequence.empty() : fnIndexOf(seq, search);
7168
}
7269

7370
/**

0 commit comments

Comments
 (0)