Skip to content

Commit

Permalink
Added support for 'treat as' Metapath expressions. Refactored IItem a…
Browse files Browse the repository at this point in the history
…nd ICollectionValue types to have a tigher integration with the data type system. Added support for full signature generation for these implementations.
  • Loading branch information
david-waltermire committed Nov 26, 2024
1 parent 6028a1f commit 2168eb8
Show file tree
Hide file tree
Showing 91 changed files with 675 additions and 138 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:
# -------------------------
- name: Build and Test Website
run: |
mvn -B -e -Prelease install site site:stage -Dmaven.test.skip=true
mvn -B -e -PCI -Prelease install site site:stage -Dmaven.test.skip=true
- name: Zip Artifacts for Upload
run: |
zip ${{ runner.temp }}/website.zip -r target/staging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class DynamicMetapathException
* "/" or "//" in a path expression is an abbreviation for an initial step that
* includes the clause <code>treat as document-node()</code>.
*/
public static final int CONTEXT_NODE_NOT_A_DOCUMENT_NODE = 50;
public static final int TREAT_DOES_NOT_MATCH_TYPE = 50;

/**
* Constructs a new exception with the provided {@code code}, {@code message},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,12 @@ static Stream<? extends IItem> normalizeAsItems(@NonNull ICollectionValue value)
*/
@NonNull
Stream<? extends IItem> flatten();

/**
* Get a representation of the value based on its type signature.
*
* @return the signature
*/
@NonNull
String toSignature();
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* the Java type of the items in a sequence
*/
@SuppressWarnings("PMD.ShortMethodName")
public interface ISequence<ITEM extends IItem> extends List<ITEM>, IPrintable, ICollectionValue {
public interface ISequence<ITEM extends IItem> extends List<ITEM>, ICollectionValue {
/**
* Get an empty sequence.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,48 +173,18 @@ public static MetapathExpression compile(@NonNull String path, @NonNull StaticCo
retval = CONTEXT_NODE;
} else {
try {
Metapath10Lexer lexer = new Metapath10Lexer(CharStreams.fromString(path));
lexer.removeErrorListeners();
lexer.addErrorListener(new FailingErrorListener());

CommonTokenStream tokens = new CommonTokenStream(lexer);
Metapath10 parser = new Metapath10(tokens);
parser.removeErrorListeners();
parser.addErrorListener(new FailingErrorListener());
parser.setErrorHandler(new DefaultErrorStrategy() {

@Override
public void sync(Parser recognizer) {
// disable
}
});

Metapath10 parser = newParser(path);
ParseTree tree = ObjectUtils.notNull(parser.metapath());

if (LOGGER.isDebugEnabled()) {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
try (PrintStream ps = new PrintStream(os, true, StandardCharsets.UTF_8)) {
ParseTreePrinter printer = new ParseTreePrinter(ps);
printer.print(tree, Metapath10.ruleNames);
ps.flush();
}
LOGGER.atDebug().log(String.format("Metapath AST:%n%s", os.toString(StandardCharsets.UTF_8)));
} catch (IOException ex) {
LOGGER.atError().withThrowable(ex).log("An unexpected error occurred while closing the steam.");
}
}

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

if (LOGGER.isDebugEnabled()) {
LOGGER.atDebug().log(String.format("Metapath CST:%n%s", CSTPrinter.toString(expr)));
}
logCst(expr);
retval = new MetapathExpression(path, expr, context);
} catch (StaticMetapathException ex) {
String message = ex.getMessageText();
throw new StaticMetapathException(
ex.getCode(),
String.format("Unable to compile path '%s'.%s", path, message == null ? "" : " " + message));
String.format("Unable to compile path '%s'.%s", path, message == null ? "" : " " + message),
ex);
} catch (MetapathException | ParseCancellationException ex) {
String msg = String.format("Unable to compile Metapath '%s'", path);
LOGGER.atError().withThrowable(ex).log(msg);
Expand All @@ -224,6 +194,47 @@ public void sync(Parser recognizer) {
return retval;
}

private static void logCst(@NonNull IExpression expr) {
if (LOGGER.isDebugEnabled()) {
LOGGER.atDebug().log(String.format("Metapath CST:%n%s", CSTPrinter.toString(expr)));
}
}

private static void logAst(@NonNull ParseTree tree) {
if (LOGGER.isDebugEnabled()) {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
try (PrintStream ps = new PrintStream(os, true, StandardCharsets.UTF_8)) {
ParseTreePrinter printer = new ParseTreePrinter(ps);
printer.print(tree, Metapath10.ruleNames);
ps.flush();
}
LOGGER.atDebug().log(String.format("Metapath AST:%n%s", os.toString(StandardCharsets.UTF_8)));
} catch (IOException ex) {
LOGGER.atError().withThrowable(ex).log("An unexpected error occurred while closing the steam.");
}
}
}

@NonNull
private static Metapath10 newParser(@NonNull String path) {
Metapath10Lexer lexer = new Metapath10Lexer(CharStreams.fromString(path));
lexer.removeErrorListeners();
lexer.addErrorListener(new FailingErrorListener());

CommonTokenStream tokens = new CommonTokenStream(lexer);
Metapath10 parser = new Metapath10(tokens);
parser.removeErrorListeners();
parser.addErrorListener(new FailingErrorListener());
parser.setErrorHandler(new DefaultErrorStrategy() {

@Override
public void sync(Parser recognizer) {
// disable
}
});
return parser;
}

/**
* Construct a new Metapath expression.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
import gov.nist.secauto.metaschema.core.qname.NamespaceCache;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
import gov.nist.secauto.metaschema.core.util.CustomCollectors;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.net.URI;
Expand Down Expand Up @@ -70,13 +71,15 @@ public final class StaticContext {
.collect(Collectors.toUnmodifiableMap(
(Function<? super Entry<String, String>, ? extends String>) Entry::getValue,
Map.Entry::getKey,
(v1, v2) -> v2)));
(v1, v2) -> v1)));
}

@Nullable
private final URI baseUri;
@NonNull
private final Map<String, String> knownNamespaces;
private final Map<String, String> knownPrefixToNamespace;
@NonNull
private final Map<String, String> knownNamespacesToPrefix;
@Nullable
private final String defaultModelNamespace;
@Nullable
Expand Down Expand Up @@ -133,7 +136,13 @@ public static StaticContext instance() {

private StaticContext(Builder builder) {
this.baseUri = builder.baseUri;
this.knownNamespaces = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(Map.copyOf(builder.namespaces)));
this.knownPrefixToNamespace = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(Map.copyOf(builder.namespaces)));
this.knownNamespacesToPrefix = ObjectUtils.notNull(builder.namespaces.entrySet().stream()
.map(entry -> Map.entry(entry.getValue(), entry.getKey()))
.collect(Collectors.toUnmodifiableMap(
Map.Entry::getKey,
Map.Entry::getValue,
CustomCollectors.useFirstMapper())));
this.defaultModelNamespace = builder.defaultModelNamespace;
this.defaultFunctionNamespace = builder.defaultFunctionNamespace;
this.useWildcardWhenNamespaceNotDefaulted = builder.useWildcardWhenNamespaceNotDefaulted;
Expand Down Expand Up @@ -171,14 +180,24 @@ public URI getBaseUri() {
*/
@Nullable
private String lookupNamespaceURIForPrefix(@NonNull String prefix) {
String retval = knownNamespaces.get(prefix);
String retval = knownPrefixToNamespace.get(prefix);
if (retval == null) {
// fall back to well-known namespaces
retval = WELL_KNOWN_NAMESPACES.get(prefix);
}
return retval;
}

@Nullable
private String lookupPrefixForNamespaceURI(@NonNull String namespace) {
String retval = knownNamespacesToPrefix.get(namespace);
if (retval == null) {
// fall back to well-known namespaces
retval = WELL_KNOWN_URI_TO_PREFIX.get(namespace);
}
return retval;
}

/**
* Get the namespace associated with the provided {@code prefix} as a string, if
* any is bound.
Expand All @@ -194,6 +213,21 @@ public String lookupNamespaceForPrefix(@NonNull String prefix) {
return result == null ? null : result;
}

/**
* Get the prefix associated with the provided {@code namespace} as a string, if
* any is bound.
*
* @param namespace
* the namespace
* @return the prefix string bound to the prefix, or {@code null} if no prefix
* is bound to the namespace
*/
@Nullable
public String lookupPrefixForNamespace(@NonNull String namespace) {
String result = lookupPrefixForNamespaceURI(namespace);
return result == null ? XMLConstants.DEFAULT_NS_PREFIX : result;
}

/**
* Get the default namespace for assembly, field, or flag references that have
* no namespace prefix.
Expand Down Expand Up @@ -551,7 +585,7 @@ public IEnhancedQName parseVariableName(@NonNull String name) {
public Builder buildFrom() {
Builder builder = builder();
builder.baseUri = this.baseUri;
builder.namespaces.putAll(this.knownNamespaces);
builder.namespaces.putAll(this.knownPrefixToNamespace);
builder.defaultModelNamespace = this.defaultModelNamespace;
builder.defaultFunctionNamespace = this.defaultFunctionNamespace;
return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import gov.nist.secauto.metaschema.core.metapath.cst.type.Cast;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Castable;
import gov.nist.secauto.metaschema.core.metapath.cst.type.InstanceOf;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Treat;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
Expand Down Expand Up @@ -407,4 +408,9 @@ public RESULT visitCast(Cast expr, CONTEXT context) {
public RESULT visitCastable(Castable expr, CONTEXT context) {
return visitChildren(expr, context);
}

@Override
public RESULT visitTreat(Treat expr, CONTEXT context) {
return visitChildren(expr, context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import gov.nist.secauto.metaschema.core.metapath.cst.type.Cast;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Castable;
import gov.nist.secauto.metaschema.core.metapath.cst.type.InstanceOf;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Treat;
import gov.nist.secauto.metaschema.core.metapath.cst.type.TypeTestSupport;
import gov.nist.secauto.metaschema.core.metapath.function.ComparisonFunctions;
import gov.nist.secauto.metaschema.core.metapath.impl.AbstractKeySpecifier;
Expand Down Expand Up @@ -1121,7 +1122,13 @@ protected IExpression handleCastableexpr(Metapath10.CastableexprContext ctx) {

@Override
protected IExpression handleTreatexpr(Metapath10.TreatexprContext ctx) {
throw new UnsupportedOperationException("expression not supported");
IExpression left = visit(ctx.castableexpr());

ISequenceType sequenceType = TypeTestSupport.parseSequenceType(
ObjectUtils.notNull(ctx.sequencetype()),
getContext());

return new Treat(left, sequenceType);
}

// =========================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import gov.nist.secauto.metaschema.core.metapath.cst.type.Cast;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Castable;
import gov.nist.secauto.metaschema.core.metapath.cst.type.InstanceOf;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Treat;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
Expand Down Expand Up @@ -394,6 +395,11 @@ public String visitCast(Cast expr, State context) {
public String visitCastable(Castable expr, State context) {
return appendNode(expr, super.visitCastable(expr, context), context);
}

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

static class State {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import gov.nist.secauto.metaschema.core.metapath.cst.type.Cast;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Castable;
import gov.nist.secauto.metaschema.core.metapath.cst.type.InstanceOf;
import gov.nist.secauto.metaschema.core.metapath.cst.type.Treat;

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

Expand Down Expand Up @@ -626,4 +627,15 @@ public interface IExpressionVisitor<RESULT, CONTEXT> {
* @return the visitation result or {@code null} if no result was produced
*/
RESULT visitCastable(@NonNull Castable 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 visitTreat(@NonNull Treat expr, @NonNull CONTEXT context);
}
Loading

0 comments on commit 2168eb8

Please sign in to comment.