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