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.model.constraint.impl.DefaultAllowedValuesConstraint;
9   import gov.nist.secauto.metaschema.core.util.ObjectUtils;
10  
11  import java.util.LinkedHashMap;
12  import java.util.Map;
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 the name of
19   * one entry in a set of enumerated values.
20   */
21  public interface IAllowedValuesConstraint extends IConstraint {
22    /**
23     * The default allow other value.
24     */
25    boolean ALLOW_OTHER_DEFAULT = false;
26    /**
27     * The default extensible value.
28     */
29    @NonNull
30    Extensible EXTENSIBLE_DEFAULT = Extensible.EXTERNAL;
31  
32    /**
33     * Indicates how an allowed values constraint can be extended, or if it can be.
34     */
35    enum Extensible {
36      /**
37       * Can be extended by external constraints. The most permissive level.
38       */
39      EXTERNAL,
40      /**
41       * Can be extended by constraints in the same model.
42       */
43      MODEL,
44      /**
45       * Cannot be extended. The most restrictive level.
46       */
47      NONE;
48    }
49  
50    @Override
51    default Type getType() {
52      return Type.ALLOWED_VALUES;
53    }
54  
55    /**
56     * Get the collection allowed values associated with this constraint.
57     *
58     * @return a mapping of value to the associated {@link IAllowedValue} item
59     */
60    @NonNull
61    Map<String, ? extends IAllowedValue> getAllowedValues();
62  
63    /**
64     * Get a specific allowed value by name, if it is defined for this constraint.
65     *
66     * @param name
67     *          the value name
68     * @return the allowed value or {@code null} if the value is not defined
69     */
70    @Nullable
71    default IAllowedValue getAllowedValue(String name) {
72      return getAllowedValues().get(name);
73    }
74  
75    /**
76     * Determines if this allowed value constraint is open-ended ({@code true}) or
77     * closed. If "open-ended", the constraint allows the target's value to by any
78     * additional unspecified value. If "closed", the constraint requries the
79     * target's value to be one of the specified values.
80     *
81     * @return {@code true} if the constraint is "open-ended", or {@code false}
82     *         otherwise
83     */
84    boolean isAllowedOther();
85  
86    /**
87     * Determines the degree to which this constraint can be extended by other
88     * constraints applied to the same value.
89     *
90     * @return the enumeration value
91     */
92    @NonNull
93    Extensible getExtensible();
94  
95    @Override
96    default <T, R> R accept(IConstraintVisitor<T, R> visitor, T state) {
97      return visitor.visitAllowedValues(this, state);
98    }
99  
100   /**
101    * Create a new constraint builder.
102    *
103    * @return the builder
104    */
105   @NonNull
106   static Builder builder() {
107     return new Builder();
108   }
109 
110   /**
111    * Provides a builder pattern for constructing a new
112    * {@link IAllowedValuesConstraint}.
113    */
114   final class Builder
115       extends AbstractConstraintBuilder<Builder, IAllowedValuesConstraint> {
116     @NonNull
117     private final Map<String, IAllowedValue> allowedValues = new LinkedHashMap<>(); // NOPMD not thread safe
118     private boolean allowedOther = ALLOW_OTHER_DEFAULT;
119     @NonNull
120     private Extensible extensible = EXTENSIBLE_DEFAULT;
121 
122     private Builder() {
123       // disable construction
124     }
125 
126     /**
127      * Use the provided allowed value to validate associated values.
128      *
129      * @param allowedValue
130      *          an expected allowed value
131      * @return this builder
132      */
133     @NonNull
134     public Builder allowedValue(@NonNull IAllowedValue allowedValue) {
135       this.allowedValues.put(allowedValue.getValue(), allowedValue);
136       return this;
137     }
138 
139     /**
140      * Use the provided allowed values to validate associated values.
141      *
142      * @param allowedValues
143      *          an expected allowed values
144      * @return this builder
145      */
146     @NonNull
147     public Builder allowedValues(@NonNull Map<String, IAllowedValue> allowedValues) {
148       this.allowedValues.putAll(allowedValues);
149       return this;
150     }
151 
152     /**
153      * Determine if unspecified values are allowed and will result in the constraint
154      * always passing.
155      *
156      * @param bool
157      *          {@code true} if other values are allowed or {@code false} otherwise
158      * @return this builder
159      */
160     @NonNull
161     public Builder allowsOther(boolean bool) {
162       this.allowedOther = bool;
163       return this;
164     }
165 
166     /**
167      * Determine the allowed scope of extension for other constraints matching this
168      * constraint's target.
169      *
170      * @param extensible
171      *          the degree of allowed extension
172      * @return this builder
173      */
174     @NonNull
175     public Builder extensible(@NonNull Extensible extensible) {
176       this.extensible = extensible;
177       return this;
178     }
179 
180     @Override
181     protected Builder getThis() {
182       return this;
183     }
184 
185     @NonNull
186     private Map<String, IAllowedValue> getAllowedValues() {
187       return allowedValues;
188     }
189 
190     private boolean isAllowedOther() {
191       return allowedOther;
192     }
193 
194     @NonNull
195     private Extensible getExtensible() {
196       return extensible;
197     }
198 
199     @Override
200     protected IAllowedValuesConstraint newInstance() {
201       return new DefaultAllowedValuesConstraint(
202           getId(),
203           getFormalName(),
204           getDescription(),
205           ObjectUtils.notNull(getSource()),
206           getLevel(),
207           getTarget(),
208           getProperties(),
209           getAllowedValues(),
210           isAllowedOther(),
211           getExtensible(),
212           getRemarks());
213     }
214   }
215 }