diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/AbstractCodedMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/AbstractCodedMetapathException.java deleted file mode 100644 index c55f6356c..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/AbstractCodedMetapathException.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.metapath; - -import edu.umd.cs.findbugs.annotations.Nullable; - -/** - * This Metapath exception base class is used for all exceptions that have a - * defined error code family and value. - */ -public abstract class AbstractCodedMetapathException - extends MetapathException { - - /** - * the serial version UID. - */ - private static final long serialVersionUID = 1L; - - /** - * The error code. - */ - private final int code; - - /** - * Constructs a new Metapath exception with the provided {@code code}, - * {@code message}, and no cause. - * - * @param code - * the error code value - * @param message - * the exception message - */ - public AbstractCodedMetapathException(int code, String message) { - super(message); - this.code = code; - } - - /** - * Constructs a new Metapath exception with the provided {@code code}, - * {@code message}, and {@code cause}. - * - * @param code - * the error code value - * @param message - * the exception message - * @param cause - * the original exception cause - */ - public AbstractCodedMetapathException(int code, String message, Throwable cause) { - super(message, cause); - this.code = code; - } - - /** - * Constructs a new Metapath exception with a {@code null} message and the - * provided {@code cause}. - * - * @param code - * the error code value - * @param cause - * the original exception cause - */ - public AbstractCodedMetapathException(int code, Throwable cause) { - super(cause); - this.code = code; - } - - @Override - public String getMessage() { - String message = getMessageText(); - return String.format("%s%s", getCodeAsString(), message == null ? "" : ": " + message); - } - - /** - * Get the message text without the error code prefix. - * - * @return the message text or {@code null} - */ - @Nullable - public String getMessageText() { - return super.getMessage(); - } - - /** - * Get the error code value. - * - * @return the error code value - */ - public int getCode() { - return code; - } - - /** - * Get the error code family. - * - * @return the error code family - */ - public abstract String getCodePrefix(); - - /** - * Get a combination of the error code family and value. - * - * @return the full error code. - */ - protected String getCodeAsString() { - return String.format("%s%04d", getCodePrefix(), getCode()); - } -} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ContextAbsentDynamicMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ContextAbsentDynamicMetapathException.java new file mode 100644 index 000000000..c5f80761d --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ContextAbsentDynamicMetapathException.java @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath; + +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * err:MPDY0002: It + * is a dynamic + * error if evaluation of an expression relies on some part of the + * dynamic + * context that is + * absent. + */ +public class ContextAbsentDynamicMetapathException + extends DynamicMetapathException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with the provided {@code message} and no cause. + * + * @param message + * the exception message + */ + public ContextAbsentDynamicMetapathException(@Nullable String message) { + super(DYNAMIC_CONTEXT_ABSENT, message); + } + + /** + * Constructs a new exception with the provided {@code message} and + * {@code cause}. + * + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public ContextAbsentDynamicMetapathException( + @Nullable String message, + @Nullable Throwable cause) { + super(DYNAMIC_CONTEXT_ABSENT, message, cause); + } + + /** + * Constructs a new exception with the provided {@code cause} and no message. + * + * @param cause + * the original exception cause + */ + public ContextAbsentDynamicMetapathException(@Nullable Throwable cause) { + super(DYNAMIC_CONTEXT_ABSENT, cause); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java index 1821d28dc..b8a3d734a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java @@ -19,7 +19,6 @@ import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.model.IUriResolver; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; -import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.io.IOException; @@ -31,11 +30,9 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -223,17 +220,15 @@ public Map getAvailableDocuments() { * Get the document loader assigned to this dynamic context. * * @return the loader - * @throws DynamicMetapathException - * with an error code - * {@link DynamicMetapathException#DYNAMIC_CONTEXT_ABSENT} if a - * document loader is not configured for this dynamic context + * @throws ContextAbsentDynamicMetapathException + * if a document loader is not configured for this dynamic context */ @NonNull public IDocumentLoader getDocumentLoader() { IDocumentLoader retval = sharedState.documentLoader; if (retval == null) { - throw new DynamicMetapathException(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, - "No document loader configured for the dynamic context."); + throw new UnsupportedOperationException( + "No document loader configured for the dynamic context. Use setDocumentLoader(loader) to confgure one."); } return retval; } @@ -324,7 +319,7 @@ public IConfiguration> getConfiguration() { * @param name * the variable qualified name * @return the non-null variable value - * @throws MetapathException + * @throws DynamicMetapathException * of the variable has not been assigned or if the variable value is * {@code null} */ @@ -332,12 +327,10 @@ public IConfiguration> getConfiguration() { public ISequence getVariableValue(@NonNull IEnhancedQName name) { ISequence retval = letVariableMap.get(name.getIndexPosition()); if (retval == null) { - if (letVariableMap.containsKey(name.getIndexPosition())) { - throw new MetapathException(String.format("Variable '%s' has null contents.", name)); - } throw new StaticMetapathException( StaticMetapathException.NOT_DEFINED, - String.format("Variable '%s' not defined in the dynamic context.", name)); + String.format("Variable '%s' not defined in the dynamic context.", name)) + .registerEvaluationContext(this); } return retval; } @@ -355,8 +348,8 @@ public ISequence getVariableValue(@NonNull IEnhancedQName name) { * a matching function was not found */ @NonNull - public IFunction getFunction(@NonNull IEnhancedQName name, int arity) { - return StaticContext.lookupFunction(name, arity); + public IFunction lookupFunction(@NonNull IEnhancedQName name, int arity) { + return getStaticContext().lookupFunction(name, arity); } /** @@ -403,8 +396,8 @@ public void popExecutionStack(@NonNull IExpression expression) { * @return the execution stack */ @NonNull - public List getExecutionStack() { - return CollectionUtil.unmodifiableList(new ArrayList<>(this.sharedState.executionStack)); + public Deque getExecutionStack() { + return new ArrayDeque<>(this.sharedState.executionStack); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java index 95d8512b7..881055430 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java @@ -5,12 +5,17 @@ package gov.nist.secauto.metaschema.core.metapath; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + /** * MPDY: Exceptions related to the Metapath dynamic context and dynamic * evaluation. */ public class DynamicMetapathException - extends AbstractCodedMetapathException { + extends MetapathException { + @NonNull + private static final String PREFIX = "MPDY"; /** * the serial version UID. @@ -25,7 +30,7 @@ public class DynamicMetapathException * context that is * absent. */ - public static final int DYNAMIC_CONTEXT_ABSENT = 2; + protected static final int DYNAMIC_CONTEXT_ABSENT = 2; /** * err:MPDY0050: It @@ -40,7 +45,7 @@ public class DynamicMetapathException * "/" or "//" in a path expression is an abbreviation for an initial step that * includes the clause treat as document-node(). */ - public static final int TREAT_DOES_NOT_MATCH_TYPE = 50; + protected static final int TREAT_DOES_NOT_MATCH_TYPE = 50; /** * Constructs a new exception with the provided {@code code}, {@code message}, @@ -51,8 +56,10 @@ public class DynamicMetapathException * @param message * the exception message */ - public DynamicMetapathException(int code, String message) { - super(code, message); + public DynamicMetapathException( + int code, + @Nullable String message) { + super(IErrorCode.of(PREFIX, code), message); } /** @@ -66,8 +73,11 @@ public DynamicMetapathException(int code, String message) { * @param cause * the original exception cause */ - public DynamicMetapathException(int code, String message, Throwable cause) { - super(code, message, cause); + public DynamicMetapathException( + int code, + @Nullable String message, + @Nullable Throwable cause) { + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -79,12 +89,9 @@ public DynamicMetapathException(int code, String message, Throwable cause) { * @param cause * the original exception cause */ - public DynamicMetapathException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "MPDY"; + public DynamicMetapathException( + int code, + @Nullable Throwable cause) { + super(IErrorCode.of(PREFIX, code), cause); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IErrorCode.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IErrorCode.java new file mode 100644 index 000000000..cee843679 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IErrorCode.java @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath; + +import gov.nist.secauto.metaschema.core.metapath.impl.ErrorCodeImpl; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Provides an error code that identifies the type of message. + *

+ * Implementations of this interface are expected to be immutable. + */ +public interface IErrorCode { + @SuppressWarnings("PMD.ShortMethodName") + @NonNull + static IErrorCode of(@NonNull String prefix, int code) { + return new ErrorCodeImpl(prefix, code); + } + + /** + * Get the error code prefix, which indicates what type of error it is. + * + * @return the error code prefix + */ + @NonNull + String getPrefix(); + + /** + * Get the error code value. + * + * @return the error code value + */ + int getCode(); + + /** + * Get a combination of the error code family and value. + * + * @return the full error code. + */ + @NonNull + default String getCodeAsString() { + return ObjectUtils.notNull(String.format("%s%04d", getPrefix(), getCode())); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IExpression.java index da67b4365..2bf1f6f1a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IExpression.java @@ -30,7 +30,7 @@ public interface IExpression { * @return the expression text */ @NonNull - String getText(); + String getPath(); /** * Retrieve the child expressions associated with this expression. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java index 14329df84..8dfa5ce69 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java @@ -5,10 +5,10 @@ package gov.nist.secauto.metaschema.core.metapath; -import gov.nist.secauto.metaschema.core.metapath.MetapathExpression.ConversionFunction; import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.library.FnBoolean; import gov.nist.secauto.metaschema.core.metapath.impl.LazyCompilationMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.impl.MetapathExpression; 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; @@ -25,7 +25,7 @@ /** * Supports compiling and executing Metapath expressions. */ -public interface IMetapathExpression { +public interface IMetapathExpression extends IExpression { /** * Identifies the expected type for a Metapath evaluation result. @@ -86,18 +86,27 @@ public Class expectedClass() { * if the provided sequence is incompatible with the expected result * type */ + // TODO: trace exceptions thrown to ensure the Javadoc matches; should be static + // type error @Nullable public T convert(@NonNull ISequence sequence) { try { return ObjectUtils.asNullableType(converter.convert(sequence)); } catch (ClassCastException ex) { - throw new InvalidTypeMetapathException(null, - String.format("Unable to cast to expected result type '%s' using expected type '%s'.", - name(), - expectedClass().getName()), + throw new InvalidTypeMetapathException( + null, + String.format("Unable to cast type '%s' to expected result type '%s'.", + expectedClass().getName(), + name()), ex); } } + + @FunctionalInterface + interface ConversionFunction { + @Nullable + Object convert(@NonNull ISequence sequence); + } } /** @@ -107,7 +116,7 @@ public T convert(@NonNull ISequence sequence) { */ @NonNull static IMetapathExpression contextNode() { - return MetapathExpression.CONTEXT_NODE; + return MetapathExpression.CONTEXT_METAPATH; } /** @@ -116,12 +125,12 @@ static IMetapathExpression contextNode() { * @param path * the metapath expression * @return the compiled expression object - * @throws MetapathException + * @throws InvalidMetapathGrammarException * if an error occurred while compiling the Metapath expression */ @NonNull static IMetapathExpression compile(@NonNull String path) { - return MetapathExpression.compile(path, StaticContext.instance()); + return compile(path, StaticContext.instance()); } /** @@ -132,7 +141,7 @@ static IMetapathExpression compile(@NonNull String path) { * @param staticContext * the static evaluation context * @return the compiled expression object - * @throws MetapathException + * @throws InvalidMetapathGrammarException * if an error occurred while compiling the Metapath expression */ @NonNull @@ -162,6 +171,7 @@ static IMetapathExpression lazyCompile(@NonNull String path, @NonNull StaticCont * * @return the expression */ + @Override @NonNull String getPath(); @@ -308,4 +318,5 @@ default ISequence evaluate( ISequence evaluate( @Nullable IItem focus, @NonNull DynamicContext dynamicContext); + } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidMetapathGrammarException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidMetapathGrammarException.java new file mode 100644 index 000000000..029d18a9e --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidMetapathGrammarException.java @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath; + +/** + * An exception to be raised when a Metapath is not a valid instance of the + * Metapath grammar. + *

+ * This exception is associated with the + * {@link StaticMetapathException#INVALID_PATH_GRAMMAR} error code. + */ +public class InvalidMetapathGrammarException + extends StaticMetapathException { + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 2L; + + /** + * Constructs a new exception with the provided {@code message} and + * {@code cause}. + * + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public InvalidMetapathGrammarException(String message, Throwable cause) { + super(INVALID_PATH_GRAMMAR, message, cause); + } + + /** + * Constructs a new exception with the provided {@code message} and no cause. + * + * @param message + * the exception message + */ + public InvalidMetapathGrammarException(String message) { + super(INVALID_PATH_GRAMMAR, message); + } + + /** + * Constructs a new exception with no message and the provided {@code cause}. + * + * @param cause + * the original exception cause + */ + public InvalidMetapathGrammarException(Throwable cause) { + super(INVALID_PATH_GRAMMAR, cause); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidTreatTypeDynamicMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidTreatTypeDynamicMetapathException.java new file mode 100644 index 000000000..01c15be30 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidTreatTypeDynamicMetapathException.java @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath; + +import java.util.Deque; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public class InvalidTreatTypeDynamicMetapathException + extends DynamicMetapathException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with the provided {@code message} and no cause. + * + * @param message + * the exception message + */ + public InvalidTreatTypeDynamicMetapathException( + @NonNull Deque evaluationStack, + @Nullable String message) { + super(TREAT_DOES_NOT_MATCH_TYPE, message); + } + + /** + * Constructs a new exception with the provided {@code message} and + * {@code cause}. + * + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public InvalidTreatTypeDynamicMetapathException( + @Nullable String message, + @Nullable Throwable cause) { + super(TREAT_DOES_NOT_MATCH_TYPE, message, cause); + } + + /** + * Constructs a new exception with the provided {@code cause} and no message. + * + * @param cause + * the original exception cause + */ + public InvalidTreatTypeDynamicMetapathException( + @Nullable Throwable cause) { + super(TREAT_DOES_NOT_MATCH_TYPE, cause); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathException.java index 78c8ad6b1..6f211ee0c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathException.java @@ -5,57 +5,156 @@ package gov.nist.secauto.metaschema.core.metapath; +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + /** * {@code MetapathException} is the superclass of all exceptions that can be * thrown during the compilation and evaluation of a Metapath. */ public class MetapathException extends RuntimeException { - /** * the serial version UID. */ private static final long serialVersionUID = 1L; - /** - * Constructs a new Metapath exception with a {@code null} message and no cause. + * The error prefix which identifies what kind of error it is. */ - public MetapathException() { - // no message - } + @NonNull + private final IErrorCode errorCode; + + @Nullable + private Deque evaluationStack = null; /** - * Constructs a new Metapath exception with the provided {@code message} and no - * cause. + * Constructs a new Metapath exception with the provided {@code code} and + * {@code message} and no cause. * + * @param errorCode + * the error code that identifies the type of error * @param message * the exception message */ - public MetapathException(String message) { + protected MetapathException( + @NonNull IErrorCode errorCode, + @Nullable String message) { super(message); + this.errorCode = errorCode; } /** * Constructs a new Metapath exception with a {@code null} message and the - * provided {@code cause}. + * provided {@code code} and {@code cause}. * + * @param errorCode + * the error code that identifies the type of error * @param cause * the exception cause */ - public MetapathException(Throwable cause) { + protected MetapathException( + @NonNull IErrorCode errorCode, + @Nullable Throwable cause) { super(cause); + this.errorCode = errorCode; } /** - * Constructs a new Metapath exception with the provided {@code message} and - * {@code cause}. + * Constructs a new Metapath exception with the provided {@code code}, + * {@code message} and {@code cause}. * + * @param errorCode + * the error code that identifies the type of error * @param message * the exception message * @param cause * the exception cause */ - public MetapathException(String message, Throwable cause) { + protected MetapathException( + @NonNull IErrorCode errorCode, + @Nullable String message, + @Nullable Throwable cause) { super(message, cause); + this.errorCode = errorCode; + } + + public MetapathException registerEvaluationContext(@NonNull DynamicContext dynamicContext) { + if (evaluationStack == null) { + evaluationStack = dynamicContext.getExecutionStack(); + } + return this; + } + + public MetapathException registerEvaluationContext(@NonNull IMetapathExpression metapath) { + if (evaluationStack == null) { + evaluationStack = new ArrayDeque<>(Collections.singleton(metapath)); + } + return this; + } + + @Nullable + protected Deque getEvaluationStack() { + return evaluationStack; + } + + @Override + public final String getMessage() { + String message = getMessageText(); + return String.format( + "%s%s", + getErrorCode().toString(), + message == null ? "" : ": " + message); + } + + /** + * Get the message text without the error code prefix. + * + * @return the message text or {@code null} + */ + @Nullable + public String getMessageText() { + Deque evaluationStack = getEvaluationStack(); + + // assert evaluationStack != null + // && !evaluationStack.isEmpty() : "The evaluation stack must contain at least + // one entry"; + return super.getMessage(); + + // TODO: get function call context + // evaluationStack.stream() + // .filter(null) + // .findFirst(); + + // new FunctionMetapathError( + // dynamicContext.getExecutionStack(), + // ex.getErrorCode(), + // String.format("Unable to execute function '%s'. %s", + // toSignature(), + // ex.getLocalizedMessage()), + // ex); + + // TODO: get expression context, which should be the head + + // IMetapathExpression head = (IMetapathExpression) + // getEvaluationStack().peekLast(); + // return String.format( + // "An error occurred while evaluating the expression '%s'%s", + // head.getPath(), + // message == null ? "" : ": " + message); } + + /** + * Get the error code, which indicates what type of error it is. + * + * @return the error code + */ + @NonNull + public final IErrorCode getErrorCode() { + return errorCode; + } + } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java index e8eb9250b..bed824d6a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java @@ -24,7 +24,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Map; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -455,7 +454,7 @@ private static void checkForUnknownPrefix(@NonNull String prefix) { @NonNull public IFunction lookupFunction(@NonNull String name, int arity) { IEnhancedQName qname = parseFunctionName(name); - return functionResolver.getFunction(qname, arity); + return lookupFunction(qname, arity); } /** @@ -471,10 +470,8 @@ public IFunction lookupFunction(@NonNull String name, int arity) { * a matching function was not found */ @NonNull - public static IFunction lookupFunction(@NonNull IEnhancedQName qname, int arity) { - return FunctionService.getInstance().getFunction( - Objects.requireNonNull(qname, "name"), - arity); + public IFunction lookupFunction(@NonNull IEnhancedQName qname, int arity) { + return functionResolver.getFunction(qname, arity); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java index 8e911952e..4c08bc7d9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java @@ -5,20 +5,29 @@ package gov.nist.secauto.metaschema.core.metapath; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + /** * MPST: Exceptions related to the Metapath static context and static * evaluation. */ +// TODO: review use of this exception +// TODO: include a reference to the Metapath expression? +// TODO: Move this to a StaticMetapathException that extends this class; align +// signatures with DynamicMetapathError @SuppressWarnings("PMD.DataClass") public class StaticMetapathException - extends AbstractCodedMetapathException { + extends MetapathException { + @NonNull + private static final String PREFIX = "MPST"; /** * err:MPST0003: It * is a static * error if an expression is not a valid instance of the Metapath grammar. */ // TODO: need a Metapath grammar link - public static final int INVALID_PATH_GRAMMAR = 3; + protected static final int INVALID_PATH_GRAMMAR = 3; /** * err:MPST0008: It @@ -132,8 +141,11 @@ public class StaticMetapathException * @param cause * the original exception cause */ - public StaticMetapathException(int code, String message, Throwable cause) { - super(code, message, cause); + public StaticMetapathException( + int code, + @Nullable String message, + @Nullable Throwable cause) { + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -145,8 +157,10 @@ public StaticMetapathException(int code, String message, Throwable cause) { * @param message * the exception message */ - public StaticMetapathException(int code, String message) { - super(code, message); + public StaticMetapathException( + int code, + @Nullable String message) { + super(IErrorCode.of(PREFIX, code), message, null); } /** @@ -158,13 +172,9 @@ public StaticMetapathException(int code, String message) { * @param cause * the original exception cause */ - public StaticMetapathException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "MPST"; + public StaticMetapathException( + int code, + @Nullable Throwable cause) { + super(IErrorCode.of(PREFIX, code), null, cause); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java index cca59fd3b..3b9e1216c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java @@ -31,7 +31,7 @@ public AbstractExpression(@NonNull String text) { } @Override - public String getText() { + public String getPath() { return text; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java index 7244db40b..7ea611990 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java @@ -6,6 +6,7 @@ package gov.nist.secauto.metaschema.core.metapath.cst; import gov.nist.secauto.metaschema.core.metapath.IExpression; +import gov.nist.secauto.metaschema.core.metapath.InvalidMetapathGrammarException; import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10; @@ -63,9 +64,9 @@ 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.function.IArgument; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractKeySpecifier; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IKeySpecifier; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractKeySpecifier; import gov.nist.secauto.metaschema.core.metapath.type.IAtomicOrUnionType; import gov.nist.secauto.metaschema.core.metapath.type.IItemType; import gov.nist.secauto.metaschema.core.metapath.type.ISequenceType; @@ -1172,8 +1173,11 @@ private IAtomicOrUnionType getTypeForCast(@NonNull String name) { try { type = getContext().lookupAtomicType(name); } catch (StaticMetapathException ex) { - if (StaticMetapathException.UNKNOWN_TYPE == ex.getCode()) { - throw new StaticMetapathException(StaticMetapathException.CAST_UNKNOWN_TYPE, ex); + if (StaticMetapathException.UNKNOWN_TYPE == ex.getErrorCode().getCode()) { + throw new StaticMetapathException( + StaticMetapathException.CAST_UNKNOWN_TYPE, + String.format("Unknown type '%s'.", name), + ex); } throw ex; } @@ -1275,8 +1279,8 @@ protected IExpression handleArrowexpr(Metapath10.ArrowexprContext context) { // function expression result = visit(arrowCtx.parenthesizedexpr().expr()); } else { - throw new StaticMetapathException( - StaticMetapathException.INVALID_PATH_GRAMMAR, + // TODO: Is this the correct exception to throw here? + throw new InvalidMetapathGrammarException( String.format("Unable to get function name using arrow specifier '%s'.", arrowCtx.getText())); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java index a13630fbb..b89bcccbf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java @@ -81,7 +81,10 @@ protected ISequence evaluate(DynamicContext dynamicContext, ISequence focu IItem collection = target.getFirstItem(true); if (!(collection instanceof IFunction)) { - throw new InvalidTypeMetapathException(collection, "The base expression did not evaluate to a function."); + throw new InvalidTypeMetapathException( + collection, + "The base expression did not evaluate to a function.") + .registerEvaluationContext(dynamicContext); } return ((IFunction) collection).execute(ObjectUtils.notNull(getArguments().stream() diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/NamedFunctionReference.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/NamedFunctionReference.java index acc6f2f0f..9056f9cf4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/NamedFunctionReference.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/NamedFunctionReference.java @@ -82,7 +82,7 @@ public RESULT accept(IExpressionVisitor visit @Override protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { - IFunction function = dynamicContext.getFunction(name, arity); + IFunction function = dynamicContext.lookupFunction(name, arity); return ISequence.of(function); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/items/MapConstructor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/items/MapConstructor.java index e080af521..b8253e5cf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/items/MapConstructor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/items/MapConstructor.java @@ -60,8 +60,11 @@ protected ISequence evaluate(DynamicContext dynamicContext, ISequence focu IAnyAtomicItem key = ISequence.of(keyExpression.accept(dynamicContext, focus).atomize()) .getFirstItem(true); if (key == null) { - throw new InvalidTypeMetapathException(null, String.format( - "The expression '%s' did not result in a single key atomic value.", keyExpression.toCSTString())); + throw new InvalidTypeMetapathException( + null, + String.format("The expression '%s' did not result in a single key atomic value.", + keyExpression.toCSTString())) + .registerEvaluationContext(dynamicContext); } ICollectionValue value = item.getValueExpression().accept(dynamicContext, focus).toCollectionValue(); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/math/Multiplication.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/math/Multiplication.java index f66648b6d..b11d01665 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/math/Multiplication.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/math/Multiplication.java @@ -138,6 +138,7 @@ Map, OperationStrategy>> generateStrategies() { * @throws InvalidTypeMetapathException * for unsupported operand combinations. */ + // TODO: Is this used? @SuppressWarnings("PMD.CyclomaticComplexity") @NonNull public static IAnyAtomicItem multiply( diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractSearchPathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractSearchPathExpression.java index 2a29bccac..c551e04b6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractSearchPathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractSearchPathExpression.java @@ -78,7 +78,7 @@ protected Stream search( = (Stream) expression.accept(dynamicContext, focus).stream(); Stream childMatches = focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep) + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item)) .flatMap(focusedNode -> { Stream matches; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractStepExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractStepExpression.java index f8de3891f..1d701aea2 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractStepExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractStepExpression.java @@ -67,7 +67,7 @@ protected ISequence evaluate( DynamicContext dynamicContext, ISequence focus) { return ISequence.of(ObjectUtils.notNull(focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep) + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item)) .flatMap(item -> { assert item != null; return match(item); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ContextItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ContextItem.java index 7afb5cbdc..93551a2d1 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ContextItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ContextItem.java @@ -5,8 +5,8 @@ package gov.nist.secauto.metaschema.core.metapath.cst.path; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.IExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; @@ -68,7 +68,8 @@ public RESULT accept(IExpressionVisitor visit @Override protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { if (focus.isEmpty()) { - throw new DynamicMetapathException(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, "The context is empty"); + throw new ContextAbsentDynamicMetapathException("The context item is empty") + .registerEvaluationContext(dynamicContext); } return focus; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java index 04c871097..fb96519db 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java @@ -50,7 +50,7 @@ public RESULT accept(IExpressionVisitor visit @Override protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { return ISequence.of(filterStream(ObjectUtils.notNull(focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep)))); + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item))))); } @SuppressWarnings("null") diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/NameNodeTest.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/NameNodeTest.java index dac3654ea..feb2e847a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/NameNodeTest.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/NameNodeTest.java @@ -63,7 +63,7 @@ public RESULT accept(IExpressionVisitor visit @Override protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { Stream nodes = ObjectUtils.notNull(focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep)); + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item))); return ISequence.of(filterStream(nodes)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootDoubleSlashPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootDoubleSlashPath.java index fe9ee3d73..3975f03bc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootDoubleSlashPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootDoubleSlashPath.java @@ -9,6 +9,7 @@ import gov.nist.secauto.metaschema.core.metapath.IExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; +import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; import edu.umd.cs.findbugs.annotations.NonNull; @@ -42,7 +43,10 @@ public RESULT accept(IExpressionVisitor visit } @Override - protected ISequence evaluate(DynamicContext dynamicContext, ISequence context) { - return ISequence.of(search(getExpression(), dynamicContext, context)); + protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { + return ISequence.of(search( + getExpression(), + dynamicContext, + ItemUtils.getDocumentNodeItems(dynamicContext, focus))); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java index 06aa685ed..e347213b6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java @@ -6,15 +6,12 @@ package gov.nist.secauto.metaschema.core.metapath.cst.path; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.IExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; -import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.util.CollectionUtil; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -61,23 +58,6 @@ public RESULT accept(IExpressionVisitor visit @Override protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { - return ISequence.of(ObjectUtils.notNull(focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep) - .map(RootSlashOnlyPath::findAndValidateDocumentRoot))); - } - - private static INodeItem findAndValidateDocumentRoot(INodeItem item) { - INodeItem root = Axis.ANCESTOR_OR_SELF.execute(ObjectUtils.notNull(item)) - .findFirst() - .orElseThrow(() -> new DynamicMetapathException( - DynamicMetapathException.TREAT_DOES_NOT_MATCH_TYPE, - "Root node not found")); - - if (!(root instanceof IDocumentNodeItem)) { - throw new DynamicMetapathException( - DynamicMetapathException.TREAT_DOES_NOT_MATCH_TYPE, - "The head of the tree is not a document node."); - } - return root; + return ItemUtils.getDocumentNodeItems(dynamicContext, focus); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java index 3106093bb..762dedc09 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java @@ -47,7 +47,7 @@ public RESULT accept(IExpressionVisitor visit @Override protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { ISequence roots = ObjectUtils.notNull(focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep) + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item)) // the previous checks for a null instance .flatMap(item -> Axis.ANCESTOR_OR_SELF.execute(ObjectUtils.notNull(item)).limit(1)) .collect(CustomCollectors.toSequence())); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java index d43580596..c3e918bed 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java @@ -115,7 +115,7 @@ protected ISequence evaluate(DynamicContext dynamicContext, ISequence focu axisResult = ISequence.empty(); } else { axisResult = ISequence.of(ObjectUtils.notNull(focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep) + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item)) .flatMap(item -> { assert item != null; return axis.execute(item); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java index 109d484a6..730430aef 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java @@ -53,7 +53,7 @@ public RESULT accept(IExpressionVisitor visit @Override protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { Stream stream = focus.stream() - .map(ItemUtils::checkItemIsNodeItemForStep); + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item)); if (matcher != null) { stream = stream.filter(this::match); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/type/Treat.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/type/Treat.java index 05dfe3e9b..9047abbcb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/type/Treat.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/type/Treat.java @@ -6,8 +6,8 @@ package gov.nist.secauto.metaschema.core.metapath.cst.type; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.IExpression; +import gov.nist.secauto.metaschema.core.metapath.InvalidTreatTypeDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.cst.AbstractExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; import gov.nist.secauto.metaschema.core.metapath.item.IItem; @@ -64,8 +64,8 @@ public List getChildren() { protected ISequence evaluate(DynamicContext dynamicContext, ISequence focus) { ISequence retval = value.accept(dynamicContext, focus); if (!type.matches(retval)) { - throw new DynamicMetapathException( - DynamicMetapathException.TREAT_DOES_NOT_MATCH_TYPE, + throw new InvalidTreatTypeDynamicMetapathException( + dynamicContext.getExecutionStack(), String.format("The sequence '%s' does not match the sequence type '%s'.", retval, type.toSignature())); } return retval; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java index 8c50a75fa..3d7336f04 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java @@ -5,13 +5,17 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; + +import edu.umd.cs.findbugs.annotations.NonNull; /** * Represents an error that occurred while performing mathematical operations. */ public class ArithmeticFunctionException - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FOAR"; /** * err:FOAR0001: @@ -46,7 +50,7 @@ public class ArithmeticFunctionException * the exception message */ public ArithmeticFunctionException(int code, String message) { - super(code, message); + super(IErrorCode.of(PREFIX, code), message); } /** @@ -61,7 +65,7 @@ public ArithmeticFunctionException(int code, String message) { * the original exception cause */ public ArithmeticFunctionException(int code, String message, Throwable cause) { - super(code, message, cause); + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -74,12 +78,6 @@ public ArithmeticFunctionException(int code, String message, Throwable cause) { * the original exception cause */ public ArithmeticFunctionException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "FOAR"; + super(IErrorCode.of(PREFIX, code), cause); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/CastFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/CastFunctionException.java index 85d7ec943..b4c092642 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/CastFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/CastFunctionException.java @@ -5,7 +5,7 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import edu.umd.cs.findbugs.annotations.NonNull; @@ -14,7 +14,9 @@ * FOCA: Exceptions related to type casting. */ public class CastFunctionException - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FOCA"; /** * err:FOCA0002: @@ -52,7 +54,7 @@ public class CastFunctionException * the exception message text */ public CastFunctionException(int code, @NonNull IAnyAtomicItem item, String message) { - super(code, message); + super(IErrorCode.of(PREFIX, code), message); this.item = item; } @@ -70,7 +72,7 @@ public CastFunctionException(int code, @NonNull IAnyAtomicItem item, String mess * the original exception cause */ public CastFunctionException(int code, @NonNull IAnyAtomicItem item, String message, Throwable cause) { - super(code, message, cause); + super(IErrorCode.of(PREFIX, code), message, cause); this.item = item; } @@ -83,9 +85,4 @@ public CastFunctionException(int code, @NonNull IAnyAtomicItem item, String mess public IAnyAtomicItem getItem() { return item; } - - @Override - public String getCodePrefix() { - return "FOCA"; - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java index 0b99a50b7..a39b9a4b9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java @@ -5,13 +5,17 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; + +import edu.umd.cs.findbugs.annotations.NonNull; /** * FODT: Exceptions related to Date/Time/Duration errors. */ public class DateTimeFunctionException - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FODT"; /** * err:FODT0001: @@ -49,7 +53,7 @@ public class DateTimeFunctionException * the exception message */ public DateTimeFunctionException(int code, String message) { - super(code, message); + super(IErrorCode.of(PREFIX, code), message); } /** @@ -64,7 +68,7 @@ public DateTimeFunctionException(int code, String message) { * the original exception cause */ public DateTimeFunctionException(int code, String message, Throwable cause) { - super(code, message, cause); + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -77,12 +81,6 @@ public DateTimeFunctionException(int code, String message, Throwable cause) { * the original exception cause */ public DateTimeFunctionException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "FODT"; + super(IErrorCode.of(PREFIX, code), cause); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java index 1875cd071..c473d47b2 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java @@ -5,14 +5,18 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; import gov.nist.secauto.metaschema.core.metapath.function.library.FnDoc; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * FODC: Exceptions representing document related errors. */ public class DocumentFunctionException - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FODC"; /** * err:FODC0002: @@ -51,7 +55,7 @@ public class DocumentFunctionException * the exception message */ public DocumentFunctionException(int code, String message) { - super(code, message); + super(IErrorCode.of(PREFIX, code), message); } /** @@ -66,7 +70,7 @@ public DocumentFunctionException(int code, String message) { * the original exception cause */ public DocumentFunctionException(int code, String message, Throwable cause) { - super(code, message, cause); + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -79,12 +83,6 @@ public DocumentFunctionException(int code, String message, Throwable cause) { * the original exception cause */ public DocumentFunctionException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "FODC"; + super(IErrorCode.of(PREFIX, code), cause); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionMetapathError.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionMetapathError.java new file mode 100644 index 000000000..afbb40cc7 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionMetapathError.java @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.function; + +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; +import gov.nist.secauto.metaschema.core.metapath.MetapathException; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +// TODO: remove this intermediate exception? +public class FunctionMetapathError + extends MetapathException { + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new Metapath exception with the provided {@code code}, + * {@code message}, and no cause. + * + * @param errorCode + * the error code that identifies the type of error + * @param message + * the exception message + */ + public FunctionMetapathError( + @NonNull IErrorCode errorCode, + @Nullable String message) { + super(errorCode, message, null); + } + + /** + * Constructs a new Metapath exception with the provided {@code code}, + * {@code message}, and {@code cause}. + * + * @param errorCode + * the error code that identifies the type of error + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public FunctionMetapathError( + @NonNull IErrorCode errorCode, + @Nullable String message, + @Nullable Throwable cause) { + super(errorCode, message, cause); + } + + /** + * Constructs a new Metapath exception with a {@code null} message and the + * provided {@code cause}. + * + * @param errorCode + * the error code that identifies the type of error + * @param cause + * the original exception cause + */ + public FunctionMetapathError( + @NonNull IErrorCode errorCode, + @Nullable Throwable cause) { + super(errorCode, null, cause); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java index 487249e26..78cc9ff76 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java @@ -55,6 +55,7 @@ private FunctionUtils() { * cast to a numeric value * */ + @Deprecated(since = "3.0.0", forRemoval = true) @Nullable public static INumericItem toNumeric(@NonNull ISequence sequence, boolean requireSingleton) { IItem item = sequence.getFirstItem(requireSingleton); @@ -71,6 +72,7 @@ public static INumericItem toNumeric(@NonNull ISequence sequence, boolean req * if the sequence contains more than one item, or the item cannot be * cast to a numeric value */ + @Deprecated(since = "3.0.0", forRemoval = true) @NonNull public static INumericItem toNumeric(@NonNull IItem item) { // atomize @@ -90,6 +92,7 @@ public static INumericItem toNumeric(@NonNull IItem item) { * @throws TypeMetapathException * if the item cannot be cast to a numeric value */ + @Deprecated(since = "3.0.0", forRemoval = true) @NonNull public static INumericItem toNumeric(@NonNull IAnyAtomicItem item) { try { @@ -127,6 +130,7 @@ public static INumericItem toNumericOrNull(@Nullable IAnyAtomicItem item) { * @throws ClassCastException * if the item's type is not compatible with the requested type */ + @Deprecated(since = "3.0.0", forRemoval = true) @SuppressWarnings("unchecked") @Nullable public static TYPE asTypeOrNull(@Nullable IItem item) { @@ -144,6 +148,7 @@ public static TYPE asTypeOrNull(@Nullable IItem item) { * @throws ClassCastException * if the item's type is not compatible with the requested type */ + @Deprecated(since = "3.0.0", forRemoval = true) @SuppressWarnings("unchecked") @NonNull public static TYPE asType(@NonNull IItem item) { @@ -161,6 +166,7 @@ public static TYPE asType(@NonNull IItem item) { * @throws ClassCastException * if the sequence's type is not compatible with the requested type */ + @Deprecated(since = "3.0.0", forRemoval = true) @SuppressWarnings("unchecked") @NonNull public static ISequence asType(@NonNull ISequence sequence) { @@ -181,6 +187,7 @@ public static ISequence asType(@NonNull ISequence * if the provided item is {@code null} or if the item's type is not * assignment compatible to the requested type */ + @Deprecated(since = "3.0.0", forRemoval = true) @NonNull public static TYPE requireType(Class clazz, IItem item) { if (item == null) { @@ -215,6 +222,7 @@ public static TYPE requireType(Class clazz, IItem ite * if the provided item is {@code null} or if the item's type is not * assignment compatible to the requested type */ + @Deprecated(since = "3.0.0", forRemoval = true) @Nullable public static TYPE requireTypeOrNull(Class clazz, @Nullable IItem item) { if (item == null || clazz.isInstance(item)) { @@ -234,6 +242,7 @@ public static TYPE requireTypeOrNull(Class clazz, @Nu * the Metapath items to get the data types for * @return a stream of data type classes */ + @Deprecated(since = "3.0.0", forRemoval = true) @NonNull public static Stream> getTypes(@NonNull Stream items) { return ObjectUtils.notNull(items.map(Object::getClass)); @@ -249,6 +258,7 @@ public static Stream> getTypes(@NonNull Stream items) * the items to get Java type class for * @return a list of corresponding Java type classes for the provided items */ + @Deprecated(since = "3.0.0", forRemoval = true) @SuppressWarnings("unchecked") @NonNull public static List> getTypes(@NonNull List items) { @@ -269,6 +279,7 @@ public static List> getTypes(@NonNull List< * the Metapath items to analyze * @return a mapping of Metapath item class to count */ + @Deprecated(since = "3.0.0", forRemoval = true) @NonNull public static Map, Integer> countTypes( @NonNull Set> classes, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java index 35d2db9fd..6d3d02d65 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java @@ -5,13 +5,17 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; + +import edu.umd.cs.findbugs.annotations.NonNull; /** * FORG: Exceptions related to argument types. */ public class InvalidArgumentFunctionException - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FORG"; /** * err:FOTY0012: @@ -61,7 +63,7 @@ public class InvalidTypeFunctionException * the item the exception applies to */ public InvalidTypeFunctionException(int code, @NonNull IItem item) { - super(code, generateMessage(item)); + super(IErrorCode.of(PREFIX, code), generateMessage(item)); } /** @@ -76,7 +78,7 @@ public InvalidTypeFunctionException(int code, @NonNull IItem item) { * the original exception cause */ public InvalidTypeFunctionException(int code, @NonNull IItem item, Throwable cause) { - super(code, generateMessage(item), cause); + super(IErrorCode.of(PREFIX, code), generateMessage(item), cause); } private static String generateMessage(@NonNull IItem item) { @@ -91,9 +93,4 @@ private static String generateMessage(@NonNull IItem item) { } return retval; } - - @Override - public String getCodePrefix() { - return "FOTY"; - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/JsonFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/JsonFunctionException.java index 240e36098..3f9481ba0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/JsonFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/JsonFunctionException.java @@ -5,10 +5,14 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; + +import edu.umd.cs.findbugs.annotations.NonNull; public class JsonFunctionException - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FOJS"; /** * err:FOJS0003: @@ -39,7 +43,7 @@ public class JsonFunctionException * the exception message */ public JsonFunctionException(int code, String message) { - super(code, message); + super(IErrorCode.of(PREFIX, code), message); } /** @@ -54,7 +58,7 @@ public JsonFunctionException(int code, String message) { * the original exception cause */ public JsonFunctionException(int code, String message, Throwable cause) { - super(code, message, cause); + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -67,11 +71,6 @@ public JsonFunctionException(int code, String message, Throwable cause) { * the original exception cause */ public JsonFunctionException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "FOJS"; + super(IErrorCode.of(PREFIX, code), cause); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UnidentifiedFunctionError.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UnidentifiedFunctionError.java index c229b573b..2a81e9ade 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UnidentifiedFunctionError.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UnidentifiedFunctionError.java @@ -5,10 +5,15 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; public class UnidentifiedFunctionError - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final IErrorCode ERROR_CODE = IErrorCode.of("FOER", 0); /** * the serial version UID. @@ -24,8 +29,8 @@ public class UnidentifiedFunctionError * @param cause * the original exception cause */ - public UnidentifiedFunctionError(String message, Throwable cause) { - super(0, message, cause); + public UnidentifiedFunctionError(@Nullable String message, @Nullable Throwable cause) { + super(ERROR_CODE, message, cause); } /** @@ -35,8 +40,8 @@ public UnidentifiedFunctionError(String message, Throwable cause) { * @param message * the exception message */ - public UnidentifiedFunctionError(String message) { - super(0, message); + public UnidentifiedFunctionError(@Nullable String message) { + super(ERROR_CODE, message); } /** @@ -46,13 +51,7 @@ public UnidentifiedFunctionError(String message) { * @param cause * the original exception cause */ - public UnidentifiedFunctionError(Throwable cause) { - super(0, cause); - } - - @Override - public String getCodePrefix() { - return "FOER"; + public UnidentifiedFunctionError(@Nullable Throwable cause) { + super(ERROR_CODE, cause); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java index e536ca5ab..d0d993796 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java @@ -5,14 +5,18 @@ package gov.nist.secauto.metaschema.core.metapath.function; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; import gov.nist.secauto.metaschema.core.metapath.function.library.FnResolveUri; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * FONS: Exceptions related to function namespaces. */ public class UriFunctionException - extends AbstractCodedMetapathException { + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FONS"; /** * err:FONS0004: @@ -45,7 +49,7 @@ public class UriFunctionException * the exception message */ public UriFunctionException(int code, String message) { - super(code, message); + super(IErrorCode.of(PREFIX, code), message); } /** @@ -60,7 +64,7 @@ public UriFunctionException(int code, String message) { * the original exception cause */ public UriFunctionException(int code, String message, Throwable cause) { - super(code, message, cause); + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -73,12 +77,6 @@ public UriFunctionException(int code, String message, Throwable cause) { * the original exception cause */ public UriFunctionException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "FONS"; + super(IErrorCode.of(PREFIX, code), cause); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/AbstractFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/AbstractFunction.java index f3e5b301d..4773aac3f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/AbstractFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/AbstractFunction.java @@ -5,8 +5,8 @@ package gov.nist.secauto.metaschema.core.metapath.function.impl; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; 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.function.CalledContext; import gov.nist.secauto.metaschema.core.metapath.function.IArgument; @@ -136,14 +136,14 @@ public static List> convertArguments( argument = argumentIterator.next(); } else if (!function.isArityUnbounded()) { throw new InvalidTypeMetapathException( - null, - String.format("argument signature doesn't match '%s'", function.toSignature())); + function, + String.format("Argument signature doesn't match '%s'.", function.toSignature())); } assert argument != null; assert parameter != null; - retval.add(convertArgument(argument, parameter)); + retval.add(convertArgument(argument, parameter, dynamicContext)); } return CollectionUtil.unmodifiableList(retval); } @@ -151,7 +151,8 @@ public static List> convertArguments( @NonNull private static ISequence convertArgument( @NonNull IArgument argument, - @NonNull ISequence parameter) { + @NonNull ISequence parameter, + @NonNull DynamicContext dynamicContext) { ISequenceType sequenceType = argument.getSequenceType(); // apply occurrence @@ -161,7 +162,7 @@ private static ISequence convertArgument( if (!result.isEmpty()) { IItemType type = sequenceType.getType(); // this is not required to be an empty sequence - result = convertSequence(argument, result, type); + result = convertSequence(argument, result, type, dynamicContext); } // verify resulting values @@ -179,13 +180,16 @@ private static ISequence convertArgument( * the sequence to convert * @param requiredSequenceType * the expected item type for the sequence + * @param dynamicContext + * the dynamic evaluation context * @return the converted sequence */ @NonNull protected static ISequence convertSequence( @NonNull IArgument argument, @NonNull ISequence sequence, - @NonNull IItemType requiredSequenceType) { + @NonNull IItemType requiredSequenceType, + @NonNull DynamicContext dynamicContext) { Class requiredSequenceTypeClass = requiredSequenceType.getItemClass(); Stream stream = sequence.safeStream(); @@ -221,13 +225,9 @@ protected static ISequence convertSequence( @Nullable private IItem getContextItem(@NonNull ISequence focus) { - IItem contextItem = isFocusDependent() + return isFocusDependent() ? focus.getFirstItem(true) : null; - if (isFocusDependent() && contextItem == null) { - throw new DynamicMetapathException(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, "The context is empty"); - } - return contextItem; } @Override @@ -238,6 +238,9 @@ public ISequence execute( try { IItem contextItem = getContextItem(focus); + if (isFocusDependent() && contextItem == null) { + throw new ContextAbsentDynamicMetapathException("The context item is empty."); + } List> convertedArguments = convertArguments(this, arguments, dynamicContext); @@ -268,12 +271,7 @@ public ISequence execute( // toSignature(), convertedArguments.toString(), result.asList().toString())); return result; } catch (MetapathException ex) { - // FIXME: avoid throwing a new exception for a function-related exception. Fix - // this after refactoring the exception hierarchy. - throw new MetapathException(String.format("Unable to execute function '%s'. %s", - toSignature(), - ex.getLocalizedMessage()), - ex); + throw ex.registerEvaluationContext(dynamicContext); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctions.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctions.java index 0f2c13595..a52851d04 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctions.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctions.java @@ -1132,14 +1132,17 @@ public static INumericItem opNumericMod(@NonNull INumericItem dividend, @NonNull * @return the number with a reversed sign */ @NonNull - public static INumericItem opNumericUnaryMinus(@NonNull INumericItem item) { + public static INumericItem opNumericUnaryMinus( + @NonNull INumericItem item) { INumericItem retval; if (item instanceof IIntegerItem) { retval = ((IIntegerItem) item).negate(); } else if (item instanceof IDecimalItem) { retval = ((IDecimalItem) item).negate(); } else { - throw new InvalidTypeMetapathException(item, String.format("Unsupported numeric type '%s'.", item.getClass())); + throw new InvalidTypeMetapathException( + item, + String.format("Unsupported numeric type '%s'.", item.getClass())); } return retval; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java index bfeed4e10..4b2fa7b92 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java @@ -14,8 +14,8 @@ 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.IIntegerItem; -import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IndexOutOfBoundsArrayMetapathException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -78,12 +78,12 @@ private static ISequence execute(@NonNull IFunction function, * @param positionItem * the integer position of the item to retrieve * @return the retrieved item - * @throws ArrayException + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull public static T get( - @NonNull List target, + @NonNull IArrayItem target, @NonNull IIntegerItem positionItem) { return get(target, positionItem.toIntValueExact()); } @@ -99,18 +99,18 @@ public static T get( * @param position * the integer position of the item to retrieve * @return the retrieved item - * @throws ArrayException + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull public static T get( - @NonNull List target, + @NonNull IArrayItem target, int position) { try { return ObjectUtils.requireNonNull(target.get(position - 1)); } catch (IndexOutOfBoundsException ex) { - throw new ArrayException( - ArrayException.INDEX_OUT_OF_BOUNDS, + throw new IndexOutOfBoundsArrayMetapathException( + target, String.format("The index %d is outside the range of values for the array size '%d'.", position, target.size()), diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java index 6c0c48365..b13cc1a11 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java @@ -14,8 +14,9 @@ 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.IIntegerItem; -import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IndexOutOfBoundsArrayMetapathException; +import gov.nist.secauto.metaschema.core.metapath.item.function.NegativeLengthArrayMetapathException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -91,7 +92,9 @@ private static ISequence> execute(@No * @param member * the Metapath item to insert into the identified array * @return a new array containing the modification - * @throws ArrayException + * @throws NegativeLengthArrayMetapathException + * if the position is negative + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull @@ -115,7 +118,9 @@ public static IArrayItem insertBefore( * @param member * the Metapath item to insert into the identified array * @return a new array containing the modification - * @throws ArrayException + * @throws NegativeLengthArrayMetapathException + * if the position is negative + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java index 2994eb6aa..88a3945b0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java @@ -14,8 +14,9 @@ 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.IIntegerItem; -import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IndexOutOfBoundsArrayMetapathException; +import gov.nist.secauto.metaschema.core.metapath.item.function.NegativeLengthArrayMetapathException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.ArrayList; @@ -92,7 +93,9 @@ private static ISequence> e * @param member * the Metapath item to replace the identified array member with * @return a new array containing the modification - * @throws ArrayException + * @throws NegativeLengthArrayMetapathException + * if the position is negative + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull @@ -116,7 +119,9 @@ public static IArrayItem put( * @param member * the Metapath item to replace the identified array member with * @return a new array containing the modification - * @throws ArrayException + * @throws NegativeLengthArrayMetapathException + * if the position is negative + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull @@ -128,8 +133,8 @@ public static IArrayItem put( try { copy.set(position - 1, member); } catch (IndexOutOfBoundsException ex) { - throw new ArrayException( - ArrayException.INDEX_OUT_OF_BOUNDS, + throw new IndexOutOfBoundsArrayMetapathException( + array, String.format("The position %d is outside the range of values for the array of size '%d'.", position, copy.size()), diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayRemove.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayRemove.java index 1727539e3..25ca67687 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayRemove.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayRemove.java @@ -13,8 +13,9 @@ 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.IIntegerItem; -import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IndexOutOfBoundsArrayMetapathException; +import gov.nist.secauto.metaschema.core.metapath.item.function.NegativeLengthArrayMetapathException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.Collection; @@ -83,7 +84,9 @@ private static ISequence> execute(@NonNull IFunc * @param positions * the integer position of the items to remove * @return a new array containing the modification - * @throws ArrayException + * @throws NegativeLengthArrayMetapathException + * if the position is negative + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull @@ -97,7 +100,7 @@ public static IArrayItem removeItems( .map(IIntegerItem::toIntValueExact) .peek(position -> { if (position < 1 || position > size) { - throw new ArrayException(ArrayException.INDEX_OUT_OF_BOUNDS, + throw new ArrayIndexOutOfBoundsException( String.format("Index position '%d' is out of bounds for the array of size '%d'.", position, size)); } }) @@ -115,7 +118,9 @@ public static IArrayItem removeItems( * @param positions * the integer position of the items to remove * @return a new array containing the modification - * @throws ArrayException + * @throws NegativeLengthArrayMetapathException + * if the position is negative + * @throws IndexOutOfBoundsArrayMetapathException * if the position is not in the range of 1 to array:size */ @NonNull diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySubarray.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySubarray.java index c1b30ed72..d11a321b8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySubarray.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySubarray.java @@ -14,8 +14,9 @@ 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.IIntegerItem; -import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IndexOutOfBoundsArrayMetapathException; +import gov.nist.secauto.metaschema.core.metapath.item.function.NegativeLengthArrayMetapathException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.ArrayList; @@ -120,8 +121,8 @@ private static ISequence> executeThre * @param startItem * the integer position of the item to start with (inclusive) * @return a new array item consisting of the items in the identified range - * @throws ArrayException - * if the position is not in the range of 1 to array:size + * @throws IndexOutOfBoundsArrayMetapathException + * if the start position is not in the range of 1 to array:size */ @SuppressWarnings("PMD.OnlyOneReturn") @NonNull @@ -145,9 +146,10 @@ public static IArrayItem subarray( * the integer count of items to include starting with the item at the * start position * @return a new array item consisting of the items in the identified range - * @throws ArrayException - * if the length is negative or the position is not in the range of 1 - * to array:size + * @throws NegativeLengthArrayMetapathException + * if the length is negative + * @throws IndexOutOfBoundsArrayMetapathException + * if the start position is not in the range of 1 to array:size */ @SuppressWarnings("PMD.OnlyOneReturn") @NonNull @@ -169,9 +171,8 @@ public static IArrayItem subarray( * @param start * the integer position of the item to start with (inclusive) * @return a new array item consisting of the items in the identified range - * @throws ArrayException - * if the length is negative or the position is not in the range of 1 - * to array:size + * @throws IndexOutOfBoundsArrayMetapathException + * if the start position is not in the range of 1 to array:size */ @NonNull public static IArrayItem subarray( @@ -194,9 +195,11 @@ public static IArrayItem subarray( * the integer count of items to include starting with the item at the * start position * @return a new array item consisting of the items in the identified range - * @throws ArrayException - * if the length is negative or the position is not in the range of 1 - * to array:size + * @throws NegativeLengthArrayMetapathException + * if the length argument is negative + * @throws IndexOutOfBoundsArrayMetapathException + * if the position of the start argument is not a value in the range + * of 1 to array:size */ @NonNull public static IArrayItem subarray( @@ -204,16 +207,17 @@ public static IArrayItem subarray( int start, int length) { if (length < 0) { - throw new ArrayException( - ArrayException.NEGATIVE_ARRAY_LENGTH, String.format("The length '%d' is negative.", length)); + throw new NegativeLengthArrayMetapathException( + array, + String.format("The length '%d' is negative.", length)); } List copy; try { copy = array.subList(start - 1, start - 1 + length); } catch (IndexOutOfBoundsException ex) { - throw new ArrayException( - ArrayException.INDEX_OUT_OF_BOUNDS, + throw new IndexOutOfBoundsArrayMetapathException( + array, String.format("The start + length (%d + %d) exceeds the array length '%d'.", start, length, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnFunctionLookup.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnFunctionLookup.java index c77bf369b..9c315f206 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnFunctionLookup.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnFunctionLookup.java @@ -7,7 +7,6 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; -import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.IArgument; @@ -65,11 +64,12 @@ private static ISequence execute(@NonNull IFunction function, IFunction matchingFunction = null; try { - matchingFunction = StaticContext.lookupFunction( + matchingFunction = dynamicContext.lookupFunction( name.toEnhancedQName(), arity.toIntValueExact()); } catch (StaticMetapathException ex) { - if (ex.getCode() != StaticMetapathException.NO_FUNCTION_MATCH) { + if (ex.getErrorCode().getCode() != StaticMetapathException.NO_FUNCTION_MATCH) { + // this is something other than a non-match throw ex; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java index b06e4b301..24a6cccb7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java @@ -8,8 +8,6 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; -import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException; 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; @@ -111,13 +109,8 @@ private static ISequence recurseDepth( @NonNull ISequence initialContext, @NonNull IStringItem recursionPath, @NonNull DynamicContext dynamicContext) { - IMetapathExpression recursionMetapath; - try { - recursionMetapath = IMetapathExpression.compile(recursionPath.asString(), dynamicContext.getStaticContext()); - } catch (MetapathException ex) { - throw new StaticMetapathException(StaticMetapathException.INVALID_PATH_GRAMMAR, ex.getMessage(), ex); - } - + IMetapathExpression recursionMetapath + = IMetapathExpression.compile(recursionPath.asString(), dynamicContext.getStaticContext()); return recurseDepth(initialContext, recursionMetapath, dynamicContext); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/regex/RegularExpressionMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/regex/RegularExpressionMetapathException.java index db981e975..4d6dd2239 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/regex/RegularExpressionMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/regex/RegularExpressionMetapathException.java @@ -5,10 +5,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.regex; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; +import gov.nist.secauto.metaschema.core.metapath.MetapathException; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; public class RegularExpressionMetapathException - extends AbstractCodedMetapathException { + extends MetapathException { + @NonNull + private static final String PREFIX = "MPRX"; /** * the serial version UID. */ @@ -64,8 +70,11 @@ public class RegularExpressionMetapathException * @param cause * the original exception cause */ - public RegularExpressionMetapathException(int code, String message, Throwable cause) { - super(code, message, cause); + public RegularExpressionMetapathException( + int code, + @Nullable String message, + @Nullable Throwable cause) { + super(IErrorCode.of(PREFIX, code), message, cause); } /** @@ -77,8 +86,10 @@ public RegularExpressionMetapathException(int code, String message, Throwable ca * @param message * the exception message */ - public RegularExpressionMetapathException(int code, String message) { - super(code, message); + public RegularExpressionMetapathException( + int code, + @Nullable String message) { + super(IErrorCode.of(PREFIX, code), message); } /** @@ -90,12 +101,9 @@ public RegularExpressionMetapathException(int code, String message) { * @param cause * the original exception cause */ - public RegularExpressionMetapathException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "MPRX"; + public RegularExpressionMetapathException( + int code, + @Nullable Throwable cause) { + super(IErrorCode.of(PREFIX, code), cause); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMetapathExpression.java new file mode 100644 index 000000000..94b1e46bc --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMetapathExpression.java @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.impl; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.IExpression; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.ISequence; +import gov.nist.secauto.metaschema.core.util.CollectionUtil; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public abstract class AbstractMetapathExpression implements IMetapathExpression { + + @NonNull + private final String path; + @NonNull + private final StaticContext staticContext; + + public AbstractMetapathExpression( + @NonNull String path, + @NonNull StaticContext context) { + this.path = path; + this.staticContext = context; + } + + @Override + public String getPath() { + return path; + } + + @Override + public StaticContext getStaticContext() { + return staticContext; + } + + @NonNull + protected abstract IExpression getExpression(); + + @Override + public Class getBaseResultType() { + return getExpression().getStaticResultType(); + } + + @Override + public List getChildren() { + return CollectionUtil.singletonList(getExpression()); + } + + @Override + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + return getExpression().accept(dynamicContext, focus); + } + + @Override + public RESULT accept(IExpressionVisitor visitor, CONTEXT context) { + return getExpression().accept(visitor, context); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ErrorCodeImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ErrorCodeImpl.java new file mode 100644 index 000000000..460cea94e --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ErrorCodeImpl.java @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.impl; + +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class ErrorCodeImpl implements IErrorCode { + /** + * The error prefix which identifies what kind of error it is. + */ + @NonNull + private final String prefix; + + /** + * The error code. + */ + private final int code; + + /** + * Construct a new error code. + * + * @param prefix + * the error code prefix, which indicates what type of error it is + * @param code + * the error code value, which indicates the specific error + */ + public ErrorCodeImpl(@NonNull String prefix, int code) { + this.prefix = prefix; + this.code = code; + } + + /** + * Get the error code prefix, which indicates what type of error it is. + * + * @return the error code prefix + */ + @Override + public String getPrefix() { + return prefix; + } + + /** + * Get the error code value. + * + * @return the error code value + */ + @Override + public int getCode() { + return code; + } + + /** + * Get a combination of the error code family and value. + * + * @return the full error code. + */ + @Override + public final String toString() { + return getCodeAsString(); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/LazyCompilationMetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/LazyCompilationMetapathExpression.java index c7a8cb99c..b785a1a89 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/LazyCompilationMetapathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/LazyCompilationMetapathExpression.java @@ -6,13 +6,18 @@ package gov.nist.secauto.metaschema.core.metapath.impl; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.IExpression; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.InvalidMetapathGrammarException; import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import java.util.List; + import edu.umd.cs.findbugs.annotations.NonNull; import nl.talsmasoftware.lazy4j.Lazy; @@ -43,7 +48,15 @@ public LazyCompilationMetapathExpression( @NonNull StaticContext staticContext) { this.path = path; this.staticContext = staticContext; - this.compiledMetapath = ObjectUtils.notNull(Lazy.lazy(() -> IMetapathExpression.compile(path, staticContext))); + this.compiledMetapath = ObjectUtils.notNull(Lazy.lazy(() -> { + IMetapathExpression result; + try { + result = IMetapathExpression.compile(path, staticContext); + } catch (InvalidMetapathGrammarException ex) { + throw new InvalidMetapathGrammarException(ex); + } + return result; + })); } @Override @@ -61,6 +74,26 @@ private IMetapathExpression getCompiledMetapath() { return ObjectUtils.notNull(compiledMetapath.get()); } + @Override + public List getChildren() { + return getCompiledMetapath().getChildren(); + } + + @Override + public Class getBaseResultType() { + return getCompiledMetapath().getBaseResultType(); + } + + @Override + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + return getCompiledMetapath().accept(dynamicContext, focus); + } + + @Override + public RESULT accept(IExpressionVisitor visitor, CONTEXT context) { + return getCompiledMetapath().accept(visitor, context); + } + @Override public ISequence evaluate(IItem focus, DynamicContext dynamicContext) { return getCompiledMetapath().evaluate(focus, dynamicContext); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MetapathExpression.java similarity index 82% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MetapathExpression.java index dde1e1ecc..24359251d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MetapathExpression.java @@ -3,8 +3,13 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath; +package gov.nist.secauto.metaschema.core.metapath.impl; +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.IExpression; +import gov.nist.secauto.metaschema.core.metapath.InvalidMetapathGrammarException; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException; import gov.nist.secauto.metaschema.core.metapath.antlr.FailingErrorListener; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10Lexer; @@ -39,22 +44,18 @@ @SuppressWarnings({ "PMD.CouplingBetweenObjects" // necessary since this class aggregates functionality }) -class MetapathExpression implements IMetapathExpression { +public class MetapathExpression + extends AbstractMetapathExpression { /** * The Metapath expression identifying the current context node. */ @NonNull - public static final MetapathExpression CONTEXT_NODE + public static final MetapathExpression CONTEXT_METAPATH = new MetapathExpression(".", ContextItem.instance(), StaticContext.instance()); private static final Logger LOGGER = LogManager.getLogger(MetapathExpression.class); - - @NonNull - private final String path; @NonNull private final IExpression expression; - @NonNull - private final StaticContext staticContext; /** * Compiles a Metapath expression string using the provided static context. @@ -64,7 +65,7 @@ class MetapathExpression implements IMetapathExpression { * @param context * the static evaluation context * @return the compiled expression object - * @throws MetapathException + * @throws InvalidMetapathGrammarException * if an error occurred while compiling the Metapath expression */ @NonNull @@ -72,7 +73,7 @@ public static MetapathExpression compile(@NonNull String path, @NonNull StaticCo @NonNull MetapathExpression retval; if (".".equals(path)) { - retval = CONTEXT_NODE; + retval = CONTEXT_METAPATH; } else { try { Metapath10 parser = newParser(path); @@ -83,14 +84,15 @@ public static MetapathExpression compile(@NonNull String path, @NonNull StaticCo retval = new MetapathExpression(path, expr, context); } catch (StaticMetapathException ex) { String message = ex.getMessageText(); - throw new StaticMetapathException( - ex.getCode(), + throw new InvalidMetapathGrammarException( String.format("Unable to compile path '%s'.%s", path, message == null ? "" : " " + message), ex); - } catch (MetapathException | ParseCancellationException ex) { + } catch (ParseCancellationException ex) { String msg = String.format("Unable to compile Metapath '%s'", path); - LOGGER.atError().withThrowable(ex).log(msg); - throw new StaticMetapathException(StaticMetapathException.INVALID_PATH_GRAMMAR, msg, ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.atDebug().withThrowable(ex).log(msg); + } + throw new InvalidMetapathGrammarException(msg, ex); } } return retval; @@ -151,14 +153,13 @@ protected MetapathExpression( @NonNull String path, @NonNull IExpression expr, @NonNull StaticContext staticContext) { - this.path = path; + super(path, staticContext); this.expression = expr; - this.staticContext = staticContext; } @Override - public String getPath() { - return path; + protected IExpression getExpression() { + return expression; } /** @@ -171,11 +172,6 @@ protected IExpression getCSTNode() { return expression; } - @Override - public StaticContext getStaticContext() { - return staticContext; - } - @Override public String toString() { return getPath(); @@ -186,21 +182,11 @@ public String toString() { public ISequence evaluate( @Nullable IItem focus, @NonNull DynamicContext dynamicContext) { + dynamicContext.pushExecutionStack(this); try { return ObjectUtils.asType(getCSTNode().accept(dynamicContext, ISequence.of(focus)).reusable()); - } catch (MetapathException ex) { - throw new MetapathException( - String.format("An error occurred while evaluating the expression '%s'. %s", - getPath(), - ex.getLocalizedMessage()), - ex); + } finally { + dynamicContext.popExecutionStack(this); } } - - @FunctionalInterface - interface ConversionFunction { - @Nullable - Object convert(@NonNull ISequence sequence); - } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/ItemUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/ItemUtils.java index 02176212f..399db185f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/ItemUtils.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/ItemUtils.java @@ -5,12 +5,15 @@ package gov.nist.secauto.metaschema.core.metapath.item; +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.InvalidTreatTypeDynamicMetapathException; +import gov.nist.secauto.metaschema.core.metapath.cst.path.Axis; +import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentBasedNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; - -import java.util.Arrays; -import java.util.Objects; -import java.util.stream.Stream; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -25,8 +28,10 @@ private ItemUtils() { } /** - * Checks that the item is a node item. + * Checks that the item is an {@link INodeItem}. * + * @param dynamicContext + * the dynamic evaluation context * @param item * the item to check * @return the item cast to a {@link INodeItem} @@ -35,18 +40,75 @@ private ItemUtils() { */ // FIXME: make this a method on the type implementation @NonNull - public static INodeItem checkItemIsNodeItemForStep(@Nullable IItem item) { - if (item instanceof INodeItem) { - return (INodeItem) item; + public static INodeItem checkItemIsNodeItem( + @NonNull DynamicContext dynamicContext, + @Nullable IItem item) { + return checkItemIsType(dynamicContext, item, INodeItem.class); + } + + /** + * Checks that the item is an {@link IDocumentNodeItem}. + * + * @param dynamicContext + * the dynamic evaluation context + * @param item + * the item to check + * @return the item cast to a {@link INodeItem} + * @throws TypeMetapathException + * if the item is {@code null} or not an {@link INodeItem} + */ + @NonNull + public static IDocumentBasedNodeItem checkItemIsDocumentNodeItem( + @NonNull DynamicContext dynamicContext, + @Nullable IItem item) { + return checkItemIsType(dynamicContext, item, IDocumentBasedNodeItem.class); + } + + @NonNull + private static T checkItemIsType( + @NonNull DynamicContext dynamicContext, + @Nullable IItem item, + @NonNull Class itemClass) { + if (itemClass.isInstance(item)) { + return ObjectUtils.notNull(itemClass.cast(item)); } if (item == null) { - throw new TypeMetapathException(TypeMetapathException.NOT_A_NODE_ITEM_FOR_STEP, - "Item is null."); + throw new TypeMetapathException(TypeMetapathException.NOT_A_NODE_ITEM_FOR_STEP, "Item is null.") + .registerEvaluationContext(dynamicContext); } - throw new TypeMetapathException(TypeMetapathException.NOT_A_NODE_ITEM_FOR_STEP, - String.format( - "The item of type '%s' is not a INodeItem.", - item.getClass().getName())); + throw new TypeMetapathException( + TypeMetapathException.NOT_A_NODE_ITEM_FOR_STEP, + String.format("The item of type '%s' is not of the type '%s'.", + item.getClass().getName(), + itemClass.getName())) + .registerEvaluationContext(dynamicContext); + } + + /** + * Get the ancestor document nodes for the provided items. + *

+ * The resulting sequence has items of the {@link IDocumentBasedNodeItem} to + * allow for both module and document querying. + * + * @param items + * the node items to get the document roots for + * @return the document root node items + */ + @NonNull + public static ISequence getDocumentNodeItems( + @NonNull DynamicContext dynamicContext, + @NonNull ISequence items) { + return ISequence.of(ObjectUtils.notNull(items.stream() + // ensures a non-null INodeItem instance + .map(item -> ItemUtils.checkItemIsNodeItem(dynamicContext, item)) + .map(item -> Axis.ANCESTOR_OR_SELF.execute(ObjectUtils.notNull(item)) + .findFirst().stream() + .filter(IDocumentBasedNodeItem.class::isInstance) + .map(firstItem -> ItemUtils.checkItemIsDocumentNodeItem(dynamicContext, firstItem)) + .findFirst().orElseThrow(() -> new InvalidTreatTypeDynamicMetapathException( + dynamicContext.getExecutionStack(), + String.format("The node '%s' is not the descendant of a document node.", + item.getMetapath())))))); } /** @@ -70,36 +132,11 @@ public static TYPE checkItemType(@NonNull IItem item, @NonNull Class Stream> interfacesFor( - @NonNull Class seed, - @NonNull Class base) { - return ancestorsOrSelf(seed) - .flatMap(clazz -> Stream.ofNullable(asSubclassOrNull(clazz, base))) - .flatMap(clazz -> Stream.concat( - Stream.of(clazz), - Arrays.stream(seed.getInterfaces()) - .flatMap(cls -> Stream.ofNullable(asSubclassOrNull(cls, base))))); - } - - private static Stream> ancestorsOrSelf(@NonNull Class seed) { - return Stream.iterate(seed, Objects::nonNull, Class::getSuperclass); - } - - @Nullable - private static Class asSubclassOrNull(Class clazz, Class base) { - Class retval = null; - try { - retval = clazz.asSubclass(base); - } catch (@SuppressWarnings("unused") ClassCastException ex) { - // not a subclass, do nothing - } - return retval; - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java index 90bf7cbc2..6d0b1a78b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java @@ -5,8 +5,8 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractStringMapKey; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java index 79c531bbe..07ad67aa1 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java @@ -67,9 +67,7 @@ static IIntegerItem valueOf(@NonNull String value) { try { return valueOf(MetaschemaDataTypeProvider.INTEGER.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidTypeMetapathException(null, - ex.getMessage(), - ex); + throw new InvalidTypeMetapathException(null, ex.getMessage(), ex); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java index c85b620f1..8569b41ef 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java @@ -291,7 +291,5 @@ default INumericItem mod(@NonNull INumericItem divisor) { * @return a new value with the sign reversed */ @NonNull - default INumericItem negate() { - return OperationFunctions.opNumericUnaryMinus(this); - } + INumericItem negate(); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractMarkupItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractMarkupItem.java index 6349ced1f..cb021f1b5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractMarkupItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractMarkupItem.java @@ -6,10 +6,10 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.markup.IMarkupString; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IMarkupItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractStringMapKey; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractStringItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractStringItem.java index 2b8fe929f..25e6baa31 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractStringItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractStringItem.java @@ -5,10 +5,10 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractStringMapKey; import java.util.regex.Pattern; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractUriItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractUriItem.java index 7ee2947e7..391a92038 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractUriItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractUriItem.java @@ -5,10 +5,10 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractStringMapKey; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.net.URI; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UuidItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UuidItemImpl.java index 166eb6fb3..eac4dea9f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UuidItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UuidItemImpl.java @@ -7,11 +7,11 @@ import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.UuidAdapter; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUuidItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractStringMapKey; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.UUID; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/ArrayException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/ArrayException.java deleted file mode 100644 index 6ba0d8dc6..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/ArrayException.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.metapath.item.function; - -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; - -/** - * Represents an error that occurred while performing mathematical operations. - */ -public class ArrayException - extends AbstractCodedMetapathException { - /** - * err:FOAY0001: - * This error is raised when the $length argument to array:subarray is negative. - */ - public static final int INDEX_OUT_OF_BOUNDS = 1; - /** - * err:FOAY0002: - * This error is raised whenever numeric operations result in an overflow or - * underflow. - */ - public static final int NEGATIVE_ARRAY_LENGTH = 2; - - /** - * the serial version UID. - */ - private static final long serialVersionUID = 2L; - - /** - * Constructs a new exception with the provided {@code code}, {@code message}, - * and no cause. - * - * @param code - * the error code value - * @param message - * the exception message - */ - public ArrayException(int code, String message) { - super(code, message); - } - - /** - * Constructs a new exception with the provided {@code code}, {@code message}, - * and {@code cause}. - * - * @param code - * the error code value - * @param message - * the exception message - * @param cause - * the original exception cause - */ - public ArrayException(int code, String message, Throwable cause) { - super(code, message, cause); - } - - /** - * Constructs a new exception with the provided {@code code}, no message, and - * the {@code cause}. - * - * @param code - * the error code value - * @param cause - * the original exception cause - */ - public ArrayException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "FOAY"; - } - -} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java index 796e64e34..2b75a9ecf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java @@ -6,13 +6,13 @@ package gov.nist.secauto.metaschema.core.metapath.item.function; import gov.nist.secauto.metaschema.core.metapath.function.IFunction; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractArrayItem; -import gov.nist.secauto.metaschema.core.metapath.impl.ArrayItemN; 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.IItemVisitor; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.ArrayItemN; import gov.nist.secauto.metaschema.core.metapath.type.IItemType; import gov.nist.secauto.metaschema.core.util.ObjectUtils; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java index 554b00729..82f0f7c7f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java @@ -6,12 +6,12 @@ package gov.nist.secauto.metaschema.core.metapath.item.function; import gov.nist.secauto.metaschema.core.metapath.function.IFunction; -import gov.nist.secauto.metaschema.core.metapath.impl.AbstractMapItem; -import gov.nist.secauto.metaschema.core.metapath.impl.MapItemN; import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue; import gov.nist.secauto.metaschema.core.metapath.item.IItemVisitor; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.AbstractMapItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.MapItemN; import gov.nist.secauto.metaschema.core.metapath.type.IItemType; import gov.nist.secauto.metaschema.core.util.ObjectUtils; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IndexOutOfBoundsArrayMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IndexOutOfBoundsArrayMetapathException.java new file mode 100644 index 000000000..45a4ab664 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IndexOutOfBoundsArrayMetapathException.java @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.item.function; + +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.ArrayMetapathException; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * err:FOAY0001: + * This error is raised when an integer used to select a member of an array is + * outside the range of values for that array. + */ +public class IndexOutOfBoundsArrayMetapathException + extends ArrayMetapathException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with the provided {@code item}, {@code message}, + * and no cause. + * + * @param item + * the array item involved + * @param message + * the exception message + */ + public IndexOutOfBoundsArrayMetapathException(@NonNull IArrayItem item, String message) { + super(INDEX_OUT_OF_BOUNDS, item, message); + } + + /** + * Constructs a new exception with the provided {@code item}, {@code message}, + * and {@code cause}. + * + * @param item + * the array item involved + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public IndexOutOfBoundsArrayMetapathException(@NonNull IArrayItem item, String message, + Throwable cause) { + super(INDEX_OUT_OF_BOUNDS, item, message, cause); + } + + /** + * Constructs a new exception with the provided {@code item}, no message, and + * the {@code cause}. + * + * @param item + * the array item involved + * @param cause + * the original exception cause + */ + public IndexOutOfBoundsArrayMetapathException(@NonNull IArrayItem item, Throwable cause) { + super(INDEX_OUT_OF_BOUNDS, item, cause); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/NegativeLengthArrayMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/NegativeLengthArrayMetapathException.java new file mode 100644 index 000000000..3639a47c8 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/NegativeLengthArrayMetapathException.java @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.item.function; + +import gov.nist.secauto.metaschema.core.metapath.item.function.impl.ArrayMetapathException; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * err:FOAY0001: + * This error is raised when an integer used to select a member of an array is + * outside the range of values for that array. + */ +public class NegativeLengthArrayMetapathException + extends ArrayMetapathException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and no cause. + * + * @param item + * the array item involved + * @param message + * the exception message + */ + public NegativeLengthArrayMetapathException(@NonNull IArrayItem item, String message) { + super(NEGATIVE_ARRAY_LENGTH, item, message); + } + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and {@code cause}. + * + * @param item + * the array item involved + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public NegativeLengthArrayMetapathException(@NonNull IArrayItem item, String message, + Throwable cause) { + super(NEGATIVE_ARRAY_LENGTH, item, message, cause); + } + + /** + * Constructs a new exception with the provided {@code code}, no message, and + * the {@code cause}. + * + * @param item + * the array item involved + * @param cause + * the original exception cause + */ + public NegativeLengthArrayMetapathException(@NonNull IArrayItem item, Throwable cause) { + super(NEGATIVE_ARRAY_LENGTH, item, cause); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractArrayItem.java similarity index 96% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractArrayItem.java index 4f4829d15..d78ca4497 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractArrayItem.java @@ -3,11 +3,12 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.impl; +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 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.impl.IFeatureCollectionFunctionItem; import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractKeySpecifier.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractKeySpecifier.java similarity index 96% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractKeySpecifier.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractKeySpecifier.java index 5d4f99d43..19c073a36 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractKeySpecifier.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractKeySpecifier.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.impl; +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.IExpression; @@ -14,10 +14,10 @@ import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IKeySpecifier; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IndexOutOfBoundsArrayMetapathException; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -174,8 +174,8 @@ protected Stream lookupInArray( try { return ObjectUtils.notNull(Stream.ofNullable(ArrayGet.get(targetItem, index))); } catch (IndexOutOfBoundsException ex) { - throw new ArrayException( - ArrayException.INDEX_OUT_OF_BOUNDS, + throw new IndexOutOfBoundsArrayMetapathException( + targetItem, String.format("The index '%d' is outside the range of values for the array size '%d'.", index + 1, targetItem.size()), @@ -243,8 +243,8 @@ protected Stream lookupInArray( try { return Stream.ofNullable(ArrayGet.get(targetItem, index)); } catch (IndexOutOfBoundsException ex) { - throw new ArrayException( - ArrayException.INDEX_OUT_OF_BOUNDS, + throw new IndexOutOfBoundsArrayMetapathException( + targetItem, String.format("The index %d is outside the range of values for the array size '%d'.", index + 1, targetItem.size()), diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractMapItem.java similarity index 96% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractMapItem.java index 59088e52c..58b886bfb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractMapItem.java @@ -3,12 +3,13 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.impl; +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 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.library.MapGet; +import gov.nist.secauto.metaschema.core.metapath.impl.IFeatureCollectionFunctionItem; import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractStringMapKey.java similarity index 78% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractStringMapKey.java index ae1f8481d..ee89d8aef 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/AbstractStringMapKey.java @@ -3,8 +3,9 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.impl; +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractMapKey; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import gov.nist.secauto.metaschema.core.metapath.item.function.IStringMapKey; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ArrayItemN.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ArrayItemN.java similarity index 94% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ArrayItemN.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ArrayItemN.java index 4f9f05dcb..20d266813 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ArrayItemN.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ArrayItemN.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.impl; +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue; import gov.nist.secauto.metaschema.core.util.CollectionUtil; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ArrayMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ArrayMetapathException.java new file mode 100644 index 000000000..02e9e5b57 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ArrayMetapathException.java @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; + +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; +import gov.nist.secauto.metaschema.core.metapath.function.FunctionMetapathError; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents an error that occurred while performing mathematical operations. + */ +public class ArrayMetapathException + extends FunctionMetapathError { + @NonNull + private static final String PREFIX = "FOAY"; + /** + * err:FOAY0001: + * This error is raised when an integer used to select a member of an array is + * outside the range of values for that array. + */ + protected static final int INDEX_OUT_OF_BOUNDS = 1; + /** + * err:FOAY0001: + * This error is raised when the $length argument to array:subarray is negative. + */ + public static final int NEGATIVE_ARRAY_LENGTH = 2; + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 2L; + + @NonNull + private final IArrayItem item; + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and no cause. + * + * @param code + * the error code value + * @param item + * the array item involved + * @param message + * the exception message + */ + public ArrayMetapathException(int code, @NonNull IArrayItem item, String message) { + super(IErrorCode.of(PREFIX, code), message); + this.item = item; + } + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and {@code cause}. + * + * @param code + * the error code value + * @param item + * the array item involved + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public ArrayMetapathException(int code, @NonNull IArrayItem item, String message, Throwable cause) { + super(IErrorCode.of(PREFIX, code), message, cause); + this.item = item; + } + + /** + * Constructs a new exception with the provided {@code code}, no message, and + * the {@code cause}. + * + * @param code + * the error code value + * @param item + * the array item involved + * @param cause + * the original exception cause + */ + public ArrayMetapathException(int code, @NonNull IArrayItem item, Throwable cause) { + super(IErrorCode.of(PREFIX, code), cause); + this.item = item; + } + + /** + * Get the array item involved in the exception. + * + * @return the array item + */ + public IArrayItem getArrayItem() { + return item; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ImmutableCollections.java similarity index 98% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ImmutableCollections.java index 2860bb25f..c9d5b5623 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/ImmutableCollections.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.impl; +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; import gov.nist.secauto.metaschema.core.util.ObjectUtils; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/MapItemN.java similarity index 95% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/MapItemN.java index babf5f81a..0b61691c7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/impl/MapItemN.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.impl; +package gov.nist.secauto.metaschema.core.metapath.item.function.impl; import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractNodeItemVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractNodeItemVisitor.java index fc55da58b..1ae212215 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractNodeItemVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractNodeItemVisitor.java @@ -14,7 +14,8 @@ * @param * the type of result produced by visitation */ -public abstract class AbstractNodeItemVisitor implements INodeItemVisitor { +public abstract class AbstractNodeItemVisitor + implements INodeItemVisitor { /** * Visit the provided {@code item}. * @@ -59,6 +60,8 @@ protected RESULT visitFlags(@NonNull INodeItem item, CONTEXT context) { * @param context * provides contextual information for use by the visitor * @return the result produced by visiting the item's child model items + * @throws EXCEPTION + * when an un-handled error was raised while visiting a child node */ protected RESULT visitModelChildren(@NonNull INodeItem item, CONTEXT context) { RESULT result = defaultResult(); @@ -153,7 +156,6 @@ protected RESULT aggregateResult(RESULT first, RESULT second, CONTEXT context) { public RESULT visitDocument(IDocumentNodeItem item, CONTEXT context) { // this is the default behavior, which can be overridden return visitModelChildren(item, context); - // return visitAssembly(item.getRootAssemblyNodeItem(), context); } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/INodeItemVisitable.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/INodeItemVisitable.java index 64dfd9823..856b925df 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/INodeItemVisitable.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/INodeItemVisitable.java @@ -17,5 +17,7 @@ public interface INodeItemVisitable { * a parameter used to pass contextual information between visitors * @return the visitor result */ - RESULT accept(@NonNull INodeItemVisitor visitor, CONTEXT context); + RESULT accept( + @NonNull INodeItemVisitor visitor, + CONTEXT context); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/InvalidTypeMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/InvalidTypeMetapathException.java index 96a61f4e3..d7e54a11b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/InvalidTypeMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/InvalidTypeMetapathException.java @@ -34,8 +34,12 @@ public class InvalidTypeMetapathException * @param cause * the original exception cause */ - public InvalidTypeMetapathException(@NonNull IItem item, @NonNull Throwable cause) { - super(INVALID_TYPE_ERROR, String.format("Invalid data type '%s'", item.getClass().getName()), + public InvalidTypeMetapathException( + @NonNull IItem item, + @NonNull Throwable cause) { + super( + INVALID_TYPE_ERROR, + String.format("Invalid data type '%s'", item.getClass().getName()), cause); this.item = item; } @@ -47,7 +51,8 @@ public InvalidTypeMetapathException(@NonNull IItem item, @NonNull Throwable caus * @param item * the item related to the invalid type error */ - public InvalidTypeMetapathException(@NonNull IItem item) { + public InvalidTypeMetapathException( + @NonNull IItem item) { super(INVALID_TYPE_ERROR, String.format("Invalid data type '%s'", item.getClass().getName())); this.item = item; } @@ -63,7 +68,10 @@ public InvalidTypeMetapathException(@NonNull IItem item) { * @param cause * the original exception cause */ - public InvalidTypeMetapathException(@Nullable IItem item, @Nullable String message, @NonNull Throwable cause) { + public InvalidTypeMetapathException( + @Nullable IItem item, + @Nullable String message, + @NonNull Throwable cause) { super(INVALID_TYPE_ERROR, message, cause); this.item = item; } @@ -77,7 +85,9 @@ public InvalidTypeMetapathException(@Nullable IItem item, @Nullable String messa * @param message * the exception message */ - public InvalidTypeMetapathException(@Nullable IItem item, @Nullable String message) { + public InvalidTypeMetapathException( + @Nullable IItem item, + @Nullable String message) { super(INVALID_TYPE_ERROR, message); this.item = item; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/TypeMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/TypeMetapathException.java index aed0b5b81..b491b7fdb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/TypeMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/TypeMetapathException.java @@ -5,13 +5,24 @@ package gov.nist.secauto.metaschema.core.metapath.type; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; +import gov.nist.secauto.metaschema.core.metapath.IErrorCode; +import gov.nist.secauto.metaschema.core.metapath.MetapathException; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; /** * MPTY: Exceptions related to Metapath type errors. */ public class TypeMetapathException - extends AbstractCodedMetapathException { + extends MetapathException { + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 2L; + @NonNull + private static final String PREFIX = "MPTY"; /** * err:MPTY0004: It * is a type error @@ -27,7 +38,8 @@ public class TypeMetapathException * 2.5.5 * SequenceType Matching. */ - public static final int INVALID_TYPE_ERROR = 4; + // FIXME: differentiate static vs dynamic errors + protected static final int INVALID_TYPE_ERROR = 4; /** * err:MPTY0019: It * is a type error @@ -36,15 +48,11 @@ public class TypeMetapathException */ public static final int BASE_PATH_NOT_A_SEQUENCE = 19; /** - * The context item is not a node when evaluating an axis step. + * err:MPTY0020: The + * context item is not a node when evaluating an axis step. */ public static final int NOT_A_NODE_ITEM_FOR_STEP = 20; - /** - * the serial version UID. - */ - private static final long serialVersionUID = 2L; - /** * Constructs a new exception with the provided {@code code}, {@code message}, * and {@code cause}. @@ -56,38 +64,27 @@ public class TypeMetapathException * @param cause * the original exception cause */ - public TypeMetapathException(int code, String message, Throwable cause) { - super(code, message, cause); + protected TypeMetapathException( + int code, + @Nullable String message, + @Nullable Throwable cause) { + super(IErrorCode.of(PREFIX, code), message, cause); } /** * Constructs a new exception with the provided {@code code}, {@code message}, * and no cause. * + * @param evaluationStack + * the Metapath evaluation stack that lead to this exception * @param code * the error code value * @param message * the exception message */ - public TypeMetapathException(int code, String message) { - super(code, message); - } - - /** - * Constructs a new exception with the provided {@code code}, no message, and - * the {@code cause}. - * - * @param code - * the error code value - * @param cause - * the original exception cause - */ - public TypeMetapathException(int code, Throwable cause) { - super(code, cause); - } - - @Override - public String getCodePrefix() { - return "MPTY"; + public TypeMetapathException( + int code, + @Nullable String message) { + super(IErrorCode.of(PREFIX, code), message); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/impl/DynamicTypeSupport.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/impl/DynamicTypeSupport.java index 7835c5402..6cfa1789b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/impl/DynamicTypeSupport.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/type/impl/DynamicTypeSupport.java @@ -104,7 +104,9 @@ private static boolean compareDefinition( try { IEnhancedQName expectedName = nameResolver.resolve(expected); retval = definition.getDefinitionQName().equals(expectedName); // AT is ET - } catch (@SuppressWarnings("unused") StaticMetapathException ex) { + } catch (StaticMetapathException ex) { + assert ex != null; + // fail the definition name test retval = false; } @@ -134,5 +136,4 @@ private static boolean compareAtomicTypes( private DynamicTypeSupport() { // disable construction } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java index f5a1f2a20..9b4b1b521 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java @@ -75,13 +75,16 @@ protected String toPath(@NonNull INodeItem item) { * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newCardinalityMinimumViolationMessage( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem target, @NonNull ISequence testedItems, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { return constraint.getMessage() == null ? ObjectUtils.notNull(String.format( "The cardinality '%d' is below the required minimum '%d' for items matching '%s'.", @@ -105,13 +108,16 @@ protected String newCardinalityMinimumViolationMessage( * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newCardinalityMaximumViolationMessage( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem target, @NonNull ISequence testedItems, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { return constraint.getMessage() == null ? ObjectUtils.notNull(String.format( "The cardinality '%d' is greater than the required maximum '%d' at: %s.", @@ -143,6 +149,9 @@ protected String newCardinalityMaximumViolationMessage( * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newIndexDuplicateKeyViolationMessage( @@ -150,7 +159,7 @@ protected String newIndexDuplicateKeyViolationMessage( @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { // TODO: render the key paths return constraint.getMessage() == null ? ObjectUtils.notNull(String.format("Index '%s' has duplicate key for items at paths '%s' and '%s'", @@ -176,6 +185,9 @@ protected String newIndexDuplicateKeyViolationMessage( * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newUniqueKeyViolationMessage( @@ -183,7 +195,7 @@ protected String newUniqueKeyViolationMessage( @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { return constraint.getMessage() == null ? ObjectUtils.notNull(String.format("Unique constraint violation at paths '%s' and '%s'", toPath(oldItem), @@ -209,6 +221,9 @@ protected String newUniqueKeyViolationMessage( * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newMatchPatternViolationMessage( @@ -217,7 +232,7 @@ protected String newMatchPatternViolationMessage( @NonNull INodeItem target, @NonNull String value, @NonNull Pattern pattern, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { return constraint.getMessage() == null ? ObjectUtils.notNull(String.format("Value '%s' did not match the pattern '%s' at path '%s'", value, @@ -244,6 +259,9 @@ protected String newMatchPatternViolationMessage( * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newMatchDatatypeViolationMessage( @@ -252,7 +270,7 @@ protected String newMatchDatatypeViolationMessage( @NonNull INodeItem target, @NonNull String value, @NonNull IDataTypeAdapter adapter, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { return constraint.getMessage() == null ? ObjectUtils.notNull(String.format("Value '%s' did not conform to the data type '%s' at path '%s'", value, @@ -275,13 +293,16 @@ protected String newMatchDatatypeViolationMessage( * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newExpectViolationMessage( @NonNull IExpectConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { return constraint.getMessage() == null ? ObjectUtils.notNull(String.format("Expect constraint '%s' did not match the data at path '%s'", constraint.getTest().getPath(), @@ -351,6 +372,9 @@ protected String newIndexDuplicateViolationMessage( * the Metapath dynamic execution context to use for Metapath * evaluation * @return the new message + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull protected String newIndexMissMessage( @@ -358,7 +382,7 @@ protected String newIndexMissMessage( @NonNull INodeItem node, @NonNull INodeItem target, @NonNull List key, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { String keyValues = key.stream() .collect(Collectors.joining(",")); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractKeyConstraintBuilder.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractKeyConstraintBuilder.java index ff3d35e59..79fdc961b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractKeyConstraintBuilder.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractKeyConstraintBuilder.java @@ -12,6 +12,16 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Provides builder methods for the core data elements of an {@link IConstraint} + * that supports a custom message. + * + * @param + * the Java type of the implementing builder + * @param + * the Java type of the resulting built object + * @since 2.0.0 + */ public abstract class AbstractKeyConstraintBuilder< T extends AbstractKeyConstraintBuilder, R extends IKeyConstraint> diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ConstraintValidationException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ConstraintValidationException.java new file mode 100644 index 000000000..b13824091 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ConstraintValidationException.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.model.constraint; + +import edu.umd.cs.findbugs.annotations.Nullable; + +public class ConstraintValidationException + extends Exception { + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 2L; + + /** + * Constructs a new exception with the specified detail message. The cause is + * not initialized, and may subsequently be initialized by a call to + * {@link #initCause}. + * + * @param message + * the detail message, which is saved for later retrieval by the + * {@link #getMessage()} method. + */ + public ConstraintValidationException(@Nullable String message) { + super(message); + } + + /** + * Constructs a new exception with the specified cause. + *

+ * If the cause has a detail message (i.e. + * {@code (cause==null ? null : cause.toString())} then the message provided by + * the cause will be used. + *

+ * This constructor is useful for when this exception is raised as a wrapper of + * another exception. + * + * @param cause + * the cause (which is saved for later retrieval by the + * {@link #getCause()} method). A {@code null} value is permitted, and + * indicates that the cause is nonexistent or unknown. + */ + public ConstraintValidationException(@Nullable Throwable cause) { + super(cause); + } + + public ConstraintValidationException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ConstraintValidationFinding.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ConstraintValidationFinding.java index 6ec8c754c..ea3d4f981 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ConstraintValidationFinding.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ConstraintValidationFinding.java @@ -182,6 +182,9 @@ public static Builder builder(@NonNull IConstraint constraint, @NonNull INodeIte return new Builder(CollectionUtil.singletonList(constraint), node); } + /** + * Implements a builder pattern for creating constraint findings. + */ public static final class Builder { @NonNull private final List constraints; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java index 67b3ce176..d6618da9e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java @@ -26,6 +26,8 @@ import gov.nist.secauto.metaschema.core.model.IFlagDefinition; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.util.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.ExceptionUtils; +import gov.nist.secauto.metaschema.core.util.ExceptionUtils.WrappedException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.apache.commons.lang3.tuple.Pair; @@ -70,8 +72,10 @@ public class DefaultConstraintValidator @NonNull private final IMutableConfiguration> configuration; + private final boolean exceptionOnError = false; + /** - * Construct a new constraint validator instance. + * Construct a new constraint validation instance. * * @param handler * the validation handler to use for handling constraint violations @@ -138,8 +142,12 @@ protected IConstraintValidationHandler getConstraintValidationHandler() { @Override public void validate( @NonNull INodeItem item, - @NonNull DynamicContext dynamicContext) { - item.accept(new Visitor(), dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { + try { + item.accept(new Visitor(), dynamicContext); + } catch (WrappedException ex) { + ex.unwrapAndThrow(ConstraintValidationException.class); + } } /** @@ -159,10 +167,14 @@ protected void validateFlag( @NonNull DynamicContext dynamicContext) { IFlagDefinition definition = item.getDefinition(); - validateExpect(definition.getExpectConstraints(), item, dynamicContext); - validateAllowedValues(definition.getAllowedValuesConstraints(), item, dynamicContext); - validateIndexHasKey(definition.getIndexHasKeyConstraints(), item, dynamicContext); - validateMatches(definition.getMatchesConstraints(), item, dynamicContext); + try { + validateExpect(definition.getExpectConstraints(), item, dynamicContext); + validateAllowedValues(definition.getAllowedValuesConstraints(), item, dynamicContext); + validateIndexHasKey(definition.getIndexHasKeyConstraints(), item, dynamicContext); + validateMatches(definition.getMatchesConstraints(), item, dynamicContext); + } catch (ConstraintValidationException ex) { + throw ExceptionUtils.wrap(ex); + } } /** @@ -182,10 +194,14 @@ protected void validateField( @NonNull DynamicContext dynamicContext) { IFieldDefinition definition = item.getDefinition(); - validateExpect(definition.getExpectConstraints(), item, dynamicContext); - validateAllowedValues(definition.getAllowedValuesConstraints(), item, dynamicContext); - validateIndexHasKey(definition.getIndexHasKeyConstraints(), item, dynamicContext); - validateMatches(definition.getMatchesConstraints(), item, dynamicContext); + try { + validateExpect(definition.getExpectConstraints(), item, dynamicContext); + validateAllowedValues(definition.getAllowedValuesConstraints(), item, dynamicContext); + validateIndexHasKey(definition.getIndexHasKeyConstraints(), item, dynamicContext); + validateMatches(definition.getMatchesConstraints(), item, dynamicContext); + } catch (ConstraintValidationException ex) { + throw ExceptionUtils.wrap(ex); + } } /** @@ -196,22 +212,25 @@ protected void validateField( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation - * @throws MetapathException - * if an error occurred while evaluating a Metapath used in a - * constraint + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ protected void validateAssembly( @NonNull IAssemblyNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { IAssemblyDefinition definition = item.getDefinition(); - validateExpect(definition.getExpectConstraints(), item, dynamicContext); - validateAllowedValues(definition.getAllowedValuesConstraints(), item, dynamicContext); - validateIndexHasKey(definition.getIndexHasKeyConstraints(), item, dynamicContext); - validateMatches(definition.getMatchesConstraints(), item, dynamicContext); - validateHasCardinality(definition.getHasCardinalityConstraints(), item, dynamicContext); - validateIndex(definition.getIndexConstraints(), item, dynamicContext); - validateUnique(definition.getUniqueConstraints(), item, dynamicContext); + try { + validateExpect(definition.getExpectConstraints(), item, dynamicContext); + validateAllowedValues(definition.getAllowedValuesConstraints(), item, dynamicContext); + validateIndexHasKey(definition.getIndexHasKeyConstraints(), item, dynamicContext); + validateMatches(definition.getMatchesConstraints(), item, dynamicContext); + validateHasCardinality(definition.getHasCardinalityConstraints(), item, dynamicContext); + validateIndex(definition.getIndexConstraints(), item, dynamicContext); + validateUnique(definition.getUniqueConstraints(), item, dynamicContext); + } catch (ConstraintValidationException ex) { + throw ExceptionUtils.wrap(ex); + } } /** @@ -225,19 +244,19 @@ protected void validateAssembly( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateHasCardinality( // NOPMD false positive @NonNull List constraints, @NonNull IAssemblyNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { for (ICardinalityConstraint constraint : constraints) { assert constraint != null; - try { ISequence> targets = constraint.matchTargets(item, dynamicContext); validateHasCardinality(constraint, item, targets, dynamicContext); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -255,12 +274,14 @@ private void validateHasCardinality( // NOPMD false positive * @param targets * the focus of Metapath evaluation for evaluating any constraint * Metapath clauses + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ private void validateHasCardinality( @NonNull ICardinalityConstraint constraint, @NonNull IAssemblyNodeItem node, @NonNull ISequence targets, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { int itemCount = targets.size(); IConstraintValidationHandler handler = getConstraintValidationHandler(); @@ -294,19 +315,20 @@ private void validateHasCardinality( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateIndex( @NonNull List constraints, @NonNull IAssemblyNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { for (IIndexConstraint constraint : constraints) { assert constraint != null; try { ISequence> targets = constraint.matchTargets(item, dynamicContext); validateIndex(constraint, item, targets, dynamicContext); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -327,37 +349,50 @@ private void validateIndex( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ private void validateIndex( @NonNull IIndexConstraint constraint, @NonNull IAssemblyNodeItem node, @NonNull ISequence targets, - @NonNull DynamicContext dynamicContext) { - String indexName = constraint.getName(); - + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { IConstraintValidationHandler handler = getConstraintValidationHandler(); - if (indexNameToIndexMap.containsKey(indexName)) { + if (indexNameToIndexMap.containsKey(constraint.getName())) { handler.handleIndexDuplicateViolation(constraint, node, dynamicContext); } else { - IIndex index = IIndex.newInstance(constraint.getKeyFields()); - targets.stream() - .forEachOrdered(item -> { - assert item != null; - if (item.hasValue()) { - try { - INodeItem oldItem = index.put(item, dynamicContext); - if (oldItem == null) { - handlePass(constraint, node, item, dynamicContext); - } else { - handler.handleIndexDuplicateKeyViolation(constraint, node, oldItem, item, dynamicContext); - } - } catch (MetapathException ex) { - handler.handleKeyMatchError(constraint, node, item, ex, dynamicContext); - } - } - }); - indexNameToIndexMap.put(indexName, index); + validateIndexEntries(constraint, node, targets, dynamicContext); + } + } + + private void validateIndexEntries( + @NonNull IIndexConstraint constraint, + @NonNull IAssemblyNodeItem node, + @NonNull ISequence targets, + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { + IIndex index = IIndex.newInstance(constraint.getKeyFields()); + for (INodeItem item : targets) { + assert item != null; + if (item.hasValue()) { + INodeItem oldItem = null; + try { + oldItem = index.put(item, IIndex.toKey(item, index.getKeyFields(), dynamicContext)); + } catch (IllegalArgumentException ex) { + // throw by IIndex.toKey + handleError(constraint, item, ex, dynamicContext); + } + try { + if (oldItem == null) { + handlePass(constraint, node, item, dynamicContext); + } else { + handler.handleIndexDuplicateKeyViolation(constraint, node, oldItem, item, dynamicContext); + } + } catch (MetapathException ex) { + handler.handleKeyMatchError(constraint, node, item, ex, dynamicContext); + } + } } + indexNameToIndexMap.put(constraint.getName(), index); } private void handlePass( @@ -374,9 +409,16 @@ private void handleError( @NonNull IConstraint constraint, @NonNull INodeItem node, @NonNull Throwable ex, - @NonNull DynamicContext dynamicContext) { - getConstraintValidationHandler() - .handleError(constraint, node, toErrorMessage(constraint, node, ex), ex, dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { + if (exceptionOnError) { + if (ex instanceof ConstraintValidationException) { + throw (ConstraintValidationException) ex; + } + throw new ConstraintValidationException(ex); + } else { + getConstraintValidationHandler() + .handleError(constraint, node, toErrorMessage(constraint, node, ex), ex, dynamicContext); + } } @NonNull @@ -418,19 +460,20 @@ private static String toErrorMessage( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateUnique( @NonNull List constraints, @NonNull IAssemblyNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { for (IUniqueConstraint constraint : constraints) { assert constraint != null; try { ISequence> targets = constraint.matchTargets(item, dynamicContext); validateUnique(constraint, item, targets, dynamicContext); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -451,32 +494,40 @@ private void validateUnique( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ private void validateUnique( @NonNull IUniqueConstraint constraint, @NonNull IAssemblyNodeItem node, @NonNull ISequence targets, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { IConstraintValidationHandler handler = getConstraintValidationHandler(); IIndex index = IIndex.newInstance(constraint.getKeyFields()); - targets.stream() - .forEachOrdered(item -> { - assert item != null; - if (item.hasValue()) { - try { - INodeItem oldItem = index.put(item, dynamicContext); - if (oldItem == null) { - handlePass(constraint, node, item, dynamicContext); - } else { - handler.handleUniqueKeyViolation(constraint, node, oldItem, item, dynamicContext); - } - } catch (MetapathException ex) { - handler.handleKeyMatchError(constraint, node, item, ex, dynamicContext); - throw ex; - } + + for (INodeItem item : targets) { + assert item != null; + if (item.hasValue()) { + INodeItem oldItem = null; + try { + oldItem = index.put(item, IIndex.toKey(item, index.getKeyFields(), dynamicContext)); + } catch (IllegalArgumentException ex) { + // raised by IIndex.toKey + handleError(constraint, item, ex, dynamicContext); + } + + try { + if (oldItem == null) { + handlePass(constraint, node, item, dynamicContext); + } else { + handler.handleUniqueKeyViolation(constraint, node, oldItem, item, dynamicContext); } - }); + } catch (MetapathException ex) { + handleError(constraint, item, ex, dynamicContext); + } + } + } } /** @@ -490,12 +541,13 @@ private void validateUnique( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") - private void validateMatches( // NOPMD false positive + private void validateMatches( @NonNull List constraints, @NonNull IDefinitionNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { for (IMatchesConstraint constraint : constraints) { assert constraint != null; @@ -503,7 +555,7 @@ private void validateMatches( // NOPMD false positive try { ISequence> targets = constraint.matchTargets(item, dynamicContext); validateMatches(constraint, item, targets, dynamicContext); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -521,26 +573,27 @@ private void validateMatches( // NOPMD false positive * @param targets * the focus of Metapath evaluation for evaluating any constraint * Metapath clauses + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ private void validateMatches( @NonNull IMatchesConstraint constraint, @NonNull INodeItem node, @NonNull ISequence targets, - @NonNull DynamicContext dynamicContext) { - targets.stream() - .forEachOrdered(item -> { - assert item != null; - if (item.hasValue()) { - validateMatchesItem(constraint, node, item, dynamicContext); - } - }); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { + for (INodeItem item : targets) { + assert item != null; + if (item.hasValue()) { + validateMatchesItem(constraint, node, item, dynamicContext); + } + } } private void validateMatchesItem( @NonNull IMatchesConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { String value = item.toAtomicItem().asString(); IConstraintValidationHandler handler = getConstraintValidationHandler(); @@ -578,12 +631,13 @@ private void validateMatchesItem( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateIndexHasKey( // NOPMD false positive @NonNull List constraints, @NonNull IDefinitionNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { for (IIndexHasKeyConstraint constraint : constraints) { assert constraint != null; @@ -591,7 +645,7 @@ private void validateIndexHasKey( // NOPMD false positive try { ISequence> targets = constraint.matchTargets(item, dynamicContext); validateIndexHasKey(constraint, item, targets); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -636,19 +690,20 @@ private void validateIndexHasKey( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateExpect( @NonNull List constraints, @NonNull IDefinitionNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { for (IExpectConstraint constraint : constraints) { assert constraint != null; try { ISequence> targets = constraint.matchTargets(item, dynamicContext); validateExpect(constraint, item, targets, dynamicContext); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -669,34 +724,31 @@ private void validateExpect( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ private void validateExpect( @NonNull IExpectConstraint constraint, @NonNull INodeItem node, @NonNull ISequence targets, - @NonNull DynamicContext dynamicContext) { - try { - IMetapathExpression metapath = constraint.getTest(); - IConstraintValidationHandler handler = getConstraintValidationHandler(); - targets.stream() - .forEachOrdered(item -> { - assert item != null; - - if (item.hasValue()) { - try { - ISequence result = metapath.evaluate(item, dynamicContext); - if (FnBoolean.fnBoolean(result).toBoolean()) { - handlePass(constraint, node, item, dynamicContext); - } else { - handler.handleExpectViolation(constraint, node, item, dynamicContext); - } - } catch (MetapathException ex) { - handleError(constraint, item, ex, dynamicContext); - } - } - }); - } catch (MetapathException ex) { - handleError(constraint, node, ex, dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { + IMetapathExpression test = constraint.getTest(); + IConstraintValidationHandler handler = getConstraintValidationHandler(); + for (INodeItem item : targets) { + assert item != null; + + if (item.hasValue()) { + try { + ISequence result = test.evaluate(item, dynamicContext); + if (FnBoolean.fnBoolean(result).toBoolean()) { + handlePass(constraint, node, item, dynamicContext); + } else { + handler.handleExpectViolation(constraint, node, item, dynamicContext); + } + } catch (MetapathException ex) { + handleError(constraint, item, ex, dynamicContext); + } + } } } @@ -711,18 +763,19 @@ private void validateExpect( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateAllowedValues( @NonNull List constraints, @NonNull IDefinitionNodeItem item, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { for (IAllowedValuesConstraint constraint : constraints) { assert constraint != null; try { ISequence> targets = constraint.matchTargets(item, dynamicContext); validateAllowedValues(constraint, item, targets, dynamicContext); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -743,23 +796,24 @@ private void validateAllowedValues( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if an unexpected error occurred while validating a constraint */ - @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateAllowedValues( @NonNull IAllowedValuesConstraint constraint, @NonNull IDefinitionNodeItem node, @NonNull ISequence> targets, - @NonNull DynamicContext dynamicContext) { - targets.stream().forEachOrdered(item -> { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { + for (INodeItem item : targets) { assert item != null; if (item.hasValue()) { try { updateValueStatus(item, constraint, node); - } catch (RuntimeException ex) { + } catch (ConstraintValidationException ex) { handleError(constraint, item, ex, dynamicContext); } } - }); + } } /** @@ -772,11 +826,14 @@ private void validateAllowedValues( * @param node * the original focus of Metapath evaluation for identifying the * targets + * @throws ConstraintValidationException + * if an unexpected error occurred while registering the allowed + * values */ protected void updateValueStatus( @NonNull INodeItem targetItem, @NonNull IAllowedValuesConstraint allowedValues, - @NonNull IDefinitionNodeItem node) { + @NonNull IDefinitionNodeItem node) throws ConstraintValidationException { // constraint.getAllowedValues().containsKey(value) @Nullable @@ -786,7 +843,7 @@ protected void updateValueStatus( valueMap.put(targetItem, valueStatus); } - valueStatus.registerAllowedValue(allowedValues, node); + valueStatus.registerAllowedValues(allowedValues, node); } /** @@ -808,9 +865,8 @@ protected void handleAllowedValues( } } - @SuppressWarnings("PMD.AvoidCatchingGenericException") @Override - public void finalizeValidation(DynamicContext dynamicContext) { + public void finalizeValidation(DynamicContext dynamicContext) throws ConstraintValidationException { // key references for (Map.Entry> entry : indexNameToKeyRefMap.entrySet()) { String indexName = ObjectUtils.notNull(entry.getKey()); @@ -827,7 +883,7 @@ public void finalizeValidation(DynamicContext dynamicContext) { assert item != null; try { validateKeyRef(constraint, node, item, indexName, index, dynamicContext); - } catch (RuntimeException ex) { + } catch (MetapathException ex) { handleError(constraint, item, ex, dynamicContext); } } @@ -841,10 +897,16 @@ private void validateKeyRef( @NonNull INodeItem item, @NonNull String indexName, @Nullable IIndex index, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { IConstraintValidationHandler handler = getConstraintValidationHandler(); try { - List key = IIndex.toKey(item, constraint.getKeyFields(), dynamicContext); + List key; + try { + key = IIndex.toKey(item, constraint.getKeyFields(), dynamicContext); + } catch (IllegalArgumentException ex) { + handler.handleError(constraint, item, toErrorMessage(constraint, item, ex), ex, dynamicContext); + throw ex; + } if (index == null) { handler.handleMissingIndexViolation( @@ -884,9 +946,9 @@ public ValueStatus(@NonNull INodeItem item) { this.value = item.toAtomicItem().asString(); } - public void registerAllowedValue( + public void registerAllowedValues( @NonNull IAllowedValuesConstraint allowedValues, - @NonNull IDefinitionNodeItem node) { + @NonNull IDefinitionNodeItem node) throws ConstraintValidationException { IAllowedValuesConstraint.Extensible newExtensible = allowedValues.getExtensible(); if (newExtensible.ordinal() > extensible.ordinal()) { // record the most restrictive value @@ -894,10 +956,11 @@ public void registerAllowedValue( } else if (IAllowedValuesConstraint.Extensible.NONE.equals(newExtensible) && IAllowedValuesConstraint.Extensible.NONE.equals(extensible)) { // this is an error, where there are two none constraints that conflict - throw new MetapathException( - String.format( + // TODO: find a different exception type to use + throw new ConstraintValidationException( + ObjectUtils.notNull(String.format( "Multiple constraints matching path '%s' have scope='none', which prevents extension. Involved" + - " constraints are those: %s", + " constraints are: %s", Stream.concat( Stream.of(allowedValues), constraints.stream() @@ -906,14 +969,14 @@ public void registerAllowedValue( constraint -> IAllowedValuesConstraint.Extensible.NONE.equals(constraint.getExtensible()))) .map(IConstraint::getConstraintIdentity) .collect(Collectors.joining(", ", "{", "}")), - item.getMetapath())); + item.getMetapath()))); } else if (allowedValues.getExtensible().ordinal() < extensible.ordinal()) { - String msg = String.format( + String msg = ObjectUtils.notNull(String.format( "An allowed values constraint with an extensibility scope '%s'" + " exceeds the allowed scope '%s' at path '%s'", - allowedValues.getExtensible().name(), extensible.name(), item.getMetapath()); + allowedValues.getExtensible().name(), extensible.name(), item.getMetapath())); LOGGER.atError().log(msg); - throw new MetapathException(msg); + throw new ConstraintValidationException(msg); } this.constraints.add(Pair.of(allowedValues, node)); if (!allowedValues.isAllowedOther()) { @@ -1014,7 +1077,11 @@ public Void visitAssembly(@NonNull IAssemblyNodeItem item, DynamicContext contex IAssemblyDefinition definition = item.getDefinition(); DynamicContext effectiveContext = handleLetStatements(item, definition.getLetExpressions(), context); - validateAssembly(item, effectiveContext); + try { + validateAssembly(item, effectiveContext); + } catch (ConstraintValidationException ex) { + throw ExceptionUtils.wrap(ex); + } super.visitAssembly(item, effectiveContext); return null; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultScopedContraints.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultScopedContraints.java index f02a03be8..3e8f0e292 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultScopedContraints.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultScopedContraints.java @@ -13,8 +13,8 @@ import edu.umd.cs.findbugs.annotations.NonNull; /** - * Represents a collection of constraints to apply to a given module identified - * by the module's namespace and short name. + * A collection of constraints to apply to a given module identified by the + * module's namespace and short name. */ public class DefaultScopedContraints implements IScopedContraints { @NonNull diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FindingCollectingConstraintValidationHandler.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FindingCollectingConstraintValidationHandler.java index 259a4938c..a6d62d26c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FindingCollectingConstraintValidationHandler.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FindingCollectingConstraintValidationHandler.java @@ -95,7 +95,7 @@ public void handleCardinalityMinimumViolation( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem target, @NonNull ISequence testedItems, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, target) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) @@ -110,7 +110,7 @@ public void handleCardinalityMaximumViolation( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem target, @NonNull ISequence testedItems, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, target) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) @@ -126,7 +126,7 @@ public void handleIndexDuplicateKeyViolation( @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, node) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) @@ -141,7 +141,7 @@ public void handleUniqueKeyViolation( @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, node) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) @@ -174,7 +174,7 @@ public void handleMatchPatternViolation( @NonNull INodeItem target, @NonNull String value, @NonNull Pattern pattern, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, node) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) @@ -191,7 +191,7 @@ public void handleMatchDatatypeViolation( @NonNull String value, @NonNull IDataTypeAdapter adapter, @NonNull IllegalArgumentException cause, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, node) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) @@ -206,7 +206,7 @@ public void handleExpectViolation( @NonNull IExpectConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, node) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) @@ -251,7 +251,7 @@ public void handleIndexMiss( @NonNull INodeItem node, @NonNull INodeItem target, @NonNull List key, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { addFinding(ConstraintValidationFinding.builder(constraint, node) .severity(constraint.getLevel()) .kind(toKind(constraint.getLevel())) diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConfigurableMessageConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConfigurableMessageConstraint.java index af718b11d..fbb8863cd 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConfigurableMessageConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConfigurableMessageConstraint.java @@ -39,7 +39,10 @@ public interface IConfigurableMessageConstraint extends IConstraint { * @throws ConstraintInitializationException * if a custom message is not defined, which will occur if this method * is called while {@link #getMessage()} returns {@code null} + * @throws ConstraintValidationException + * if the custom message contains a Metapath expression that is + * invalid or if the expression failed to evaluate */ @NonNull - String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context); + String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context) throws ConstraintValidationException; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidationHandler.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidationHandler.java index e6327feab..d7b749826 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidationHandler.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidationHandler.java @@ -34,12 +34,15 @@ public interface IConstraintValidationHandler { * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleCardinalityMinimumViolation( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem target, @NonNull ISequence testedItems, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle a cardinality constraint maximum violation. @@ -53,12 +56,15 @@ void handleCardinalityMinimumViolation( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleCardinalityMaximumViolation( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem target, @NonNull ISequence testedItems, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle a duplicate index violation. @@ -94,13 +100,16 @@ void handleIndexDuplicateViolation( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleIndexDuplicateKeyViolation( @NonNull IIndexConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle an unique key violation. @@ -119,13 +128,16 @@ void handleIndexDuplicateKeyViolation( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleUniqueKeyViolation( @NonNull IUniqueConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle an error that occurred while generating a key. @@ -193,13 +205,16 @@ void handleMissingIndexViolation( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleIndexMiss( @NonNull IIndexHasKeyConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem target, @NonNull List key, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle a match pattern violation. @@ -220,6 +235,9 @@ void handleIndexMiss( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleMatchPatternViolation( @NonNull IMatchesConstraint constraint, @@ -227,7 +245,7 @@ void handleMatchPatternViolation( @NonNull INodeItem target, @NonNull String value, @NonNull Pattern pattern, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle a match data type violation. @@ -251,6 +269,9 @@ void handleMatchPatternViolation( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleMatchDatatypeViolation( @NonNull IMatchesConstraint constraint, @@ -259,7 +280,7 @@ void handleMatchDatatypeViolation( @NonNull String value, @NonNull IDataTypeAdapter adapter, @NonNull IllegalArgumentException cause, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle an expect test violation. @@ -276,12 +297,15 @@ void handleMatchDatatypeViolation( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException + * if the constraint has a custom message that contains a Metapath + * expression that is invalid or if the expression failed to evaluate */ void handleExpectViolation( @NonNull IExpectConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Handle an allowed values constraint violation. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidator.java index 3405382a1..156751dac 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidator.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraintValidator.java @@ -24,13 +24,14 @@ public interface IConstraintValidator { * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException * @throws MetapathException * if an error occurred while evaluating a Metapath used in a * constraint */ void validate( @NonNull INodeItem item, - @NonNull DynamicContext dynamicContext); + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException; /** * Complete any validations that require full analysis of the content model. @@ -38,9 +39,10 @@ void validate( * @param dynamicContext * the Metapath dynamic execution context to use for Metapath * evaluation + * @throws ConstraintValidationException * @throws MetapathException * if an error occurred while evaluating a Metapath used in a * constraint */ - void finalizeValidation(@NonNull DynamicContext dynamicContext); + void finalizeValidation(@NonNull DynamicContext dynamicContext) throws ConstraintValidationException; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java index 45914b155..56ab93236 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java @@ -10,7 +10,6 @@ import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; -import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.model.constraint.impl.DefaultIndex; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -24,6 +23,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * An index that can support the {@link IIndexConstraint}, + * {@link IIndexHasKeyConstraint}, and {@link IUniqueConstraint}. + */ public interface IIndex { /** @@ -64,22 +67,6 @@ static boolean isAllNulls(@NonNull Iterable key) { @NonNull List getKeyFields(); - /** - * Store the provided item in the index using the index's key field components - * to generate the key. - * - * @param item - * the item to store in the index - * @param dynamicContext - * the Metapath evaluation context - * @return the previous item stored in the index, or {@code null} otherwise - */ - @Nullable - default INodeItem put(@NonNull INodeItem item, @NonNull DynamicContext dynamicContext) { - List key = toKey(item, getKeyFields(), dynamicContext); - return put(item, key); - } - /** * Store the provided item using the provided key. * @@ -93,22 +80,6 @@ default INodeItem put(@NonNull INodeItem item, @NonNull DynamicContext dynamicCo @Nullable INodeItem put(@NonNull INodeItem item, @NonNull List key); - /** - * Retrieve the item from the index that matches the key generated by evaluating - * the index's default key field components against the provided item. - * - * @param item - * the item to store in the index - * @param dynamicContext - * the Metapath evaluation context - * @return the previous item stored in the index, or {@code null} otherwise - */ - @Nullable - default INodeItem get(@NonNull INodeItem item, @NonNull DynamicContext dynamicContext) { - List key = toKey(item, getKeyFields(), dynamicContext); - return get(key); - } - /** * Retrieve the item from the index that matches the provided key. * @@ -130,9 +101,17 @@ default INodeItem get(@NonNull INodeItem item, @NonNull DynamicContext dynamicCo * @param dynamicContext * the Metapath evaluation context * @return a new key + * @throws IllegalArgumentException + * if a key field has a configured pattern that fails to match the key + * item value or if the pattern is malformed + * @throws MetapathException + * if the evaluation of a key field's metapath resulted in an + * unexpected error */ @NonNull - static List toKey(@NonNull INodeItem item, @NonNull List keyFields, + static List toKey( + @NonNull INodeItem item, + @NonNull List keyFields, @NonNull DynamicContext dynamicContext) { return CollectionUtil.unmodifiableList( ObjectUtils.notNull(keyFields.stream() @@ -154,6 +133,12 @@ static List toKey(@NonNull INodeItem item, @NonNull List + * The provided pattern is expected to have a single matching group, which will + * contain the final key value on match * * @param keyItem * the node item used to form the key field @@ -191,21 +174,31 @@ private static String buildKeyItem( * @param keyValue * the current key value * @return the final key value + * @throws IllegalArgumentException + * if the provided key value does not match the provided pattern or if + * the pattern is malformed */ - private static String applyPattern(@NonNull IMetapathExpression keyMetapath, @NonNull String keyValue, + @Nullable + private static String applyPattern( + @NonNull IMetapathExpression keyMetapath, + @NonNull String keyValue, @NonNull Pattern pattern) { Matcher matcher = pattern.matcher(keyValue); if (!matcher.matches()) { - throw new MetapathException( + // TODO: use a different exception type? + throw new IllegalArgumentException( String.format("Key field declares the pattern '%s' which does not match the value '%s' of node '%s'", pattern.pattern(), keyValue, keyMetapath)); } if (matcher.groupCount() != 1) { - throw new MetapathException(String.format( - "The first group was not a match for value '%s' of node '%s' for key field pattern '%s'", - keyValue, keyMetapath, pattern.pattern())); + throw new IllegalArgumentException( + String.format("The first group was not a match for value '%s' of node '%s' for key field pattern '%s'", + keyValue, keyMetapath, pattern.pattern())); } - return matcher.group(1); + + String result = matcher.group(1); + + return result == null || result.isEmpty() ? null : result; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ILet.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ILet.java index c64ae358d..79dc7f236 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ILet.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ILet.java @@ -7,7 +7,6 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.impl.DefaultLet; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; @@ -46,21 +45,11 @@ static ILet of( @NonNull String valueExpression, @NonNull ISource source, @Nullable MarkupMultiline remarks) { - try { - return of( - name, - IMetapathExpression.lazyCompile(valueExpression, source.getStaticContext()), - source, - remarks); - } catch (MetapathException ex) { - throw new MetapathException( - String.format("Unable to compile the let expression '%s=%s'%s. %s", - name, - valueExpression, - source.getSource() == null ? "" : " in " + source.getSource(), - ex.getMessage()), - ex); - } + return of( + name, + IMetapathExpression.lazyCompile(valueExpression, source.getStaticContext()), + source, + remarks); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/LoggingConstraintValidationHandler.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/LoggingConstraintValidationHandler.java index ea5a50927..a74f6b2fd 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/LoggingConstraintValidationHandler.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/LoggingConstraintValidationHandler.java @@ -122,7 +122,7 @@ public void handleCardinalityMinimumViolation( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem node, @NonNull ISequence targets, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( @@ -143,7 +143,7 @@ public void handleCardinalityMaximumViolation( @NonNull ICardinalityConstraint constraint, @NonNull INodeItem node, @NonNull ISequence targets, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( @@ -165,7 +165,7 @@ public void handleIndexDuplicateKeyViolation( @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( @@ -188,7 +188,7 @@ public void handleUniqueKeyViolation( @NonNull INodeItem node, @NonNull INodeItem oldItem, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( @@ -231,7 +231,7 @@ public void handleMatchPatternViolation( @NonNull INodeItem target, @NonNull String value, @NonNull Pattern pattern, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( @@ -257,7 +257,7 @@ public void handleMatchDatatypeViolation( @NonNull String value, @NonNull IDataTypeAdapter adapter, @NonNull IllegalArgumentException cause, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( @@ -280,7 +280,7 @@ public void handleExpectViolation( @NonNull IExpectConstraint constraint, @NonNull INodeItem node, @NonNull INodeItem target, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( @@ -343,7 +343,7 @@ public void handleIndexMiss( @NonNull INodeItem node, @NonNull INodeItem target, @NonNull List key, - @NonNull DynamicContext dynamicContext) { + @NonNull DynamicContext dynamicContext) throws ConstraintValidationException { Level level = constraint.getLevel(); if (isLogged(level)) { logMessage( diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java index 0513c96b1..b2f6d5053 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java @@ -9,11 +9,13 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.InvalidMetapathGrammarException; import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintInitializationException; +import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationException; import gov.nist.secauto.metaschema.core.model.constraint.IConfigurableMessageConstraint; import gov.nist.secauto.metaschema.core.model.constraint.IConstraint; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -85,7 +87,8 @@ public String getMessage() { } @Override - public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context) { + public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context) + throws ConstraintValidationException { String message = getMessage(); if (message == null) { throw new ConstraintInitializationException( @@ -93,10 +96,9 @@ public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext c IConstraint.getConstraintIdentity(this), getSource().getLocationHint())); } - - return ObjectUtils.notNull(StringUtils.replaceTokens(message, METAPATH_VALUE_TEMPLATE_PATTERN, match -> { - String metapath = ObjectUtils.notNull(match.group(2)); - try { + try { + return ObjectUtils.notNull(StringUtils.replaceTokens(message, METAPATH_VALUE_TEMPLATE_PATTERN, match -> { + String metapath = ObjectUtils.notNull(match.group(2)); IMetapathExpression expr = IMetapathExpression.compile( metapath, // need to use the static context of the source to resolve prefixes, etc., since @@ -108,14 +110,19 @@ public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext c // here we are using the static context of the instance, since this is how // variables and nodes are resolved. context); - } catch (MetapathException ex) { - throw new MetapathException( - String.format("Unable to evaluate the message replacement expression '%s' in constraint '%s'. %s", - metapath, - IConstraint.getConstraintIdentity(this), - ex.getLocalizedMessage()), - ex); - } - }).toString()); + }).toString()); + } catch (InvalidMetapathGrammarException ex) { + throw new ConstraintValidationException( + String.format("Unable to compile a message replacement expression in constraint '%s'. %s", + IConstraint.getConstraintIdentity(this), + ex.getLocalizedMessage()), + ex); + } catch (MetapathException ex) { + throw new ConstraintValidationException( + String.format("Unable to evaluate a message replacement expression in constraint '%s'. %s", + IConstraint.getConstraintIdentity(this), + ex.getLocalizedMessage()), + ex); + } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValue.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValue.java index d1f219efc..f5de750fa 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValue.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValue.java @@ -12,6 +12,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * Organizes information for a single allowed value in a set of allowed values. + */ public class DefaultAllowedValue implements IAllowedValue { @NonNull private final String value; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java index fd1291c8f..834a52775 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java @@ -6,7 +6,6 @@ package gov.nist.secauto.metaschema.core.model.xml; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.AbstractLoader; import gov.nist.secauto.metaschema.core.model.IConstraintLoader; @@ -38,7 +37,6 @@ import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; -import org.apache.xmlbeans.impl.values.XmlValueNotSupportedException; import java.io.IOException; import java.net.URI; @@ -181,18 +179,7 @@ protected List parseScopedConstraints( assert scope != null; List targetedConstraints = new LinkedList<>(); // NOPMD - intentional - try { - SCOPE_PARSER.parse(source, scope, Pair.of(source, targetedConstraints)); - } catch (MetapathException | XmlValueNotSupportedException ex) { - if (ex.getCause() instanceof MetapathException) { - throw new MetapathException( - String.format("Unable to compile a Metapath in '%s'. %s", - source.getSource(), - ex.getLocalizedMessage()), - ex); - } - throw ex; - } + SCOPE_PARSER.parse(source, scope, Pair.of(source, targetedConstraints)); URI namespace = ObjectUtils.notNull(URI.create(scope.getMetaschemaNamespace())); String shortName = ObjectUtils.requireNonNull(scope.getMetaschemaShortName()); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java index f3c2f9fe1..a85bd4680 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java @@ -9,7 +9,6 @@ import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue; @@ -53,7 +52,6 @@ import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; -import org.apache.xmlbeans.impl.values.XmlValueNotSupportedException; import java.math.BigInteger; import java.util.List; @@ -253,18 +251,7 @@ private static void parse( @NonNull T constraints, @NonNull XmlObject xmlObject, @NonNull ISource source) { - try { - parser.parse(source, xmlObject, constraints); - } catch (MetapathException | XmlValueNotSupportedException ex) { - if (ex.getCause() instanceof MetapathException) { - throw new MetapathException( - String.format("Unable to compile a Metapath in '%s'. %s", - source.getSource(), - ex.getLocalizedMessage()), - ex); - } - throw ex; - } + parser.parse(source, xmlObject, constraints); } @SuppressWarnings("PMD.UnusedPrivateMethod") diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java index 8a0207953..87fbc1bdf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder; @@ -503,10 +504,10 @@ public static ICardinalityConstraint newCardinalityConstraint( public static ILet newLet( @NonNull ConstraintLetType xmlObject, @NonNull ISource source) { - + StaticContext staticContext = source.getStaticContext(); return ILet.of( - source.getStaticContext().parseVariableName(ObjectUtils.requireNonNull(xmlObject.getVar())), - IMetapathExpression.lazyCompile(ObjectUtils.notNull(xmlObject.getExpression()), source.getStaticContext()), + staticContext.parseVariableName(ObjectUtils.requireNonNull(xmlObject.getVar())), + IMetapathExpression.lazyCompile(ObjectUtils.notNull(xmlObject.getExpression()), staticContext), source, xmlObject.isSetRemarks() ? remarks(ObjectUtils.notNull(xmlObject.getRemarks())) diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/util/ExceptionUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/util/ExceptionUtils.java new file mode 100644 index 000000000..9463c83bb --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/util/ExceptionUtils.java @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.util; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Provides a means for throwing important checked exceptions over non-checked + * methods, e.g. lambda invocations. + *

+ * This capability should be used with care, and generally in limited + * circumstances. + */ +public final class ExceptionUtils { + @NonNull + public static WrappedException wrap(@NonNull Throwable ex) { + return new WrappedException(ex); + } + + @NonNull + public static WrappedException wrapAndThrow(@NonNull Throwable ex) { + return new WrappedException(ex); + } + + @NonNull + public static Throwable unwrap( + @NonNull WrappedException ex) { + return ex.unwrap(); + } + + @NonNull + public static E unwrap( + @NonNull WrappedException ex, + @NonNull Class wrappedExceptionClass) { + return ex.unwrap(wrappedExceptionClass); + } + + public static final class WrappedException + extends RuntimeException { + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 2L; + + public WrappedException(@NonNull Throwable cause) { + super(cause); + } + + @Override + public synchronized Throwable initCause(Throwable cause) { + throw new UnsupportedOperationException("must set cause in constructor"); + } + + @NonNull + public Throwable unwrap() { + return ObjectUtils.notNull(getCause()); + } + + @NonNull + public E unwrap(@NonNull Class wrappedExceptionClass) { + Throwable cause = unwrap(); + if (wrappedExceptionClass.isInstance(cause)) { + E unwrappedEx = wrappedExceptionClass.cast(cause); + unwrappedEx.addSuppressed(this); + return unwrappedEx; + } + throw new IllegalArgumentException( + String.format("Wrapped exception '%s' did not match excpeted type '%s'.", + cause.getClass().getName(), + wrappedExceptionClass.getName())); + } + + public void unwrapAndThrow() throws Throwable { + throw unwrap(); + } + + public void unwrapAndThrow(@NonNull Class wrappedExceptionClass) throws E { + throw unwrap(wrappedExceptionClass); + } + } + + private ExceptionUtils() { + // prevent construction + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpressionTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpressionTest.java index 89d31e20b..1ff709792 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpressionTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpressionTest.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.metapath; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -13,8 +14,12 @@ import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; -import org.junit.jupiter.api.Disabled; +import org.antlr.v4.runtime.misc.ParseCancellationException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvFileSource; + +import java.io.IOException; import io.hosuaby.inject.resources.junit.jupiter.GivenTextResource; import io.hosuaby.inject.resources.junit.jupiter.TestWithResources; @@ -28,12 +33,16 @@ class MetapathExpressionTest { // @GivenTextResource(from = "/incorrect-examples.txt", charset = "UTF-8") // String incorrectMetapathInstances; - @Test - @Disabled - void testCorrect() { - for (String line : correctMetapathInstances.split("\\r?\\n")) { - if (line.startsWith("# ")) { - continue; + @ParameterizedTest + @CsvFileSource(resources = "/correct-examples.txt", delimiter = ';') + void testCorrect(String line) { + if (!line.startsWith("# ") + && !line.contains("text()") + && !line.contains("number(") + && !line.contains("current(") + && !line.contains("last(")) { + if (line.endsWith(";")) { + line = line.substring(0, line.length() - 1); } // System.out.println(line); IMetapathExpression.compile(line); @@ -74,10 +83,13 @@ void test() { } @Test - void testMalformedIf() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + void testMalformedIf() throws IOException { + InvalidMetapathGrammarException thrown = assertThrows(InvalidMetapathGrammarException.class, () -> { IMetapathExpression.compile("if 'a' = '1.1.2' then true() else false()"); }); - assertEquals(StaticMetapathException.INVALID_PATH_GRAMMAR, ex.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(InvalidMetapathGrammarException.class) + .cause() + .isExactlyInstanceOf(ParseCancellationException.class); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/StaticContextTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/StaticContextTest.java index f2d5e69ac..45b230abc 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/StaticContextTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/StaticContextTest.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.metapath; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -141,11 +142,12 @@ void testVariableValues( @Test void lookupNonExistantDataType() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + StaticMetapathException thrown = assertThrows(StaticMetapathException.class, () -> { StaticContext.instance().lookupAtomicType("xs:string"); }); - - assertEquals(StaticMetapathException.PREFIX_NOT_EXPANDABLE, ex.getCode()); + assertThat(thrown) + .extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(StaticMetapathException.PREFIX_NOT_EXPANDABLE); } @Test diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/ArrowExpressionTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/ArrowExpressionTest.java index dbd0a97b2..aa0e0dbad 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/ArrowExpressionTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/ArrowExpressionTest.java @@ -7,19 +7,17 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.bool; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -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.ISequence; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -72,11 +70,15 @@ void testArrowExpressionWithUndefinedVariable() { .build(); DynamicContext dynamicContext = new DynamicContext(staticContext); - MetapathException ex = assertThrows( - MetapathException.class, + StaticMetapathException thrown = assertThrows(StaticMetapathException.class, () -> IMetapathExpression.compile("() => $ex:undefined()", staticContext) .evaluate(null, dynamicContext)); - assertEquals(StaticMetapathException.NOT_DEFINED, - ObjectUtils.requireNonNull((AbstractCodedMetapathException) ex.getCause()).getCode()); + assertThat(thrown) + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ex.getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.NOT_DEFINED); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java index e17699788..54c7a0b07 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCstVisitorTest.java @@ -10,6 +10,7 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.eqname; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.contains; @@ -26,7 +27,6 @@ import gov.nist.secauto.metaschema.core.metapath.IExpression; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; -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.antlr.FailingErrorListener; @@ -297,16 +297,16 @@ void testNamedFunctionRefNotFound( StaticContext staticContext = StaticContext.builder().build(); DynamicContext dynamicContext = new DynamicContext(staticContext); - MetapathException thrown = assertThrows(MetapathException.class, + StaticMetapathException thrown = assertThrows(StaticMetapathException.class, () -> IMetapathExpression.compile(metapath, staticContext).evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext)); - Throwable cause = thrown.getCause(); - - assertEquals( - StaticMetapathException.NO_FUNCTION_MATCH, - cause instanceof StaticMetapathException - ? ((StaticMetapathException) cause).getCode() - : null); + assertThat(thrown) + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ex.getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.NO_FUNCTION_MATCH); } static Stream testNamedFunctionRefCall() { diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPathTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPathTest.java index 2d54e8ab2..7d037855d 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPathTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPathTest.java @@ -10,8 +10,8 @@ import static org.mockito.Mockito.doReturn; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; +import gov.nist.secauto.metaschema.core.metapath.InvalidTreatTypeDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; @@ -54,11 +54,10 @@ void testRootSlashOnlyPathUsingNonDocument() { RootSlashOnlyPath expr = new RootSlashOnlyPath("test data"); DynamicContext dynamicContext = newDynamicContext(); - DynamicMetapathException thrown = assertThrows(DynamicMetapathException.class, () -> { - ISequence result = expr.accept(dynamicContext, ISequence.of(item)); - assertEquals(ISequence.of(item), result); + assertThrows(InvalidTreatTypeDynamicMetapathException.class, () -> { + expr.accept(dynamicContext, ISequence.of(item)) + // ensure the stream is processed + .safeStream(); }); - - assertEquals(DynamicMetapathException.TREAT_DOES_NOT_MATCH_TYPE, thrown.getCode()); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastTest.java index 05c872f4b..1f52242e5 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastTest.java @@ -13,11 +13,13 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.yearMonthDuration; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.InvalidMetapathGrammarException; import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; @@ -116,25 +118,46 @@ void testCast(@NonNull IAnyAtomicItem actual, @NonNull String singleType, @NonNu @Test void testInvalidTypePrefix() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + InvalidMetapathGrammarException thrown = assertThrows(InvalidMetapathGrammarException.class, () -> { IMetapathExpression.compile("'a' cast as foo:bar"); }); - assertEquals(StaticMetapathException.PREFIX_NOT_EXPANDABLE, ex.getCode()); + assertThat(thrown) + .cause() + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ((StaticMetapathException) ex).getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.PREFIX_NOT_EXPANDABLE); } @Test void testInvalidType() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + InvalidMetapathGrammarException thrown = assertThrows(InvalidMetapathGrammarException.class, () -> { IMetapathExpression.compile("'a' cast as meta:bar"); }); - assertEquals(StaticMetapathException.CAST_UNKNOWN_TYPE, ex.getCode()); + assertThat(thrown) + .cause() + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ((StaticMetapathException) ex).getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.CAST_UNKNOWN_TYPE); } @Test void testAnyAtomicType() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + InvalidMetapathGrammarException thrown = assertThrows(InvalidMetapathGrammarException.class, () -> { IMetapathExpression.compile("'a' cast as meta:any-atomic-type"); }); - assertEquals(StaticMetapathException.CAST_ANY_ATOMIC, ex.getCode()); + assertThat(thrown) + .cause() + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ((StaticMetapathException) ex).getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.CAST_ANY_ATOMIC); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastableTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastableTest.java index 5a774a4c4..daaf31326 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastableTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/type/CastableTest.java @@ -5,12 +5,13 @@ package gov.nist.secauto.metaschema.core.metapath.cst.type; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.InvalidMetapathGrammarException; import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -48,25 +49,46 @@ void testCastable(@NonNull IAnyAtomicItem actual, @NonNull String singleType) { @Test void testInvalidTypePrefix() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + InvalidMetapathGrammarException thrown = assertThrows(InvalidMetapathGrammarException.class, () -> { IMetapathExpression.compile("'a' castable as foo:bar"); }); - assertEquals(StaticMetapathException.PREFIX_NOT_EXPANDABLE, ex.getCode()); + assertThat(thrown) + .cause() + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ((StaticMetapathException) ex).getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.PREFIX_NOT_EXPANDABLE); } @Test void testInvalidType() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + InvalidMetapathGrammarException thrown = assertThrows(InvalidMetapathGrammarException.class, () -> { IMetapathExpression.compile("'a' castable as meta:bar"); }); - assertEquals(StaticMetapathException.CAST_UNKNOWN_TYPE, ex.getCode()); + assertThat(thrown) + .cause() + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ((StaticMetapathException) ex).getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.CAST_UNKNOWN_TYPE); } @Test void testAnyAtomicType() { - StaticMetapathException ex = assertThrows(StaticMetapathException.class, () -> { + InvalidMetapathGrammarException thrown = assertThrows(InvalidMetapathGrammarException.class, () -> { IMetapathExpression.compile("'a' castable as meta:any-atomic-type"); }); - assertEquals(StaticMetapathException.CAST_ANY_ATOMIC, ex.getCode()); + assertThat(thrown) + .cause() + .isExactlyInstanceOf(StaticMetapathException.class) + .extracting( + ex -> ex instanceof StaticMetapathException + ? ((StaticMetapathException) ex).getErrorCode().getCode() + : null) + .isEqualTo(StaticMetapathException.CAST_ANY_ATOMIC); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctionsTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctionsTest.java index fd8e1d943..310217cdf 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctionsTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/impl/OperationFunctionsTest.java @@ -14,12 +14,15 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.time; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.yearMonthDuration; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.from; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.function.ArithmeticFunctionException; +import gov.nist.secauto.metaschema.core.metapath.function.CastFunctionException; import gov.nist.secauto.metaschema.core.metapath.function.DateTimeFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; @@ -147,7 +150,12 @@ void testOpNumericDivideByZero( ArithmeticFunctionException thrown = assertThrows(ArithmeticFunctionException.class, () -> { OperationFunctions.opNumericDivide(dividend, divisor); }); - assertEquals(ArithmeticFunctionException.DIVISION_BY_ZERO, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(ArithmeticFunctionException.class) + .returns( + ArithmeticFunctionException.DIVISION_BY_ZERO, + from(ex -> ex.getErrorCode().getCode())) + .hasNoCause(); } } @@ -193,7 +201,12 @@ void testOpNumericIntegerDivideByZero( ArithmeticFunctionException thrown = assertThrows(ArithmeticFunctionException.class, () -> { OperationFunctions.opNumericIntegerDivide(dividend, divisor); }); - assertEquals(ArithmeticFunctionException.DIVISION_BY_ZERO, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(ArithmeticFunctionException.class) + .returns( + ArithmeticFunctionException.DIVISION_BY_ZERO, + from(ex -> ex.getErrorCode().getCode())) + .hasNoCause(); } } @@ -465,7 +478,13 @@ void testOpAddYearMonthDurationsOverflow() { IYearMonthDurationItem.valueOf("P" + Integer.MAX_VALUE + "Y"), IYearMonthDurationItem.valueOf("P" + Integer.MAX_VALUE + "Y")); }); - assertEquals(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(DateTimeFunctionException.class) + .returns( + DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, + from(ex -> ex.getErrorCode().getCode())) + .cause() + .isExactlyInstanceOf(ArithmeticException.class); } @Test @@ -486,7 +505,13 @@ void testOpSubtractYearMonthDurationsOverflow() { IYearMonthDurationItem.valueOf("-P" + Integer.MAX_VALUE + "Y"), IYearMonthDurationItem.valueOf("P" + Integer.MAX_VALUE + "Y")); }); - assertEquals(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(DateTimeFunctionException.class) + .returns( + DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, + from(ex -> ex.getErrorCode().getCode())) + .cause() + .isExactlyInstanceOf(ArithmeticException.class); } @Test @@ -518,7 +543,18 @@ void testOpMultiplyYearMonthDurationsOverflow() { IYearMonthDurationItem.valueOf("P" + Integer.MAX_VALUE + "Y"), IDecimalItem.valueOf("2.5")); }); - assertEquals(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(DateTimeFunctionException.class) + .returns( + DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, + from(ex -> ex.getErrorCode().getCode())) + .cause() + .isExactlyInstanceOf(CastFunctionException.class) + .returns( + CastFunctionException.INPUT_VALUE_TOO_LARGE, + from(ex -> ex instanceof CastFunctionException + ? ((CastFunctionException) ex).getErrorCode().getCode() + : null)); } @Test @@ -564,7 +600,12 @@ void testOpAddDayTimeDurationsOverflow() { IDayTimeDurationItem.valueOf("PT" + (Long.MAX_VALUE - 807) + "S"), IDayTimeDurationItem.valueOf("PT" + (Long.MAX_VALUE - 807) + "S")); }); - assertEquals(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(DateTimeFunctionException.class) + .hasCauseExactlyInstanceOf(ArithmeticException.class) + .returns( + DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, + from(ex -> ex.getErrorCode().getCode())); } @Test @@ -585,7 +626,12 @@ void testOpSubtractDayTimeDurationsOverflow() { IDayTimeDurationItem.valueOf("-PT" + (Long.MAX_VALUE - 807) + "S"), IDayTimeDurationItem.valueOf("PT" + (Long.MAX_VALUE - 807) + "S")); }); - assertEquals(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(DateTimeFunctionException.class) + .hasCauseExactlyInstanceOf(ArithmeticException.class) + .returns( + DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, + from(ex -> ex.getErrorCode().getCode())); } @Test @@ -606,7 +652,18 @@ void testOpMultiplyDayTimeDurationsOverflow() { IDayTimeDurationItem.valueOf("PT" + Long.MAX_VALUE / 2 + "S"), IDecimalItem.valueOf("5")); }); - assertEquals(DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, thrown.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(DateTimeFunctionException.class) + .returns( + DateTimeFunctionException.DURATION_OVERFLOW_UNDERFLOW_ERROR, + from(ex -> ex.getErrorCode().getCode())) + .cause() + .isExactlyInstanceOf(CastFunctionException.class) + .returns( + CastFunctionException.INPUT_VALUE_TOO_LARGE, + from(ex -> ex instanceof CastFunctionException + ? ((CastFunctionException) ex).getErrorCode().getCode() + : null)); } @Test diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvgTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvgTest.java index d36ab830d..c6a651b8f 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvgTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvgTest.java @@ -10,10 +10,8 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.yearMonthDuration; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; @@ -26,6 +24,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.List; import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; @@ -52,18 +51,20 @@ private static Stream provideValuesForAvg() { @ParameterizedTest @MethodSource("provideValuesForAvg") void testAvg(@Nullable IAnyAtomicItem expected, @NonNull IAnyAtomicItem... values) { - try { + List> arguments = CollectionUtil.singletonList(ISequence.of(values)); + if (expected == null) { + assertThrows(InvalidArgumentFunctionException.class, () -> { + FunctionTestBase.executeFunction( + FnAvg.SIGNATURE, + newDynamicContext(), + null, + arguments); + }); + } else { assertFunctionResult( FnAvg.SIGNATURE, ISequence.of(expected), - CollectionUtil.singletonList(ISequence.of(values))); - } catch (MetapathException ex) { - if (expected == null) { - assertAll( - () -> assertInstanceOf(InvalidArgumentFunctionException.class, ex.getCause())); - } else { - throw ex; - } + arguments); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBooleanTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBooleanTest.java index f41de971f..9f9021b7a 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBooleanTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBooleanTest.java @@ -10,11 +10,8 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.uri; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.yearMonthDuration; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; @@ -29,6 +26,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.List; import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; @@ -55,15 +53,20 @@ static Stream provideValues() { @ParameterizedTest @MethodSource("provideValues") void test(@Nullable IBooleanItem expected, @NonNull IItem... values) { - try { + List> arguments = CollectionUtil.singletonList(ISequence.of(values)); + if (expected == null) { + assertThrows(InvalidArgumentFunctionException.class, () -> { + FunctionTestBase.executeFunction( + FnBoolean.SIGNATURE, + newDynamicContext(), + null, + arguments); + }); + } else { assertFunctionResult( FnBoolean.SIGNATURE, ISequence.of(expected), - CollectionUtil.singletonList(ISequence.of(values))); - } catch (MetapathException ex) { - assertAll( - () -> assertNull(expected), - () -> assertInstanceOf(InvalidArgumentFunctionException.class, ex.getCause())); + arguments); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnExactlyOneTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnExactlyOneTest.java index 8b85cb303..4c7418819 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnExactlyOneTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnExactlyOneTest.java @@ -7,13 +7,12 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; -import static org.junit.jupiter.api.Assertions.assertAll; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; @@ -47,20 +46,15 @@ private static Stream provideValues() { // NOPMD - false positive @ParameterizedTest @MethodSource("provideValues") void test(@Nullable ISequence expected, @NonNull String metapath) { - try { + if (expected == null) { + InvalidArgumentFunctionException thrown = assertThrows(InvalidArgumentFunctionException.class, () -> { + IMetapathExpression.compile(metapath).evaluate(null, newDynamicContext()); + }); + assertThat(thrown).extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(InvalidArgumentFunctionException.INVALID_ARGUMENT_EXACTLY_ONE); + } else { assertEquals(expected, IMetapathExpression.compile(metapath) .evaluate(null, newDynamicContext())); - } catch (MetapathException ex) { - // FIXME: After refactoring the exception hierarchy, target the actual exception - Throwable cause = ex.getCause() == null ? ex.getCause() : ex.getCause().getCause(); - assertAll( - () -> assertNull(expected), - () -> assertEquals(InvalidArgumentFunctionException.class, cause.getClass()), - () -> assertEquals( - InvalidArgumentFunctionException.INVALID_ARGUMENT_EXACTLY_ONE, - cause instanceof InvalidArgumentFunctionException - ? ((InvalidArgumentFunctionException) cause).getCode() - : null)); } } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java index 9aaf85dd4..7aeb67a0f 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java @@ -5,19 +5,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; import gov.nist.secauto.metaschema.core.testing.model.mocking.MockedDocumentGenerator; import org.junit.jupiter.api.Test; @@ -72,37 +69,19 @@ void test(boolean expected, @NonNull String metapath) { void testContextAbsent() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { IMetapathExpression.compile("has-children()", dynamicContext.getStaticContext()) .evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(DynamicMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, cause instanceof DynamicMetapathException - ? ((DynamicMetapathException) cause).getCode() - : null)); } @Test void testNotANode() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(InvalidTypeMetapathException.class, () -> { IMetapathExpression.compile("has-children()", dynamicContext.getStaticContext()) .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(InvalidTypeMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException - ? ((TypeMetapathException) cause).getCode() - : null)); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java index 04c697714..ae3d85b25 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java @@ -6,19 +6,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.testing.model.mocking.MockedDocumentGenerator; @@ -77,19 +74,9 @@ void test(@NonNull String expectedValueMetapath, @NonNull String actualValuesMet @Test void testNotANode() { DynamicContext dynamicContext = newDynamicContext(); - - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(InvalidTypeMetapathException.class, () -> { IMetapathExpression.compile("innermost('test')", dynamicContext.getStaticContext()) .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(InvalidTypeMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException - ? ((TypeMetapathException) cause).getCode() - : null)); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java index a5d75bba5..355fef05a 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java @@ -5,19 +5,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.testing.model.mocking.MockedDocumentGenerator; @@ -81,37 +78,19 @@ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) { void testContextAbsent() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { IMetapathExpression.compile("local-name()", dynamicContext.getStaticContext()) .evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(DynamicMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, cause instanceof DynamicMetapathException - ? ((DynamicMetapathException) cause).getCode() - : null)); } @Test void testNotANode() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(InvalidTypeMetapathException.class, () -> { IMetapathExpression.compile("local-name()", dynamicContext.getStaticContext()) .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(InvalidTypeMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException - ? ((TypeMetapathException) cause).getCode() - : null)); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMatchesTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMatchesTest.java index 18f40ab92..321fd1f80 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMatchesTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMatchesTest.java @@ -8,13 +8,14 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.bool; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.from; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.regex.RegularExpressionMetapathException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; @@ -28,6 +29,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.List; +import java.util.regex.PatternSyntaxException; import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; @@ -78,46 +80,41 @@ void test(@NonNull IBooleanItem expected, @NonNull String metapath) { @Test void testInvalidPattern() { - RegularExpressionMetapathException throwable = assertThrows(RegularExpressionMetapathException.class, + RegularExpressionMetapathException thrown = assertThrows(RegularExpressionMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnMatches.SIGNATURE_TWO_ARG, - newDynamicContext(), - ISequence.empty(), - ObjectUtils.notNull(List.of(sequence(string("input")), sequence(string("pattern["))))); - } catch (MetapathException ex) { - Throwable cause = ex.getCause(); - if (cause != null) { - throw cause; - } - throw ex; - } + FunctionTestBase.executeFunction( + FnMatches.SIGNATURE_TWO_ARG, + newDynamicContext(), + ISequence.empty(), + ObjectUtils.notNull(List.of(sequence(string("input")), sequence(string("pattern["))))); }); - assertEquals(RegularExpressionMetapathException.INVALID_EXPRESSION, throwable.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(RegularExpressionMetapathException.class) + .hasCauseExactlyInstanceOf(PatternSyntaxException.class) + .returns( + RegularExpressionMetapathException.INVALID_EXPRESSION, + from(ex -> ex.getErrorCode().getCode())); + } @Test void testInvalidFlag() { - RegularExpressionMetapathException throwable = assertThrows(RegularExpressionMetapathException.class, + RegularExpressionMetapathException thrown = assertThrows(RegularExpressionMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnMatches.SIGNATURE_THREE_ARG, - newDynamicContext(), - ISequence.empty(), - ObjectUtils.notNull(List.of( - sequence(string("input")), - sequence(string("pattern")), - sequence(string("dsm"))))); - } catch (MetapathException ex) { - Throwable cause = ex.getCause(); - if (cause != null) { - throw cause; - } - throw ex; - } + FunctionTestBase.executeFunction( + FnMatches.SIGNATURE_THREE_ARG, + newDynamicContext(), + ISequence.empty(), + ObjectUtils.notNull(List.of( + sequence(string("input")), + sequence(string("pattern")), + sequence(string("dsm"))))); }); - assertEquals(RegularExpressionMetapathException.INVALID_FLAG, throwable.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(RegularExpressionMetapathException.class) + .hasNoCause() + .returns( + RegularExpressionMetapathException.INVALID_FLAG, + from(ex -> ex.getErrorCode().getCode())); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMaxTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMaxTest.java index 7726b9ff8..0b325e4a8 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMaxTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMaxTest.java @@ -9,10 +9,8 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.uri; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; @@ -23,6 +21,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.List; import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; @@ -46,18 +45,20 @@ private static Stream provideValuesMin() { @ParameterizedTest @MethodSource("provideValuesMin") void testMin(@Nullable IAnyAtomicItem expected, @NonNull IAnyAtomicItem... values) { - try { + List> arguments = CollectionUtil.singletonList(ISequence.of(values)); + if (expected == null) { + assertThrows(InvalidArgumentFunctionException.class, () -> { + FunctionTestBase.executeFunction( + FnMinMax.SIGNATURE_MIN, + newDynamicContext(), + null, + arguments); + }); + } else { assertFunctionResult( FnMinMax.SIGNATURE_MIN, ISequence.of(expected), - CollectionUtil.singletonList(ISequence.of(values))); - } catch (MetapathException ex) { - if (expected == null) { - assertAll( - () -> assertInstanceOf(InvalidArgumentFunctionException.class, ex.getCause())); - } else { - throw ex; - } + arguments); } } @@ -84,18 +85,20 @@ private static Stream provideValuesMax() { @ParameterizedTest @MethodSource("provideValuesMax") void testMax(@Nullable IAnyAtomicItem expected, @NonNull IAnyAtomicItem... values) { - try { + List> arguments = CollectionUtil.singletonList(ISequence.of(values)); + if (expected == null) { + assertThrows(InvalidArgumentFunctionException.class, () -> { + FunctionTestBase.executeFunction( + FnMinMax.SIGNATURE_MAX, + newDynamicContext(), + null, + arguments); + }); + } else { assertFunctionResult( FnMinMax.SIGNATURE_MAX, ISequence.of(expected), CollectionUtil.singletonList(ISequence.of(values))); - } catch (MetapathException ex) { - if (expected == null) { - assertAll( - () -> assertInstanceOf(InvalidArgumentFunctionException.class, ex.getCause())); - } else { - throw ex; - } } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java index 5ae111f4b..27a309930 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java @@ -5,19 +5,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.testing.model.mocking.MockedDocumentGenerator; @@ -80,37 +77,19 @@ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) { void testContextAbsent() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { IMetapathExpression.compile("name()", dynamicContext.getStaticContext()) .evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(DynamicMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, cause instanceof DynamicMetapathException - ? ((DynamicMetapathException) cause).getCode() - : null)); } @Test void testNotANode() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(InvalidTypeMetapathException.class, () -> { IMetapathExpression.compile("name()", dynamicContext.getStaticContext()) .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(InvalidTypeMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException - ? ((TypeMetapathException) cause).getCode() - : null)); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java index d5f96ea88..d06da856c 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java @@ -5,19 +5,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.testing.model.mocking.MockedDocumentGenerator; @@ -81,37 +78,19 @@ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) { void testContextAbsent() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { IMetapathExpression.compile("namespace-uri()", dynamicContext.getStaticContext()) .evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(DynamicMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, cause instanceof DynamicMetapathException - ? ((DynamicMetapathException) cause).getCode() - : null)); } @Test void testNotANode() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(InvalidTypeMetapathException.class, () -> { IMetapathExpression.compile("namespace-uri()", dynamicContext.getStaticContext()) .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(InvalidTypeMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException - ? ((TypeMetapathException) cause).getCode() - : null)); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNormalizeSpaceTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNormalizeSpaceTest.java index 25dbe5214..f4875349b 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNormalizeSpaceTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNormalizeSpaceTest.java @@ -10,10 +10,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.util.CollectionUtil; @@ -46,18 +45,13 @@ void testExpression(@NonNull IStringItem expected, @NonNull String metapath) { @Test void testNoFocus() { - DynamicMetapathException throwable = assertThrows(DynamicMetapathException.class, + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnNormalizeSpace.SIGNATURE_NO_ARG, - newDynamicContext(), - null, - CollectionUtil.singletonList(sequence())); - } catch (MetapathException ex) { - throw ex.getCause(); - } + FunctionTestBase.executeFunction( + FnNormalizeSpace.SIGNATURE_NO_ARG, + newDynamicContext(), + null, + CollectionUtil.singletonList(sequence())); }); - assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, throwable.getCode()); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNotTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNotTest.java index 848b09dfb..278282ae0 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNotTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNotTest.java @@ -10,11 +10,8 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.uri; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.yearMonthDuration; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; @@ -29,6 +26,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.List; import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; @@ -55,15 +53,20 @@ static Stream provideValues() { @ParameterizedTest @MethodSource("provideValues") void test(@Nullable IBooleanItem expected, @NonNull IItem... values) { - try { + List> arguments = CollectionUtil.singletonList(ISequence.of(values)); + if (expected == null) { + assertThrows(InvalidArgumentFunctionException.class, () -> { + FunctionTestBase.executeFunction( + FnNot.SIGNATURE, + newDynamicContext(), + null, + arguments); + }); + } else { assertFunctionResult( FnNot.SIGNATURE, ISequence.of(expected), - CollectionUtil.singletonList(ISequence.of(values))); - } catch (MetapathException ex) { - assertAll( - () -> assertNull(expected), - () -> assertInstanceOf(InvalidArgumentFunctionException.class, ex.getCause())); + arguments); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMoreTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMoreTest.java index 016f0cf5a..eaf8ad00d 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMoreTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMoreTest.java @@ -7,13 +7,12 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; -import static org.junit.jupiter.api.Assertions.assertAll; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; @@ -47,20 +46,15 @@ private static Stream provideValues() { // NOPMD - false positive @ParameterizedTest @MethodSource("provideValues") void test(@Nullable ISequence expected, @NonNull String metapath) { - try { + if (expected == null) { + InvalidArgumentFunctionException thrown = assertThrows(InvalidArgumentFunctionException.class, () -> { + IMetapathExpression.compile(metapath).evaluate(null, newDynamicContext()); + }); + assertThat(thrown).extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(InvalidArgumentFunctionException.INVALID_ARGUMENT_ONE_OR_MORE); + } else { assertEquals(expected, IMetapathExpression.compile(metapath) .evaluate(null, newDynamicContext())); - } catch (MetapathException ex) { - // FIXME: After refactoring the exception hierarchy, target the actual exception - Throwable cause = ex.getCause() == null ? ex.getCause() : ex.getCause().getCause(); - assertAll( - () -> assertNull(expected), - () -> assertEquals(InvalidArgumentFunctionException.class, cause == null ? null : cause.getClass()), - () -> assertEquals( - InvalidArgumentFunctionException.INVALID_ARGUMENT_ONE_OR_MORE, - cause instanceof InvalidArgumentFunctionException - ? ((InvalidArgumentFunctionException) cause).getCode() - : null)); } } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java index 9b0695044..b44cd65aa 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java @@ -6,19 +6,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.testing.model.mocking.MockedDocumentGenerator; @@ -89,18 +86,9 @@ void test(@NonNull String expectedValueMetapath, @NonNull String actualValuesMet void testNotANode() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(InvalidTypeMetapathException.class, () -> { IMetapathExpression.compile("outermost('test')", dynamicContext.getStaticContext()) .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(InvalidTypeMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException - ? ((TypeMetapathException) cause).getCode() - : null)); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java index 9013d6795..c8fcccb6c 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java @@ -5,19 +5,16 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; import gov.nist.secauto.metaschema.core.testing.model.mocking.MockedDocumentGenerator; import org.junit.jupiter.api.Test; @@ -69,37 +66,19 @@ void test(@NonNull String metapath) { void testContextAbsent() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { IMetapathExpression.compile("root()", dynamicContext.getStaticContext()) .evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(DynamicMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, cause instanceof DynamicMetapathException - ? ((DynamicMetapathException) cause).getCode() - : null)); } @Test void testNotANode() { DynamicContext dynamicContext = newDynamicContext(); - MetapathException ex = assertThrows(MetapathException.class, () -> { + assertThrows(InvalidTypeMetapathException.class, () -> { IMetapathExpression.compile("root()", dynamicContext.getStaticContext()) .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext); }); - Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null; - - assertAll( - () -> assertEquals(InvalidTypeMetapathException.class, cause == null - ? null - : cause.getClass()), - () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException - ? ((TypeMetapathException) cause).getCode() - : null)); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringLengthTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringLengthTest.java index 70dace849..39c2f30ae 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringLengthTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringLengthTest.java @@ -10,10 +10,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; @@ -61,18 +60,13 @@ void testFocusStringTest() { @Test void testNoFocus() { - DynamicMetapathException throwable = assertThrows(DynamicMetapathException.class, + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnStringLength.SIGNATURE_NO_ARG, - newDynamicContext(), - null, - CollectionUtil.singletonList(sequence())); - } catch (MetapathException ex) { - throw ex.getCause(); - } + FunctionTestBase.executeFunction( + FnStringLength.SIGNATURE_NO_ARG, + newDynamicContext(), + null, + CollectionUtil.singletonList(sequence())); }); - assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, throwable.getCode()); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringTest.java index 35fbd51c9..9c4956e78 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStringTest.java @@ -9,13 +9,13 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; +import gov.nist.secauto.metaschema.core.metapath.ContextAbsentDynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidTypeFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; @@ -58,34 +58,25 @@ void testExpression(@NonNull IStringItem expected, @NonNull String metapath) { @Test void testNoFocus() { - DynamicMetapathException throwable = assertThrows(DynamicMetapathException.class, + assertThrows(ContextAbsentDynamicMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnString.SIGNATURE_NO_ARG, - newDynamicContext(), - null, - CollectionUtil.singletonList(sequence())); - } catch (MetapathException ex) { - throw ex.getCause(); - } + FunctionTestBase.executeFunction( + FnString.SIGNATURE_NO_ARG, + newDynamicContext(), + null, + CollectionUtil.singletonList(sequence())); }); - assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, throwable.getCode()); } @Test void testInvalidArgument() { assertThrows(InvalidTypeMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnString.SIGNATURE_ONE_ARG, - newDynamicContext(), - ISequence.empty(), - ObjectUtils.notNull(List.of(sequence(integer(1), integer(2))))); - } catch (MetapathException ex) { - throw ex.getCause(); - } + FunctionTestBase.executeFunction( + FnString.SIGNATURE_ONE_ARG, + newDynamicContext(), + ISequence.empty(), + ObjectUtils.notNull(List.of(sequence(integer(1), integer(2))))); }); } @@ -93,20 +84,18 @@ void testInvalidArgument() { void testInvalidArgumentType() { InvalidTypeFunctionException throwable = assertThrows(InvalidTypeFunctionException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnString.SIGNATURE_ONE_ARG, - newDynamicContext(), - ISequence.empty(), - ObjectUtils.notNull(List.of( - sequence( - array( - array(integer(1), integer(2)), - array(integer(3), integer(4))))))); - } catch (MetapathException ex) { - throw ex.getCause(); - } + FunctionTestBase.executeFunction( + FnString.SIGNATURE_ONE_ARG, + newDynamicContext(), + ISequence.empty(), + ObjectUtils.notNull(List.of( + sequence( + array( + array(integer(1), integer(2)), + array(integer(3), integer(4))))))); }); - assertEquals(InvalidTypeFunctionException.ARGUMENT_TO_STRING_IS_FUNCTION, throwable.getCode()); + assertThat(throwable) + .extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(InvalidTypeFunctionException.ARGUMENT_TO_STRING_IS_FUNCTION); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSumTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSumTest.java index 2e7c317c3..fd353a3a7 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSumTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSumTest.java @@ -9,10 +9,8 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.yearMonthDuration; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; @@ -53,19 +51,21 @@ private static Stream provideValuesOneArg() { @ParameterizedTest @MethodSource("provideValuesOneArg") - void testAvg(@Nullable IAnyAtomicItem expected, @NonNull IAnyAtomicItem... values) { - try { + void testSum(@Nullable IAnyAtomicItem expected, @NonNull IAnyAtomicItem... values) { + List> arguments = CollectionUtil.singletonList(ISequence.of(values)); + if (expected == null) { + assertThrows(InvalidArgumentFunctionException.class, () -> { + FunctionTestBase.executeFunction( + FnSum.SIGNATURE_ONE_ARG, + newDynamicContext(), + null, + arguments); + }); + } else { assertFunctionResult( FnSum.SIGNATURE_ONE_ARG, ISequence.of(expected), - CollectionUtil.singletonList(ISequence.of(values))); - } catch (MetapathException ex) { - if (expected == null) { - assertAll( - () -> assertInstanceOf(InvalidArgumentFunctionException.class, ex.getCause())); - } else { - throw ex; - } + arguments); } } @@ -84,25 +84,27 @@ private static Stream provideValuesTwoArg() { @ParameterizedTest @MethodSource("provideValuesTwoArg") - void testAvgWithZero(@Nullable IAnyAtomicItem expected, @Nullable IAnyAtomicItem zero, + void testSumWithZero(@Nullable IAnyAtomicItem expected, @Nullable IAnyAtomicItem zero, @NonNull IAnyAtomicItem... values) { - try { + List> arguments = ObjectUtils.notNull(List.of(ISequence.of(values), ISequence.of(zero))); + if (expected == null) { + assertThrows(InvalidArgumentFunctionException.class, () -> { + FunctionTestBase.executeFunction( + FnSum.SIGNATURE_TWO_ARG, + newDynamicContext(), + null, + arguments); + }); + } else { assertFunctionResult( FnSum.SIGNATURE_TWO_ARG, ISequence.of(expected), - ObjectUtils.notNull(List.of(ISequence.of(values), ISequence.of(zero)))); - } catch (MetapathException ex) { - if (expected == null) { - assertAll( - () -> assertInstanceOf(InvalidArgumentFunctionException.class, ex.getCause())); - } else { - throw ex; - } + arguments); } } @Test - void testAvgNoOpTwoArg() { + void testSumNoOpTwoArg() { assertFunctionResult( FnSum.SIGNATURE_TWO_ARG, ISequence.empty(), diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTokenizeTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTokenizeTest.java index a44f7c390..911ba7f7d 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTokenizeTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTokenizeTest.java @@ -7,12 +7,12 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.regex.RegularExpressionMetapathException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -23,6 +23,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.List; +import java.util.regex.PatternSyntaxException; import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; @@ -58,69 +59,58 @@ void test(@NonNull ISequence expected, @NonNull String metapath) { assertEquals(expected, IMetapathExpression.compile(metapath).evaluate(null, newDynamicContext())); } + // TODO: make sure this (and others) exception chain is flattened @Test void testMatchZeroLengthString() { - RegularExpressionMetapathException throwable = assertThrows(RegularExpressionMetapathException.class, + RegularExpressionMetapathException thrown = assertThrows(RegularExpressionMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnTokenize.SIGNATURE_TWO_ARG, - newDynamicContext(), - ISequence.empty(), - ObjectUtils.notNull(List.of(sequence(string("abba")), sequence(string(".?"))))); - } catch (MetapathException ex) { - Throwable cause = ex.getCause(); - if (cause != null) { - throw cause; - } - throw ex; - } + FunctionTestBase.executeFunction( + FnTokenize.SIGNATURE_TWO_ARG, + newDynamicContext(), + ISequence.empty(), + ObjectUtils.notNull(List.of(sequence(string("abba")), sequence(string(".?"))))); }); - assertEquals(RegularExpressionMetapathException.MATCHES_ZERO_LENGTH_STRING, throwable.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(RegularExpressionMetapathException.class) + .hasNoCause() + .extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(RegularExpressionMetapathException.MATCHES_ZERO_LENGTH_STRING); } @Test void testInvalidPattern() { - RegularExpressionMetapathException throwable = assertThrows(RegularExpressionMetapathException.class, + RegularExpressionMetapathException thrown = assertThrows(RegularExpressionMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnTokenize.SIGNATURE_TWO_ARG, - newDynamicContext(), - ISequence.empty(), - ObjectUtils.notNull(List.of(sequence(string("input")), sequence(string("pattern["))))); - } catch (MetapathException ex) { - Throwable cause = ex.getCause(); - if (cause != null) { - throw cause; - } - throw ex; - } + FunctionTestBase.executeFunction( + FnTokenize.SIGNATURE_TWO_ARG, + newDynamicContext(), + ISequence.empty(), + ObjectUtils.notNull(List.of(sequence(string("input")), sequence(string("pattern["))))); }); - assertEquals(RegularExpressionMetapathException.INVALID_EXPRESSION, throwable.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(RegularExpressionMetapathException.class) + .hasCauseExactlyInstanceOf(PatternSyntaxException.class) + .extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(RegularExpressionMetapathException.INVALID_EXPRESSION); } @Test void testInvalidFlag() { - RegularExpressionMetapathException throwable = assertThrows(RegularExpressionMetapathException.class, + RegularExpressionMetapathException thrown = assertThrows(RegularExpressionMetapathException.class, () -> { - try { - FunctionTestBase.executeFunction( - FnTokenize.SIGNATURE_THREE_ARG, - newDynamicContext(), - ISequence.empty(), - ObjectUtils.notNull(List.of( - sequence(string("input")), - sequence(string("pattern")), - sequence(string("dsm"))))); - } catch (MetapathException ex) { - Throwable cause = ex.getCause(); - if (cause != null) { - throw cause; - } - throw ex; - } + FunctionTestBase.executeFunction( + FnTokenize.SIGNATURE_THREE_ARG, + newDynamicContext(), + ISequence.empty(), + ObjectUtils.notNull(List.of( + sequence(string("input")), + sequence(string("pattern")), + sequence(string("dsm"))))); }); - assertEquals(RegularExpressionMetapathException.INVALID_FLAG, throwable.getCode()); + assertThat(thrown) + .isExactlyInstanceOf(RegularExpressionMetapathException.class) + .hasNoCause() + .extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(RegularExpressionMetapathException.INVALID_FLAG); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnZeroOrOneTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnZeroOrOneTest.java index 048b64420..37ed5bf93 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnZeroOrOneTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnZeroOrOneTest.java @@ -7,13 +7,12 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; -import static org.junit.jupiter.api.Assertions.assertAll; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidArgumentFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; @@ -47,20 +46,15 @@ private static Stream provideValues() { // NOPMD - false positive @ParameterizedTest @MethodSource("provideValues") void test(@Nullable ISequence expected, @NonNull String metapath) { - try { + if (expected == null) { + InvalidArgumentFunctionException thrown = assertThrows(InvalidArgumentFunctionException.class, () -> { + IMetapathExpression.compile(metapath).evaluate(null, newDynamicContext()); + }); + assertThat(thrown).extracting(ex -> ex.getErrorCode().getCode()) + .isEqualTo(InvalidArgumentFunctionException.INVALID_ARGUMENT_ZERO_OR_ONE); + } else { assertEquals(expected, IMetapathExpression.compile(metapath) .evaluate(null, newDynamicContext())); - } catch (MetapathException ex) { - // FIXME: After refactoring the exception hierarchy, target the actual exception - Throwable cause = ex.getCause() == null ? ex.getCause() : ex.getCause().getCause(); - assertAll( - () -> assertNull(expected), - () -> assertEquals(InvalidArgumentFunctionException.class, cause == null ? null : cause.getClass()), - () -> assertEquals( - InvalidArgumentFunctionException.INVALID_ARGUMENT_ZERO_OR_ONE, - cause instanceof InvalidArgumentFunctionException - ? ((InvalidArgumentFunctionException) cause).getCode() - : null)); } } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java index 8fd319fcb..fef6d728b 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java @@ -11,6 +11,7 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.map; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -154,11 +155,7 @@ void testUnaryLookupMissingMember() { assertNotNull(result); result.safeStream(); }); - Throwable cause = thrown.getCause(); - assertEquals( - ArrayException.INDEX_OUT_OF_BOUNDS, - cause instanceof ArrayException - ? ((ArrayException) cause).getCode() - : null); + assertThat(thrown) + .isExactlyInstanceOf(IndexOutOfBoundsArrayMetapathException.class); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidatorTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidatorTest.java index 775f16974..ded14d332 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidatorTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidatorTest.java @@ -51,7 +51,7 @@ private static IEnhancedQName qname(@NonNull String name) { @SuppressWarnings("null") @Test - void testAllowedValuesAllowOther() { + void testAllowedValuesAllowOther() throws ConstraintValidationException { MockNodeItemFactory itemFactory = new MockNodeItemFactory(); IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("value")); @@ -92,7 +92,7 @@ void testAllowedValuesAllowOther() { @SuppressWarnings("null") @Test - void testAllowedValuesMultipleAllowOther() { + void testAllowedValuesMultipleAllowOther() throws ConstraintValidationException { MockNodeItemFactory itemFactory = new MockNodeItemFactory(); IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("value")); @@ -143,7 +143,7 @@ void testAllowedValuesMultipleAllowOther() { @SuppressWarnings("null") @Test - void testMultipleAllowedValuesConflictingAllowOther() { + void testMultipleAllowedValuesConflictingAllowOther() throws ConstraintValidationException { MockNodeItemFactory itemFactory = new MockNodeItemFactory(); IFlagNodeItem flag1 = itemFactory.flag(qname("value"), IStringItem.valueOf("value")); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraintTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraintTest.java index 5bd3bf03a..654b2b364 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraintTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraintTest.java @@ -15,6 +15,7 @@ import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.model.ISource; +import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationException; import gov.nist.secauto.metaschema.core.model.constraint.DefaultConstraintValidator; import gov.nist.secauto.metaschema.core.model.constraint.FindingCollectingConstraintValidationHandler; import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint; @@ -29,7 +30,7 @@ class AbstractConfigurableMessageConstraintTest extends ExpressionTestBase { @Test - void testDifferentNS() { + void testDifferentNS() throws ConstraintValidationException { StaticContext constraintContext = StaticContext.builder() .defaultModelNamespace(NS) .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) @@ -56,7 +57,7 @@ void testDifferentNS() { } @Test - void testWildCard() { + void testWildCard() throws ConstraintValidationException { StaticContext constraintContext = StaticContext.builder() .defaultModelNamespace(NS) .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) @@ -83,7 +84,7 @@ void testWildCard() { } @Test - void testPrefix() { + void testPrefix() throws ConstraintValidationException { StaticContext constraintContext = StaticContext.builder() .defaultModelNamespace(NS) .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) @@ -111,7 +112,7 @@ void testPrefix() { } @Test - void testQualifiedName() { + void testQualifiedName() throws ConstraintValidationException { StaticContext constraintContext = StaticContext.builder() .defaultModelNamespace(NS) .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/model/xml/MetaConstraintLoaderTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/model/xml/MetaConstraintLoaderTest.java index 1841dbf7a..037e68426 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/model/xml/MetaConstraintLoaderTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/model/xml/MetaConstraintLoaderTest.java @@ -9,8 +9,6 @@ import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.function.library.FnPath; -import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IModuleNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory; @@ -44,14 +42,15 @@ void test() throws MetaschemaException, IOException { IMetapathExpression expression = IMetapathExpression.compile("//@id", module.getModuleStaticContext()); IModuleNodeItem moduleItem = INodeItemFactory.instance().newModuleNodeItem(module); - for (IItem item : expression.evaluate(moduleItem)) { - IDefinitionNodeItem nodeItem = (IDefinitionNodeItem) item; - System.out.print(FnPath.fnPath(nodeItem)); - System.out.print(": "); - System.out.println(Long.toString(nodeItem.getDefinition().getMatchesConstraints().stream() - .filter(matches -> MetaschemaDataTypeProvider.UUID.equals(matches.getDataType())) - .count())); - } + // for (IItem item : expression.evaluate(moduleItem)) { + // IDefinitionNodeItem nodeItem = (IDefinitionNodeItem) item; + // System.out.print(FnPath.fnPath(nodeItem)); + // System.out.print(": "); + // System.out.println(Long.toString(nodeItem.getDefinition().getMatchesConstraints().stream() + // .filter(matches -> + // MetaschemaDataTypeProvider.UUID.equals(matches.getDataType())) + // .count())); + // } expression.evaluate(moduleItem).stream() .map(item -> (IDefinitionNodeItem) item) diff --git a/core/src/test/resources/correct-examples.txt b/core/src/test/resources/correct-examples.txt index 52548c8f0..401bf5cd7 100644 --- a/core/src/test/resources/correct-examples.txt +++ b/core/src/test/resources/correct-examples.txt @@ -322,7 +322,7 @@ text; //button[text()="Submit"]; //c/@x; //center; -//center//processing-instruction('b-pi'); +#//center//processing-instruction('b-pi'); //center/@center-attr-1; //center/@center-attr-3; //center/text()[1]; @@ -525,7 +525,7 @@ text; @val; @value; @width; -@xml:att1; +#@xml:att1; @xx; @x|@z; @z; @@ -668,9 +668,9 @@ count(a[string-length(@ex) > 0]); count(a[string-length(@ex)=0]); count(alpha/*[last()][name()='z']); count(div); -count(id('c')); -count(id('d b c k')); -count(id('k')); +#count(id('c')); +#count(id('d b c k')); +#count(id('k')); count(section[1]//@*); count(section[1]//@title); count(section[2]//@*); @@ -747,20 +747,20 @@ foo[(bar[2][(baz[2])='goodbye'])]; grandchild; growth; human; -id('c a d'); -id('c')/@id; -id('d b c'); -id('id0')/a/b; -id('id0')/c/c/a; -id('id10')/a; -id('id13'); -id('id2')/a | id('id5') | id('id15')/a; -id('id4'); -id('id8')/b/b; -id('id9'); -id('nomatch')/@id; -id(main/@list); -id(main/b); +#id('c a d'); +#id('c')/@id; +#id('d b c'); +#id('id0')/a/b; +#id('id0')/c/c/a; +#id('id10')/a; +#id('id13'); +#id('id2')/a | id('id5') | id('id15')/a; +#id('id4'); +#id('id8')/b/b; +#id('id9'); +#id('nomatch')/@id; +#id(main/@list); +#id(main/b); inner; item; item[1][last()]; @@ -869,7 +869,7 @@ Personal_Information/Age; pos-bad; primary/name/first; primary/name/last; -processing-instruction(); +#processing-instruction(); r; revenue; root/x; @@ -991,7 +991,7 @@ text()[1]; text()[3]; time; title; -town[generate-id() = generate-id(key('places',@state)[1])]; +#town[generate-id() = generate-id(key('places',@state)[1])]; translate("BAR","abc","ABC"); translate("BAR","Rab","TxX"); translate("bar","RAB","xyz"); @@ -1019,6 +1019,6 @@ z[1]; zoneone; xs:date("2000-01-01"); xs:dayTimeDuration("P21D"); -17 cast as apple; -apple(17); +#17 cast as apple; +#apple(17); //product[id = 47]; diff --git a/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java b/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java index f9e842c98..e5c7e35f5 100644 --- a/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java +++ b/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java @@ -9,6 +9,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.IResourceLocation; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationFinding; import gov.nist.secauto.metaschema.core.model.constraint.IConstraint; import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level; @@ -292,7 +293,7 @@ private Sarif generateSarif(@NonNull URI outputUri) throws IOException { */ @NonNull public String writeToString(@NonNull IBindingContext bindingContext) throws IOException { - bindingContext.registerModule(SarifModule.class); + registerSarifMetaschemaModule(bindingContext); try (StringWriter writer = new StringWriter()) { bindingContext.newSerializer(Format.JSON, Sarif.class) .disableFeature(SerializationFeature.SERIALIZE_ROOT) @@ -319,7 +320,7 @@ public void write( URI output = ObjectUtils.notNull(outputFile.toUri()); Sarif sarif = generateSarif(output); - bindingContext.registerModule(SarifModule.class); + registerSarifMetaschemaModule(bindingContext); bindingContext.newSerializer(Format.JSON, Sarif.class) .disableFeature(SerializationFeature.SERIALIZE_ROOT) .serialize( @@ -330,6 +331,14 @@ public void write( StandardOpenOption.TRUNCATE_EXISTING); } + private static void registerSarifMetaschemaModule(@NonNull IBindingContext bindingContext) { + try { + bindingContext.registerModule(SarifModule.class); + } catch (MetaschemaException ex) { + throw new IllegalStateException("Unable to register the builtin SARIF module.", ex); + } + } + private interface IResult { @NonNull IValidationFinding getFinding(); diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/AbstractModuleLoaderStrategy.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/AbstractModuleLoaderStrategy.java index f64c771d1..e0dc9b9af 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/AbstractModuleLoaderStrategy.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/AbstractModuleLoaderStrategy.java @@ -7,8 +7,11 @@ import gov.nist.secauto.metaschema.core.model.IBoundObject; import gov.nist.secauto.metaschema.core.model.IModule; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.model.constraint.DefaultConstraintValidator; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.util.ExceptionUtils; +import gov.nist.secauto.metaschema.core.util.ExceptionUtils.WrappedException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.IBindingContext.IBindingMatcher; import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly; @@ -72,9 +75,10 @@ public IBoundModule loadModule( } @Override + @SuppressWarnings("PMD.ExceptionAsFlowControl") public IBoundModule registerModule( IModule module, - IBindingContext bindingContext) { + IBindingContext bindingContext) throws MetaschemaException { modulesLock.lock(); try { return ObjectUtils.notNull(moduleToBoundModuleMap.computeIfAbsent(module, key -> { @@ -84,8 +88,12 @@ public IBoundModule registerModule( if (key instanceof IBoundModule) { boundModule = (IBoundModule) key; } else { - Class moduleClass = handleUnboundModule(key); - boundModule = lookupInstance(moduleClass, bindingContext); + try { + Class moduleClass = handleUnboundModule(key); + boundModule = lookupInstance(moduleClass, bindingContext); + } catch (MetaschemaException ex) { + throw ExceptionUtils.wrap(ex); + } } boundModule.getExportedAssemblyDefinitions().forEach(assembly -> { @@ -98,13 +106,15 @@ public IBoundModule registerModule( return boundModule; })); + } catch (WrappedException ex) { + throw ExceptionUtils.unwrap(ex, MetaschemaException.class); } finally { modulesLock.unlock(); } } @NonNull - protected abstract Class handleUnboundModule(@NonNull IModule key); + protected abstract Class handleUnboundModule(@NonNull IModule key) throws MetaschemaException; /** * Get the Module instance for a given class annotated by the diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/DefaultBindingContext.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/DefaultBindingContext.java index c6dfad591..070a6fa03 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/DefaultBindingContext.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/DefaultBindingContext.java @@ -98,7 +98,11 @@ public DefaultBindingContext() { public DefaultBindingContext(@NonNull IBindingContext.IModuleLoaderStrategy strategy) { // only allow extended classes moduleLoaderStrategy = strategy; - registerModule(MetaschemaModelModule.class); + try { + registerModule(MetaschemaModelModule.class); + } catch (MetaschemaException ex) { + throw new IllegalStateException("Unable to register the builtin Metaschema module.", ex); + } } @Override @@ -114,20 +118,20 @@ public IBindingModuleLoader newModuleLoader() { @Override @NonNull - public final IBoundModule registerModule(@NonNull Class clazz) { + public final IBoundModule registerModule(@NonNull Class clazz) throws MetaschemaException { IModuleLoaderStrategy strategy = getModuleLoaderStrategy(); IBoundModule module = strategy.loadModule(clazz, this); registerImportedModules(module); return strategy.registerModule(module, this); } - private void registerImportedModules(@NonNull IBoundModule module) { + private void registerImportedModules(@NonNull IBoundModule module) throws MetaschemaException { IModuleLoaderStrategy strategy = getModuleLoaderStrategy(); - module.getImportedModules().stream() - .forEachOrdered(parentModule -> { - registerImportedModules(ObjectUtils.notNull(parentModule)); - strategy.registerModule(ObjectUtils.notNull(parentModule), this); - }); + for (IBoundModule parentModule : module.getImportedModules()) { + assert parentModule != null; + registerImportedModules(parentModule); + strategy.registerModule(parentModule, this); + } } /** diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/IBindingContext.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/IBindingContext.java index aa3985eaf..66849402b 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/IBindingContext.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/IBindingContext.java @@ -17,6 +17,7 @@ import gov.nist.secauto.metaschema.core.model.IModule; import gov.nist.secauto.metaschema.core.model.IModuleLoader; import gov.nist.secauto.metaschema.core.model.MetaschemaException; +import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationException; import gov.nist.secauto.metaschema.core.model.constraint.DefaultConstraintValidator; import gov.nist.secauto.metaschema.core.model.constraint.ExternalConstraintsModulePostProcessor; import gov.nist.secauto.metaschema.core.model.constraint.FindingCollectingConstraintValidationHandler; @@ -244,9 +245,11 @@ default IConstraintLoader newConstraintLoader() { * @param clazz * the class implementing a bound Metaschema module * @return the loaded module + * @throws MetaschemaException + * if an error occurred while registering the module */ @NonNull - IBoundModule registerModule(@NonNull Class clazz); + IBoundModule registerModule(@NonNull Class clazz) throws MetaschemaException; /** * Registers the provided Metaschema module with this binding context. @@ -259,6 +262,8 @@ default IConstraintLoader newConstraintLoader() { * the Module module to generate classes for * @return the registered module, which may be a different instance than what * was provided if dynamic compilation was performed + * @throws MetaschemaException + * if an error occurred while registering the module * @throws UnsupportedOperationException * if this binding context is not configured to support dynamic bound * module loading and the module instance is not a subclass of @@ -266,7 +271,7 @@ default IConstraintLoader newConstraintLoader() { * @since 2.0.0 */ @NonNull - default IBoundModule registerModule(@NonNull IModule module) { + default IBoundModule registerModule(@NonNull IModule module) throws MetaschemaException { return getModuleLoaderStrategy().registerModule(module, this); } @@ -463,13 +468,13 @@ default IConstraintValidator newValidator( * @param config * the validation configuration * @return the validation result + * @throws ConstraintValidationException * @throws IllegalArgumentException - * if the provided class is not bound to a Module assembly or field */ default IValidationResult validate( @NonNull IDocumentNodeItem nodeItem, @NonNull IBoundLoader loader, - @Nullable IConfiguration> config) { + @Nullable IConfiguration> config) throws ConstraintValidationException { IRootAssemblyNodeItem root = nodeItem.getRootAssemblyNodeItem(); return validate(root, loader, config); } @@ -485,13 +490,13 @@ default IValidationResult validate( * @param config * the validation configuration * @return the validation result + * @throws ConstraintValidationException * @throws IllegalArgumentException - * if the provided class is not bound to a Module assembly or field */ default IValidationResult validate( @NonNull IDefinitionNodeItem nodeItem, @NonNull IBoundLoader loader, - @Nullable IConfiguration> config) { + @Nullable IConfiguration> config) throws ConstraintValidationException { FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler(); IConstraintValidator validator = newValidator(handler, config); @@ -519,12 +524,13 @@ default IValidationResult validate( * @return the validation result * @throws IOException * if an error occurred while reading the target + * @throws ConstraintValidationException */ default IValidationResult validate( @NonNull URI target, @NonNull Format asFormat, @NonNull ISchemaValidationProvider schemaProvider, - @Nullable IConfiguration> config) throws IOException { + @Nullable IConfiguration> config) throws IOException, ConstraintValidationException { IValidationResult retval = schemaProvider.validateWithSchema(target, asFormat, this); @@ -546,11 +552,12 @@ default IValidationResult validate( * @return the validation results * @throws IOException * if an error occurred while parsing the target + * @throws ConstraintValidationException */ default IValidationResult validateWithConstraints( @NonNull URI target, @Nullable IConfiguration> config) - throws IOException { + throws IOException, ConstraintValidationException { IBoundLoader loader = newBoundLoader(); loader.disableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS); IDocumentNodeItem nodeItem = loader.loadAsNodeItem(target); @@ -628,6 +635,8 @@ default void postProcessModule( * the Metaschema binding context used to load bound resources * @return the registered module, which may be a different instance than what * was provided if dynamic compilation was performed + * @throws MetaschemaException + * if an error occurred while dynamically binding the provided module * @throws UnsupportedOperationException * if this binding context is not configured to support dynamic bound * module loading and the module instance is not a subclass of @@ -637,7 +646,7 @@ default void postProcessModule( @NonNull IBoundModule registerModule( @NonNull IModule module, - @NonNull IBindingContext bindingContext); + @NonNull IBindingContext bindingContext) throws MetaschemaException; // // /** // * Register a matcher used to identify a bound class by the definition's root diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/PostProcessingModuleLoaderStrategy.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/PostProcessingModuleLoaderStrategy.java index c32bd8cb3..b419b6afd 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/PostProcessingModuleLoaderStrategy.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/PostProcessingModuleLoaderStrategy.java @@ -8,6 +8,7 @@ import gov.nist.secauto.metaschema.core.model.IBoundObject; import gov.nist.secauto.metaschema.core.model.IModule; import gov.nist.secauto.metaschema.core.model.IModuleLoader; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.databind.IBindingContext.IBindingMatcher; import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelComplex; @@ -62,7 +63,7 @@ public void postProcessModule(IModule module, IBindingContext bindingContext) { } @Override - public IBoundModule registerModule(IModule module, IBindingContext bindingContext) { + public IBoundModule registerModule(IModule module, IBindingContext bindingContext) throws MetaschemaException { IBoundModule boundModule; postProcessedModulesLock.lock(); try { diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/SimpleModuleLoaderStrategy.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/SimpleModuleLoaderStrategy.java index b568fbef1..f127a0174 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/SimpleModuleLoaderStrategy.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/SimpleModuleLoaderStrategy.java @@ -6,6 +6,7 @@ package gov.nist.secauto.metaschema.databind; import gov.nist.secauto.metaschema.core.model.IModule; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.databind.codegen.IModuleBindingGenerator; import gov.nist.secauto.metaschema.databind.model.IBoundModule; @@ -33,7 +34,7 @@ public SimpleModuleLoaderStrategy(@NonNull IModuleBindingGenerator generator) { } @Override - protected Class handleUnboundModule(IModule module) { + protected Class handleUnboundModule(IModule module) throws MetaschemaException { return generator.generate(module); } } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/DefaultModuleBindingGenerator.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/DefaultModuleBindingGenerator.java index fd78c17c9..1aa802413 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/DefaultModuleBindingGenerator.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/DefaultModuleBindingGenerator.java @@ -5,8 +5,8 @@ package gov.nist.secauto.metaschema.databind.codegen; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.model.IModule; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.model.IBoundModule; @@ -24,7 +24,7 @@ public DefaultModuleBindingGenerator(@NonNull Path compilePath) { } @Override - public Class generate(IModule module) { + public Class generate(IModule module) throws MetaschemaException { ClassLoader classLoader = ModuleCompilerHelper.newClassLoader( compilePath, ObjectUtils.notNull(Thread.currentThread().getContextClassLoader())); @@ -33,7 +33,7 @@ public Class generate(IModule module) { try { production = ModuleCompilerHelper.compileMetaschema(module, compilePath); } catch (IOException ex) { - throw new MetapathException( + throw new MetaschemaException( String.format("Unable to generate and compile classes for module '%s'.", module.getLocation()), ex); } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/IModuleBindingGenerator.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/IModuleBindingGenerator.java index d64bbb0fc..00b84b2b8 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/IModuleBindingGenerator.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/IModuleBindingGenerator.java @@ -6,11 +6,12 @@ package gov.nist.secauto.metaschema.databind.codegen; import gov.nist.secauto.metaschema.core.model.IModule; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.databind.model.IBoundModule; import edu.umd.cs.findbugs.annotations.NonNull; public interface IModuleBindingGenerator { @NonNull - Class generate(@NonNull IModule module); + Class generate(@NonNull IModule module) throws MetaschemaException; } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/AbstractDeserializer.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/AbstractDeserializer.java index b14aee1b5..6587cd92d 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/AbstractDeserializer.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/AbstractDeserializer.java @@ -12,6 +12,7 @@ import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.model.IBoundObject; +import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationException; import gov.nist.secauto.metaschema.core.model.constraint.DefaultConstraintValidator; import gov.nist.secauto.metaschema.core.model.constraint.IConstraintValidationHandler; import gov.nist.secauto.metaschema.core.model.constraint.LoggingConstraintValidationHandler; @@ -91,7 +92,11 @@ public INodeItem deserializeToNodeItem(Reader reader, URI documentUri) throws IO } if (isValidating()) { - validate(nodeItem); + try { + validate(nodeItem); + } catch (ConstraintValidationException ex) { + throw new IOException(ex); + } } return nodeItem; } @@ -117,7 +122,11 @@ public final CLASS deserializeToValue(Reader reader, URI documentUri) throws IOE if (isValidating()) { INodeItem nodeItem = deserializeToNodeItemInternal(reader, documentUri); - validate(nodeItem); + try { + validate(nodeItem); + } catch (ConstraintValidationException ex) { + throw new IOException(ex); + } retval = ObjectUtils.asType(ObjectUtils.requireNonNull(nodeItem.getValue())); } else { retval = deserializeToValueInternal(reader, documentUri); @@ -125,7 +134,7 @@ public final CLASS deserializeToValue(Reader reader, URI documentUri) throws IOE return retval; } - private void validate(@NonNull INodeItem nodeItem) { + private void validate(@NonNull INodeItem nodeItem) throws ConstraintValidationException { IDefinitionNodeItem definitionNodeItem; if (nodeItem instanceof IDocumentNodeItem) { definitionNodeItem = ((IDocumentNodeItem) nodeItem).getRootAssemblyNodeItem(); diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintSupport.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintSupport.java index 5533d91dd..836bdba99 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintSupport.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintSupport.java @@ -5,7 +5,6 @@ package gov.nist.secauto.metaschema.databind.model.impl; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained; import gov.nist.secauto.metaschema.core.model.constraint.IValueConstrained; @@ -39,27 +38,21 @@ public static void parse( // NOPMD - intentional @NonNull ISource source, @NonNull IValueConstrained set) { if (valueAnnotation != null) { - try { - Arrays.stream(valueAnnotation.lets()) - .map(annotation -> ConstraintFactory.newLetExpression(annotation, source)) - .forEachOrdered(set::addLetExpression); - Arrays.stream(valueAnnotation.allowedValues()) - .map(annotation -> ConstraintFactory.newAllowedValuesConstraint(annotation, source)) - .forEachOrdered(set::addConstraint); - Arrays.stream(valueAnnotation.matches()) - .map(annotation -> ConstraintFactory.newMatchesConstraint(annotation, source)) - .forEachOrdered(set::addConstraint); - Arrays.stream(valueAnnotation.indexHasKey()) - .map(annotation -> ConstraintFactory.newIndexHasKeyConstraint(annotation, source)) - .forEachOrdered(set::addConstraint); - Arrays.stream(valueAnnotation.expect()) - .map(annotation -> ConstraintFactory.newExpectConstraint(annotation, source)) - .forEachOrdered(set::addConstraint); - } catch (MetapathException ex) { - throw new MetapathException( - String.format("Unable to compile a Metapath in '%s'. %s", source.getSource(), ex.getLocalizedMessage()), - ex); - } + Arrays.stream(valueAnnotation.lets()) + .map(annotation -> ConstraintFactory.newLetExpression(annotation, source)) + .forEachOrdered(set::addLetExpression); + Arrays.stream(valueAnnotation.allowedValues()) + .map(annotation -> ConstraintFactory.newAllowedValuesConstraint(annotation, source)) + .forEachOrdered(set::addConstraint); + Arrays.stream(valueAnnotation.matches()) + .map(annotation -> ConstraintFactory.newMatchesConstraint(annotation, source)) + .forEachOrdered(set::addConstraint); + Arrays.stream(valueAnnotation.indexHasKey()) + .map(annotation -> ConstraintFactory.newIndexHasKeyConstraint(annotation, source)) + .forEachOrdered(set::addConstraint); + Arrays.stream(valueAnnotation.expect()) + .map(annotation -> ConstraintFactory.newExpectConstraint(annotation, source)) + .forEachOrdered(set::addConstraint); } } @@ -80,23 +73,17 @@ public static void parse( // NOPMD - intentional @NonNull ISource source, @NonNull IModelConstrained set) { if (assemblyAnnotation != null) { - try { - Arrays.stream(assemblyAnnotation.index()) - .map(annotation -> ConstraintFactory.newIndexConstraint(annotation, source)) - .forEachOrdered(set::addConstraint); + Arrays.stream(assemblyAnnotation.index()) + .map(annotation -> ConstraintFactory.newIndexConstraint(annotation, source)) + .forEachOrdered(set::addConstraint); - Arrays.stream(assemblyAnnotation.unique()) - .map(annotation -> ConstraintFactory.newUniqueConstraint(annotation, source)) - .forEachOrdered(set::addConstraint); + Arrays.stream(assemblyAnnotation.unique()) + .map(annotation -> ConstraintFactory.newUniqueConstraint(annotation, source)) + .forEachOrdered(set::addConstraint); - Arrays.stream(assemblyAnnotation.cardinality()) - .map(annotation -> ConstraintFactory.newCardinalityConstraint(annotation, source)) - .forEachOrdered(set::addConstraint); - } catch (MetapathException ex) { - throw new MetapathException( - String.format("Unable to compile a Metapath in '%s'. %s", source.getSource(), ex.getLocalizedMessage()), - ex); - } + Arrays.stream(assemblyAnnotation.cardinality()) + .map(annotation -> ConstraintFactory.newCardinalityConstraint(annotation, source)) + .forEachOrdered(set::addConstraint); } } } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java index b44cfdb5d..9e0e7ef52 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java @@ -6,7 +6,6 @@ package gov.nist.secauto.metaschema.databind.model.metaschema; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.AbstractLoader; import gov.nist.secauto.metaschema.core.model.IConstraintLoader; @@ -36,8 +35,6 @@ import gov.nist.secauto.metaschema.databind.model.metaschema.binding.MetaschemaModuleConstraints; import gov.nist.secauto.metaschema.databind.model.metaschema.impl.ConstraintBindingSupport; -import org.apache.xmlbeans.impl.values.XmlValueNotSupportedException; - import java.io.IOException; import java.net.URI; import java.util.Collections; @@ -181,31 +178,20 @@ protected List parseScopedConstraints( assert scope != null; List targetedConstraints = new LinkedList<>(); - try { - for (IValueConstraintsBase constraintsObj : CollectionUtil.listOrEmpty(scope.getConstraints())) { - if (constraintsObj instanceof MetaschemaModuleConstraints.Scope.Assembly) { - targetedConstraints.add(handleScopedAssembly( - (MetaschemaModuleConstraints.Scope.Assembly) constraintsObj, - source)); - } else if (constraintsObj instanceof MetaschemaModuleConstraints.Scope.Field) { - targetedConstraints.add(handleScopedField( - (MetaschemaModuleConstraints.Scope.Field) constraintsObj, - source)); - } else if (constraintsObj instanceof MetaschemaModuleConstraints.Scope.Flag) { - targetedConstraints.add(handleScopedFlag( - (MetaschemaModuleConstraints.Scope.Flag) constraintsObj, - source)); - } - } - } catch (MetapathException | XmlValueNotSupportedException ex) { - if (ex.getCause() instanceof MetapathException) { - throw new MetapathException( - String.format("Unable to compile a Metapath in '%s'. %s", - source.getSource(), - ex.getLocalizedMessage()), - ex); + for (IValueConstraintsBase constraintsObj : CollectionUtil.listOrEmpty(scope.getConstraints())) { + if (constraintsObj instanceof MetaschemaModuleConstraints.Scope.Assembly) { + targetedConstraints.add(handleScopedAssembly( + (MetaschemaModuleConstraints.Scope.Assembly) constraintsObj, + source)); + } else if (constraintsObj instanceof MetaschemaModuleConstraints.Scope.Field) { + targetedConstraints.add(handleScopedField( + (MetaschemaModuleConstraints.Scope.Field) constraintsObj, + source)); + } else if (constraintsObj instanceof MetaschemaModuleConstraints.Scope.Flag) { + targetedConstraints.add(handleScopedFlag( + (MetaschemaModuleConstraints.Scope.Flag) constraintsObj, + source)); } - throw ex; } URI namespace = ObjectUtils.requireNonNull(scope.getMetaschemaNamespace()); diff --git a/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/Issue206MetaschemaReaderTest.java b/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/Issue206MetaschemaReaderTest.java index 8cedf50d5..deb55e096 100644 --- a/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/Issue206MetaschemaReaderTest.java +++ b/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/Issue206MetaschemaReaderTest.java @@ -14,6 +14,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.model.IBoundObject; import gov.nist.secauto.metaschema.core.model.IMetaschemaData; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.IBindingContext; import gov.nist.secauto.metaschema.databind.io.json.JsonFactoryFactory; @@ -49,7 +50,7 @@ class Issue206MetaschemaReaderTest { JUnit5Mockery context = new JUnit5Mockery(); @Test - void testIssue205Json() throws IOException { + void testIssue205Json() throws IOException, MetaschemaException { String json = "{" + " \"flag\": \"flag-value\"" + "}"; @@ -76,7 +77,7 @@ void testIssue205Json() throws IOException { } @Test - void testIssue205XmlNoValue() throws IOException, XMLStreamException { + void testIssue205XmlNoValue() throws IOException, XMLStreamException, MetaschemaException { String xml = ""; URI source = ObjectUtils.notNull(URI.create("https://example.com/not-a-resource")); @@ -100,7 +101,7 @@ void testIssue205XmlNoValue() throws IOException, XMLStreamException { } @Test - void testIssue205XmlEmptyValue() throws IOException, XMLStreamException { + void testIssue205XmlEmptyValue() throws IOException, XMLStreamException, MetaschemaException { String xml = ""; URI source = ObjectUtils.notNull(URI.create("https://example.com/not-a-resource")); diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java index e4cafba16..e7552733d 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java @@ -11,6 +11,7 @@ import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.util.AutoCloser; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.IBindingContext; @@ -94,9 +95,12 @@ protected AbstractConversionCommandExecutor( * @return the context * @throws CommandExecutionException * if an error occurred getting the binding context + * @throws MetaschemaException + * if an error occurred while setting up the binding context, such as + * pre-loading any needed modules */ @NonNull - protected abstract IBindingContext getBindingContext() throws CommandExecutionException; + protected abstract IBindingContext getBindingContext() throws CommandExecutionException, MetaschemaException; @SuppressWarnings({ "PMD.OnlyOneReturn", // readability @@ -120,9 +124,8 @@ public void execute() throws CommandExecutionException { Format toFormat = MetaschemaCommands.getFormat(cmdLine, MetaschemaCommands.TO_OPTION); - IBindingContext bindingContext = getBindingContext(); - try { + IBindingContext bindingContext = getBindingContext(); IBoundLoader loader = bindingContext.newBoundLoader(); if (LOGGER.isInfoEnabled()) { LOGGER.info("Converting '{}'.", source); @@ -145,7 +148,7 @@ public void execute() throws CommandExecutionException { handleConversion(source, toFormat, writer, loader); } } - } catch (IllegalArgumentException ex) { + } catch (IllegalArgumentException | MetaschemaException ex) { throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex); } catch (IOException ex) { throw new CommandExecutionException(ExitCode.IO_ERROR, ex); diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java index 89d270a73..5a6d1d045 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java @@ -17,6 +17,8 @@ import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.model.IModule; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; +import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationException; import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet; import gov.nist.secauto.metaschema.core.model.constraint.ValidationFeature; import gov.nist.secauto.metaschema.core.model.validation.AggregateValidationResult; @@ -162,6 +164,7 @@ protected abstract IBindingContext getBindingContext(@NonNull Set constraintSets) } @Override - protected IModule getModule(CommandLine commandLine, IBindingContext bindingContext) { - return bindingContext.registerModule(MetaschemaModelModule.class); + protected IModule getModule(CommandLine commandLine, IBindingContext bindingContext) + throws CommandExecutionException { + try { + return bindingContext.registerModule(MetaschemaModelModule.class); + } catch (MetaschemaException ex) { + throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex); + } } @Override diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java index 2e679a1cc..89329b90a 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java @@ -21,6 +21,7 @@ import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory; import gov.nist.secauto.metaschema.core.model.IModule; +import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.IBindingContext; @@ -127,11 +128,15 @@ private void executeCommand( if (cmdLine.hasOption(MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION)) { IBindingContext bindingContext = MetaschemaCommands.newBindingContextWithDynamicCompilation(); - module = bindingContext.registerModule(MetaschemaCommands.loadModule( - cmdLine, - MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION, - ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()), - bindingContext)); + try { + module = bindingContext.registerModule(MetaschemaCommands.loadModule( + cmdLine, + MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION, + ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()), + bindingContext)); + } catch (MetaschemaException ex) { + throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex); + } // determine if the query is evaluated against the module or the instance if (cmdLine.hasOption(CONTENT_OPTION)) { diff --git a/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java b/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java index c27cf17cc..65aa80526 100644 --- a/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java +++ b/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java @@ -5,12 +5,12 @@ package gov.nist.secauto.metaschema.maven.plugin; -import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.model.IConstraintLoader; import gov.nist.secauto.metaschema.core.model.IModule; import gov.nist.secauto.metaschema.core.model.IModuleLoader; import gov.nist.secauto.metaschema.core.model.IResourceLocation; import gov.nist.secauto.metaschema.core.model.MetaschemaException; +import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationException; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationFinding; import gov.nist.secauto.metaschema.core.model.constraint.ExternalConstraintsModulePostProcessor; import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet; @@ -425,7 +425,7 @@ protected Set getClassPath() throws DependencyResolutionRequiredExceptio protected Set getModulesToGenerateFor( @NonNull IBindingContext bindingContext, @NonNull IModuleLoader.IModulePostProcessor modulePostProcessor) - throws MetaschemaException, IOException { + throws MetaschemaException, IOException, ConstraintValidationException { // Don't use the normal loader, since it attempts to register and compile the // module. @@ -718,12 +718,12 @@ public ModuleBindingGenerator( } @NonNull - public IProduction generateClasses(@NonNull IModule module) { + public IProduction generateClasses(@NonNull IModule module) throws MetaschemaException { IProduction production; try { production = JavaGenerator.generate(module, compilePath, bindingConfiguration); } catch (IOException ex) { - throw new MetapathException( + throw new MetaschemaException( String.format("Unable to generate and compile classes for module '%s'.", module.getLocation()), ex); } @@ -779,7 +779,7 @@ public void info(String msg) { } @Override - public Class generate(IModule module) { + public Class generate(IModule module) throws MetaschemaException { IProduction production = generateClasses(module); try { compileClasses(production, compilePath); diff --git a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java index 75872f719..21309911c 100644 --- a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java +++ b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java @@ -94,6 +94,7 @@ protected static AllowedValueCollection getContextIndependentEnumeratedValues( closed = true; } + // FIXME: Should this compare the actual compiled expression? if (!IMetapathExpression.contextNode().getPath().equals(constraint.getTarget().getPath())) { values = CollectionUtil.emptyList(); break; diff --git a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGenerator.java b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGenerator.java index 31a2c3d95..42b55feb1 100644 --- a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGenerator.java +++ b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGenerator.java @@ -109,9 +109,7 @@ protected List analyzeDefinitions( @NonNull S generationState, @Nullable BiConsumer handler) { // TODO: use of handler here is confusing and introduces side effects. Consider - // refactoring this in - // the caller - + // refactoring this in the caller List rootAssemblyDefinitions = new LinkedList<>(); for (ModuleIndex.DefinitionEntry entry : generationState.getMetaschemaIndex().getDefinitions()) { diff --git a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/json/impl/JsonSchemaHelper.java b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/json/impl/JsonSchemaHelper.java index fd0d12fc2..fb59894a5 100644 --- a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/json/impl/JsonSchemaHelper.java +++ b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/json/impl/JsonSchemaHelper.java @@ -20,7 +20,6 @@ import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; -import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException; import gov.nist.secauto.metaschema.core.model.IChoiceInstance; import gov.nist.secauto.metaschema.core.model.IContainerModelAbsolute; import gov.nist.secauto.metaschema.core.model.IFlagInstance; @@ -199,12 +198,7 @@ public static List buildFlagProperties( // determine the flag instances to generate if (jsonKeyFlagName != null) { - IFlagInstance jsonKeyFlag; - try { - jsonKeyFlag = definition.getFlagInstanceByName(jsonKeyFlagName.getIndexPosition()); - } catch (StaticMetapathException ex) { - throw new IllegalArgumentException(ex); - } + IFlagInstance jsonKeyFlag = definition.getFlagInstanceByName(jsonKeyFlagName.getIndexPosition()); if (jsonKeyFlag == null) { throw new IllegalArgumentException( String.format("The referenced json-key flag-name '%s' does not exist on definition '%s'.",