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 IConstraint {
22    /**
23     * Get the expected pattern.
24     *
25     * @return the expected pattern or {@code null} if there is no expected pattern
26     */
27    @Nullable
28    Pattern getPattern();
29  
30    /**
31     * Get the expected data type.
32     *
33     * @return the expected data type or {@code null} if there is no expected data
34     *         type
35     */
36    @Nullable
37    IDataTypeAdapter<?> getDataType();
38  
39    @Override
40    default <T, R> R accept(IConstraintVisitor<T, R> visitor, T state) {
41      return visitor.visitMatchesConstraint(this, state);
42    }
43  
44    /**
45     * Create a new constraint builder.
46     *
47     * @return the builder
48     */
49    @NonNull
50    static Builder builder() {
51      return new Builder();
52    }
53  
54    final class Builder
55        extends AbstractConstraintBuilder<Builder, IMatchesConstraint> {
56      private Pattern pattern;
57      private IDataTypeAdapter<?> datatype;
58  
59      private Builder() {
60        // disable construction
61      }
62  
63      /**
64       * Use the provided pattern to validate associated values.
65       *
66       * @param pattern
67       *          the pattern to use
68       * @return this builder
69       */
70      public Builder regex(@NonNull String pattern) {
71        return regex(ObjectUtils.notNull(Pattern.compile(pattern)));
72      }
73  
74      /**
75       * Use the provided pattern to validate associated values.
76       *
77       * @param pattern
78       *          the expected pattern
79       * @return this builder
80       */
81      public Builder regex(@NonNull Pattern pattern) {
82        this.pattern = pattern;
83        return this;
84      }
85  
86      /**
87       * Use the provided data type to validate associated values.
88       *
89       * @param datatype
90       *          the expected data type
91       * @return this builder
92       */
93      public Builder datatype(@NonNull IDataTypeAdapter<?> datatype) {
94        this.datatype = datatype;
95        return this;
96      }
97  
98      @Override
99      protected Builder getThis() {
100       return this;
101     }
102 
103     @Override
104     protected void validate() {
105       super.validate();
106 
107       if (getPattern() == null && getDatatype() == null) {
108         throw new IllegalStateException("A pattern or data type must be provided at minimum.");
109       }
110     }
111 
112     private Pattern getPattern() {
113       return pattern;
114     }
115 
116     private IDataTypeAdapter<?> getDatatype() {
117       return datatype;
118     }
119 
120     @Override
121     protected IMatchesConstraint newInstance() {
122       return new DefaultMatchesConstraint(
123           getId(),
124           getFormalName(),
125           getDescription(),
126           ObjectUtils.notNull(getSource()),
127           getLevel(),
128           getTarget(),
129           getProperties(),
130           getPattern(),
131           getDatatype(),
132           getRemarks());
133     }
134   }
135 }