1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.databind.model;
28
29 import gov.nist.secauto.metaschema.core.datatype.DataTypeService;
30 import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
31 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
32 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
33 import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
34 import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraint.AbstractConstraintBuilder;
35 import gov.nist.secauto.metaschema.core.model.constraint.AbstractKeyConstraint.AbstractKeyConstraintBuilder;
36 import gov.nist.secauto.metaschema.core.model.constraint.DefaultAllowedValue;
37 import gov.nist.secauto.metaschema.core.model.constraint.DefaultAllowedValuesConstraint;
38 import gov.nist.secauto.metaschema.core.model.constraint.DefaultCardinalityConstraint;
39 import gov.nist.secauto.metaschema.core.model.constraint.DefaultExpectConstraint;
40 import gov.nist.secauto.metaschema.core.model.constraint.DefaultIndexConstraint;
41 import gov.nist.secauto.metaschema.core.model.constraint.DefaultIndexHasKeyConstraint;
42 import gov.nist.secauto.metaschema.core.model.constraint.DefaultKeyField;
43 import gov.nist.secauto.metaschema.core.model.constraint.DefaultMatchesConstraint;
44 import gov.nist.secauto.metaschema.core.model.constraint.DefaultUniqueConstraint;
45 import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
46 import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.ISource;
47 import gov.nist.secauto.metaschema.databind.model.annotations.AllowedValue;
48 import gov.nist.secauto.metaschema.databind.model.annotations.AllowedValues;
49 import gov.nist.secauto.metaschema.databind.model.annotations.Expect;
50 import gov.nist.secauto.metaschema.databind.model.annotations.HasCardinality;
51 import gov.nist.secauto.metaschema.databind.model.annotations.Index;
52 import gov.nist.secauto.metaschema.databind.model.annotations.IndexHasKey;
53 import gov.nist.secauto.metaschema.databind.model.annotations.IsUnique;
54 import gov.nist.secauto.metaschema.databind.model.annotations.KeyField;
55 import gov.nist.secauto.metaschema.databind.model.annotations.Matches;
56 import gov.nist.secauto.metaschema.databind.model.annotations.NullJavaTypeAdapter;
57 import gov.nist.secauto.metaschema.databind.model.annotations.Property;
58
59 import org.apache.logging.log4j.LogManager;
60 import org.apache.logging.log4j.Logger;
61
62 import java.util.Arrays;
63 import java.util.LinkedHashSet;
64 import java.util.List;
65 import java.util.Set;
66 import java.util.regex.Pattern;
67
68 import javax.xml.namespace.QName;
69
70 import edu.umd.cs.findbugs.annotations.NonNull;
71 import edu.umd.cs.findbugs.annotations.Nullable;
72
73 final class ConstraintFactory {
74 private static final Logger LOGGER = LogManager.getLogger(ConstraintFactory.class);
75
76 private ConstraintFactory() {
77
78 }
79
80 static MarkupMultiline toRemarks(@NonNull String remarks) {
81 return remarks.isBlank() ? null : MarkupMultiline.fromMarkdown(remarks);
82 }
83
84 @NonNull
85 static MetapathExpression toMetapath(@NonNull String metapath) {
86 String path = metapath;
87 if (path.startsWith("/")) {
88 String newPath = "." + path;
89
90 if (LOGGER.isInfoEnabled()) {
91 StringBuilder builder = new StringBuilder(80)
92 .append("The path '")
93 .append(path)
94 .append("' is not properly contextualized using '.'. Using '")
95 .append(newPath)
96 .append("' instead.");
97 LOGGER.atInfo().log(builder.toString());
98 }
99 path = newPath;
100 }
101
102 return path.isBlank() ? IConstraint.DEFAULT_TARGET : MetapathExpression.compile(path);
103 }
104
105 @NonNull
106 static <T extends AbstractConstraintBuilder<T, ?>> T applyId(@NonNull T builder, @NonNull String id) {
107 if (!id.isBlank()) {
108 builder.identifier(id);
109 }
110 return builder;
111 }
112
113 @NonNull
114 static <T extends AbstractConstraintBuilder<T, ?>> T applyFormalName(@NonNull T builder, @NonNull String name) {
115 if (!name.isBlank()) {
116 builder.formalName(name);
117 }
118 return builder;
119 }
120
121 @NonNull
122 static <T extends AbstractConstraintBuilder<T, ?>> T applyDescription(@NonNull T builder, @NonNull String value) {
123 if (!value.isBlank()) {
124 builder.description(MarkupLine.fromMarkdown(value));
125 }
126 return builder;
127 }
128
129 @NonNull
130 static <T extends AbstractConstraintBuilder<T, ?>> T applyTarget(@NonNull T builder, @NonNull String target) {
131 builder.target(toMetapath(target));
132 return builder;
133 }
134
135 @NonNull
136 static <T extends AbstractConstraintBuilder<T, ?>> T applyProperties(
137 @NonNull T builder,
138 @Nullable Property... properties) {
139 if (properties != null) {
140 for (Property property : properties) {
141 String name = property.name();
142 String namespace = property.namespace();
143 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
144 QName qname = new QName(namespace, name);
145
146 String[] values = property.values();
147 List<String> valueList = Arrays.asList(values);
148 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
149 Set<String> valueSet = new LinkedHashSet<>(valueList);
150 builder.property(qname, valueSet);
151 }
152 }
153 return builder;
154 }
155
156 static <T extends AbstractConstraintBuilder<T, ?>> T applyRemarks(@NonNull T builder, @NonNull String remarks) {
157 if (!remarks.isBlank()) {
158 builder.remarks(MarkupMultiline.fromMarkdown(remarks));
159 }
160 return builder;
161 }
162
163 @NonNull
164 static DefaultAllowedValuesConstraint.Builder applyAllowedValues(
165 @NonNull DefaultAllowedValuesConstraint.Builder builder,
166 @NonNull AllowedValues constraint) {
167 for (AllowedValue value : constraint.values()) {
168 DefaultAllowedValue allowedValue
169 = new DefaultAllowedValue(value.value(), MarkupLine.fromMarkdown(value.description()));
170 builder.allowedValue(allowedValue);
171 }
172 return builder;
173 }
174
175 @Nullable
176 static Pattern toPattern(@NonNull String pattern) {
177 return pattern.isBlank() ? null : Pattern.compile(pattern);
178 }
179
180 @Nullable
181 static String toMessage(@NonNull String message) {
182 return message.isBlank() ? null : message;
183 }
184
185 @Nullable
186 static IDataTypeAdapter<?> toDataType(@NonNull Class<? extends IDataTypeAdapter<?>> adapterClass) {
187 return adapterClass.isAssignableFrom(NullJavaTypeAdapter.class) ? null
188 : DataTypeService.getInstance().getJavaTypeAdapterByClass(adapterClass);
189 }
190
191 @NonNull
192 static DefaultAllowedValuesConstraint newAllowedValuesConstraint(
193 @NonNull AllowedValues constraint,
194 @NonNull ISource source) {
195 DefaultAllowedValuesConstraint.Builder builder = DefaultAllowedValuesConstraint.builder();
196 applyId(builder, constraint.id());
197 applyFormalName(builder, constraint.formalName());
198 applyDescription(builder, constraint.description());
199 builder
200 .source(source)
201 .level(constraint.level());
202 applyTarget(builder, constraint.target());
203 applyProperties(builder, constraint.properties());
204 applyRemarks(builder, constraint.remarks());
205
206 applyAllowedValues(builder, constraint);
207 builder.allowedOther(constraint.allowOthers());
208 builder.extensible(constraint.extensible());
209
210 return builder.build();
211 }
212
213 @NonNull
214 static DefaultMatchesConstraint newMatchesConstraint(Matches constraint, @NonNull ISource source) {
215 DefaultMatchesConstraint.Builder builder = DefaultMatchesConstraint.builder();
216 applyId(builder, constraint.id());
217 applyFormalName(builder, constraint.formalName());
218 applyDescription(builder, constraint.description());
219 builder
220 .source(source)
221 .level(constraint.level());
222 applyTarget(builder, constraint.target());
223 applyProperties(builder, constraint.properties());
224 applyRemarks(builder, constraint.remarks());
225
226 Pattern pattern = toPattern(constraint.pattern());
227 if (pattern != null) {
228 builder.regex(pattern);
229 }
230
231 IDataTypeAdapter<?> dataType = toDataType(constraint.typeAdapter());
232 if (dataType != null) {
233 builder.datatype(dataType);
234 }
235
236 return builder.build();
237 }
238
239 @NonNull
240 static <T extends AbstractKeyConstraintBuilder<T, ?>> T applyKeyFields(@NonNull T builder,
241 @NonNull KeyField... keyFields) {
242 for (KeyField keyField : keyFields) {
243 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
244 DefaultKeyField field = new DefaultKeyField(
245 toMetapath(keyField.target()),
246 toPattern(keyField.pattern()),
247 toRemarks(keyField.remarks()));
248 builder.keyField(field);
249 }
250 return builder;
251 }
252
253 @NonNull
254 static DefaultUniqueConstraint newUniqueConstraint(@NonNull IsUnique constraint, @NonNull ISource source) {
255 DefaultUniqueConstraint.Builder builder = DefaultUniqueConstraint.builder();
256 applyId(builder, constraint.id());
257 applyFormalName(builder, constraint.formalName());
258 applyDescription(builder, constraint.description());
259 builder
260 .source(source)
261 .level(constraint.level());
262 applyTarget(builder, constraint.target());
263 applyProperties(builder, constraint.properties());
264 applyRemarks(builder, constraint.remarks());
265
266 applyKeyFields(builder, constraint.keyFields());
267
268 return builder.build();
269 }
270
271 @NonNull
272 static DefaultIndexConstraint newIndexConstraint(@NonNull Index constraint, @NonNull ISource source) {
273 DefaultIndexConstraint.Builder builder = DefaultIndexConstraint.builder();
274 applyId(builder, constraint.id());
275 applyFormalName(builder, constraint.formalName());
276 applyDescription(builder, constraint.description());
277 builder
278 .source(source)
279 .level(constraint.level());
280 applyTarget(builder, constraint.target());
281 applyProperties(builder, constraint.properties());
282 applyRemarks(builder, constraint.remarks());
283
284 builder.name(constraint.name());
285 applyKeyFields(builder, constraint.keyFields());
286
287 return builder.build();
288 }
289
290 @NonNull
291 static DefaultIndexHasKeyConstraint newIndexHasKeyConstraint(@NonNull IndexHasKey constraint,
292 @NonNull ISource source) {
293 DefaultIndexHasKeyConstraint.Builder builder = DefaultIndexHasKeyConstraint.builder();
294 applyId(builder, constraint.id());
295 applyFormalName(builder, constraint.formalName());
296 applyDescription(builder, constraint.description());
297 builder
298 .source(source)
299 .level(constraint.level());
300 applyTarget(builder, constraint.target());
301 applyProperties(builder, constraint.properties());
302 applyRemarks(builder, constraint.remarks());
303
304 builder.name(constraint.indexName());
305 applyKeyFields(builder, constraint.keyFields());
306
307 return builder.build();
308 }
309
310 @NonNull
311 static DefaultExpectConstraint newExpectConstraint(@NonNull Expect constraint, @NonNull ISource source) {
312 DefaultExpectConstraint.Builder builder = DefaultExpectConstraint.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 applyRemarks(builder, constraint.remarks());
322
323 builder.test(toMetapath(constraint.test()));
324
325 String message = constraint.message();
326 if (!message.isBlank()) {
327 builder.message(message);
328 }
329
330 return builder.build();
331 }
332
333 @Nullable
334 static Integer toCardinality(int value) {
335 return value < 0 ? null : value;
336 }
337
338 @NonNull
339 static DefaultCardinalityConstraint newCardinalityConstraint(@NonNull HasCardinality constraint,
340 @NonNull ISource source) {
341 DefaultCardinalityConstraint.Builder builder = DefaultCardinalityConstraint.builder();
342 applyId(builder, constraint.id());
343 applyFormalName(builder, constraint.formalName());
344 applyDescription(builder, constraint.description());
345 builder
346 .source(source)
347 .level(constraint.level());
348 applyTarget(builder, constraint.target());
349 applyProperties(builder, constraint.properties());
350 applyRemarks(builder, constraint.remarks());
351
352 Integer min = toCardinality(constraint.minOccurs());
353 if (min != null) {
354 builder.minOccurs(min);
355 }
356 Integer max = toCardinality(constraint.maxOccurs());
357 if (max != null) {
358 builder.maxOccurs(max);
359 }
360
361 return builder.build();
362 }
363 }