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.markup.MarkupLine;
30  import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
31  import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
32  import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
33  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
34  import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
35  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
36  import gov.nist.secauto.metaschema.core.util.ReplacementScanner;
37  
38  import java.util.Map;
39  import java.util.Objects;
40  import java.util.Set;
41  import java.util.regex.Pattern;
42  
43  import javax.xml.namespace.QName;
44  
45  import edu.umd.cs.findbugs.annotations.NonNull;
46  import edu.umd.cs.findbugs.annotations.Nullable;
47  
48  public final class DefaultExpectConstraint
49      extends AbstractConstraint
50      implements IExpectConstraint {
51    @SuppressWarnings("null")
52    @NonNull
53    private static final Pattern METAPATH_VALUE_TEMPLATE_PATTERN
54        = Pattern.compile("(?<!\\\\)(\\{\\s*((?:(?:\\\\})|[^}])*)\\s*\\})");
55    @NonNull
56    private final MetapathExpression test;
57    private final String message;
58  
59    /**
60     * Construct a new expect constraint which requires that the associated test
61     * evaluates to {@link IBooleanItem#TRUE} against the target.
62     *
63     * @param id
64     *          the optional identifier for the constraint
65     * @param formalName
66     *          the constraint's formal name or {@code null} if not provided
67     * @param description
68     *          the constraint's semantic description or {@code null} if not
69     *          provided
70     * @param source
71     *          information about the constraint source
72     * @param level
73     *          the significance of a violation of this constraint
74     * @param target
75     *          the Metapath expression identifying the nodes the constraint targets
76     * @param properties
77     *          a collection of associated properties
78     * @param test
79     *          a Metapath expression that is evaluated against the target node to
80     *          determine if the constraint passes
81     * @param message
82     *          an optional message to emit when the constraint is violated
83     * @param remarks
84     *          optional remarks describing the intent of the constraint
85     */
86    private DefaultExpectConstraint(
87        @Nullable String id,
88        @Nullable String formalName,
89        @Nullable MarkupLine description,
90        @NonNull ISource source,
91        @NonNull Level level,
92        @NonNull MetapathExpression target,
93        @NonNull Map<QName, Set<String>> properties,
94        @NonNull MetapathExpression test,
95        @Nullable String message,
96        MarkupMultiline remarks) {
97      super(id, formalName, description, source, level, target, properties, remarks);
98      this.test = Objects.requireNonNull(test);
99      this.message = message;
100   }
101 
102   @Override
103   public MetapathExpression getTest() {
104     return test;
105   }
106 
107   @Override
108   public String getMessage() {
109     return message;
110   }
111 
112   @Override
113   public CharSequence generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context) {
114     String message = getMessage();
115 
116     return message == null ? null
117         : ReplacementScanner.replaceTokens(message, METAPATH_VALUE_TEMPLATE_PATTERN, match -> {
118           @SuppressWarnings("null")
119           @NonNull String metapath = match.group(2);
120           MetapathExpression expr = MetapathExpression.compile(metapath);
121           return expr.evaluateAs(item, MetapathExpression.ResultType.STRING, context);
122         });
123   }
124 
125   @Override
126   public <T, R> R accept(IConstraintVisitor<T, R> visitor, T state) {
127     return visitor.visitExpectConstraint(this, state);
128   }
129 
130   @NonNull
131   public static Builder builder() {
132     return new Builder();
133   }
134 
135   public static final class Builder
136       extends AbstractConstraintBuilder<Builder, DefaultExpectConstraint> {
137     private MetapathExpression test;
138     private String message;
139 
140     private Builder() {
141       // disable construction
142     }
143 
144     public Builder test(@NonNull MetapathExpression test) {
145       this.test = test;
146       return this;
147     }
148 
149     public Builder message(@NonNull String message) {
150       this.message = message;
151       return this;
152     }
153 
154     @Override
155     protected Builder getThis() {
156       return this;
157     }
158 
159     @Override
160     protected void validate() {
161       super.validate();
162 
163       ObjectUtils.requireNonNull(getTest());
164     }
165 
166     protected MetapathExpression getTest() {
167       return test;
168     }
169 
170     protected String getMessage() {
171       return message;
172     }
173 
174     @Override
175     protected DefaultExpectConstraint newInstance() {
176       return new DefaultExpectConstraint(
177           getId(),
178           getFormalName(),
179           getDescription(),
180           ObjectUtils.notNull(getSource()),
181           getLevel(),
182           getTarget(),
183           getProperties(),
184           ObjectUtils.requireNonNull(getTest()),
185           getMessage(),
186           getRemarks());
187     }
188   }
189 }