Skip to content

Commit

Permalink
Completed testing eqname and varref cases for the arrow operator.
Browse files Browse the repository at this point in the history
  • Loading branch information
david-waltermire committed Nov 26, 2024
1 parent b26afc5 commit 0804a78
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
* The implementation of a Metapath
* <a href="https://www.w3.org/TR/xpath-31/#static_context">static context</a>.
*/
// FIXME: refactor well-known into a new class
public final class StaticContext {
@NonNull
private static final Map<String, String> WELL_KNOWN_NAMESPACES;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,12 @@ public RESULT visitFlag(Flag expr, CONTEXT context) {
}

@Override
public RESULT visitFunctionCall(StaticFunctionCall expr, CONTEXT context) {
public RESULT visitStaticFunctionCall(StaticFunctionCall expr, CONTEXT context) {
return visitChildren(expr, context);
}

@Override
public RESULT visitDynamicFunctionCall(DynamicFunctionCall expr, CONTEXT context) {
return visitChildren(expr, context);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1154,29 +1154,48 @@ protected IExpression handleSimplemapexpr(Metapath10.SimplemapexprContext contex

@Override
protected IExpression handleArrowexpr(Metapath10.ArrowexprContext context) {
// FIXME: handle additional syntax for varef and parenthesized

return handleGroupedNAiry(context, 0, 3, (ctx, idx, left) -> {
// the next child is "=>"
assert "=>".equals(ctx.getChild(idx).getText());

int offset = (idx - 1) / 3;

Metapath10.ArrowfunctionspecifierContext fcCtx
= ctx.getChild(Metapath10.ArrowfunctionspecifierContext.class, offset);
Metapath10.ArgumentlistContext argumentCtx = ctx.getChild(Metapath10.ArgumentlistContext.class, offset);

try (Stream<IExpression> args = Stream.concat(
Stream.of(left),
parseArgumentList(ObjectUtils.notNull(argumentCtx)))) {
assert args != null;

List<IExpression> arguments = ObjectUtils.notNull(args.collect(Collectors.toUnmodifiableList()));
// prepend the focus
List<IExpression> arguments = ObjectUtils.notNull(args
.collect(Collectors.toUnmodifiableList()));

Metapath10.ArrowfunctionspecifierContext arrowCtx
= ctx.getChild(Metapath10.ArrowfunctionspecifierContext.class, offset);
if (arrowCtx.eqname() != null) {
// named function
return new StaticFunctionCall(
() -> getContext().lookupFunction(ObjectUtils.notNull(arrowCtx.eqname().getText()), arguments.size()),
arguments);
}

IExpression result;
if (arrowCtx.varref() != null) {
// function instance or name reference
result = new VariableReference(getContext().parseVariableName(
ObjectUtils.notNull(arrowCtx.varref().varname().eqname().getText())));
} else if (arrowCtx.parenthesizedexpr() != null) {
// function expression
result = visit(arrowCtx.parenthesizedexpr().expr());
} else {
throw new StaticMetapathException(
StaticMetapathException.INVALID_PATH_GRAMMAR,
String.format("Unable to get function name using arrow specifier '%s'.", arrowCtx.getText()));
}

return new StaticFunctionCall(
() -> getContext().lookupFunction(
ObjectUtils.notNull(fcCtx.eqname().getText()),
arguments.size()),
return new DynamicFunctionCall(
result,
arguments);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,13 @@ public String visitFlag(Flag expr, State context) {
}

@Override
public String visitFunctionCall(StaticFunctionCall expr, State context) {
return appendNode(expr, super.visitFunctionCall(expr, context), context);
public String visitStaticFunctionCall(StaticFunctionCall expr, State context) {
return appendNode(expr, super.visitStaticFunctionCall(expr, context), context);
}

@Override
public String visitDynamicFunctionCall(DynamicFunctionCall expr, State context) {
return appendNode(expr, super.visitDynamicFunctionCall(expr, context), context);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException;
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

/**
* Executes a function call based on a specifier expression that is used to
* dtermine the function and multiple argument expressions that are used to
* determine the function arguments.
*/
public class DynamicFunctionCall implements IExpression {
@NonNull
private final IExpression functionIdentifier;
@NonNull
private final List<IExpression> arguments;

/**
* Construct a new function call expression.
*
* @param functionIdentifier
* the function expression, identifying either a function or function
* name
* @param arguments
* the expressions used to provide arguments to the function call
*/
public DynamicFunctionCall(@NonNull IExpression functionIdentifier, @NonNull List<IExpression> arguments) {
this.functionIdentifier = functionIdentifier;
this.arguments = arguments;
}

@Override
public List<IExpression> getChildren() {
return ObjectUtils.notNull(Stream.concat(
Stream.of(functionIdentifier),
arguments.stream())
.collect(Collectors.toUnmodifiableList()));
}

@Override
public Class<? extends IItem> getBaseResultType() {
return IItem.class;
}

@Override
public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
return visitor.visitDynamicFunctionCall(this, context);
}

@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()));

IItem specifier = functionIdentifier.accept(dynamicContext, focus).getFirstItem(true);
IFunction function;
if (specifier instanceof IFunction) {
function = (IFunction) specifier;
} else if (specifier != null) {
function = dynamicContext.getStaticContext().lookupFunction(
specifier.toAtomicItem().asString(),
arguments.size());
} else {
throw new StaticMetapathException(
StaticMetapathException.NO_FUNCTION_MATCH,
"Unable to get function name. The error specifier is an empty sequence.");
}
return function.execute(arguments, dynamicContext, focus);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,18 @@ public interface IExpressionVisitor<RESULT, CONTEXT> {
* the processing context
* @return the visitation result or {@code null} if no result was produced
*/
RESULT visitFunctionCall(@NonNull StaticFunctionCall expr, @NonNull CONTEXT context);
RESULT visitStaticFunctionCall(@NonNull StaticFunctionCall expr, @NonNull CONTEXT context);

/**
* Visit the CST node.
*
* @param expr
* the CST node to visit
* @param context
* the processing context
* @return the visitation result or {@code null} if no result was produced
*/
RESULT visitDynamicFunctionCall(@NonNull DynamicFunctionCall expr, @NonNull CONTEXT context);

/**
* Visit the CST node.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,30 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import nl.talsmasoftware.lazy4j.Lazy;

/**
* Executes a function call based on the provided function and multiple argument
* expressions that are used to determine the function arguments.
*/

public class StaticFunctionCall implements IExpression {
@NonNull
private final List<IExpression> arguments;
@NonNull
private final Lazy<IFunction> functionSupplier;
@NonNull
private final List<IExpression> arguments;

/**
* Construct a new function call expression.
*
* @param functionSupplier
* the function implementation supplier
* the function supplier
* @param arguments
* the expressions used to provide arguments to the function call
*/
public StaticFunctionCall(@NonNull Supplier<IFunction> functionSupplier, @NonNull List<IExpression> arguments) {
this.arguments = arguments;
// lazy fetches the function so that Metapaths can parse even if a function does
// not exist
this.functionSupplier = ObjectUtils.notNull(Lazy.lazy(functionSupplier));
this.arguments = arguments;
}

/**
Expand All @@ -47,7 +54,7 @@ public StaticFunctionCall(@NonNull Supplier<IFunction> functionSupplier, @NonNul
* if the function was not found
*/
public IFunction getFunction() {
return ObjectUtils.notNull(functionSupplier.get());
return functionSupplier.get();
}

@Override
Expand All @@ -63,17 +70,18 @@ public Class<? extends IItem> getBaseResultType() {
@SuppressWarnings("null")
@Override
public String toASTString() {
return String.format("%s[name=%s]", getClass().getName(), getFunction().getQName());
return String.format("%s[name=%s, arity=%d]", getClass().getName(), getFunction().getQName(),
getFunction().arity());
}

@Override
public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
return visitor.visitFunctionCall(this, context);
return visitor.visitStaticFunctionCall(this, context);
}

@Override
public ISequence<?> accept(DynamicContext dynamicContext, ISequence<?> focus) {
List<ISequence<?>> arguments = ObjectUtils.notNull(getChildren().stream()
List<ISequence<?>> arguments = ObjectUtils.notNull(this.arguments.stream()
.map(expression -> expression.accept(dynamicContext, focus)).collect(Collectors.toList()));

IFunction function = getFunction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
* <a href= "https://www.w3.org/TR/xpath-functions-31/">function
* specification</a>.
*/
@SuppressWarnings({ "removal" })
@SuppressFBWarnings("UWF_UNWRITTEN_FIELD")
public class DefaultFunctionLibrary
extends FunctionLibrary {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public String asString() {
public String toSignature() {
return ObjectUtils.notNull(new StringBuilder()
.append(getType().toSignature())
.append("(")
.append('(')
.append(getValueSignature())
.append(")")
.append(')')
.toString());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ default IAnyAtomicItem toAtomicItem() {
*
* @return a new string item
*/
@NonNull
default IStringItem asStringItem() {
return IStringItem.valueOf(asString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ public abstract class AbstractNodeItem implements INodeItem {
public final String toSignature() {
StringBuilder builder = new StringBuilder()
.append(getType().toSignature())
.append("⪻")
.append('⪻')
.append(getMetapath())
.append("⪼");
.append('⪼');
String value = getValueSignature();
if (value != null) {
builder.append("(");
builder.append(value);
builder.append(")");
builder.append('(')
.append(value)
.append(')');
}
return ObjectUtils.notNull(builder.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
package gov.nist.secauto.metaschema.core.metapath.cst;

import static gov.nist.secauto.metaschema.core.metapath.TestUtils.bool;
import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer;
import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string;
import static org.junit.jupiter.api.Assertions.assertEquals;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand All @@ -23,19 +26,31 @@

class ArrowExpressionTest
extends ExpressionTestBase {
private static final String NS = "http://example.com/ns";

private static Stream<Arguments> provideValues() { // NOPMD - false positive
return Stream.of(
// Arguments.of(ISequence.of(string("true")), "true() => string()"),
Arguments.of(ISequence.of(bool(false)), "() => exists()"),
Arguments.of(ISequence.of(integer(3)), "(1, 2) => sum()"));
Arguments.of(ISequence.of(string("ABC")), "'abc' => upper-case()"),
Arguments.of(ISequence.of(string("123")), "'1' => concat('2') => concat('3')"),
Arguments.of(ISequence.of(bool(true)), "() => $ex:var1()"));
}

@ParameterizedTest
@MethodSource("provideValues")
void testArrowExpression(@NonNull ISequence<?> expected, @NonNull String metapath) {
StaticContext staticContext = StaticContext.builder()
.namespace("ex", NS)
.build();
DynamicContext dynamicContext = new DynamicContext(staticContext);
dynamicContext.bindVariableValue(IEnhancedQName.of(NS, "var1"), ISequence.of(string("fn:empty")));

assertEquals(
expected,
MetapathExpression.compile(metapath).evaluateAs(null, MetapathExpression.ResultType.SEQUENCE,
newDynamicContext()));
MetapathExpression.compile(metapath, staticContext)
.evaluateAs(
null,
MetapathExpression.ResultType.SEQUENCE,
dynamicContext));
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/

package gov.nist.secauto.metaschema.core.metapath.function.library;

Expand Down
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,13 @@
<failOnError>false</failOnError>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<projectVersionPolicyId>SemVerVersionPolicy</projectVersionPolicyId>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
Expand Down

0 comments on commit 0804a78

Please sign in to comment.