Skip to content

Commit

Permalink
Added support for the Metapath function deep-equal. (metaschema-frame…
Browse files Browse the repository at this point in the history
  • Loading branch information
david-waltermire authored Dec 7, 2024
1 parent 793e73e commit bcfee31
Show file tree
Hide file tree
Showing 30 changed files with 733 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visit
@Override
public ISequence<?> accept(DynamicContext dynamicContext, ISequence<?> focus) {
List<ISequence<?>> arguments = ObjectUtils.notNull(this.arguments.stream()
.map(expression -> expression.accept(dynamicContext, focus)).collect(Collectors.toList()));
.map(expression -> expression.accept(dynamicContext, focus).contentsAsSequence())
.collect(Collectors.toList()));

IFunction function = getFunction();
return function.execute(arguments, dynamicContext, focus);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,97 @@

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

@SuppressWarnings("PMD.ShortClassName") // intentional
/**
* An implementation of <a href="https://www.w3.org/TR/xpath-31/#axes">Metapath
* axes</a>.
*/
@SuppressWarnings("PMD.ShortClassName")
public enum Axis implements IExpression {
/**
* The {@code self::} axis, referring to the current context node.
*/
SELF(Stream::of),
/**
* The {@code parent::} axis, referring to the current context node's parent.
*
* @see INodeItem#getParentNodeItem()
*/
PARENT(focus -> Stream.ofNullable(focus.getParentNodeItem())),
/**
* The {@code flag::} axis, referring to the current context node's flags.
*
* @see INodeItem#getFlags()
*/
FLAG(INodeItem::flags),
/**
* The {@code ancestor::} axis, referring to the current context node's
* parentage.
*
* @see INodeItem#ancestor()
*/
ANCESTOR(INodeItem::ancestor),
/**
* The {@code ancestor-or-self::} axis, referring to the current context node
* and its parentage.
*
* @see INodeItem#ancestorOrSelf()
*/
ANCESTOR_OR_SELF(INodeItem::ancestorOrSelf),
/**
* The {@code children::} axis, referring to the current context node's direct
* children.
*
* @see INodeItem#modelItems()
*/
CHILDREN(INodeItem::modelItems),
/**
* The {@code descendant::} axis, referring to all of the current context node's
* descendants (i.e., the children, the children of the children, etc).
*
* @see INodeItem#descendant()
*/
DESCENDANT(INodeItem::descendant),
/**
* The {@code descendant-or-self::} axis, referring to the current context node
* and all of the current context node's descendants (i.e., the children, the
* children of the children, etc).
*
* @see INodeItem#descendantOrSelf()
*/
DESCENDANT_OR_SELF(INodeItem::descendantOrSelf),
/**
* The {@code following-sibling::} axis, referring to those children of the
* context node's parent that occur after the context node in
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
* order</a>.
*/
FOLLOWING_SIBLING(INodeItem::followingSibling),
/**
* The {@code preceding-sibling::} axis, referring to those children of the
* context node's parent that occur before the context node in
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
* order</a>.
*/
PRECEDING_SIBLING(INodeItem::precedingSibling),
/**
* The {@code preceding-sibling::} axis, referring to all nodes that are
* descendants of the root of the tree in which the context node is found, are
* not descendants of the context node, and occur after the context node in
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
* order</a>.
*/
FOLLOWING(INodeItem::following),
/**
* The {@code preceding-sibling::} axis, referring to all nodes that are
* descendants of the root of the tree in which the context node is found, are
* not ancestors of the context node, and occur before the context node in
* <a href="https://www.w3.org/TR/xpath-31/#dt-document-order">document
* order</a>.
*/
PRECEDING(INodeItem::preceding),
/**
* This axis is not supported.
*/
NAMESPACE(focus -> {
throw new StaticMetapathException(
StaticMetapathException.AXIS_NAMESPACE_UNSUPPORTED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import gov.nist.secauto.metaschema.core.metapath.MetapathException;
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException;
import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue;
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.IAnyAtomicItem;
Expand Down Expand Up @@ -180,6 +181,12 @@ default boolean isArityUnbounded() {
// */
// boolean isSupported(List<IExpression<?>> arguments);

@Override
default boolean deepEquals(ICollectionValue other) {
// this is the expected result
return false;
}

/**
* Execute the function with the provided {@code arguments}, using the provided
* {@code DynamicContext} and {@code focus}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException;
import gov.nist.secauto.metaschema.core.metapath.MetapathException;
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
import gov.nist.secauto.metaschema.core.metapath.function.CalledContext;
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
Expand All @@ -19,6 +18,7 @@
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
import gov.nist.secauto.metaschema.core.metapath.type.IItemType;
import gov.nist.secauto.metaschema.core.metapath.type.ISequenceType;
import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
Expand All @@ -32,19 +32,53 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

/**
* This abstract implementation provides common functionality shared by all
* functions.
*/
public abstract class AbstractFunction implements IFunction {
@NonNull
private final IEnhancedQName qname;
@NonNull
private final List<IArgument> arguments;

/**
* Construct a new function using the provided name and namespace, used together
* to form the function's qualified name, and the provided arguments.
* <p>
* This constructor is equivalent to calling:
*
* <pre>
* {@code
* String name = ...;
* String namespace = ...;
* List<IArgument> arguments = ...;
* new AbstractFunction(IEnhancedQName.of(namespace, name), arguments);
* }
* </pre>
*
* @param name
* the function's name
* @param namespace
* the function's namespace
* @param arguments
* the function's arguments
*/
protected AbstractFunction(
@NonNull String name,
@NonNull String namespace,
@NonNull List<IArgument> arguments) {
this(IEnhancedQName.of(namespace, name), arguments);
}

/**
* Construct a new function using the provided qualified name and arguments.
*
* @param qname
* the function's qualified name
* @param arguments
* the function's arguments
*/
protected AbstractFunction(
@NonNull IEnhancedQName qname,
@NonNull List<IArgument> arguments) {
Expand Down Expand Up @@ -118,26 +152,24 @@ public static List<ISequence<?>> convertArguments(
private static ISequence<?> convertArgument(
@NonNull IArgument argument,
@NonNull ISequence<?> parameter) {
ISequenceType sequenceType = argument.getSequenceType();

// apply occurrence
ISequence<?> retval = argument.getSequenceType().getOccurrence().getSequenceHandler().handle(parameter);
ISequence<?> retval = sequenceType.getOccurrence().getSequenceHandler().handle(parameter);

// apply function conversion and type promotion to the parameter
if (!retval.isEmpty()) {
IItemType type = argument.getSequenceType().getType();
IItemType type = sequenceType.getType();
// this is not required to be an empty sequence
retval = convertSequence(argument, retval, type);

// verify resulting values
Class<? extends IItem> argumentClass = type.getItemClass();
for (IItem item : retval.getValue()) {
Class<? extends IItem> itemClass = item.getClass();
if (!argumentClass.isAssignableFrom(itemClass)) {
throw new InvalidTypeMetapathException(
item,
String.format("The type '%s' is not a subtype of '%s'",
StaticContext.lookupItemType(itemClass),
type));
}
if (!sequenceType.matches(retval)) {
throw new InvalidTypeMetapathException(
null,
String.format("The argument '%s' is not a '%s'",
retval.toSignature(),
sequenceType.toSignature()));
}
}
return retval;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import gov.nist.secauto.metaschema.core.metapath.function.DateTimeFunctionException;
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem;
Expand All @@ -20,6 +21,8 @@
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUntypedAtomicItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;
import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
Expand Down Expand Up @@ -1066,4 +1069,17 @@ public static IBooleanItem opBooleanLessThan(@Nullable IBooleanItem arg1, @Nulla

return IBooleanItem.valueOf(!left && right);
}

public static boolean opSameKey(@NonNull IAnyAtomicItem k1, @NonNull IAnyAtomicItem k2) {
boolean retval;
if ((k1 instanceof IStringItem || k1 instanceof IAnyUriItem || k1 instanceof IUntypedAtomicItem)
&& (k2 instanceof IStringItem || k2 instanceof IAnyUriItem || k2 instanceof IUntypedAtomicItem)) {
retval = k1.asString().equals(k2.asString());
} else if (k1 instanceof IDecimalItem && k2 instanceof IDecimalItem) {
retval = ((IDecimalItem) k1).asDecimal().equals(((IDecimalItem) k2).asDecimal());
} else {
retval = k1.deepEquals(k2);
}
return retval;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ public DefaultFunctionLibrary() { // NOPMD - intentional
// https://www.w3.org/TR/xpath-functions-31/#func-day-from-date
// https://www.w3.org/TR/xpath-functions-31/#func-day-from-dateTime
// https://www.w3.org/TR/xpath-functions-31/#func-days-from-duration
// P1: https://www.w3.org/TR/xpath-functions-31/#func-deep-equal
// https://www.w3.org/TR/xpath-functions-31/#func-deep-equal
registerFunction(FnDeepEqual.SIGNATURE_TWO_ARG);
// P1: https://www.w3.org/TR/xpath-functions-31/#func-distinct-values
// https://www.w3.org/TR/xpath-functions-31/#func-doc
registerFunction(FnDoc.SIGNATURE);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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.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-deep-equal">fn:deep-equal</a>
* functions.
* <p>
* This implementation does not implement the three-arg variant with collation
* at this time.
*/
public final class FnDeepEqual {
@NonNull
private static final String NAME = "deep-equal";
@NonNull
static final IFunction SIGNATURE_TWO_ARG = IFunction.builder()
.name(NAME)
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
.deterministic()
.contextDependent()
.focusIndependent()
.argument(IArgument.builder()
.name("parameter1")
.type(IItem.type())
.zeroOrMore()
.build())
.argument(IArgument.builder()
.name("parameter2")
.type(IItem.type())
.zeroOrMore()
.build())
.returnType(IBooleanItem.type())
.returnOne()
.functionHandler(FnDeepEqual::executeTwoArg)
.build();

@SuppressWarnings("unused")
@NonNull
private static ISequence<IBooleanItem> executeTwoArg(@NonNull IFunction function,
@NonNull List<ISequence<?>> arguments,
@NonNull DynamicContext dynamicContext,
IItem focus) {
ISequence<?> parameter1 = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
ISequence<?> parameter2 = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1)));

return ISequence.of(IBooleanItem.valueOf(parameter1.deepEquals(parameter2)));
}

private FnDeepEqual() {
// disable construction
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,7 @@ private static ISequence<IIntegerItem> executeTwoArg(@NonNull IFunction function
ISequence<IAnyAtomicItem> seq = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
IAnyAtomicItem search = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));

if (seq.size() == 0) {
return ISequence.empty();
}
return fnIndexOf(seq, search);
return seq.isEmpty() ? ISequence.empty() : fnIndexOf(seq, search);
}

/**
Expand Down
Loading

0 comments on commit bcfee31

Please sign in to comment.