diff --git a/core/metaschema b/core/metaschema
index fbd6dd7c7..20c107794 160000
--- a/core/metaschema
+++ b/core/metaschema
@@ -1 +1 @@
-Subproject commit fbd6dd7c78ef7a73c47a17675e2cc6afc91683f5
+Subproject commit 20c107794327ff3c2cc39ce98f462c8c0791d916
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConfigurableMessageConstraintBuilder.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConfigurableMessageConstraintBuilder.java
new file mode 100644
index 000000000..0d1867d81
--- /dev/null
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConfigurableMessageConstraintBuilder.java
@@ -0,0 +1,52 @@
+/*
+ * SPDX-FileCopyrightText: none
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+package gov.nist.secauto.metaschema.core.model.constraint;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
+/**
+ * Provides builder methods for the core data elements of an
+ * {@link IConstraint}.
+ *
+ * The base class of all constraint builders.
+ *
+ * @param
+ * the Java type of the implementing builder
+ * @param
+ * the Java type of the resulting built object
+ * @since 2.0.0
+ */
+public abstract class AbstractConfigurableMessageConstraintBuilder<
+ T extends AbstractConfigurableMessageConstraintBuilder,
+ R extends IConfigurableMessageConstraint>
+ extends AbstractConstraintBuilder {
+ private String message;
+
+ /**
+ * A message to emit when the constraint is violated. Allows embedded Metapath
+ * expressions using the syntax {@code \{ metapath \}}.
+ *
+ * @param message
+ * the message if defined or {@code null} otherwise
+ * @return this builder
+ */
+ @NonNull
+ public T message(@NonNull String message) {
+ this.message = message;
+ return getThis();
+ }
+
+ /**
+ * Get the constraint message provided to the builder.
+ *
+ * @return the message or {@code null} if no message is set
+ */
+ @Nullable
+ protected String getMessage() {
+ return message;
+ }
+}
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java
index 069ee502a..d2f49bc4e 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java
@@ -44,7 +44,6 @@ public abstract class AbstractConstraintBuilder<
private String target = IConstraint.DEFAULT_TARGET_METAPATH;
@NonNull
private Map> properties = new LinkedHashMap<>(); // NOPMD not thread safe
- private String message;
private MarkupMultiline remarks;
/**
@@ -180,20 +179,6 @@ public T property(@NonNull IAttributable.Key key, @NonNull Set values) {
return getThis();
}
- /**
- * A message to emit when the constraint is violated. Allows embedded Metapath
- * expressions using the syntax {@code \{ metapath \}}.
- *
- * @param message
- * the message if defined or {@code null} otherwise
- * @return this builder
- */
- @NonNull
- public T message(@NonNull String message) {
- this.message = message;
- return getThis();
- }
-
/**
* Set the provided {@code remarks}.
*
@@ -309,16 +294,6 @@ protected Map> getProperties() {
return properties;
}
- /**
- * Get the constraint message provided to the builder.
- *
- * @return the message or {@code null} if no message is set
- */
- @Nullable
- protected String getMessage() {
- return message;
- }
-
/**
* Get the remarks provided to the builder.
*
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 0ac063366..3e718ee63 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
@@ -12,6 +12,7 @@
import gov.nist.secauto.metaschema.core.metapath.function.library.FnData;
import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
import gov.nist.secauto.metaschema.core.util.CustomCollectors;
+import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import java.util.List;
import java.util.Objects;
@@ -67,23 +68,28 @@ protected String toPath(@NonNull INodeItem item) {
*
* @param constraint
* the constraint the requested message pertains to
- * @param node
+ * @param target
* the item the constraint targeted
- * @param targets
- * the targets matching the constraint
+ * @param testedItems
+ * the items tested by the constraint
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newCardinalityMinimumViolationMessage(
@NonNull ICardinalityConstraint constraint,
- @NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
- return String.format(
- "The cardinality '%d' is below the required minimum '%d' for items matching '%s'.",
- targets.size(),
- constraint.getMinOccurs(),
- constraint.getTarget());
+ @NonNull INodeItem target,
+ @NonNull ISequence extends INodeItem> testedItems,
+ @NonNull DynamicContext dynamicContext) {
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format(
+ "The cardinality '%d' is below the required minimum '%d' for items matching '%s'.",
+ testedItems.size(),
+ constraint.getMinOccurs(),
+ constraint.getTarget()))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -92,29 +98,34 @@ protected String newCardinalityMinimumViolationMessage(
*
* @param constraint
* the constraint the requested message pertains to
- * @param node
+ * @param target
* the item the constraint targeted
- * @param targets
- * the targets matching the constraint
+ * @param testedItems
+ * the items tested by the constraint
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newCardinalityMaximumViolationMessage(
@NonNull ICardinalityConstraint constraint,
- @NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
- return String.format(
- "The cardinality '%d' is greater than the required maximum '%d' at: %s.",
- targets.size(),
- constraint.getMinOccurs(),
- targets.safeStream()
- .map(item -> new StringBuilder(12)
- .append('\'')
- .append(toPath(item))
- .append('\'')
- .toString())
- .collect(CustomCollectors.joiningWithOxfordComma("and")));
+ @NonNull INodeItem target,
+ @NonNull ISequence extends INodeItem> testedItems,
+ @NonNull DynamicContext dynamicContext) {
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format(
+ "The cardinality '%d' is greater than the required maximum '%d' at: %s.",
+ testedItems.size(),
+ constraint.getMinOccurs(),
+ testedItems.safeStream()
+ .map(item -> new StringBuilder(12)
+ .append('\'')
+ .append(toPath(ObjectUtils.notNull(item)))
+ .append('\'')
+ .toString())
+ .collect(CustomCollectors.joiningWithOxfordComma("and"))))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -127,22 +138,27 @@ protected String newCardinalityMaximumViolationMessage(
* the item the constraint targeted
* @param oldItem
* the original item matching the constraint
- * @param newItem
+ * @param target
* the new item matching the constraint
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newIndexDuplicateKeyViolationMessage(
@NonNull IIndexConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem newItem) {
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
// TODO: render the key paths
- return String.format("Index '%s' has duplicate key for items at paths '%s' and '%s'",
- constraint.getName(),
- toPath(oldItem),
- toPath(newItem));
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format("Index '%s' has duplicate key for items at paths '%s' and '%s'",
+ constraint.getName(),
+ toPath(oldItem),
+ toPath(target)))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -155,20 +171,25 @@ protected String newIndexDuplicateKeyViolationMessage(
* the item the constraint targeted
* @param oldItem
* the original item matching the constraint
- * @param newItem
+ * @param target
* the new item matching the constraint
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newUniqueKeyViolationMessage(
@NonNull IUniqueConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem newItem) {
- return String.format("Unique constraint violation at paths '%s' and '%s'",
- toPath(oldItem),
- toPath(newItem));
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format("Unique constraint violation at paths '%s' and '%s'",
+ toPath(oldItem),
+ toPath(target)))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -185,20 +206,25 @@ protected String newUniqueKeyViolationMessage(
* the target's value
* @param pattern
* the expected pattern
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newMatchPatternViolationMessage(
@NonNull IMatchesConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
@NonNull String value,
- @NonNull Pattern pattern) {
- return String.format("Value '%s' did not match the pattern '%s' at path '%s'",
- value,
- pattern.pattern(),
- toPath(target));
+ @NonNull Pattern pattern,
+ @NonNull DynamicContext dynamicContext) {
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format("Value '%s' did not match the pattern '%s' at path '%s'",
+ value,
+ pattern.pattern(),
+ toPath(target)))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -215,18 +241,25 @@ protected String newMatchPatternViolationMessage(
* the target's value
* @param adapter
* the expected data type adapter
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newMatchDatatypeViolationMessage(
@NonNull IMatchesConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
@NonNull String value,
- @NonNull IDataTypeAdapter> adapter) {
- return String.format("Value '%s' did not conform to the data type '%s' at path '%s'", value,
- adapter.getPreferredName(), toPath(target));
+ @NonNull IDataTypeAdapter> adapter,
+ @NonNull DynamicContext dynamicContext) {
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format("Value '%s' did not conform to the data type '%s' at path '%s'",
+ value,
+ adapter.getPreferredName(),
+ toPath(target)))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -244,22 +277,17 @@ protected String newMatchDatatypeViolationMessage(
* evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newExpectViolationMessage(
@NonNull IExpectConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
@NonNull DynamicContext dynamicContext) {
- String message;
- if (constraint.getMessage() != null) {
- message = constraint.generateMessage(target, dynamicContext);
- } else {
- message = String.format("Expect constraint '%s' did not match the data at path '%s'",
- constraint.getTest(),
- toPath(target));
- }
- return message;
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format("Expect constraint '%s' did not match the data at path '%s'",
+ constraint.getTest(),
+ toPath(target)))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -272,12 +300,10 @@ protected String newExpectViolationMessage(
* the target matching the constraint
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newAllowedValuesViolationMessage(
@NonNull List constraints,
@NonNull INodeItem target) {
-
String allowedValues = constraints.stream()
.flatMap(constraint -> constraint.getAllowedValues().values().stream())
.map(IAllowedValue::getValue)
@@ -285,10 +311,10 @@ protected String newAllowedValuesViolationMessage(
.distinct()
.collect(CustomCollectors.joiningWithOxfordComma("or"));
- return String.format("Value '%s' doesn't match one of '%s' at path '%s'",
+ return ObjectUtils.notNull(String.format("Value '%s' doesn't match one of '%s' at path '%s'",
FnData.fnDataItem(target).asString(),
allowedValues,
- toPath(target));
+ toPath(target)));
}
/**
@@ -301,14 +327,13 @@ protected String newAllowedValuesViolationMessage(
* the item the constraint targeted
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newIndexDuplicateViolationMessage(
@NonNull IIndexConstraint constraint,
@NonNull INodeItem node) {
- return String.format("Duplicate index named '%s' found at path '%s'",
+ return ObjectUtils.notNull(String.format("Duplicate index named '%s' found at path '%s'",
constraint.getName(),
- node.getMetapath());
+ node.getMetapath()));
}
/**
@@ -323,22 +348,27 @@ protected String newIndexDuplicateViolationMessage(
* the target matching the constraint
* @param key
* the key derived from the target that failed to be found in the index
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
- @SuppressWarnings("null")
@NonNull
protected String newIndexMissMessage(
@NonNull IIndexHasKeyConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull List key) {
+ @NonNull List key,
+ @NonNull DynamicContext dynamicContext) {
String keyValues = key.stream()
.collect(Collectors.joining(","));
- return String.format("Key reference [%s] not found in index '%s' for item at path '%s'",
- keyValues,
- constraint.getIndexName(),
- target.getMetapath());
+ return constraint.getMessage() == null
+ ? ObjectUtils.notNull(String.format("Key reference [%s] not found in index '%s' for item at path '%s'",
+ keyValues,
+ constraint.getIndexName(),
+ target.getMetapath()))
+ : constraint.generateMessage(target, dynamicContext);
}
/**
@@ -353,6 +383,9 @@ protected String newIndexMissMessage(
* the target matching the constraint
* @param message
* the message to be added before information about the target path
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
* @return the new message
*/
@SuppressWarnings("null")
@@ -361,7 +394,8 @@ protected String newMissingIndexViolationMessage(
@NonNull IIndexHasKeyConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull String message) {
+ @NonNull String message,
+ @NonNull DynamicContext dynamicContext) {
return String.format("%s for constraint '%s' for item at path '%s'",
message,
Objects.requireNonNullElse(constraint.getId(), "?"),
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 2004439d8..ff3d35e59 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
@@ -15,7 +15,7 @@
public abstract class AbstractKeyConstraintBuilder<
T extends AbstractKeyConstraintBuilder,
R extends IKeyConstraint>
- extends AbstractConstraintBuilder {
+ extends AbstractConfigurableMessageConstraintBuilder {
@NonNull
private final List keyFields = new LinkedList<>();
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 6cbcadf31..71d57aef3 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
@@ -233,9 +233,9 @@ private void validateHasCardinality( // NOPMD false positive
for (ICardinalityConstraint constraint : constraints) {
ISequence extends IDefinitionNodeItem, ?>> targets = constraint.matchTargets(item, dynamicContext);
try {
- validateHasCardinality(constraint, item, targets);
+ validateHasCardinality(constraint, item, targets, dynamicContext);
} catch (MetapathException ex) {
- handleError(constraint, item, ex);
+ handleError(constraint, item, ex, dynamicContext);
}
}
}
@@ -256,7 +256,8 @@ private void validateHasCardinality( // NOPMD false positive
private void validateHasCardinality(
@NonNull ICardinalityConstraint constraint,
@NonNull IAssemblyNodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
+ @NonNull ISequence extends INodeItem> targets,
+ @NonNull DynamicContext dynamicContext) {
int itemCount = targets.size();
IConstraintValidationHandler handler = getConstraintValidationHandler();
@@ -264,18 +265,18 @@ private void validateHasCardinality(
boolean violation = false;
Integer minOccurs = constraint.getMinOccurs();
if (minOccurs != null && itemCount < minOccurs) {
- handler.handleCardinalityMinimumViolation(constraint, node, targets);
+ handler.handleCardinalityMinimumViolation(constraint, node, targets, dynamicContext);
violation = true;
}
Integer maxOccurs = constraint.getMaxOccurs();
if (maxOccurs != null && itemCount > maxOccurs) {
- handler.handleCardinalityMaximumViolation(constraint, node, targets);
+ handler.handleCardinalityMaximumViolation(constraint, node, targets, dynamicContext);
violation = true;
}
if (!violation) {
- handlePass(constraint, node, node);
+ handlePass(constraint, node, node, dynamicContext);
}
}
@@ -300,7 +301,7 @@ private void validateIndex(
try {
validateIndex(constraint, item, targets, dynamicContext);
} catch (MetapathException ex) {
- handleError(constraint, item, ex);
+ handleError(constraint, item, ex, dynamicContext);
}
}
}
@@ -330,7 +331,7 @@ private void validateIndex(
IConstraintValidationHandler handler = getConstraintValidationHandler();
if (indexNameToIndexMap.containsKey(indexName)) {
- handler.handleIndexDuplicateViolation(constraint, node);
+ handler.handleIndexDuplicateViolation(constraint, node, dynamicContext);
} else {
IIndex index = IIndex.newInstance(constraint.getKeyFields());
targets.stream()
@@ -340,12 +341,12 @@ private void validateIndex(
try {
INodeItem oldItem = index.put(item, dynamicContext);
if (oldItem == null) {
- handlePass(constraint, node, item);
+ handlePass(constraint, node, item, dynamicContext);
} else {
- handler.handleIndexDuplicateKeyViolation(constraint, node, oldItem, item);
+ handler.handleIndexDuplicateKeyViolation(constraint, node, oldItem, item, dynamicContext);
}
} catch (MetapathException ex) {
- handler.handleKeyMatchError(constraint, node, item, ex);
+ handler.handleKeyMatchError(constraint, node, item, ex, dynamicContext);
}
}
});
@@ -356,17 +357,20 @@ private void validateIndex(
private void handlePass(
@NonNull IConstraint constraint,
@NonNull INodeItem node,
- @NonNull INodeItem item) {
+ @NonNull INodeItem item,
+ @NonNull DynamicContext dynamicContext) {
if (isFeatureEnabled(ValidationFeature.VALIDATE_GENERATE_PASS_FINDINGS)) {
- getConstraintValidationHandler().handlePass(constraint, node, item);
+ getConstraintValidationHandler().handlePass(constraint, node, item, dynamicContext);
}
}
private void handleError(
@NonNull IConstraint constraint,
@NonNull INodeItem node,
- @NonNull MetapathException ex) {
- getConstraintValidationHandler().handleError(constraint, node, toErrorMessage(constraint, node, ex), ex);
+ @NonNull MetapathException ex,
+ @NonNull DynamicContext dynamicContext) {
+ getConstraintValidationHandler()
+ .handleError(constraint, node, toErrorMessage(constraint, node, ex), ex, dynamicContext);
}
@NonNull
@@ -418,7 +422,7 @@ private void validateUnique(
try {
validateUnique(constraint, item, targets, dynamicContext);
} catch (MetapathException ex) {
- handleError(constraint, item, ex);
+ handleError(constraint, item, ex, dynamicContext);
}
}
}
@@ -454,12 +458,12 @@ private void validateUnique(
try {
INodeItem oldItem = index.put(item, dynamicContext);
if (oldItem == null) {
- handlePass(constraint, node, item);
+ handlePass(constraint, node, item, dynamicContext);
} else {
- handler.handleUniqueKeyViolation(constraint, node, oldItem, item);
+ handler.handleUniqueKeyViolation(constraint, node, oldItem, item, dynamicContext);
}
} catch (MetapathException ex) {
- handler.handleKeyMatchError(constraint, node, item, ex);
+ handler.handleKeyMatchError(constraint, node, item, ex, dynamicContext);
throw ex;
}
}
@@ -486,9 +490,9 @@ private void validateMatches( // NOPMD false positive
for (IMatchesConstraint constraint : constraints) {
ISequence extends IDefinitionNodeItem, ?>> targets = constraint.matchTargets(item, dynamicContext);
try {
- validateMatches(constraint, item, targets);
+ validateMatches(constraint, item, targets, dynamicContext);
} catch (MetapathException ex) {
- handleError(constraint, item, ex);
+ handleError(constraint, item, ex, dynamicContext);
}
}
}
@@ -509,12 +513,13 @@ private void validateMatches( // NOPMD false positive
private void validateMatches(
@NonNull IMatchesConstraint constraint,
@NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
+ @NonNull ISequence extends INodeItem> targets,
+ @NonNull DynamicContext dynamicContext) {
targets.stream()
.forEachOrdered(item -> {
assert item != null;
if (item.hasValue()) {
- validateMatchesItem(constraint, node, item);
+ validateMatchesItem(constraint, node, item, dynamicContext);
}
});
}
@@ -522,7 +527,8 @@ private void validateMatches(
private void validateMatchesItem(
@NonNull IMatchesConstraint constraint,
@NonNull INodeItem node,
- @NonNull INodeItem item) {
+ @NonNull INodeItem item,
+ @NonNull DynamicContext dynamicContext) {
String value = FnData.fnDataItem(item).asString();
IConstraintValidationHandler handler = getConstraintValidationHandler();
@@ -530,7 +536,7 @@ private void validateMatchesItem(
Pattern pattern = constraint.getPattern();
if (pattern != null && !pattern.asMatchPredicate().test(value)) {
// failed pattern match
- handler.handleMatchPatternViolation(constraint, node, item, value, pattern);
+ handler.handleMatchPatternViolation(constraint, node, item, value, pattern, dynamicContext);
valid = false;
}
@@ -539,13 +545,13 @@ private void validateMatchesItem(
try {
adapter.parse(value);
} catch (IllegalArgumentException ex) {
- handler.handleMatchDatatypeViolation(constraint, node, item, value, adapter, ex);
+ handler.handleMatchDatatypeViolation(constraint, node, item, value, adapter, ex, dynamicContext);
valid = false;
}
}
if (valid) {
- handlePass(constraint, node, item);
+ handlePass(constraint, node, item, dynamicContext);
}
}
@@ -657,12 +663,12 @@ private void validateExpect(
try {
ISequence> result = metapath.evaluate(item, dynamicContext);
if (FnBoolean.fnBoolean(result).toBoolean()) {
- handlePass(constraint, node, item);
+ handlePass(constraint, node, item, dynamicContext);
} else {
handler.handleExpectViolation(constraint, node, item, dynamicContext);
}
} catch (MetapathException ex) {
- handleError(constraint, item, ex);
+ handleError(constraint, item, ex, dynamicContext);
}
}
});
@@ -686,7 +692,7 @@ private void validateAllowedValues(
@NonNull DynamicContext dynamicContext) {
for (IAllowedValuesConstraint constraint : constraints) {
ISequence extends IDefinitionNodeItem, ?>> targets = constraint.matchTargets(item, dynamicContext);
- validateAllowedValues(constraint, item, targets);
+ validateAllowedValues(constraint, item, targets, dynamicContext);
}
}
@@ -702,18 +708,22 @@ private void validateAllowedValues(
* @param targets
* the focus of Metapath evaluation for evaluating any constraint
* Metapath clauses
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
private void validateAllowedValues(
@NonNull IAllowedValuesConstraint constraint,
@NonNull IDefinitionNodeItem, ?> node,
- @NonNull ISequence extends IDefinitionNodeItem, ?>> targets) {
+ @NonNull ISequence extends IDefinitionNodeItem, ?>> targets,
+ @NonNull DynamicContext dynamicContext) {
targets.stream().forEachOrdered(item -> {
assert item != null;
if (item.hasValue()) {
try {
updateValueStatus(item, constraint, node);
} catch (MetapathException ex) {
- handleError(constraint, item, ex);
+ handleError(constraint, item, ex, dynamicContext);
}
}
});
@@ -751,11 +761,16 @@ protected void updateValueStatus(
*
* @param targetItem
* the item whose value will be validated
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
- protected void handleAllowedValues(@NonNull INodeItem targetItem) {
+ protected void handleAllowedValues(
+ @NonNull INodeItem targetItem,
+ @NonNull DynamicContext dynamicContext) {
ValueStatus valueStatus = valueMap.remove(targetItem);
if (valueStatus != null) {
- valueStatus.validate();
+ valueStatus.validate(dynamicContext);
}
}
@@ -794,19 +809,24 @@ private void validateKeyRef(
List key = IIndex.toKey(item, constraint.getKeyFields(), dynamicContext);
if (index == null) {
- handler.handleMissingIndexViolation(constraint, contextNode, item, ObjectUtils.notNull(
- String.format("Key reference to undefined index with name '%s'", indexName)));
+ handler.handleMissingIndexViolation(
+ constraint,
+ contextNode,
+ item,
+ ObjectUtils.notNull(String.format("Key reference to undefined index with name '%s'",
+ indexName)),
+ dynamicContext);
} else {
INodeItem referencedItem = index.get(key);
if (referencedItem == null) {
- handler.handleIndexMiss(constraint, contextNode, item, key);
+ handler.handleIndexMiss(constraint, contextNode, item, key, dynamicContext);
} else {
- handlePass(constraint, contextNode, item);
+ handlePass(constraint, contextNode, item, dynamicContext);
}
}
} catch (MetapathException ex) {
- handler.handleKeyMatchError(constraint, contextNode, item, ex);
+ handler.handleKeyMatchError(constraint, contextNode, item, ex, dynamicContext);
}
}
@@ -854,7 +874,7 @@ public void registerAllowedValue(
}
}
- public void validate() {
+ public void validate(@NonNull DynamicContext dynamicContext) {
if (!constraints.isEmpty()) {
boolean match = false;
List failedConstraints = new LinkedList<>();
@@ -865,7 +885,7 @@ public void validate() {
IAllowedValue matchingValue = allowedValues.getAllowedValue(value);
if (matchingValue != null) {
match = true;
- handlePass(allowedValues, node, item);
+ handlePass(allowedValues, node, item, dynamicContext);
} else if (IAllowedValuesConstraint.Extensible.NONE.equals(allowedValues.getExtensible())) {
// hard failure, since no other values can satisfy this constraint
failedConstraints = CollectionUtil.singletonList(allowedValues);
@@ -878,7 +898,7 @@ public void validate() {
// it's not a failure if allow others is true
if (!match && !allowOthers) {
- handler.handleAllowedValuesViolation(failedConstraints, item);
+ handler.handleAllowedValuesViolation(failedConstraints, item, dynamicContext);
}
}
}
@@ -923,7 +943,7 @@ public Void visitFlag(@NonNull IFlagNodeItem item, DynamicContext context) {
validateFlag(item, effectiveContext);
super.visitFlag(item, effectiveContext);
- handleAllowedValues(item);
+ handleAllowedValues(item, context);
return null;
}
@@ -936,7 +956,7 @@ public Void visitField(@NonNull IFieldNodeItem item, DynamicContext context) {
validateField(item, effectiveContext);
super.visitField(item, effectiveContext);
- handleAllowedValues(item);
+ handleAllowedValues(item, context);
return null;
}
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 bb06900e1..29130e7c7 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
@@ -25,6 +25,12 @@
import edu.umd.cs.findbugs.annotations.NonNull;
+/**
+ * A validation result handler that collects the resulting findings for later
+ * retrieval using the {@link #getFindings()} method.
+ *
+ * This class is not thread safe.
+ */
@SuppressWarnings("PMD.CouplingBetweenObjects")
public class FindingCollectingConstraintValidationHandler
extends AbstractConstraintValidationHandler
@@ -88,28 +94,30 @@ private static Kind toKind(@NonNull Level level) {
@Override
public void handleCardinalityMinimumViolation(
@NonNull ICardinalityConstraint constraint,
- @NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
- addFinding(ConstraintValidationFinding.builder(constraint, node)
+ @NonNull INodeItem target,
+ @NonNull ISequence extends INodeItem> testedItems,
+ @NonNull DynamicContext dynamicContext) {
+ addFinding(ConstraintValidationFinding.builder(constraint, target)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
- .target(node)
- .subjects(targets.getValue())
- .message(newCardinalityMinimumViolationMessage(constraint, node, targets))
+ .target(target)
+ .subjects(testedItems.getValue())
+ .message(newCardinalityMinimumViolationMessage(constraint, target, testedItems, dynamicContext))
.build());
}
@Override
public void handleCardinalityMaximumViolation(
@NonNull ICardinalityConstraint constraint,
- @NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
- addFinding(ConstraintValidationFinding.builder(constraint, node)
+ @NonNull INodeItem target,
+ @NonNull ISequence extends INodeItem> testedItems,
+ @NonNull DynamicContext dynamicContext) {
+ addFinding(ConstraintValidationFinding.builder(constraint, target)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
- .target(node)
- .subjects(targets.getValue())
- .message(newCardinalityMaximumViolationMessage(constraint, node, targets))
+ .target(target)
+ .subjects(testedItems.getValue())
+ .message(newCardinalityMaximumViolationMessage(constraint, target, testedItems, dynamicContext))
.build());
}
@@ -118,12 +126,13 @@ public void handleIndexDuplicateKeyViolation(
@NonNull IIndexConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem target) {
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
.target(target)
- .message(newIndexDuplicateKeyViolationMessage(constraint, node, oldItem, target))
+ .message(newIndexDuplicateKeyViolationMessage(constraint, node, oldItem, target, dynamicContext))
.build());
}
@@ -132,12 +141,13 @@ public void handleUniqueKeyViolation(
@NonNull IUniqueConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem target) {
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
.target(target)
- .message(newUniqueKeyViolationMessage(constraint, node, oldItem, target))
+ .message(newUniqueKeyViolationMessage(constraint, node, oldItem, target, dynamicContext))
.build());
}
@@ -147,7 +157,8 @@ public void handleKeyMatchError(
@NonNull IKeyConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull MetapathException cause) {
+ @NonNull MetapathException cause,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
@@ -163,12 +174,13 @@ public void handleMatchPatternViolation(
@NonNull INodeItem node,
@NonNull INodeItem target,
@NonNull String value,
- @NonNull Pattern pattern) {
+ @NonNull Pattern pattern,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
.target(target)
- .message(newMatchPatternViolationMessage(constraint, node, target, value, pattern))
+ .message(newMatchPatternViolationMessage(constraint, node, target, value, pattern, dynamicContext))
.build());
}
@@ -179,12 +191,13 @@ public void handleMatchDatatypeViolation(
@NonNull INodeItem target,
@NonNull String value,
@NonNull IDataTypeAdapter> adapter,
- @NonNull IllegalArgumentException cause) {
+ @NonNull IllegalArgumentException cause,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
.target(target)
- .message(newMatchDatatypeViolationMessage(constraint, node, target, value, adapter))
+ .message(newMatchDatatypeViolationMessage(constraint, node, target, value, adapter, dynamicContext))
.cause(cause)
.build());
}
@@ -206,7 +219,8 @@ public void handleExpectViolation(
@Override
public void handleAllowedValuesViolation(
@NonNull List failedConstraints,
- @NonNull INodeItem target) {
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
Level maxLevel = ObjectUtils.notNull(failedConstraints.stream()
.map(IAllowedValuesConstraint::getLevel)
.reduce(Level.NONE, (l1, l2) -> l1.ordinal() >= l2.ordinal() ? l1 : l2));
@@ -220,7 +234,10 @@ public void handleAllowedValuesViolation(
}
@Override
- public void handleIndexDuplicateViolation(IIndexConstraint constraint, INodeItem node) {
+ public void handleIndexDuplicateViolation(
+ IIndexConstraint constraint,
+ INodeItem node,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.kind(Kind.FAIL)
.severity(Level.CRITICAL)
@@ -230,31 +247,41 @@ public void handleIndexDuplicateViolation(IIndexConstraint constraint, INodeItem
}
@Override
- public void handleIndexMiss(IIndexHasKeyConstraint constraint, INodeItem node, INodeItem target, List key) {
+ public void handleIndexMiss(
+ @NonNull IIndexHasKeyConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull INodeItem target,
+ @NonNull List key,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
.target(target)
- .message(newIndexMissMessage(constraint, node, target, key))
+ .message(newIndexMissMessage(constraint, node, target, key, dynamicContext))
.build());
}
@Override
public void handleMissingIndexViolation(
- IIndexHasKeyConstraint constraint,
- INodeItem node,
- INodeItem target,
- String message) {
+ @NonNull IIndexHasKeyConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull INodeItem target,
+ @NonNull String message,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.severity(constraint.getLevel())
.kind(toKind(constraint.getLevel()))
.target(target)
- .message(newMissingIndexViolationMessage(constraint, node, target, message))
+ .message(newMissingIndexViolationMessage(constraint, node, target, message, dynamicContext))
.build());
}
@Override
- public void handlePass(IConstraint constraint, INodeItem node, INodeItem target) {
+ public void handlePass(
+ @NonNull IConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
addFinding(ConstraintValidationFinding.builder(constraint, node)
.kind(Kind.PASS)
.severity(Level.NONE)
@@ -264,10 +291,11 @@ public void handlePass(IConstraint constraint, INodeItem node, INodeItem target)
@Override
public void handleError(
- IConstraint constraint,
- INodeItem node,
- String message,
- Throwable exception) {
+ @NonNull IConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull String message,
+ @NonNull Throwable exception,
+ @NonNull DynamicContext dynamicContext) {
LOGGER.atError().withThrowable(exception).log(message);
addFinding(ConstraintValidationFinding.builder(constraint, node)
.kind(Kind.FAIL)
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IAllowedValuesConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IAllowedValuesConstraint.java
index 3ff683191..5bc44b3c9 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IAllowedValuesConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IAllowedValuesConstraint.java
@@ -204,7 +204,6 @@ protected IAllowedValuesConstraint newInstance() {
getAllowedValues(),
isAllowedOther(),
getExtensible(),
- getMessage(),
getRemarks());
}
}
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ICardinalityConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ICardinalityConstraint.java
index 1c60897ab..4d78547eb 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ICardinalityConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ICardinalityConstraint.java
@@ -16,7 +16,7 @@
* Represents a rule requiring a Metaschema assembly data instance to have
* elements with a minimum and/or maximum occurrence.
*/
-public interface ICardinalityConstraint extends IConstraint {
+public interface ICardinalityConstraint extends IConfigurableMessageConstraint {
/**
* Retrieve the required minimum occurrence of the target instance. If
* specified, this value must be less than or equal to the value of
@@ -59,7 +59,7 @@ static Builder builder() {
* {@link ICardinalityConstraint}.
*/
final class Builder
- extends AbstractConstraintBuilder {
+ extends AbstractConfigurableMessageConstraintBuilder {
private Integer minOccurs;
private Integer maxOccurs;
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
new file mode 100644
index 000000000..c5548fa81
--- /dev/null
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConfigurableMessageConstraint.java
@@ -0,0 +1,45 @@
+/*
+ * SPDX-FileCopyrightText: none
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+package gov.nist.secauto.metaschema.core.model.constraint;
+
+import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
+import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
+/**
+ * Represents a constraint that allows a configurable message.
+ *
+ * @since 2.0.0
+ */
+public interface IConfigurableMessageConstraint extends IConstraint {
+
+ /**
+ * A message to emit when the constraint is violated. Allows embedded Metapath
+ * expressions using the syntax {@code \{ metapath \}}.
+ *
+ * @return the message if defined or {@code null} otherwise
+ */
+ @Nullable
+ String getMessage();
+
+ /**
+ * Generate a violation message using the provide item and dynamic context for
+ * inline Metapath value insertion.
+ *
+ * @param item
+ * the target Metapath item to use as the focus for Metapath evaluation
+ * @param context
+ * the dynamic context for Metapath evaluation
+ * @return the message
+ * @throws IllegalStateException
+ * if a custom message is not defined, which will occur if this method
+ * is called while {@link #getMessage()} returns {@code null}
+ */
+ @NonNull
+ String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context);
+}
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java
index 5824c13ac..0060ac9c4 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java
@@ -9,7 +9,6 @@
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem;
-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.IDescribable;
@@ -117,31 +116,6 @@ enum Level {
@NonNull IDefinitionNodeItem, ?> item,
@NonNull DynamicContext dynamicContext);
- /**
- * A message to emit when the constraint is violated. Allows embedded Metapath
- * expressions using the syntax {@code \{ metapath \}}.
- *
- * @return the message if defined or {@code null} otherwise
- */
- @Nullable
- String getMessage();
-
- /**
- * Generate a violation message using the provide item and dynamic context for
- * inline Metapath value insertion.
- *
- * @param item
- * the target Metapath item to use as the focus for Metapath evaluation
- * @param context
- * the dynamic context for Metapath evaluation
- * @return the message
- * @throws IllegalStateException
- * if a custom message is not defined, which will occur if this method
- * is called while {@link #getMessage()} returns {@code null}
- */
- @NonNull
- String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context);
-
/**
* Retrieve the remarks associated with the constraint.
*
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 4c23c0181..5bc3a09b0 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
@@ -27,32 +27,38 @@ public interface IConstraintValidationHandler {
*
* @param constraint
* the constraint that was evaluated
- * @param node
- * the node used as the evaluation focus to determine constraint
- * targets
- * @param targets
- * the targets of evaluation
+ * @param target
+ * the node used as the evaluation focus to determine the items to test
+ * @param testedItems
+ * the items tested
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleCardinalityMinimumViolation(
@NonNull ICardinalityConstraint constraint,
- @NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets);
+ @NonNull INodeItem target,
+ @NonNull ISequence extends INodeItem> testedItems,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle a cardinality constraint maximum violation.
*
* @param constraint
* the constraint that was evaluated
- * @param node
- * the node used as the evaluation focus to determine constraint
- * targets
- * @param targets
- * the targets of evaluation
+ * @param target
+ * the node used as the evaluation focus to determine the items to test
+ * @param testedItems
+ * the items tested
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleCardinalityMaximumViolation(
@NonNull ICardinalityConstraint constraint,
- @NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets);
+ @NonNull INodeItem target,
+ @NonNull ISequence extends INodeItem> testedItems,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle a duplicate index violation.
@@ -62,10 +68,14 @@ void handleCardinalityMaximumViolation(
* @param node
* the node used as the evaluation focus to determine constraint
* targets
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleIndexDuplicateViolation(
@NonNull IIndexConstraint constraint,
- @NonNull INodeItem node);
+ @NonNull INodeItem node,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle an index duplicate key violation.
@@ -81,12 +91,16 @@ void handleIndexDuplicateViolation(
* the node that exists in the index for the related key
* @param target
* the target of evaluation
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleIndexDuplicateKeyViolation(
@NonNull IIndexConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem target);
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle an unique key violation.
@@ -102,12 +116,16 @@ void handleIndexDuplicateKeyViolation(
* the other node with the same key
* @param target
* the target of evaluation
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleUniqueKeyViolation(
@NonNull IUniqueConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem target);
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle an error that occurred while generating a key.
@@ -121,12 +139,16 @@ void handleUniqueKeyViolation(
* the target of evaluation
* @param exception
* the resulting Metapath exception
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleKeyMatchError(
@NonNull IKeyConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull MetapathException exception);
+ @NonNull MetapathException exception,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle a missing index violation.
@@ -142,12 +164,16 @@ void handleKeyMatchError(
* the target of evaluation
* @param message
* the error message
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleMissingIndexViolation(
@NonNull IIndexHasKeyConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull String message);
+ @NonNull String message,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle an index lookup key miss violation.
@@ -164,12 +190,16 @@ void handleMissingIndexViolation(
* the target of evaluation
* @param key
* the key that was used to lookup the index entry
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleIndexMiss(
@NonNull IIndexHasKeyConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull List key);
+ @NonNull List key,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle a match pattern violation.
@@ -187,13 +217,17 @@ void handleIndexMiss(
* the value used for pattern matching
* @param pattern
* the pattern used for pattern matching
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleMatchPatternViolation(
@NonNull IMatchesConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
@NonNull String value,
- @NonNull Pattern pattern);
+ @NonNull Pattern pattern,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle a match data type violation.
@@ -214,6 +248,9 @@ void handleMatchPatternViolation(
* the data type used for data type matching
* @param cause
* the data type exception related to this violation
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleMatchDatatypeViolation(
@NonNull IMatchesConstraint constraint,
@@ -221,7 +258,8 @@ void handleMatchDatatypeViolation(
@NonNull INodeItem target,
@NonNull String value,
@NonNull IDataTypeAdapter> adapter,
- @NonNull IllegalArgumentException cause);
+ @NonNull IllegalArgumentException cause,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle an expect test violation.
@@ -235,14 +273,15 @@ void handleMatchDatatypeViolation(
* targets
* @param target
* the target of evaluation
- * @param metapathContext
- * the Metapath evaluation context
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleExpectViolation(
@NonNull IExpectConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull DynamicContext metapathContext);
+ @NonNull DynamicContext dynamicContext);
/**
* Handle an allowed values constraint violation.
@@ -251,10 +290,14 @@ void handleExpectViolation(
* the allowed values constraints that did not match.
* @param target
* the target of evaluation
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleAllowedValuesViolation(
@NonNull List failedConstraints,
- @NonNull INodeItem target);
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle a constraint that has passed validation.
@@ -266,11 +309,15 @@ void handleAllowedValuesViolation(
* targets
* @param target
* the target of evaluation
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handlePass(
@NonNull IConstraint constraint,
@NonNull INodeItem node,
- @NonNull INodeItem target);
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext);
/**
* Handle a constraint that whose evaluation resulted in an unexpected error
@@ -285,10 +332,14 @@ void handlePass(
* the error message
* @param exception
* the causing exception
+ * @param dynamicContext
+ * the Metapath dynamic execution context to use for Metapath
+ * evaluation
*/
void handleError(
@NonNull IConstraint constraint,
@NonNull INodeItem node,
@NonNull String message,
- @NonNull Throwable exception);
+ @NonNull Throwable exception,
+ @NonNull DynamicContext dynamicContext);
}
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java
index f63904fb1..d1bf60ffb 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java
@@ -16,7 +16,7 @@
*
* A custom message can be used to indicate what a test failure signifies.
*/
-public interface IExpectConstraint extends IConstraint {
+public interface IExpectConstraint extends IConfigurableMessageConstraint {
/**
* Get the test to use to validate selected nodes.
*
@@ -44,7 +44,7 @@ static Builder builder() {
* Provides a builder pattern for constructing a new {@link IExpectConstraint}.
*/
final class Builder
- extends AbstractConstraintBuilder {
+ extends AbstractConfigurableMessageConstraintBuilder {
private String test;
private Builder() {
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyConstraint.java
index 2901b176d..fcc5328b0 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyConstraint.java
@@ -12,7 +12,7 @@
/**
* A common interface used for constraints oriented around key-based indexes.
*/
-public interface IKeyConstraint extends IConstraint {
+public interface IKeyConstraint extends IConfigurableMessageConstraint {
/**
* Retrieve the list of keys to use in creating and looking up an entry in a
* given index.
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IMatchesConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IMatchesConstraint.java
index 130a2c477..d2a6a6c0b 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IMatchesConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IMatchesConstraint.java
@@ -18,7 +18,7 @@
* Represents a rule requiring the value of a field or flag to match a pattern
* and/or conform to an identified data type.
*/
-public interface IMatchesConstraint extends IConstraint {
+public interface IMatchesConstraint extends IConfigurableMessageConstraint {
/**
* Get the expected pattern.
*
@@ -55,7 +55,7 @@ static Builder builder() {
* Provides a builder pattern for constructing a new {@link IMatchesConstraint}.
*/
final class Builder
- extends AbstractConstraintBuilder {
+ extends AbstractConfigurableMessageConstraintBuilder {
private Pattern pattern;
private IDataTypeAdapter> datatype;
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 ea837667b..f872edacd 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
@@ -101,10 +101,19 @@ private void logConstraint(
public void handleCardinalityMinimumViolation(
@NonNull ICardinalityConstraint constraint,
@NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
+ @NonNull ISequence extends INodeItem> targets,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, node, newCardinalityMinimumViolationMessage(constraint, node, targets), null);
+ logConstraint(
+ level,
+ node,
+ newCardinalityMinimumViolationMessage(
+ constraint,
+ node,
+ targets,
+ dynamicContext),
+ null);
}
}
@@ -112,10 +121,19 @@ public void handleCardinalityMinimumViolation(
public void handleCardinalityMaximumViolation(
@NonNull ICardinalityConstraint constraint,
@NonNull INodeItem node,
- @NonNull ISequence extends INodeItem> targets) {
+ @NonNull ISequence extends INodeItem> targets,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, node, newCardinalityMaximumViolationMessage(constraint, node, targets), null);
+ logConstraint(
+ level,
+ node,
+ newCardinalityMaximumViolationMessage(
+ constraint,
+ node,
+ targets,
+ dynamicContext),
+ null);
}
}
@@ -124,10 +142,20 @@ public void handleIndexDuplicateKeyViolation(
@NonNull IIndexConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem target) {
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, target, newIndexDuplicateKeyViolationMessage(constraint, node, oldItem, target), null);
+ logConstraint(
+ level,
+ target,
+ newIndexDuplicateKeyViolationMessage(
+ constraint,
+ node,
+ oldItem,
+ target,
+ dynamicContext),
+ null);
}
}
@@ -136,10 +164,20 @@ public void handleUniqueKeyViolation(
@NonNull IUniqueConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem oldItem,
- @NonNull INodeItem target) {
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, target, newUniqueKeyViolationMessage(constraint, node, oldItem, target), null);
+ logConstraint(
+ level,
+ target,
+ newUniqueKeyViolationMessage(
+ constraint,
+ node,
+ oldItem,
+ target,
+ dynamicContext),
+ null);
}
}
@@ -149,7 +187,8 @@ public void handleKeyMatchError(
@NonNull IKeyConstraint constraint,
@NonNull INodeItem node,
@NonNull INodeItem target,
- @NonNull MetapathException cause) {
+ @NonNull MetapathException cause,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
logConstraint(level, target, cause.getLocalizedMessage(), cause);
@@ -162,10 +201,21 @@ public void handleMatchPatternViolation(
@NonNull INodeItem node,
@NonNull INodeItem target,
@NonNull String value,
- @NonNull Pattern pattern) {
+ @NonNull Pattern pattern,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, target, newMatchPatternViolationMessage(constraint, node, target, value, pattern), null);
+ logConstraint(
+ level,
+ target,
+ newMatchPatternViolationMessage(
+ constraint,
+ node,
+ target,
+ value,
+ pattern,
+ dynamicContext),
+ null);
}
}
@@ -176,10 +226,21 @@ public void handleMatchDatatypeViolation(
@NonNull INodeItem target,
@NonNull String value,
@NonNull IDataTypeAdapter> adapter,
- @NonNull IllegalArgumentException cause) {
+ @NonNull IllegalArgumentException cause,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, target, newMatchDatatypeViolationMessage(constraint, node, target, value, adapter), cause);
+ logConstraint(
+ level,
+ target,
+ newMatchDatatypeViolationMessage(
+ constraint,
+ node,
+ target,
+ value,
+ adapter,
+ dynamicContext),
+ cause);
}
}
@@ -191,59 +252,117 @@ public void handleExpectViolation(
@NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, target, newExpectViolationMessage(constraint, node, target, dynamicContext), null);
+ logConstraint(
+ level,
+ target,
+ newExpectViolationMessage(
+ constraint,
+ node,
+ target,
+ dynamicContext),
+ null);
}
}
@Override
- public void handleAllowedValuesViolation(@NonNull List failedConstraints,
- @NonNull INodeItem target) {
+ public void handleAllowedValuesViolation(
+ List failedConstraints,
+ INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
Level level = ObjectUtils.notNull(failedConstraints.stream()
.map(IConstraint::getLevel)
.max(Comparator.comparing(Level::ordinal))
.get());
if (isLogged(level)) {
- logConstraint(level, target, newAllowedValuesViolationMessage(failedConstraints, target), null);
+ logConstraint(
+ level,
+ target,
+ newAllowedValuesViolationMessage(
+ failedConstraints,
+ target),
+ null);
}
}
@Override
- public void handleIndexDuplicateViolation(IIndexConstraint constraint, INodeItem node) {
+ public void handleIndexDuplicateViolation(
+ @NonNull IIndexConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull DynamicContext dynamicContext) {
+ // always log at level critical
Level level = Level.CRITICAL;
if (isLogged(level)) {
- logConstraint(level, node, newIndexDuplicateViolationMessage(constraint, node), null);
+ logConstraint(
+ level,
+ node,
+ newIndexDuplicateViolationMessage(
+ constraint,
+ node),
+ null);
}
}
@Override
- public void handleIndexMiss(IIndexHasKeyConstraint constraint, INodeItem node, INodeItem target, List key) {
+ public void handleIndexMiss(
+ @NonNull IIndexHasKeyConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull INodeItem target,
+ @NonNull List key,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, node, newIndexMissMessage(constraint, node, target, key), null);
+ logConstraint(
+ level,
+ node,
+ newIndexMissMessage(
+ constraint,
+ node,
+ target,
+ key,
+ dynamicContext),
+ null);
}
}
@Override
- public void handleMissingIndexViolation(IIndexHasKeyConstraint constraint, INodeItem node, INodeItem target,
- String message) {
+ public void handleMissingIndexViolation(
+ @NonNull IIndexHasKeyConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull INodeItem target,
+ @NonNull String message,
+ @NonNull DynamicContext dynamicContext) {
Level level = constraint.getLevel();
if (isLogged(level)) {
- logConstraint(level, node, newMissingIndexViolationMessage(constraint, node, target, message), null);
+ logConstraint(
+ level,
+ node,
+ newMissingIndexViolationMessage(
+ constraint,
+ node,
+ target,
+ message,
+ dynamicContext),
+ null);
}
}
@Override
- public void handlePass(IConstraint constraint, INodeItem node, INodeItem target) {
+ public void handlePass(
+ @NonNull IConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull INodeItem target,
+ @NonNull DynamicContext dynamicContext) {
// do nothing
}
@Override
public void handleError(
- IConstraint constraint,
- INodeItem node,
- String message,
- Throwable exception) {
+ @NonNull IConstraint constraint,
+ @NonNull INodeItem node,
+ @NonNull String message,
+ @NonNull Throwable exception,
+ @NonNull DynamicContext dynamicContext) {
Level level = Level.CRITICAL;
if (isLogged(level)) {
logConstraint(level, node, message, exception);
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
new file mode 100644
index 000000000..1b3b4b937
--- /dev/null
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java
@@ -0,0 +1,97 @@
+/*
+ * SPDX-FileCopyrightText: none
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+package gov.nist.secauto.metaschema.core.model.constraint.impl;
+
+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.DynamicContext;
+import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
+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.constraint.IConfigurableMessageConstraint;
+import gov.nist.secauto.metaschema.core.model.constraint.ISource;
+import gov.nist.secauto.metaschema.core.util.ObjectUtils;
+import gov.nist.secauto.metaschema.core.util.ReplacementScanner;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
+/**
+ * The base class for all constraint implementations that allow a configurable
+ * message.
+ *
+ * @since 2.0.0
+ */
+public abstract class AbstractConfigurableMessageConstraint
+ extends AbstractConstraint
+ implements IConfigurableMessageConstraint {
+ @NonNull
+ private static final Pattern METAPATH_VALUE_TEMPLATE_PATTERN
+ = ObjectUtils.notNull(Pattern.compile("(?> properties,
+ @Nullable String message,
+ @Nullable MarkupMultiline remarks) {
+ super(id, formalName, description, source, level, target, properties, remarks);
+ this.message = message;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context) {
+ String message = getMessage();
+ if (message == null) {
+ throw new IllegalStateException("A custom message is not defined.");
+ }
+
+ return ObjectUtils.notNull(ReplacementScanner.replaceTokens(message, METAPATH_VALUE_TEMPLATE_PATTERN, match -> {
+ String metapath = ObjectUtils.notNull(match.group(2));
+ MetapathExpression expr = MetapathExpression.compile(metapath, context.getStaticContext());
+ return expr.evaluateAs(item, MetapathExpression.ResultType.STRING, context);
+ }).toString());
+ }
+}
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java
index cc8a3f3aa..64fb4f7d5 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java
@@ -11,18 +11,15 @@
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem;
-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.constraint.IConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.ISource;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
-import gov.nist.secauto.metaschema.core.util.ReplacementScanner;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.regex.Pattern;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
@@ -32,9 +29,6 @@
* The base class for all constraint implementations.
*/
public abstract class AbstractConstraint implements IConstraint { // NOPMD - intentional data class
- @NonNull
- private static final Pattern METAPATH_VALUE_TEMPLATE_PATTERN
- = ObjectUtils.notNull(Pattern.compile("(?> properties;
@@ -72,8 +64,6 @@ public abstract class AbstractConstraint implements IConstraint { // NOPMD - int
* the Metapath expression identifying the nodes the constraint targets
* @param properties
* a collection of associated properties
- * @param message
- * an optional message to emit when the constraint is violated
* @param remarks
* optional remarks describing the intent of the constraint
*/
@@ -85,7 +75,6 @@ protected AbstractConstraint(
@NonNull Level level,
@NonNull String target,
@NonNull Map> properties,
- @Nullable String message,
@Nullable MarkupMultiline remarks) {
Objects.requireNonNull(target);
this.id = id;
@@ -94,7 +83,6 @@ protected AbstractConstraint(
this.source = source;
this.level = ObjectUtils.requireNonNull(level, "level");
this.properties = properties;
- this.message = message;
this.remarks = remarks;
this.targetMetapath = ObjectUtils.notNull(
Lazy.lazy(() -> MetapathExpression.compile(
@@ -153,25 +141,6 @@ public MetapathExpression getTargetMetapath() {
return ObjectUtils.notNull(targetMetapath.get());
}
- @Override
- public String getMessage() {
- return message;
- }
-
- @Override
- public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context) {
- String message = getMessage();
- if (message == null) {
- throw new IllegalStateException("A custom message is not defined.");
- }
-
- return ObjectUtils.notNull(ReplacementScanner.replaceTokens(message, METAPATH_VALUE_TEMPLATE_PATTERN, match -> {
- String metapath = ObjectUtils.notNull(match.group(2));
- MetapathExpression expr = MetapathExpression.compile(metapath, context.getStaticContext());
- return expr.evaluateAs(item, MetapathExpression.ResultType.STRING, context);
- }).toString());
- }
-
@Override
@NonNull
public ISequence extends IDefinitionNodeItem, ?>> matchTargets(
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java
index bd9469c52..a38cf834f 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java
@@ -21,7 +21,7 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
abstract class AbstractKeyConstraint
- extends AbstractConstraint
+ extends AbstractConfigurableMessageConstraint
implements IKeyConstraint {
@NonNull
private final List keyFields;
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java
index 6d2303558..6f9dce6af 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java
@@ -61,8 +61,6 @@ public final class DefaultAllowedValuesConstraint
* {@code allowedValues} are allowed, or disallowed if {@code false}
* @param extensible
* indicates the degree to which extended values should be allowed
- * @param message
- * an optional message to emit when the constraint is violated
* @param remarks
* optional remarks describing the intent of the constraint
*/
@@ -77,9 +75,8 @@ public DefaultAllowedValuesConstraint( // NOPMD necessary
@NonNull Map allowedValues,
boolean allowedOther,
@NonNull Extensible extensible,
- @Nullable String message,
@Nullable MarkupMultiline remarks) {
- super(id, formalName, description, source, level, target, properties, message, remarks);
+ super(id, formalName, description, source, level, target, properties, remarks);
this.allowedValues = allowedValues;
this.allowedOther = allowedOther;
this.extensible = extensible;
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java
index f48727887..86d34b426 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java
@@ -25,7 +25,7 @@
* values.
*/
public final class DefaultCardinalityConstraint
- extends AbstractConstraint
+ extends AbstractConfigurableMessageConstraint
implements ICardinalityConstraint {
@Nullable
private final Integer minOccurs;
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java
index 61ffee36f..0f36cb8a4 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java
@@ -28,7 +28,7 @@
* against the target.
*/
public final class DefaultExpectConstraint
- extends AbstractConstraint
+ extends AbstractConfigurableMessageConstraint
implements IExpectConstraint {
@NonNull
private final Lazy testMetapath;
diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java
index ce7a78acd..d500ecdc5 100644
--- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java
+++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java
@@ -25,7 +25,7 @@
* Enforces a value pattern and/or data type.
*/
public final class DefaultMatchesConstraint
- extends AbstractConstraint
+ extends AbstractConfigurableMessageConstraint
implements IMatchesConstraint {
private final Pattern pattern;
private final IDataTypeAdapter> dataType;
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 f2149a271..c429bf834 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
@@ -65,6 +65,9 @@
import edu.umd.cs.findbugs.annotations.NonNull;
+/**
+ * Provides XMLBeans-based parsing support for constraints.
+ */
@SuppressWarnings("PMD.CouplingBetweenObjects")
public final class ConstraintXmlSupport {
@SuppressWarnings("PMD.UseConcurrentHashMap")
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java
index ee810edbd..c9751550d 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java
@@ -248,11 +248,6 @@ private static void applyAllowedValuesConstraints(
constraintAnnotation.addMember("values", "$L", valueAnnotation.build());
}
- String message = constraint.getMessage();
- if (message != null) {
- constraintAnnotation.addMember("message", "$S", message);
- }
-
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
constraintAnnotation.addMember("remarks", "$S", remarks.toMarkdown());
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonReader.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonReader.java
index 6018eb3b6..bc9238121 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonReader.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonReader.java
@@ -407,7 +407,7 @@ private IBoundObject readComplexDefinitionObject(
? bodyHandler
: new JsonKeyBodyHandler(jsonKey, bodyHandler);
- JsonLocation location = getReader().currentLocation();
+ @SuppressWarnings("resource") JsonLocation location = getReader().currentLocation();
// construct the item
IBoundObject item = definition.newInstance(
@@ -486,7 +486,7 @@ public void accept(
IBoundObject parent,
IJsonProblemHandler problemHandler)
throws IOException {
- JsonParser parser = getReader();
+ @SuppressWarnings("resource") JsonParser parser = getReader();
JsonUtil.assertCurrent(parser, JsonToken.FIELD_NAME);
// the field will be the JSON key
@@ -523,7 +523,7 @@ public void accept(
IBoundObject parent,
IJsonProblemHandler problemHandler)
throws IOException {
- JsonParser parser = getReader();
+ @SuppressWarnings("resource") JsonParser parser = getReader();
// advance past the start object
JsonUtil.assertAndAdvance(parser, JsonToken.START_OBJECT);
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java
index f4e4641fd..e2ee240dc 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java
@@ -105,14 +105,6 @@
@NonNull
IAllowedValuesConstraint.Extensible extensible() default IAllowedValuesConstraint.Extensible.EXTERNAL;
- /**
- * The message to emit when the constraint is violated.
- *
- * @return the message or an empty string otherwise
- */
- @NonNull
- String message() default "";
-
/**
* Any remarks about the constraint, encoded as an escaped Markdown string.
*
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java
index 0e07aa0e3..42bdbb8f6 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java
@@ -10,6 +10,7 @@
import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
import gov.nist.secauto.metaschema.core.model.IAttributable;
+import gov.nist.secauto.metaschema.core.model.constraint.AbstractConfigurableMessageConstraintBuilder;
import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder;
import gov.nist.secauto.metaschema.core.model.constraint.AbstractKeyConstraintBuilder;
import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
@@ -114,7 +115,8 @@ static String toMetapath(@NonNull String metapath) {
return builder;
}
- static > T applyMessage(@NonNull T builder, @Nullable String message) {
+ static > T applyMessage(@NonNull T builder,
+ @Nullable String message) {
if (message != null && !message.isBlank()) {
builder.message(message);
}
@@ -168,7 +170,6 @@ static IAllowedValuesConstraint newAllowedValuesConstraint(
.level(constraint.level());
applyTarget(builder, constraint.target());
applyProperties(builder, constraint.properties());
- applyMessage(builder, constraint.message());
applyRemarks(builder, constraint.remarks());
applyAllowedValues(builder, constraint);
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 c45ffd26d..8456e7d34 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
@@ -100,8 +100,7 @@ protected List parseResource(@NonNull URI resource, @NonNull Deq
// now check if this constraint set imports other constraint sets
List imports = CollectionUtil.listOrEmpty(obj.getImports());
- @NonNull
- Set importedConstraints;
+ @NonNull Set importedConstraints;
if (imports.isEmpty()) {
importedConstraints = CollectionUtil.emptySet();
} else {
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IConfigurableMessageConstraintBase.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IConfigurableMessageConstraintBase.java
new file mode 100644
index 000000000..0fbec1dea
--- /dev/null
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IConfigurableMessageConstraintBase.java
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: none
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+package gov.nist.secauto.metaschema.databind.model.metaschema;
+
+import edu.umd.cs.findbugs.annotations.Nullable;
+
+/**
+ * Represents constraint metadata that is common to all constraints.
+ */
+public interface IConfigurableMessageConstraintBase extends IConstraintBase {
+ /**
+ * Get a custom message to use when the constraint is not satisfied.
+ *
+ * A custom message allow for more meaningful information, tailored to the test
+ * case, to be provided in the case a constraint is not satisfied.
+ *
+ * @return the message or {@code null} if a default message is to be used
+ */
+ @Nullable
+ String getMessage();
+}
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IConstraintBase.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IConstraintBase.java
index d64b3979c..57d32402a 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IConstraintBase.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IConstraintBase.java
@@ -60,17 +60,6 @@ public interface IConstraintBase {
@Nullable
List getProps();
- /**
- * Get a custom message to use when the constraint is not satisfied.
- *
- * A custom message allow for more meaningful information, tailored to the test
- * case, to be provided in the case a constraint is not satisfied.
- *
- * @return the message or {@code null} if a default message is to be used
- */
- @Nullable
- String getMessage();
-
/**
* Get the optional remarks that provide additional details explanation the
* intent or use of the constraint.
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagAllowedValues.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagAllowedValues.java
index d8cd4de81..69da4cc2b 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagAllowedValues.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagAllowedValues.java
@@ -118,11 +118,6 @@ public class FlagAllowedValues implements IBoundObject, IConstraintBase {
groupAs = @GroupAs(name = "enums", inJson = JsonGroupAsBehavior.LIST))
private List _enums;
- @BoundField(
- formalName = "Constraint Condition Violation Message",
- useName = "message")
- private String _message;
-
@BoundField(
formalName = "Remarks",
description = "Any explanatory or helpful information to be provided about the remarks parent.",
@@ -267,15 +262,6 @@ public boolean removeEnum(ConstraintValueEnum item) {
return _enums != null && _enums.remove(value);
}
- @Override
- public String getMessage() {
- return _message;
- }
-
- public void setMessage(String value) {
- _message = value;
- }
-
@Override
public Remarks getRemarks() {
return _remarks;
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagExpect.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagExpect.java
index da7b4ee76..5aa748e67 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagExpect.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagExpect.java
@@ -22,7 +22,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
-import gov.nist.secauto.metaschema.databind.model.metaschema.IConstraintBase;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -38,7 +38,7 @@
formalName = "Expect Condition Constraint",
name = "flag-expect",
moduleClass = MetaschemaModelModule.class)
-public class FlagExpect implements IBoundObject, IConstraintBase {
+public class FlagExpect implements IBoundObject, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagIndexHasKey.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagIndexHasKey.java
index 41f3b293a..4be00448c 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagIndexHasKey.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagIndexHasKey.java
@@ -21,7 +21,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
-import gov.nist.secauto.metaschema.databind.model.metaschema.IConstraintBase;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -37,7 +37,7 @@
formalName = "Index Has Key Constraint",
name = "flag-index-has-key",
moduleClass = MetaschemaModelModule.class)
-public class FlagIndexHasKey implements IBoundObject, IConstraintBase {
+public class FlagIndexHasKey implements IBoundObject, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagMatches.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagMatches.java
index b42bfd1c0..b6f78c1b1 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagMatches.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/FlagMatches.java
@@ -22,7 +22,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
-import gov.nist.secauto.metaschema.databind.model.metaschema.IConstraintBase;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -38,7 +38,7 @@
formalName = "Value Matches Constraint",
name = "flag-matches",
moduleClass = MetaschemaModelModule.class)
-public class FlagMatches implements IBoundObject, IConstraintBase {
+public class FlagMatches implements IBoundObject, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedAllowedValuesConstraint.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedAllowedValuesConstraint.java
index d548760e8..835abba9e 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedAllowedValuesConstraint.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedAllowedValuesConstraint.java
@@ -126,11 +126,6 @@ public class TargetedAllowedValuesConstraint implements IBoundObject, ITargetedC
groupAs = @GroupAs(name = "enums", inJson = JsonGroupAsBehavior.LIST))
private List _enums;
- @BoundField(
- formalName = "Constraint Condition Violation Message",
- useName = "message")
- private String _message;
-
@BoundField(
formalName = "Remarks",
description = "Any explanatory or helpful information to be provided about the remarks parent.",
@@ -284,15 +279,6 @@ public boolean removeEnum(ConstraintValueEnum item) {
return _enums != null && _enums.remove(value);
}
- @Override
- public String getMessage() {
- return _message;
- }
-
- public void setMessage(String value) {
- _message = value;
- }
-
@Override
public Remarks getRemarks() {
return _remarks;
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedExpectConstraint.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedExpectConstraint.java
index c90f4063a..c7aec1ab7 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedExpectConstraint.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedExpectConstraint.java
@@ -22,6 +22,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.ITargetedConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@@ -38,7 +39,8 @@
formalName = "Expect Condition Constraint",
name = "targeted-expect-constraint",
moduleClass = MetaschemaModelModule.class)
-public class TargetedExpectConstraint implements IBoundObject, ITargetedConstraintBase {
+public class TargetedExpectConstraint
+ implements IBoundObject, ITargetedConstraintBase, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedHasCardinalityConstraint.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedHasCardinalityConstraint.java
index c8a55d301..3968594f6 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedHasCardinalityConstraint.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedHasCardinalityConstraint.java
@@ -24,6 +24,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.Matches;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.ITargetedConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@@ -41,7 +42,8 @@
formalName = "Targeted Cardinality Constraint",
name = "targeted-has-cardinality-constraint",
moduleClass = MetaschemaModelModule.class)
-public class TargetedHasCardinalityConstraint implements IBoundObject, ITargetedConstraintBase {
+public class TargetedHasCardinalityConstraint
+ implements IBoundObject, ITargetedConstraintBase, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexConstraint.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexConstraint.java
index 9aed9e682..c2199ea3f 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexConstraint.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexConstraint.java
@@ -22,6 +22,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.ITargetedConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@@ -38,7 +39,8 @@
formalName = "Targeted Index Constraint",
name = "targeted-index-constraint",
moduleClass = MetaschemaModelModule.class)
-public class TargetedIndexConstraint implements IBoundObject, ITargetedConstraintBase {
+public class TargetedIndexConstraint
+ implements IBoundObject, ITargetedConstraintBase, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexHasKeyConstraint.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexHasKeyConstraint.java
index 1cb2aa8c7..1dd967039 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexHasKeyConstraint.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIndexHasKeyConstraint.java
@@ -22,6 +22,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.ITargetedConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@@ -38,7 +39,8 @@
formalName = "Targeted Index Has Key Constraint",
name = "targeted-index-has-key-constraint",
moduleClass = MetaschemaModelModule.class)
-public class TargetedIndexHasKeyConstraint implements IBoundObject, ITargetedConstraintBase {
+public class TargetedIndexHasKeyConstraint
+ implements IBoundObject, ITargetedConstraintBase, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIsUniqueConstraint.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIsUniqueConstraint.java
index 76ab5cf2e..5cc57fc37 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIsUniqueConstraint.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedIsUniqueConstraint.java
@@ -22,6 +22,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.ITargetedConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@@ -38,7 +39,8 @@
formalName = "Targeted Unique Constraint",
name = "targeted-is-unique-constraint",
moduleClass = MetaschemaModelModule.class)
-public class TargetedIsUniqueConstraint implements IBoundObject, ITargetedConstraintBase {
+public class TargetedIsUniqueConstraint
+ implements IBoundObject, ITargetedConstraintBase, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedMatchesConstraint.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedMatchesConstraint.java
index 5035e981b..3ed89ac72 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedMatchesConstraint.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/binding/TargetedMatchesConstraint.java
@@ -22,6 +22,7 @@
import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.ITargetedConstraintBase;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@@ -38,7 +39,8 @@
formalName = "Value Matches Constraint",
name = "targeted-matches-constraint",
moduleClass = MetaschemaModelModule.class)
-public class TargetedMatchesConstraint implements IBoundObject, ITargetedConstraintBase {
+public class TargetedMatchesConstraint
+ implements IBoundObject, ITargetedConstraintBase, IConfigurableMessageConstraintBase {
private final IMetaschemaData __metaschemaData;
@BoundFlag(
diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java
index 292bb3096..e2c270153 100644
--- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java
+++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java
@@ -8,6 +8,7 @@
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.model.constraint.AbstractConfigurableMessageConstraintBuilder;
import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder;
import gov.nist.secauto.metaschema.core.model.constraint.AbstractKeyConstraintBuilder;
import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
@@ -24,6 +25,7 @@
import gov.nist.secauto.metaschema.core.model.constraint.IUniqueConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IValueConstrained;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
+import gov.nist.secauto.metaschema.databind.model.metaschema.IConfigurableMessageConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.IConstraintBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.IModelConstraintsBase;
import gov.nist.secauto.metaschema.databind.model.metaschema.IValueConstraintsBase;
@@ -241,7 +243,7 @@ private static IExpectConstraint newExpect(
@NonNull ISource source) {
IExpectConstraint.Builder builder = IExpectConstraint.builder()
.test(target(ObjectUtils.requireNonNull(obj.getTest())));
- applyCommonValues(obj, null, source, builder);
+ applyConfigurableCommonValues(obj, null, source, builder);
String message = obj.getMessage();
if (message != null) {
@@ -257,7 +259,7 @@ private static IExpectConstraint newExpect(
@NonNull ISource source) {
IExpectConstraint.Builder builder = IExpectConstraint.builder()
.test(target(ObjectUtils.requireNonNull(obj.getTest())));
- applyCommonValues(obj, obj.getTarget(), source, builder);
+ applyConfigurableCommonValues(obj, obj.getTarget(), source, builder);
return builder.build();
}
@@ -285,7 +287,7 @@ private static IIndexHasKeyConstraint newIndexHasKey(
@NonNull FlagIndexHasKey obj,
@NonNull ISource source) {
IIndexHasKeyConstraint.Builder builder = IIndexHasKeyConstraint.builder(ObjectUtils.requireNonNull(obj.getName()));
- applyCommonValues(obj, null, source, builder);
+ applyConfigurableCommonValues(obj, null, source, builder);
handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
return builder.build();
}
@@ -295,7 +297,7 @@ private static IIndexHasKeyConstraint newIndexHasKey(
@NonNull TargetedIndexHasKeyConstraint obj,
@NonNull ISource source) {
IIndexHasKeyConstraint.Builder builder = IIndexHasKeyConstraint.builder(ObjectUtils.requireNonNull(obj.getName()));
- applyCommonValues(obj, obj.getTarget(), source, builder);
+ applyConfigurableCommonValues(obj, obj.getTarget(), source, builder);
handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
return builder.build();
}
@@ -305,7 +307,7 @@ private static IMatchesConstraint newMatches(
@NonNull FlagMatches obj,
@NonNull ISource source) {
IMatchesConstraint.Builder builder = IMatchesConstraint.builder();
- applyCommonValues(obj, null, source, builder);
+ applyConfigurableCommonValues(obj, null, source, builder);
Pattern regex = pattern(obj.getRegex());
if (regex != null) {
@@ -326,7 +328,7 @@ private static IMatchesConstraint newMatches(
@NonNull TargetedMatchesConstraint obj,
@NonNull ISource source) {
IMatchesConstraint.Builder builder = IMatchesConstraint.builder();
- applyCommonValues(obj, obj.getTarget(), source, builder);
+ applyConfigurableCommonValues(obj, obj.getTarget(), source, builder);
Pattern regex = pattern(obj.getRegex());
if (regex != null) {
@@ -347,7 +349,7 @@ private static IIndexConstraint newIndex(
@NonNull TargetedIndexConstraint obj,
@NonNull ISource source) {
IIndexConstraint.Builder builder = IIndexConstraint.builder(ObjectUtils.requireNonNull(obj.getName()));
- applyCommonValues(obj, obj.getTarget(), source, builder);
+ applyConfigurableCommonValues(obj, obj.getTarget(), source, builder);
handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
return builder.build();
@@ -358,7 +360,7 @@ private static ICardinalityConstraint newHasCardinality(
@NonNull TargetedHasCardinalityConstraint obj,
@NonNull ISource source) {
ICardinalityConstraint.Builder builder = ICardinalityConstraint.builder();
- applyCommonValues(obj, obj.getTarget(), source, builder);
+ applyConfigurableCommonValues(obj, obj.getTarget(), source, builder);
BigInteger minOccurs = obj.getMinOccurs();
if (minOccurs != null) {
@@ -378,12 +380,27 @@ private static IUniqueConstraint newUnique(
@NonNull TargetedIsUniqueConstraint obj,
@NonNull ISource source) {
IUniqueConstraint.Builder builder = IUniqueConstraint.builder();
- applyCommonValues(obj, obj.getTarget(), source, builder);
+ applyConfigurableCommonValues(obj, obj.getTarget(), source, builder);
handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
return builder.build();
}
+ @NonNull
+ private static > T applyConfigurableCommonValues(
+ @NonNull IConfigurableMessageConstraintBase constraint,
+ @Nullable String target,
+ @NonNull ISource source,
+ @NonNull T builder) {
+ applyCommonValues(constraint, target, source, builder);
+
+ String message = constraint.getMessage();
+ if (message != null) {
+ builder.message(message);
+ }
+ return builder;
+ }
+
@NonNull
private static > T applyCommonValues(
@NonNull IConstraintBase constraint,
@@ -410,11 +427,6 @@ private static IUniqueConstraint newUnique(
List props = ObjectUtils.requireNonNull(constraint.getProps());
builder.properties(ModelSupport.parseProperties(props));
- String message = constraint.getMessage();
- if (message != null) {
- builder.message(message);
- }
-
Remarks remarks = constraint.getRemarks();
if (remarks != null) {
builder.remarks(ObjectUtils.notNull(remarks.getRemark()));