001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.model.constraint; 028 029import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; 030import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; 031import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 032import gov.nist.secauto.metaschema.core.metapath.MetapathExpression; 033import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; 034import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; 035import gov.nist.secauto.metaschema.core.util.ObjectUtils; 036import gov.nist.secauto.metaschema.core.util.ReplacementScanner; 037 038import java.util.Map; 039import java.util.Objects; 040import java.util.Set; 041import java.util.regex.Pattern; 042 043import javax.xml.namespace.QName; 044 045import edu.umd.cs.findbugs.annotations.NonNull; 046import edu.umd.cs.findbugs.annotations.Nullable; 047 048public final class DefaultExpectConstraint 049 extends AbstractConstraint 050 implements IExpectConstraint { 051 @SuppressWarnings("null") 052 @NonNull 053 private static final Pattern METAPATH_VALUE_TEMPLATE_PATTERN 054 = Pattern.compile("(?<!\\\\)(\\{\\s*((?:(?:\\\\})|[^}])*)\\s*\\})"); 055 @NonNull 056 private final MetapathExpression test; 057 private final String message; 058 059 /** 060 * Construct a new expect constraint which requires that the associated test 061 * evaluates to {@link IBooleanItem#TRUE} against the target. 062 * 063 * @param id 064 * the optional identifier for the constraint 065 * @param formalName 066 * the constraint's formal name or {@code null} if not provided 067 * @param description 068 * the constraint's semantic description or {@code null} if not 069 * provided 070 * @param source 071 * information about the constraint source 072 * @param level 073 * the significance of a violation of this constraint 074 * @param target 075 * the Metapath expression identifying the nodes the constraint targets 076 * @param properties 077 * a collection of associated properties 078 * @param test 079 * a Metapath expression that is evaluated against the target node to 080 * determine if the constraint passes 081 * @param message 082 * an optional message to emit when the constraint is violated 083 * @param remarks 084 * optional remarks describing the intent of the constraint 085 */ 086 private DefaultExpectConstraint( 087 @Nullable String id, 088 @Nullable String formalName, 089 @Nullable MarkupLine description, 090 @NonNull ISource source, 091 @NonNull Level level, 092 @NonNull MetapathExpression target, 093 @NonNull Map<QName, Set<String>> properties, 094 @NonNull MetapathExpression test, 095 @Nullable String message, 096 MarkupMultiline remarks) { 097 super(id, formalName, description, source, level, target, properties, remarks); 098 this.test = Objects.requireNonNull(test); 099 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}