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