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