Skip to content

Commit

Permalink
Add support for Expanded QNames (#18)
Browse files Browse the repository at this point in the history
* Completed qualified name refacotring. JUnit tests are now running clean.
* Updated spotbugs to use the most recent version. Fixed a bunch of spotbugs issues.
* Refactored model path handling in Metapath parsing to use proper namespacing.
* adjusted codereview settings.
  • Loading branch information
david-waltermire authored Apr 23, 2024
1 parent 6a858f6 commit c8ef0d7
Show file tree
Hide file tree
Showing 210 changed files with 2,161 additions and 1,209 deletions.
2 changes: 0 additions & 2 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,5 @@ reviews:
drafts: true
base_branches:
- "develop"
path_filters:
- "**/*.java"
chat:
auto_reply: true
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

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

public class CLIProcessor {
private static final Logger LOGGER = LogManager.getLogger(CLIProcessor.class);
Expand Down Expand Up @@ -253,6 +254,7 @@ public class CallingContext {
@NonNull
private final List<String> extraArgs;

@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
public CallingContext(@NonNull List<String> args) {
Map<String, ICommand> topLevelCommandMap = getTopLevelCommands().stream()
.collect(Collectors.toUnmodifiableMap(ICommand::getName, Function.identity()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@

package gov.nist.secauto.metaschema.cli.processor.command;

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

public class DefaultExtraArgument implements ExtraArgument {
private final String name;
private final boolean required;
private final int number;

@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
public DefaultExtraArgument(String name, boolean required) {
this(name, required, 1);
}

@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
public DefaultExtraArgument(String name, boolean required, int number) {
if (number < 1) {
throw new IllegalArgumentException("number must be a positive value");
Expand Down
9 changes: 3 additions & 6 deletions core/src/main/antlr4/Metapath10.g4
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,11 @@ relativepathexpr : stepexpr (( SLASH | SS) stepexpr)* ;
stepexpr : postfixexpr | axisstep ;
axisstep : (reversestep | forwardstep) predicatelist ;
// [40]
// forwardstep : forwardaxis nodetest | abbrevforwardstep ;
forwardstep : forwardaxis nametest | abbrevforwardstep ;
forwardstep : forwardaxis nodetest | abbrevforwardstep ;
// forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_ATTRIBUTE COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON | KW_FOLLOWING_SIBLING COLONCOLON | KW_FOLLOWING COLONCOLON | KW_NAMESPACE COLONCOLON ;
forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON ;
// abbrevforwardstep : AT? nodetest ;
abbrevforwardstep : AT? nametest ;
// reversestep : reverseaxis nodetest | abbrevreversestep ;
reversestep : reverseaxis nametest | abbrevreversestep ;
abbrevforwardstep : AT? nodetest ;
reversestep : reverseaxis nodetest | abbrevreversestep ;
// reverseaxis : KW_PARENT COLONCOLON | KW_ANCESTOR COLONCOLON | KW_PRECEDING_SIBLING COLONCOLON | KW_PRECEDING COLONCOLON | KW_ANCESTOR_OR_SELF COLONCOLON ;
reverseaxis : KW_PARENT COLONCOLON | KW_ANCESTOR COLONCOLON | KW_ANCESTOR_OR_SELF COLONCOLON ;
// [45]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

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

public class MarkupLine
public final class MarkupLine
extends AbstractMarkupString<MarkupLine> {

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.namespace.QName;

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

// TODO: add support for in-scope namespaces
Expand All @@ -54,17 +56,21 @@
*/
public class DynamicContext { // NOPMD - intentional data class
@NonNull
private final Map<String, ISequence<?>> letVariableMap;
private final Map<QName, ISequence<?>> letVariableMap;
@NonNull
private final SharedState sharedState;

public DynamicContext() {
this(StaticContext.instance());
}

/**
* Construct a new Metapath dynamic context.
*
* @param staticContext
* the Metapath static context
*/
DynamicContext(@NonNull StaticContext staticContext) {
public DynamicContext(@NonNull StaticContext staticContext) {
this.letVariableMap = new ConcurrentHashMap<>();
this.sharedState = new SharedState(staticContext);
}
Expand Down Expand Up @@ -181,7 +187,7 @@ public void cacheResult(@NonNull CallingContext callingContext, @NonNull ISequen
}

@NonNull
public ISequence<?> getVariableValue(@NonNull String name) {
public ISequence<?> getVariableValue(@NonNull QName name) {
return ObjectUtils.requireNonNull(letVariableMap.get(name));
}

Expand All @@ -193,7 +199,7 @@ public ISequence<?> getVariableValue(@NonNull String name) {
* @param boundValue
* the value to bind to the variable
*/
public void bindVariableValue(@NonNull String name, @NonNull ISequence<?> boundValue) {
public void bindVariableValue(@NonNull QName name, @NonNull ISequence<?> boundValue) {
letVariableMap.put(name, boundValue);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Portions of this software was developed by employees of the National Institute
* of Standards and Technology (NIST), an agency of the Federal Government and is
* being made available as a public service. Pursuant to title 17 United States
* Code Section 105, works of NIST employees are not subject to copyright
* protection in the United States. This software may be subject to foreign
* copyright. Permission in the United States and in foreign countries, to the
* extent that NIST may hold copyright, to use, copy, modify, create derivative
* works, and distribute this software and its documentation without fee is hereby
* granted on a non-exclusive basis, provided that this notice and disclaimer
* of warranty appears in all copies.
*
* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
* EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
* THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
* INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
* SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
* SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
* INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
* OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
* CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
* PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
* OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
*/

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

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;

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

public final class EQNameUtils {
private static final Pattern URI_QUALIFIED_NAME = Pattern.compile("^Q\\{([^{}]*)\\}(.+)$");
private static final Pattern LEXICAL_NAME = Pattern.compile("^(?:([^:]+):)?(.+)$");
private static final Pattern NCNAME = Pattern.compile(String.format("^(\\p{L}|_)(\\p{L}|\\p{N}|[.\\-_])*$"));

private EQNameUtils() {
// disable construction
}

@NonNull
public static QName parseName(
@NonNull String name,
@Nullable IEQNamePrefixResolver resolver) {
Matcher matcher = URI_QUALIFIED_NAME.matcher(name);
return matcher.matches()
? newUriQualifiedName(matcher)
: parseLexicalQName(name, resolver);
}

@NonNull
public static QName parseUriQualifiedName(@NonNull String name) {
Matcher matcher = URI_QUALIFIED_NAME.matcher(name);
if (!matcher.matches()) {
throw new IllegalArgumentException(
String.format("The name '%s' is not a valid BracedURILiteral of the form: Q{URI}local-name", name));
}
return newUriQualifiedName(matcher);
}

@NonNull
private static QName newUriQualifiedName(@NonNull Matcher matcher) {
return new QName(matcher.group(1), matcher.group(2));
}

@NonNull
public static QName parseLexicalQName(
@NonNull String name,
@Nullable IEQNamePrefixResolver resolver) {
Matcher matcher = LEXICAL_NAME.matcher(name);
if (!matcher.matches()) {
throw new IllegalArgumentException(
String.format("The name '%s' is not a valid lexical QName of the form: prefix:local-name or local-name",
name));
}
String prefix = matcher.group(1);

if (prefix == null) {
prefix = XMLConstants.DEFAULT_NS_PREFIX;
}

String namespace = resolver == null ? XMLConstants.NULL_NS_URI : resolver.resolve(prefix);
return new QName(namespace, matcher.group(2), prefix);
}

public static boolean isNcName(@NonNull String name) {
return NCNAME.matcher(name).matches();
}

@FunctionalInterface
public interface IEQNamePrefixResolver {
@NonNull
String resolve(@NonNull String prefix);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,14 @@ public enum ResultType {
private static final Logger LOGGER = LogManager.getLogger(MetapathExpression.class);

@NonNull
public static final MetapathExpression CONTEXT_NODE = new MetapathExpression(".", ContextItem.instance());
public static final MetapathExpression CONTEXT_NODE
= new MetapathExpression(".", ContextItem.instance(), StaticContext.instance());

private final String path;
@NonNull
private final IExpression node;
private final IExpression expression;
@NonNull
private final StaticContext staticContext;

/**
* Compiles a Metapath expression string.
Expand All @@ -111,6 +114,24 @@ public enum ResultType {
*/
@NonNull
public static MetapathExpression compile(@NonNull String path) {
StaticContext context = StaticContext.builder().build();

return compile(path, context);
}

/**
* Compiles a Metapath expression string using the provided static context.
*
* @param path
* the metapath expression
* @param context
* the static evaluation context
* @return the compiled expression object
* @throws MetapathException
* if an error occurred while compiling the Metapath expression
*/
@NonNull
public static MetapathExpression compile(@NonNull String path, @NonNull StaticContext context) {
@NonNull MetapathExpression retval;
if (".".equals(path)) {
retval = CONTEXT_NODE;
Expand Down Expand Up @@ -140,12 +161,12 @@ public static MetapathExpression compile(@NonNull String path) {
}
}

IExpression expr = new BuildCSTVisitor().visit(tree);
IExpression expr = new BuildCSTVisitor(context).visit(tree);

if (LOGGER.isDebugEnabled()) {
LOGGER.atDebug().log(String.format("Metapath CST:%n%s", CSTPrinter.toString(expr)));
}
retval = new MetapathExpression(path, expr);
retval = new MetapathExpression(path, expr, context);
} catch (MetapathException | ParseCancellationException ex) {
String msg = String.format("Unable to compile Metapath '%s'", path);
LOGGER.atError().withThrowable(ex).log(msg);
Expand All @@ -162,10 +183,13 @@ public static MetapathExpression compile(@NonNull String path) {
* the Metapath as a string
* @param expr
* the Metapath as a compiled abstract syntax tree (AST)
* @param staticContext
* the static evaluation context
*/
protected MetapathExpression(@NonNull String path, @NonNull IExpression expr) {
protected MetapathExpression(@NonNull String path, @NonNull IExpression expr, @NonNull StaticContext staticContext) {
this.path = path;
this.node = expr;
this.expression = expr;
this.staticContext = staticContext;
}

/**
Expand All @@ -184,7 +208,22 @@ public String getPath() {
*/
@NonNull
protected IExpression getASTNode() {
return node;
return expression;
}

@NonNull
protected StaticContext getStaticContext() {
return staticContext;
}

/**
* Generate a new dynamic context.
*
* @return the generated dynamic context
*/
@NonNull
public DynamicContext dynamicContext() {
return new DynamicContext(getStaticContext());
}

@Override
Expand Down Expand Up @@ -360,10 +399,7 @@ public <T extends IItem> ISequence<T> evaluate() {
@NonNull
public <T extends IItem> ISequence<T> evaluate(
@Nullable IItem focus) {
return (ISequence<T>) evaluate(
focus,
StaticContext.builder()
.build().dynamicContext());
return (ISequence<T>) evaluate(focus, dynamicContext());
}

/**
Expand Down
Loading

0 comments on commit c8ef0d7

Please sign in to comment.