View Javadoc
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  
27  package gov.nist.secauto.metaschema.core.model.constraint;
28  
29  import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
30  import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
31  import gov.nist.secauto.metaschema.core.metapath.ISequence;
32  import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter;
33  import gov.nist.secauto.metaschema.core.metapath.function.library.FnData;
34  import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
35  import gov.nist.secauto.metaschema.core.util.CustomCollectors;
36  
37  import java.util.List;
38  import java.util.stream.Collectors;
39  
40  import edu.umd.cs.findbugs.annotations.NonNull;
41  
42  public abstract class AbstractConstraintValidationHandler implements IConstraintValidationHandler {
43    @NonNull
44    public abstract IPathFormatter getPathFormatter();
45  
46    protected String toPath(@NonNull INodeItem nodeItem) {
47      return nodeItem.toPath(getPathFormatter());
48    }
49  
50    @SuppressWarnings("null")
51    @NonNull
52    protected String newCardinalityMinimumViolationMessage(
53        @NonNull ICardinalityConstraint constraint,
54        @SuppressWarnings("unused") @NonNull INodeItem node,
55        @NonNull ISequence<? extends INodeItem> targets) {
56      // TODO: render the item paths instead of the expression
57      return String.format(
58          "The cardinality '%d' is below the required minimum '%d' for items matching the expression '%s'.",
59          targets.size(), constraint.getMinOccurs(), constraint.getTarget().getPath());
60    }
61  
62    @SuppressWarnings("null")
63    @NonNull
64    protected String newCardinalityMaximumViolationMessage(
65        @NonNull ICardinalityConstraint constraint,
66        @SuppressWarnings("unused") @NonNull INodeItem node,
67        @NonNull ISequence<? extends INodeItem> targets) {
68      // TODO: render the item paths instead of the expression
69      return String.format(
70          "The cardinality '%d' is greater than the required maximum '%d' for items matching the expression '%s'.",
71          targets.size(), constraint.getMinOccurs(), constraint.getTarget().getPath());
72    }
73  
74    @SuppressWarnings("null")
75    @NonNull
76    protected String newIndexDuplicateKeyViolationMessage(
77        @NonNull IIndexConstraint constraint,
78        @SuppressWarnings("unused") @NonNull INodeItem node,
79        @NonNull INodeItem oldItem,
80        @NonNull INodeItem target) {
81      // TODO: render the key paths
82      return String.format("Index '%s' has duplicate key for items at paths '%s' and '%s'", constraint.getName(),
83          toPath(oldItem), toPath(target));
84    }
85  
86    @SuppressWarnings("null")
87    @NonNull
88    protected String newUniqueKeyViolationMessage(
89        @SuppressWarnings("unused") @NonNull IUniqueConstraint constraint,
90        @SuppressWarnings("unused") @NonNull INodeItem node,
91        @NonNull INodeItem oldItem,
92        @NonNull INodeItem target) {
93      // TODO: render the key paths
94      return String.format("Unique constraint violation at paths '%s' and '%s'",
95          toPath(oldItem), toPath(target));
96    }
97  
98    @SuppressWarnings("null")
99    @NonNull
100   protected String newMatchPatternViolationMessage(
101       @NonNull IMatchesConstraint constraint,
102       @SuppressWarnings("unused") @NonNull INodeItem node,
103       @NonNull INodeItem target,
104       @NonNull String value) {
105     return String.format("Value '%s' did not match the pattern '%s' at path '%s'",
106         value,
107         constraint.getPattern().pattern(),
108         toPath(target));
109   }
110 
111   @SuppressWarnings("null")
112   @NonNull
113   protected String newMatchDatatypeViolationMessage(
114       @NonNull IMatchesConstraint constraint,
115       @SuppressWarnings("unused") @NonNull INodeItem node,
116       @NonNull INodeItem target,
117       @NonNull String value) {
118     IDataTypeAdapter<?> adapter = constraint.getDataType();
119     return String.format("Value '%s' did not conform to the data type '%s' at path '%s'", value,
120         adapter.getPreferredName(), toPath(target));
121   }
122 
123   @SuppressWarnings("null")
124   @NonNull
125   protected CharSequence newExpectViolationMessage(
126       @NonNull IExpectConstraint constraint,
127       @SuppressWarnings("unused") @NonNull INodeItem node,
128       @NonNull INodeItem target,
129       @NonNull DynamicContext dynamicContext) {
130     CharSequence message;
131     if (constraint.getMessage() != null) {
132       message = constraint.generateMessage(target, dynamicContext);
133     } else {
134       message = String.format("Expect constraint '%s' did not match the data at path '%s'",
135           constraint.getTest().getPath(),
136           toPath(target));
137     }
138     return message;
139   }
140 
141   @SuppressWarnings("null")
142   @NonNull
143   protected CharSequence newAllowedValuesViolationMessage(
144       @NonNull List<IAllowedValuesConstraint> constraints,
145       @NonNull INodeItem target) {
146 
147     String allowedValues = constraints.stream()
148         .flatMap(constraint -> constraint.getAllowedValues().values().stream())
149         .map(allowedValue -> allowedValue.getValue())
150         .sorted()
151         .distinct()
152         .collect(CustomCollectors.joiningWithOxfordComma("or"));
153 
154     return String.format("Value '%s' doesn't match one of '%s' at path '%s'",
155         FnData.fnDataItem(target).asString(),
156         allowedValues,
157         toPath(target));
158   }
159 
160   @SuppressWarnings("null")
161   @NonNull
162   protected CharSequence newIndexDuplicateViolationMessage(
163       @NonNull IIndexConstraint constraint,
164       @NonNull INodeItem node) {
165     return String.format("Duplicate index named '%s' found at path '%s'",
166         constraint.getName(),
167         node.getMetapath());
168   }
169 
170   @SuppressWarnings("null")
171   @NonNull
172   protected CharSequence newIndexMissMessage(
173       @NonNull IIndexHasKeyConstraint constraint,
174       @SuppressWarnings("unused") @NonNull INodeItem node,
175       @NonNull INodeItem target,
176       @NonNull List<String> key) {
177     String keyValues = key.stream()
178         .collect(Collectors.joining(","));
179 
180     return String.format("Key reference [%s] not found in index '%s' for item at path '%s'",
181         keyValues,
182         constraint.getIndexName(),
183         target.getMetapath());
184   }
185 
186 }