1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.model.constraint;
7   
8   import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
9   import gov.nist.secauto.metaschema.core.model.constraint.impl.DefaultMatchesConstraint;
10  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
11  
12  import java.util.regex.Pattern;
13  
14  import edu.umd.cs.findbugs.annotations.NonNull;
15  import edu.umd.cs.findbugs.annotations.Nullable;
16  
17  /**
18   * Represents a rule requiring the value of a field or flag to match a pattern
19   * and/or conform to an identified data type.
20   */
21  public interface IMatchesConstraint extends IConfigurableMessageConstraint {
22    @Override
23    default Type getType() {
24      return Type.MATCHES;
25    }
26  
27    /**
28     * Get the expected pattern.
29     *
30     * @return the expected pattern or {@code null} if there is no expected pattern
31     */
32    @Nullable
33    Pattern getPattern();
34  
35    /**
36     * Get the expected data type.
37     *
38     * @return the expected data type or {@code null} if there is no expected data
39     *         type
40     */
41    @Nullable
42    IDataTypeAdapter<?> getDataType();
43  
44    @Override
45    default <T, R> R accept(IConstraintVisitor<T, R> visitor, T state) {
46      return visitor.visitMatchesConstraint(this, state);
47    }
48  
49    /**
50     * Create a new constraint builder.
51     *
52     * @return the builder
53     */
54    @NonNull
55    static Builder builder() {
56      return new Builder();
57    }
58  
59    /**
60     * Provides a builder pattern for constructing a new {@link IMatchesConstraint}.
61     */
62    final class Builder
63        extends AbstractConfigurableMessageConstraintBuilder<Builder, IMatchesConstraint> {
64      private Pattern pattern;
65      private IDataTypeAdapter<?> datatype;
66  
67      private Builder() {
68        // disable construction
69      }
70  
71      /**
72       * Use the provided pattern to validate associated values.
73       *
74       * @param pattern
75       *          the pattern to use
76       * @return this builder
77       */
78      public Builder regex(@NonNull String pattern) {
79        return regex(ObjectUtils.notNull(Pattern.compile(pattern)));
80      }
81  
82      /**
83       * Use the provided pattern to validate associated values.
84       *
85       * @param pattern
86       *          the expected pattern
87       * @return this builder
88       */
89      public Builder regex(@NonNull Pattern pattern) {
90        this.pattern = pattern;
91        return this;
92      }
93  
94      /**
95       * Use the provided data type to validate associated values.
96       *
97       * @param datatype
98       *          the expected data type
99       * @return this builder
100      */
101     public Builder datatype(@NonNull IDataTypeAdapter<?> datatype) {
102       this.datatype = datatype;
103       return this;
104     }
105 
106     @Override
107     protected Builder getThis() {
108       return this;
109     }
110 
111     @Override
112     protected void validate() {
113       super.validate();
114 
115       if (getPattern() == null && getDatatype() == null) {
116         throw new ConstraintInitializationException("A pattern or data type must be provided at minimum.");
117       }
118     }
119 
120     private Pattern getPattern() {
121       return pattern;
122     }
123 
124     private IDataTypeAdapter<?> getDatatype() {
125       return datatype;
126     }
127 
128     @Override
129     protected IMatchesConstraint newInstance() {
130       return new DefaultMatchesConstraint(
131           getId(),
132           getFormalName(),
133           getDescription(),
134           ObjectUtils.notNull(getSource()),
135           getLevel(),
136           getTarget(),
137           getProperties(),
138           getPattern(),
139           getDatatype(),
140           getMessage(),
141           getRemarks());
142     }
143   }
144 }