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 }