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