View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.core.model.xml;
28  
29  import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
30  import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
31  import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
32  import gov.nist.secauto.metaschema.core.model.IModule;
33  import gov.nist.secauto.metaschema.core.model.constraint.DefaultAllowedValuesConstraint;
34  import gov.nist.secauto.metaschema.core.model.constraint.DefaultCardinalityConstraint;
35  import gov.nist.secauto.metaschema.core.model.constraint.DefaultExpectConstraint;
36  import gov.nist.secauto.metaschema.core.model.constraint.DefaultIndexConstraint;
37  import gov.nist.secauto.metaschema.core.model.constraint.DefaultIndexHasKeyConstraint;
38  import gov.nist.secauto.metaschema.core.model.constraint.DefaultMatchesConstraint;
39  import gov.nist.secauto.metaschema.core.model.constraint.DefaultUniqueConstraint;
40  import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
41  import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
42  import gov.nist.secauto.metaschema.core.model.constraint.ICardinalityConstraint;
43  import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
44  import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.ISource;
45  import gov.nist.secauto.metaschema.core.model.constraint.IConstraintVisitor;
46  import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint;
47  import gov.nist.secauto.metaschema.core.model.constraint.IIndexConstraint;
48  import gov.nist.secauto.metaschema.core.model.constraint.IIndexHasKeyConstraint;
49  import gov.nist.secauto.metaschema.core.model.constraint.IKeyConstraint;
50  import gov.nist.secauto.metaschema.core.model.constraint.IKeyField;
51  import gov.nist.secauto.metaschema.core.model.constraint.IMatchesConstraint;
52  import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
53  import gov.nist.secauto.metaschema.core.model.constraint.IUniqueConstraint;
54  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ConstraintType;
55  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.DefineAssemblyConstraintsType;
56  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.EnumType;
57  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.GlobalAssemblyDefinitionType;
58  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.HasCardinalityConstraintType;
59  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.KeyConstraintType;
60  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.KeyConstraintType.KeyField;
61  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.PropertyType;
62  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.RemarksType;
63  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedAllowedValuesType;
64  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedExpectConstraintType;
65  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedIndexConstraintType;
66  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedIndexHasKeyConstraintType;
67  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedKeyConstraintType;
68  import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedMatchesConstraintType;
69  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
70  
71  import org.apache.xmlbeans.XmlCursor;
72  import org.apache.xmlbeans.XmlObject;
73  
74  import java.math.BigInteger;
75  import java.util.LinkedList;
76  import java.util.List;
77  import java.util.Map;
78  import java.util.Set;
79  import java.util.regex.Pattern;
80  
81  import javax.xml.namespace.QName;
82  
83  import edu.umd.cs.findbugs.annotations.NonNull;
84  
85  /**
86   * Provides support for parsing and maintaining a set of Metaschema constraints.
87   * Constraints are parsed from XML.
88   */
89  @SuppressWarnings("PMD.CouplingBetweenObjects")
90  class AssemblyConstraintSupport
91      extends ValueConstraintSupport
92      implements IModelConstrained {
93    @NonNull
94    private static final String PATH = "declare namespace m='http://csrc.nist.gov/ns/oscal/metaschema/1.0';"
95        + "$this/m:allowed-values|$this/m:index|$this/m:index-has-key|$this/m:is-unique|"
96        + "$this/m:has-cardinality|$this/m:matches|$this/m:expect";
97    @NonNull
98    private final List<IIndexConstraint> indexConstraints = new LinkedList<>();
99    @NonNull
100   private final List<IUniqueConstraint> uniqueConstraints = new LinkedList<>();
101   @NonNull
102   private final List<ICardinalityConstraint> cardinalityConstraints = new LinkedList<>();
103 
104   public static AssemblyConstraintSupport newInstance(
105       @NonNull GlobalAssemblyDefinitionType definition,
106       @NonNull ISource source) {
107     AssemblyConstraintSupport retval;
108     if (definition.isSetConstraint()) {
109       retval = new AssemblyConstraintSupport(ObjectUtils.notNull(definition.getConstraint()), source);
110     } else {
111       retval = new AssemblyConstraintSupport();
112     }
113     return retval;
114   }
115 
116   public AssemblyConstraintSupport() {
117     // do nothing
118   }
119 
120   /**
121    * Generate a set of constraints from the provided XMLBeans instance.
122    *
123    * @param xmlConstraints
124    *          the XMLBeans instance
125    * @param source
126    *          information about the source of the constraints
127    */
128   public AssemblyConstraintSupport( // NOPMD - unavoidable
129       @NonNull DefineAssemblyConstraintsType xmlConstraints,
130       @NonNull ISource source) {
131     try (XmlCursor cursor = xmlConstraints.newCursor()) {
132       assert cursor != null;
133       parse(source, cursor, PATH);
134     }
135   }
136 
137   @Override
138   protected boolean parseXmlObject(
139       @NonNull ISource source,
140       @NonNull XmlObject obj) {
141     boolean handled = false;
142     if (obj instanceof ScopedAllowedValuesType) {
143       DefaultAllowedValuesConstraint constraint
144           = ModelFactory.newAllowedValuesConstraint((ScopedAllowedValuesType) obj, source);
145       addConstraint(constraint);
146       handled = true;
147     } else if (obj instanceof ScopedIndexConstraintType) {
148       DefaultIndexConstraint constraint
149           = ModelFactory.newIndexConstraint((ScopedIndexConstraintType) obj, source);
150       addConstraint(constraint);
151       handled = true;
152     } else if (obj instanceof ScopedIndexHasKeyConstraintType) {
153       DefaultIndexHasKeyConstraint constraint
154           = ModelFactory.newIndexHasKeyConstraint((ScopedIndexHasKeyConstraintType) obj, source);
155       addConstraint(constraint);
156       handled = true;
157     } else if (obj instanceof ScopedKeyConstraintType) {
158       DefaultUniqueConstraint constraint
159           = ModelFactory.newUniqueConstraint((ScopedKeyConstraintType) obj, source);
160       addConstraint(constraint);
161       handled = true;
162     } else if (obj instanceof HasCardinalityConstraintType) {
163       DefaultCardinalityConstraint constraint
164           = ModelFactory.newCardinalityConstraint((HasCardinalityConstraintType) obj, source);
165       addConstraint(constraint);
166       handled = true;
167     } else if (obj instanceof ScopedMatchesConstraintType) {
168       DefaultMatchesConstraint constraint
169           = ModelFactory.newMatchesConstraint((ScopedMatchesConstraintType) obj, source);
170       addConstraint(constraint);
171       handled = true;
172     } else if (obj instanceof ScopedExpectConstraintType) {
173       DefaultExpectConstraint constraint
174           = ModelFactory.newExpectConstraint((ScopedExpectConstraintType) obj, source);
175       addConstraint(constraint);
176       handled = true;
177     }
178     return handled;
179   }
180 
181   @Override
182   public List<IIndexConstraint> getIndexConstraints() {
183     synchronized (this) {
184       return indexConstraints;
185     }
186   }
187 
188   @Override
189   public List<IUniqueConstraint> getUniqueConstraints() {
190     synchronized (this) {
191       return uniqueConstraints;
192     }
193   }
194 
195   @Override
196   public List<ICardinalityConstraint> getHasCardinalityConstraints() {
197     synchronized (this) {
198       return cardinalityConstraints;
199     }
200   }
201 
202   @Override
203   public final void addConstraint(@NonNull IIndexConstraint constraint) {
204     synchronized (this) {
205       getConstraints().add(constraint);
206       indexConstraints.add(constraint);
207     }
208   }
209 
210   @Override
211   public final void addConstraint(@NonNull IUniqueConstraint constraint) {
212     synchronized (this) {
213       getConstraints().add(constraint);
214       uniqueConstraints.add(constraint);
215     }
216   }
217 
218   @Override
219   public final void addConstraint(@NonNull ICardinalityConstraint constraint) {
220     synchronized (this) {
221       getConstraints().add(constraint);
222       cardinalityConstraints.add(constraint);
223     }
224   }
225 
226   public final DefineAssemblyConstraintsType generate() {
227     DefineAssemblyConstraintsType retval = DefineAssemblyConstraintsType.Factory.newInstance();
228 
229     XmlbeanGeneratingVisitor visitor = new XmlbeanGeneratingVisitor();
230 
231     for (IConstraint constraint : getConstraints()) {
232       constraint.accept(visitor, retval);
233     }
234     return retval;
235   }
236 
237   private static final class XmlbeanGeneratingVisitor
238       implements IConstraintVisitor<DefineAssemblyConstraintsType, Void> {
239 
240     private static void applyCommonValues(@NonNull IConstraint constraint, @NonNull ConstraintType bean) {
241       MarkupLine description = constraint.getDescription();
242       if (description != null) {
243         bean.setDescription(MarkupStringConverter.toMarkupLineDatatype(description));
244       }
245       String formalName = constraint.getFormalName();
246       if (formalName != null) {
247         bean.setFormalName(formalName);
248       }
249 
250       String id = constraint.getId();
251       if (id != null) {
252         bean.setId(constraint.getId());
253       }
254 
255       IConstraint.Level level = constraint.getLevel();
256       if (!IConstraint.DEFAULT_LEVEL.equals(level)) {
257         bean.setLevel(level);
258       }
259 
260       for (Map.Entry<QName, Set<String>> entry : constraint.getProperties().entrySet()) {
261         QName qname = entry.getKey();
262         Set<String> values = entry.getValue();
263         for (String value : values) {
264           PropertyType prop = bean.addNewProp();
265           prop.setName(qname.getLocalPart());
266 
267           String namespace = qname.getNamespaceURI();
268           if (namespace != null && !namespace.isEmpty()) {
269             prop.setNamespace(namespace);
270           }
271           prop.setValue(value);
272         }
273       }
274     }
275 
276     @Override
277     public Void visitAllowedValues(IAllowedValuesConstraint constraint, DefineAssemblyConstraintsType state) {
278       ScopedAllowedValuesType bean = state.addNewAllowedValues();
279       assert bean != null;
280       applyCommonValues(constraint, bean);
281 
282       if (Boolean.compare(IAllowedValuesConstraint.DEFAULT_ALLOW_OTHER, constraint.isAllowedOther()) != 0) {
283         bean.setAllowOther(constraint.isAllowedOther());
284       }
285       bean.setTarget(constraint.getTarget());
286       bean.setExtensible(constraint.getExtensible());
287 
288       for (Map.Entry<String, ? extends IAllowedValue> entry : constraint.getAllowedValues().entrySet()) {
289         String value = entry.getKey();
290         IAllowedValue allowedValue = entry.getValue();
291 
292         assert value.equals(allowedValue.getValue());
293 
294         MarkupLine description = allowedValue.getDescription();
295         EnumType enumType = bean.addNewEnum();
296         enumType.setValue(value);
297 
298         XmlbeansMarkupVisitor.visit(description, IModule.METASCHEMA_XML_NS, enumType);
299       }
300 
301       MarkupMultiline remarks = constraint.getRemarks();
302       if (remarks != null) {
303         RemarksType remarksType = bean.addNewRemarks();
304         assert remarksType != null;
305         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
306       }
307       return null;
308     }
309 
310     @Override
311     public Void visitCardinalityConstraint(ICardinalityConstraint constraint, DefineAssemblyConstraintsType state) {
312       HasCardinalityConstraintType bean = state.addNewHasCardinality();
313       assert bean != null;
314       applyCommonValues(constraint, bean);
315 
316       Integer minOccurs = constraint.getMinOccurs();
317       if (minOccurs != null) {
318         bean.setMinOccurs(BigInteger.valueOf(minOccurs));
319       }
320 
321       Integer maxOccurs = constraint.getMaxOccurs();
322       if (maxOccurs != null) {
323         bean.setMaxOccurs(BigInteger.valueOf(maxOccurs));
324       }
325 
326       MarkupMultiline remarks = constraint.getRemarks();
327       if (remarks != null) {
328         RemarksType remarksType = bean.addNewRemarks();
329         assert remarksType != null;
330         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
331       }
332       return null;
333     }
334 
335     @Override
336     public Void visitExpectConstraint(IExpectConstraint constraint, DefineAssemblyConstraintsType state) {
337       ScopedExpectConstraintType bean = state.addNewExpect();
338       assert bean != null;
339       applyCommonValues(constraint, bean);
340 
341       bean.setTest(constraint.getTest());
342 
343       String message = constraint.getMessage();
344       if (message != null) {
345         bean.setMessage(message);
346       }
347 
348       MarkupMultiline remarks = constraint.getRemarks();
349       if (remarks != null) {
350         RemarksType remarksType = bean.addNewRemarks();
351         assert remarksType != null;
352         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
353       }
354       return null;
355     }
356 
357     @Override
358     public Void visitMatchesConstraint(IMatchesConstraint constraint, DefineAssemblyConstraintsType state) {
359       ScopedMatchesConstraintType bean = state.addNewMatches();
360       assert bean != null;
361       applyCommonValues(constraint, bean);
362 
363       Pattern pattern = constraint.getPattern();
364       if (pattern != null) {
365         bean.setRegex(pattern);
366       }
367 
368       IDataTypeAdapter<?> dataType = constraint.getDataType();
369       if (dataType != null) {
370         bean.setDatatype(dataType);
371       }
372 
373       MarkupMultiline remarks = constraint.getRemarks();
374       if (remarks != null) {
375         RemarksType remarksType = bean.addNewRemarks();
376         assert remarksType != null;
377         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
378       }
379       return null;
380     }
381 
382     private static void applyKeyFields(@NonNull IKeyConstraint constraint, @NonNull KeyConstraintType bean) {
383       for (IKeyField keyField : constraint.getKeyFields()) {
384         KeyField keyFieldBean = bean.addNewKeyField();
385         assert keyField != null;
386         assert keyFieldBean != null;
387         applyKeyField(keyField, keyFieldBean);
388       }
389     }
390 
391     private static void applyKeyField(@NonNull IKeyField keyField, @NonNull KeyField bean) {
392       Pattern pattern = keyField.getPattern();
393       if (pattern != null) {
394         bean.setPattern(pattern);
395       }
396 
397       bean.setTarget(keyField.getTarget());
398 
399       MarkupMultiline remarks = keyField.getRemarks();
400       if (remarks != null) {
401         RemarksType remarksType = bean.addNewRemarks();
402         assert remarksType != null;
403         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
404       }
405     }
406 
407     @Override
408     public Void visitIndexConstraint(IIndexConstraint constraint, DefineAssemblyConstraintsType state) {
409       ScopedIndexConstraintType bean = state.addNewIndex();
410       assert bean != null;
411       applyCommonValues(constraint, bean);
412       applyKeyFields(constraint, bean);
413 
414       bean.setName(constraint.getName());
415 
416       MarkupMultiline remarks = constraint.getRemarks();
417       if (remarks != null) {
418         RemarksType remarksType = bean.addNewRemarks();
419         assert remarksType != null;
420         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
421       }
422       return null;
423     }
424 
425     @Override
426     public Void visitIndexHasKeyConstraint(IIndexHasKeyConstraint constraint, DefineAssemblyConstraintsType state) {
427       ScopedIndexHasKeyConstraintType bean = state.addNewIndexHasKey();
428       assert bean != null;
429       applyCommonValues(constraint, bean);
430       applyKeyFields(constraint, bean);
431 
432       bean.setName(constraint.getIndexName());
433 
434       MarkupMultiline remarks = constraint.getRemarks();
435       if (remarks != null) {
436         RemarksType remarksType = bean.addNewRemarks();
437         assert remarksType != null;
438         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
439       }
440       return null;
441     }
442 
443     @Override
444     public Void visitUniqueConstraint(IUniqueConstraint constraint, DefineAssemblyConstraintsType state) {
445       ScopedIndexHasKeyConstraintType bean = state.addNewIndexHasKey();
446       assert bean != null;
447       applyCommonValues(constraint, bean);
448       applyKeyFields(constraint, bean);
449 
450       MarkupMultiline remarks = constraint.getRemarks();
451       if (remarks != null) {
452         RemarksType remarksType = bean.addNewRemarks();
453         assert remarksType != null;
454         XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
455       }
456       return null;
457     }
458   }
459 }