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