1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.databind.model.metaschema.impl;
7   
8   import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
9   import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
10  import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder;
11  import gov.nist.secauto.metaschema.core.model.constraint.AbstractKeyConstraintBuilder;
12  import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
13  import gov.nist.secauto.metaschema.core.model.constraint.ICardinalityConstraint;
14  import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
15  import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint;
16  import gov.nist.secauto.metaschema.core.model.constraint.IIndexConstraint;
17  import gov.nist.secauto.metaschema.core.model.constraint.IIndexHasKeyConstraint;
18  import gov.nist.secauto.metaschema.core.model.constraint.IKeyField;
19  import gov.nist.secauto.metaschema.core.model.constraint.ILet;
20  import gov.nist.secauto.metaschema.core.model.constraint.IMatchesConstraint;
21  import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
22  import gov.nist.secauto.metaschema.core.model.constraint.ISource;
23  import gov.nist.secauto.metaschema.core.model.constraint.IUniqueConstraint;
24  import gov.nist.secauto.metaschema.core.model.constraint.IValueConstrained;
25  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
26  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.ConstraintLetExpression;
27  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.ConstraintValueEnum;
28  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.FlagAllowedValues;
29  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.FlagExpect;
30  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.FlagIndexHasKey;
31  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.FlagMatches;
32  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.KeyConstraintField;
33  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.Property;
34  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.Remarks;
35  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.TargetedAllowedValuesConstraint;
36  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.TargetedExpectConstraint;
37  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.TargetedHasCardinalityConstraint;
38  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.TargetedIndexConstraint;
39  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.TargetedIndexHasKeyConstraint;
40  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.TargetedIsUniqueConstraint;
41  import gov.nist.secauto.metaschema.databind.model.binding.metaschema.TargetedMatchesConstraint;
42  import gov.nist.secauto.metaschema.databind.model.metaschema.IConstraintBase;
43  import gov.nist.secauto.metaschema.databind.model.metaschema.IModelConstraintsBase;
44  import gov.nist.secauto.metaschema.databind.model.metaschema.IValueConstraintsBase;
45  import gov.nist.secauto.metaschema.databind.model.metaschema.IValueTargetedConstraintsBase;
46  
47  import java.math.BigInteger;
48  import java.util.List;
49  import java.util.regex.Pattern;
50  
51  import javax.xml.namespace.QName;
52  
53  import edu.umd.cs.findbugs.annotations.NonNull;
54  import edu.umd.cs.findbugs.annotations.Nullable;
55  
56  public final class ConstraintBindingSupport {
57    private ConstraintBindingSupport() {
58      // disable construction
59    }
60  
61    public static void parse(
62        @NonNull IValueConstrained constraintSet,
63        @NonNull IValueConstraintsBase constraints,
64        @NonNull ISource source) {
65      parseLet(constraintSet, constraints, source);
66  
67      // parse rules
68      for (IConstraintBase ruleObj : constraints.getRules()) {
69        if (ruleObj instanceof FlagAllowedValues) {
70          IAllowedValuesConstraint constraint = newAllowedValues((FlagAllowedValues) ruleObj, source);
71          constraintSet.addConstraint(constraint);
72        } else if (ruleObj instanceof FlagExpect) {
73          IExpectConstraint constraint = newExpect((FlagExpect) ruleObj, source);
74          constraintSet.addConstraint(constraint);
75        } else if (ruleObj instanceof FlagIndexHasKey) {
76          IIndexHasKeyConstraint constraint = newIndexHasKey((FlagIndexHasKey) ruleObj, source);
77          constraintSet.addConstraint(constraint);
78        } else if (ruleObj instanceof FlagMatches) {
79          IMatchesConstraint constraint = newMatches((FlagMatches) ruleObj, source);
80          constraintSet.addConstraint(constraint);
81        }
82      }
83    }
84  
85    public static void parse(
86        @NonNull IValueConstrained constraintSet,
87        @NonNull IValueTargetedConstraintsBase constraints,
88        @NonNull ISource source) {
89      parseLet(constraintSet, constraints, source);
90  
91      // parse rules
92      for (IConstraintBase ruleObj : constraints.getRules()) {
93        if (ruleObj instanceof TargetedAllowedValuesConstraint) {
94          IAllowedValuesConstraint constraint = newAllowedValues((TargetedAllowedValuesConstraint) ruleObj, source);
95          constraintSet.addConstraint(constraint);
96        } else if (ruleObj instanceof TargetedExpectConstraint) {
97          IExpectConstraint constraint = newExpect((TargetedExpectConstraint) ruleObj, source);
98          constraintSet.addConstraint(constraint);
99        } else if (ruleObj instanceof TargetedIndexHasKeyConstraint) {
100         IIndexHasKeyConstraint constraint = newIndexHasKey((TargetedIndexHasKeyConstraint) ruleObj, source);
101         constraintSet.addConstraint(constraint);
102       } else if (ruleObj instanceof TargetedMatchesConstraint) {
103         IMatchesConstraint constraint = newMatches((TargetedMatchesConstraint) ruleObj, source);
104         constraintSet.addConstraint(constraint);
105       }
106     }
107   }
108 
109   public static void parse(
110       @NonNull IModelConstrained constraintSet,
111       @NonNull IModelConstraintsBase constraints,
112       @NonNull ISource source) {
113     parseLet(constraintSet, constraints, source);
114 
115     // parse rules
116     for (IConstraintBase ruleObj : constraints.getRules()) {
117       if (ruleObj instanceof TargetedAllowedValuesConstraint) {
118         IAllowedValuesConstraint constraint = newAllowedValues((TargetedAllowedValuesConstraint) ruleObj, source);
119         constraintSet.addConstraint(constraint);
120       } else if (ruleObj instanceof TargetedExpectConstraint) {
121         IExpectConstraint constraint = newExpect((TargetedExpectConstraint) ruleObj, source);
122         constraintSet.addConstraint(constraint);
123       } else if (ruleObj instanceof TargetedIndexHasKeyConstraint) {
124         IIndexHasKeyConstraint constraint = newIndexHasKey((TargetedIndexHasKeyConstraint) ruleObj, source);
125         constraintSet.addConstraint(constraint);
126       } else if (ruleObj instanceof TargetedMatchesConstraint) {
127         IMatchesConstraint constraint = newMatches((TargetedMatchesConstraint) ruleObj, source);
128         constraintSet.addConstraint(constraint);
129       } else if (ruleObj instanceof TargetedIndexConstraint) {
130         IIndexConstraint constraint = newIndex((TargetedIndexConstraint) ruleObj, source);
131         constraintSet.addConstraint(constraint);
132       } else if (ruleObj instanceof TargetedHasCardinalityConstraint) {
133         ICardinalityConstraint constraint = newHasCardinality((TargetedHasCardinalityConstraint) ruleObj, source);
134         constraintSet.addConstraint(constraint);
135       } else if (ruleObj instanceof TargetedIsUniqueConstraint) {
136         IUniqueConstraint constraint = newUnique((TargetedIsUniqueConstraint) ruleObj, source);
137         constraintSet.addConstraint(constraint);
138       }
139     }
140   }
141 
142   public static void parseLet(
143       @NonNull IValueConstrained constraintSet,
144       @NonNull IValueConstraintsBase constraints,
145       @NonNull ISource source) {
146     // parse let expressions
147     for (ConstraintLetExpression letObj : constraints.getLets()) {
148       ILet let = ILet.of(
149           ObjectUtils.requireNonNull(new QName(letObj.getVar())),
150           ObjectUtils.requireNonNull(letObj.getExpression()), source);
151       constraintSet.addLetExpression(let);
152     }
153   }
154 
155   @NonNull
156   private static IAllowedValuesConstraint newAllowedValues(
157       @NonNull FlagAllowedValues obj,
158       @NonNull ISource source) {
159     IAllowedValuesConstraint.Builder builder = IAllowedValuesConstraint.builder()
160         .allowsOther(ModelSupport.yesOrNo(obj.getAllowOther()))
161         .extensible(extensible(obj.getExtensible()));
162     applyCommonValues(obj, null, source, builder);
163 
164     for (ConstraintValueEnum value : ObjectUtils.requireNonNull(obj.getEnums())) {
165       builder.allowedValue(ObjectUtils.requireNonNull(value));
166     }
167     return builder.build();
168   }
169 
170   @NonNull
171   private static IAllowedValuesConstraint newAllowedValues(
172       @NonNull TargetedAllowedValuesConstraint obj,
173       @NonNull ISource source) {
174     IAllowedValuesConstraint.Builder builder = IAllowedValuesConstraint.builder()
175         .allowsOther(ModelSupport.yesOrNo(obj.getAllowOther()))
176         .extensible(extensible(ObjectUtils.requireNonNull(obj.getExtensible())));
177     applyCommonValues(obj, obj.getTarget(), source, builder);
178 
179     for (ConstraintValueEnum value : ObjectUtils.requireNonNull(obj.getEnums())) {
180       builder.allowedValue(ObjectUtils.requireNonNull(value));
181     }
182     return builder.build();
183   }
184 
185   @NonNull
186   private static IExpectConstraint newExpect(
187       @NonNull FlagExpect obj,
188       @NonNull ISource source) {
189     IExpectConstraint.Builder builder = IExpectConstraint.builder()
190         .test(target(ObjectUtils.requireNonNull(obj.getTest())));
191     applyCommonValues(obj, null, source, builder);
192 
193     String message = obj.getMessage();
194     if (message != null) {
195       builder.message(message);
196     }
197 
198     return builder.build();
199   }
200 
201   @NonNull
202   private static IExpectConstraint newExpect(
203       @NonNull TargetedExpectConstraint obj,
204       @NonNull ISource source) {
205     IExpectConstraint.Builder builder = IExpectConstraint.builder()
206         .test(target(ObjectUtils.requireNonNull(obj.getTest())));
207     applyCommonValues(obj, obj.getTarget(), source, builder);
208 
209     String message = obj.getMessage();
210     if (message != null) {
211       builder.message(message);
212     }
213 
214     return builder.build();
215   }
216 
217   @NonNull
218   private static <T extends AbstractKeyConstraintBuilder<T, ?>> T handleKeyConstraints(
219       @NonNull List<KeyConstraintField> keys,
220       @NonNull T builder,
221       @NonNull ISource source) {
222     for (KeyConstraintField value : keys) {
223       assert value != null;
224 
225       IKeyField keyField = IKeyField.of(
226           target(ObjectUtils.requireNonNull(value.getTarget())),
227           pattern(value.getPattern()),
228           ModelSupport.remarks(value.getRemarks()),
229           source);
230       builder.keyField(keyField);
231     }
232     return builder;
233   }
234 
235   @NonNull
236   private static IIndexHasKeyConstraint newIndexHasKey(
237       @NonNull FlagIndexHasKey obj,
238       @NonNull ISource source) {
239     IIndexHasKeyConstraint.Builder builder = IIndexHasKeyConstraint.builder(ObjectUtils.requireNonNull(obj.getName()));
240     applyCommonValues(obj, null, source, builder);
241     handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
242     return builder.build();
243   }
244 
245   @NonNull
246   private static IIndexHasKeyConstraint newIndexHasKey(
247       @NonNull TargetedIndexHasKeyConstraint obj,
248       @NonNull ISource source) {
249     IIndexHasKeyConstraint.Builder builder = IIndexHasKeyConstraint.builder(ObjectUtils.requireNonNull(obj.getName()));
250     applyCommonValues(obj, obj.getTarget(), source, builder);
251     handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
252     return builder.build();
253   }
254 
255   @NonNull
256   private static IMatchesConstraint newMatches(
257       @NonNull FlagMatches obj,
258       @NonNull ISource source) {
259     IMatchesConstraint.Builder builder = IMatchesConstraint.builder();
260     applyCommonValues(obj, null, source, builder);
261 
262     Pattern regex = pattern(obj.getRegex());
263     if (regex != null) {
264       builder.regex(regex);
265     }
266 
267     String dataType = obj.getDatatype();
268     if (dataType != null) {
269       IDataTypeAdapter<?> javaTypeAdapter = ModelSupport.dataType(obj.getDatatype());
270       builder.datatype(javaTypeAdapter);
271     }
272 
273     return builder.build();
274   }
275 
276   @NonNull
277   private static IMatchesConstraint newMatches(
278       @NonNull TargetedMatchesConstraint obj,
279       @NonNull ISource source) {
280     IMatchesConstraint.Builder builder = IMatchesConstraint.builder();
281     applyCommonValues(obj, obj.getTarget(), source, builder);
282 
283     Pattern regex = pattern(obj.getRegex());
284     if (regex != null) {
285       builder.regex(regex);
286     }
287 
288     String dataType = obj.getDatatype();
289     if (dataType != null) {
290       IDataTypeAdapter<?> javaTypeAdapter = ModelSupport.dataType(obj.getDatatype());
291       builder.datatype(javaTypeAdapter);
292     }
293 
294     return builder.build();
295   }
296 
297   @NonNull
298   private static IIndexConstraint newIndex(
299       @NonNull TargetedIndexConstraint obj,
300       @NonNull ISource source) {
301     IIndexConstraint.Builder builder = IIndexConstraint.builder(ObjectUtils.requireNonNull(obj.getName()));
302     applyCommonValues(obj, obj.getTarget(), source, builder);
303     handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
304 
305     return builder.build();
306   }
307 
308   @NonNull
309   private static ICardinalityConstraint newHasCardinality(
310       @NonNull TargetedHasCardinalityConstraint obj,
311       @NonNull ISource source) {
312     ICardinalityConstraint.Builder builder = ICardinalityConstraint.builder();
313     applyCommonValues(obj, obj.getTarget(), source, builder);
314 
315     BigInteger minOccurs = obj.getMinOccurs();
316     if (minOccurs != null) {
317       builder.minOccurs(minOccurs.intValueExact());
318     }
319     String maxOccurs = obj.getMaxOccurs();
320     if (maxOccurs != null) {
321       int occurance = ModelSupport.maxOccurs(maxOccurs);
322       builder.maxOccurs(occurance);
323     }
324 
325     return builder.build();
326   }
327 
328   @NonNull
329   private static IUniqueConstraint newUnique(
330       @NonNull TargetedIsUniqueConstraint obj,
331       @NonNull ISource source) {
332     IUniqueConstraint.Builder builder = IUniqueConstraint.builder();
333     applyCommonValues(obj, obj.getTarget(), source, builder);
334     handleKeyConstraints(ObjectUtils.requireNonNull(obj.getKeyFields()), builder, source);
335 
336     return builder.build();
337   }
338 
339   @NonNull
340   private static <T extends AbstractConstraintBuilder<T, ?>> T applyCommonValues(
341       @NonNull IConstraintBase constraint,
342       @Nullable String target,
343       @NonNull ISource source,
344       @NonNull T builder) {
345 
346     String id = constraint.getId();
347 
348     if (id != null) {
349       builder.identifier(id);
350     }
351 
352     String formalName = constraint.getFormalName();
353     if (formalName != null) {
354       builder.formalName(formalName);
355     }
356 
357     MarkupLine description = constraint.getDescription();
358     if (description != null) {
359       builder.description(description);
360     }
361 
362     List<Property> props = ObjectUtils.requireNonNull(constraint.getProps());
363     builder.properties(ModelSupport.parseProperties(props));
364 
365     Remarks remarks = constraint.getRemarks();
366     if (remarks != null) {
367       builder.remarks(ObjectUtils.notNull(remarks.getRemark()));
368     }
369 
370     builder.target(target(target));
371     builder.level(level(constraint.getLevel()));
372     builder.source(source);
373     return builder;
374   }
375 
376   @NonNull
377   private static String target(@Nullable String target) {
378     return target == null
379         ? IConstraint.DEFAULT_TARGET_METAPATH
380         : target;
381   }
382 
383   @NonNull
384   private static IConstraint.Level level(@Nullable String level) {
385     IConstraint.Level retval = IConstraint.DEFAULT_LEVEL;
386     if (level != null) {
387       switch (level) {
388       case "CRITICAL":
389         retval = IConstraint.Level.CRITICAL;
390         break;
391       case "ERROR":
392         retval = IConstraint.Level.ERROR;
393         break;
394       case "WARNING":
395         retval = IConstraint.Level.WARNING;
396         break;
397       case "INFORMATIONAL":
398         retval = IConstraint.Level.INFORMATIONAL;
399         break;
400       case "DEBUG":
401         retval = IConstraint.Level.DEBUG;
402         break;
403       default:
404         throw new UnsupportedOperationException(level);
405       }
406     }
407     return retval;
408   }
409 
410   @NonNull
411   private static IAllowedValuesConstraint.Extensible extensible(@Nullable String extensible) {
412     IAllowedValuesConstraint.Extensible retval = IAllowedValuesConstraint.EXTENSIBLE_DEFAULT;
413     if (extensible != null) {
414       switch (extensible) {
415       case "model":
416         retval = IAllowedValuesConstraint.Extensible.MODEL;
417         break;
418       case "external":
419         retval = IAllowedValuesConstraint.Extensible.EXTERNAL;
420         break;
421       case "none":
422         retval = IAllowedValuesConstraint.Extensible.NONE;
423         break;
424       default:
425         throw new UnsupportedOperationException(extensible);
426       }
427     }
428     return retval;
429   }
430 
431   @Nullable
432   private static Pattern pattern(@Nullable String pattern) {
433     return pattern == null ? null : Pattern.compile(pattern);
434   }
435 
436 }