AbstractConstraintValidationHandler.java

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

  26. package gov.nist.secauto.metaschema.core.model.constraint;

  27. import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
  28. import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
  29. import gov.nist.secauto.metaschema.core.metapath.ISequence;
  30. import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter;
  31. import gov.nist.secauto.metaschema.core.metapath.function.library.FnData;
  32. import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
  33. import gov.nist.secauto.metaschema.core.util.CustomCollectors;

  34. import java.util.List;
  35. import java.util.stream.Collectors;

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

  37. public abstract class AbstractConstraintValidationHandler implements IConstraintValidationHandler {
  38.   @NonNull
  39.   public abstract IPathFormatter getPathFormatter();

  40.   protected String toPath(@NonNull INodeItem nodeItem) {
  41.     return nodeItem.toPath(getPathFormatter());
  42.   }

  43.   @SuppressWarnings("null")
  44.   @NonNull
  45.   protected String newCardinalityMinimumViolationMessage(
  46.       @NonNull ICardinalityConstraint constraint,
  47.       @SuppressWarnings("unused") @NonNull INodeItem node,
  48.       @NonNull ISequence<? extends INodeItem> targets) {
  49.     // TODO: render the item paths instead of the expression
  50.     return String.format(
  51.         "The cardinality '%d' is below the required minimum '%d' for items matching the expression '%s'.",
  52.         targets.size(), constraint.getMinOccurs(), constraint.getTarget().getPath());
  53.   }

  54.   @SuppressWarnings("null")
  55.   @NonNull
  56.   protected String newCardinalityMaximumViolationMessage(
  57.       @NonNull ICardinalityConstraint constraint,
  58.       @SuppressWarnings("unused") @NonNull INodeItem node,
  59.       @NonNull ISequence<? extends INodeItem> targets) {
  60.     // TODO: render the item paths instead of the expression
  61.     return String.format(
  62.         "The cardinality '%d' is greater than the required maximum '%d' for items matching the expression '%s'.",
  63.         targets.size(), constraint.getMinOccurs(), constraint.getTarget().getPath());
  64.   }

  65.   @SuppressWarnings("null")
  66.   @NonNull
  67.   protected String newIndexDuplicateKeyViolationMessage(
  68.       @NonNull IIndexConstraint constraint,
  69.       @SuppressWarnings("unused") @NonNull INodeItem node,
  70.       @NonNull INodeItem oldItem,
  71.       @NonNull INodeItem target) {
  72.     // TODO: render the key paths
  73.     return String.format("Index '%s' has duplicate key for items at paths '%s' and '%s'", constraint.getName(),
  74.         toPath(oldItem), toPath(target));
  75.   }

  76.   @SuppressWarnings("null")
  77.   @NonNull
  78.   protected String newUniqueKeyViolationMessage(
  79.       @SuppressWarnings("unused") @NonNull IUniqueConstraint constraint,
  80.       @SuppressWarnings("unused") @NonNull INodeItem node,
  81.       @NonNull INodeItem oldItem,
  82.       @NonNull INodeItem target) {
  83.     // TODO: render the key paths
  84.     return String.format("Unique constraint violation at paths '%s' and '%s'",
  85.         toPath(oldItem), toPath(target));
  86.   }

  87.   @SuppressWarnings("null")
  88.   @NonNull
  89.   protected String newMatchPatternViolationMessage(
  90.       @NonNull IMatchesConstraint constraint,
  91.       @SuppressWarnings("unused") @NonNull INodeItem node,
  92.       @NonNull INodeItem target,
  93.       @NonNull String value) {
  94.     return String.format("Value '%s' did not match the pattern '%s' at path '%s'",
  95.         value,
  96.         constraint.getPattern().pattern(),
  97.         toPath(target));
  98.   }

  99.   @SuppressWarnings("null")
  100.   @NonNull
  101.   protected String newMatchDatatypeViolationMessage(
  102.       @NonNull IMatchesConstraint constraint,
  103.       @SuppressWarnings("unused") @NonNull INodeItem node,
  104.       @NonNull INodeItem target,
  105.       @NonNull String value) {
  106.     IDataTypeAdapter<?> adapter = constraint.getDataType();
  107.     return String.format("Value '%s' did not conform to the data type '%s' at path '%s'", value,
  108.         adapter.getPreferredName(), toPath(target));
  109.   }

  110.   @SuppressWarnings("null")
  111.   @NonNull
  112.   protected CharSequence newExpectViolationMessage(
  113.       @NonNull IExpectConstraint constraint,
  114.       @SuppressWarnings("unused") @NonNull INodeItem node,
  115.       @NonNull INodeItem target,
  116.       @NonNull DynamicContext dynamicContext) {
  117.     CharSequence message;
  118.     if (constraint.getMessage() != null) {
  119.       message = constraint.generateMessage(target, dynamicContext);
  120.     } else {
  121.       message = String.format("Expect constraint '%s' did not match the data at path '%s'",
  122.           constraint.getTest().getPath(),
  123.           toPath(target));
  124.     }
  125.     return message;
  126.   }

  127.   @SuppressWarnings("null")
  128.   @NonNull
  129.   protected CharSequence newAllowedValuesViolationMessage(
  130.       @NonNull List<IAllowedValuesConstraint> constraints,
  131.       @NonNull INodeItem target) {

  132.     String allowedValues = constraints.stream()
  133.         .flatMap(constraint -> constraint.getAllowedValues().values().stream())
  134.         .map(allowedValue -> allowedValue.getValue())
  135.         .sorted()
  136.         .distinct()
  137.         .collect(CustomCollectors.joiningWithOxfordComma("or"));

  138.     return String.format("Value '%s' doesn't match one of '%s' at path '%s'",
  139.         FnData.fnDataItem(target).asString(),
  140.         allowedValues,
  141.         toPath(target));
  142.   }

  143.   @SuppressWarnings("null")
  144.   @NonNull
  145.   protected CharSequence newIndexDuplicateViolationMessage(
  146.       @NonNull IIndexConstraint constraint,
  147.       @NonNull INodeItem node) {
  148.     return String.format("Duplicate index named '%s' found at path '%s'",
  149.         constraint.getName(),
  150.         node.getMetapath());
  151.   }

  152.   @SuppressWarnings("null")
  153.   @NonNull
  154.   protected CharSequence newIndexMissMessage(
  155.       @NonNull IIndexHasKeyConstraint constraint,
  156.       @SuppressWarnings("unused") @NonNull INodeItem node,
  157.       @NonNull INodeItem target,
  158.       @NonNull List<String> key) {
  159.     String keyValues = key.stream()
  160.         .collect(Collectors.joining(","));

  161.     return String.format("Key reference [%s] not found in index '%s' for item at path '%s'",
  162.         keyValues,
  163.         constraint.getIndexName(),
  164.         target.getMetapath());
  165.   }

  166. }