1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.model.impl;
7
8 import gov.nist.secauto.metaschema.core.datatype.DataTypeService;
9 import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
10 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
11 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
12 import gov.nist.secauto.metaschema.core.model.ISource;
13 import gov.nist.secauto.metaschema.core.model.constraint.AbstractConfigurableMessageConstraintBuilder;
14 import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder;
15 import gov.nist.secauto.metaschema.core.model.constraint.AbstractKeyConstraintBuilder;
16 import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
17 import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
18 import gov.nist.secauto.metaschema.core.model.constraint.ICardinalityConstraint;
19 import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
20 import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint;
21 import gov.nist.secauto.metaschema.core.model.constraint.IIndexConstraint;
22 import gov.nist.secauto.metaschema.core.model.constraint.IIndexHasKeyConstraint;
23 import gov.nist.secauto.metaschema.core.model.constraint.IKeyField;
24 import gov.nist.secauto.metaschema.core.model.constraint.ILet;
25 import gov.nist.secauto.metaschema.core.model.constraint.IMatchesConstraint;
26 import gov.nist.secauto.metaschema.core.model.constraint.IUniqueConstraint;
27 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
28 import gov.nist.secauto.metaschema.databind.model.annotations.AllowedValue;
29 import gov.nist.secauto.metaschema.databind.model.annotations.AllowedValues;
30 import gov.nist.secauto.metaschema.databind.model.annotations.Expect;
31 import gov.nist.secauto.metaschema.databind.model.annotations.HasCardinality;
32 import gov.nist.secauto.metaschema.databind.model.annotations.Index;
33 import gov.nist.secauto.metaschema.databind.model.annotations.IndexHasKey;
34 import gov.nist.secauto.metaschema.databind.model.annotations.IsUnique;
35 import gov.nist.secauto.metaschema.databind.model.annotations.KeyField;
36 import gov.nist.secauto.metaschema.databind.model.annotations.Let;
37 import gov.nist.secauto.metaschema.databind.model.annotations.Matches;
38 import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
39 import gov.nist.secauto.metaschema.databind.model.annotations.NullJavaTypeAdapter;
40 import gov.nist.secauto.metaschema.databind.model.annotations.Property;
41
42 import java.util.Arrays;
43 import java.util.regex.Pattern;
44
45 import edu.umd.cs.findbugs.annotations.NonNull;
46 import edu.umd.cs.findbugs.annotations.Nullable;
47
48 @SuppressWarnings("PMD.CouplingBetweenObjects")
49 final class ConstraintFactory {
50 private ConstraintFactory() {
51
52 }
53
54 static MarkupMultiline toRemarks(@NonNull String remarks) {
55 return remarks.isBlank() ? null : MarkupMultiline.fromMarkdown(remarks);
56 }
57
58 @NonNull
59 static String toMetapath(@NonNull String metapath) {
60 return metapath.isBlank() ? IConstraint.DEFAULT_TARGET_METAPATH : metapath;
61 }
62
63 @NonNull
64 static <T extends AbstractConstraintBuilder<T, ?>> T applyId(@NonNull T builder, @NonNull String id) {
65 if (!id.isBlank()) {
66 builder.identifier(id);
67 }
68 return builder;
69 }
70
71 @NonNull
72 static <T extends AbstractConstraintBuilder<T, ?>> T applyFormalName(@NonNull T builder, @NonNull String name) {
73 if (!name.isBlank()) {
74 builder.formalName(name);
75 }
76 return builder;
77 }
78
79 @NonNull
80 static <T extends AbstractConstraintBuilder<T, ?>> T applyDescription(@NonNull T builder, @NonNull String value) {
81 if (!value.isBlank()) {
82 builder.description(MarkupLine.fromMarkdown(value));
83 }
84 return builder;
85 }
86
87 @NonNull
88 static <T extends AbstractConstraintBuilder<T, ?>> T applyTarget(@NonNull T builder, @NonNull String target) {
89 builder.target(toMetapath(target));
90 return builder;
91 }
92
93 @NonNull
94 static <T extends AbstractConstraintBuilder<T, ?>> T applyProperties(
95 @NonNull T builder,
96 @Nullable Property... properties) {
97 if (properties != null) {
98 Arrays.stream(properties)
99 .map(ModelUtil::toPropertyEntry)
100 .forEachOrdered(entry -> builder.property(
101 ObjectUtils.notNull(entry.getKey()),
102 ObjectUtils.notNull(entry.getValue())));
103 }
104 return builder;
105 }
106
107 static <T extends AbstractConfigurableMessageConstraintBuilder<T, ?>> T applyMessage(@NonNull T builder,
108 @Nullable String message) {
109 if (message != null && !message.isBlank()) {
110 builder.message(message);
111 }
112 return builder;
113 }
114
115 static <T extends AbstractConstraintBuilder<T, ?>> T applyRemarks(@NonNull T builder, @NonNull String remarks) {
116 if (!remarks.isBlank()) {
117 builder.remarks(MarkupMultiline.fromMarkdown(remarks));
118 }
119 return builder;
120 }
121
122 @SuppressWarnings("PMD.NullAssignment")
123 @NonNull
124 static IAllowedValuesConstraint.Builder applyAllowedValues(
125 @NonNull IAllowedValuesConstraint.Builder builder,
126 @NonNull AllowedValues constraint) {
127 for (AllowedValue value : constraint.values()) {
128 String deprecatedVersion = value.deprecatedVersion();
129 if (deprecatedVersion.isBlank()) {
130 deprecatedVersion = null;
131 }
132
133 IAllowedValue allowedValue = IAllowedValue.of(
134 value.value(),
135 MarkupLine.fromMarkdown(value.description()),
136 deprecatedVersion);
137 builder.allowedValue(allowedValue);
138 }
139 return builder;
140 }
141
142 @Nullable
143 static Pattern toPattern(@NonNull String pattern) {
144 return pattern.isBlank() ? null : Pattern.compile(pattern);
145 }
146
147 @Nullable
148 static String toMessage(@NonNull String message) {
149 return message.isBlank() ? null : message;
150 }
151
152 @Nullable
153 static IDataTypeAdapter<?> toDataType(@NonNull Class<? extends IDataTypeAdapter<?>> adapterClass) {
154 return adapterClass.isAssignableFrom(NullJavaTypeAdapter.class) ? null
155 : DataTypeService.instance().getDataTypeByAdapterClass(adapterClass);
156 }
157
158 @NonNull
159 static IAllowedValuesConstraint newAllowedValuesConstraint(
160 @NonNull AllowedValues constraint,
161 @NonNull ISource source) {
162 IAllowedValuesConstraint.Builder builder = IAllowedValuesConstraint.builder();
163 applyId(builder, constraint.id());
164 applyFormalName(builder, constraint.formalName());
165 applyDescription(builder, constraint.description());
166 builder
167 .source(source)
168 .level(constraint.level());
169 applyTarget(builder, constraint.target());
170 applyProperties(builder, constraint.properties());
171 applyRemarks(builder, constraint.remarks());
172
173 applyAllowedValues(builder, constraint);
174 builder.allowsOther(constraint.allowOthers());
175 builder.extensible(constraint.extensible());
176
177 return builder.build();
178 }
179
180 @NonNull
181 static IMatchesConstraint newMatchesConstraint(Matches constraint, @NonNull ISource source) {
182 IMatchesConstraint.Builder builder = IMatchesConstraint.builder();
183 applyId(builder, constraint.id());
184 applyFormalName(builder, constraint.formalName());
185 applyDescription(builder, constraint.description());
186 builder
187 .source(source)
188 .level(constraint.level());
189 applyTarget(builder, constraint.target());
190 applyProperties(builder, constraint.properties());
191 applyMessage(builder, constraint.message());
192 applyRemarks(builder, constraint.remarks());
193
194 Pattern pattern = toPattern(constraint.pattern());
195 if (pattern != null) {
196 builder.regex(pattern);
197 }
198
199 IDataTypeAdapter<?> dataType = toDataType(constraint.typeAdapter());
200 if (dataType != null) {
201 builder.datatype(dataType);
202 }
203
204 return builder.build();
205 }
206
207 @NonNull
208 static <T extends AbstractKeyConstraintBuilder<T, ?>> T applyKeyFields(
209 @NonNull T builder,
210 @NonNull ISource source,
211 @NonNull KeyField... keyFields) {
212 for (KeyField keyField : keyFields) {
213 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
214 IKeyField field = IKeyField.of(
215 toMetapath(keyField.target()),
216 toPattern(keyField.pattern()),
217 toRemarks(keyField.remarks()),
218 source);
219 builder.keyField(field);
220 }
221 return builder;
222 }
223
224 @NonNull
225 static IUniqueConstraint newUniqueConstraint(@NonNull IsUnique constraint, @NonNull ISource source) {
226 IUniqueConstraint.Builder builder = IUniqueConstraint.builder();
227 applyId(builder, constraint.id());
228 applyFormalName(builder, constraint.formalName());
229 applyDescription(builder, constraint.description());
230 builder
231 .source(source)
232 .level(constraint.level());
233 applyTarget(builder, constraint.target());
234 applyProperties(builder, constraint.properties());
235 applyMessage(builder, constraint.message());
236 applyRemarks(builder, constraint.remarks());
237
238 applyKeyFields(builder, source, constraint.keyFields());
239
240 return builder.build();
241 }
242
243 @NonNull
244 static IIndexConstraint newIndexConstraint(@NonNull Index constraint, @NonNull ISource source) {
245 IIndexConstraint.Builder builder = IIndexConstraint.builder(constraint.name());
246 applyId(builder, constraint.id());
247 applyFormalName(builder, constraint.formalName());
248 applyDescription(builder, constraint.description());
249 builder
250 .source(source)
251 .level(constraint.level());
252 applyTarget(builder, constraint.target());
253 applyProperties(builder, constraint.properties());
254 applyMessage(builder, constraint.message());
255 applyRemarks(builder, constraint.remarks());
256
257 applyKeyFields(builder, source, constraint.keyFields());
258
259 return builder.build();
260 }
261
262 @NonNull
263 static IIndexHasKeyConstraint newIndexHasKeyConstraint(
264 @NonNull IndexHasKey constraint,
265 @NonNull ISource source) {
266 IIndexHasKeyConstraint.Builder builder = IIndexHasKeyConstraint.builder(constraint.indexName());
267 applyId(builder, constraint.id());
268 applyFormalName(builder, constraint.formalName());
269 applyDescription(builder, constraint.description());
270 builder
271 .source(source)
272 .level(constraint.level());
273 applyTarget(builder, constraint.target());
274 applyProperties(builder, constraint.properties());
275 applyMessage(builder, constraint.message());
276 applyRemarks(builder, constraint.remarks());
277
278 applyKeyFields(builder, source, constraint.keyFields());
279
280 return builder.build();
281 }
282
283 @NonNull
284 static IExpectConstraint newExpectConstraint(@NonNull Expect constraint, @NonNull ISource source) {
285 IExpectConstraint.Builder builder = IExpectConstraint.builder();
286 applyId(builder, constraint.id());
287 applyFormalName(builder, constraint.formalName());
288 applyDescription(builder, constraint.description());
289 builder
290 .source(source)
291 .level(constraint.level());
292 applyTarget(builder, constraint.target());
293 applyProperties(builder, constraint.properties());
294 applyMessage(builder, constraint.message());
295 applyRemarks(builder, constraint.remarks());
296
297 builder.test(toMetapath(constraint.test()));
298
299 return builder.build();
300 }
301
302 @Nullable
303 static Integer toCardinality(int value) {
304 return value < 0 ? null : value;
305 }
306
307 @NonNull
308 static ICardinalityConstraint newCardinalityConstraint(@NonNull HasCardinality constraint,
309 @NonNull ISource source) {
310 ICardinalityConstraint.Builder builder = ICardinalityConstraint.builder();
311 applyId(builder, constraint.id());
312 applyFormalName(builder, constraint.formalName());
313 applyDescription(builder, constraint.description());
314 builder
315 .source(source)
316 .level(constraint.level());
317 applyTarget(builder, constraint.target());
318 applyProperties(builder, constraint.properties());
319 applyMessage(builder, constraint.message());
320 applyRemarks(builder, constraint.remarks());
321
322 Integer min = toCardinality(constraint.minOccurs());
323 if (min != null) {
324 builder.minOccurs(min);
325 }
326 Integer max = toCardinality(constraint.maxOccurs());
327 if (max != null) {
328 builder.maxOccurs(max);
329 }
330
331 return builder.build();
332 }
333
334 @NonNull
335 static ILet newLetExpression(@NonNull Let annotation, @NonNull ISource source) {
336 String remarkMarkdown = annotation.remarks();
337 MarkupMultiline remarks = remarkMarkdown.isBlank()
338 ? null
339 : MarkupMultiline.fromMarkdown(remarkMarkdown);
340 return ILet.of(
341 source.getStaticContext().parseVariableName(annotation.name()),
342 annotation.target(),
343 source,
344 remarks);
345 }
346 }