AssemblyConstraintSupport.java
/*
* Portions of this software was developed by employees of the National Institute
* of Standards and Technology (NIST), an agency of the Federal Government and is
* being made available as a public service. Pursuant to title 17 United States
* Code Section 105, works of NIST employees are not subject to copyright
* protection in the United States. This software may be subject to foreign
* copyright. Permission in the United States and in foreign countries, to the
* extent that NIST may hold copyright, to use, copy, modify, create derivative
* works, and distribute this software and its documentation without fee is hereby
* granted on a non-exclusive basis, provided that this notice and disclaimer
* of warranty appears in all copies.
*
* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
* EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
* THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
* INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
* SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
* SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
* INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
* OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
* CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
* PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
* OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
*/
package gov.nist.secauto.metaschema.core.model.xml;
import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
import gov.nist.secauto.metaschema.core.model.IModule;
import gov.nist.secauto.metaschema.core.model.constraint.DefaultAllowedValuesConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.DefaultCardinalityConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.DefaultExpectConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.DefaultIndexConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.DefaultIndexHasKeyConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.DefaultMatchesConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.DefaultUniqueConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.ICardinalityConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.ISource;
import gov.nist.secauto.metaschema.core.model.constraint.IConstraintVisitor;
import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IIndexConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IIndexHasKeyConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IKeyConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IKeyField;
import gov.nist.secauto.metaschema.core.model.constraint.IMatchesConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
import gov.nist.secauto.metaschema.core.model.constraint.IUniqueConstraint;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ConstraintType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.DefineAssemblyConstraintsType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.EnumType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.GlobalAssemblyDefinitionType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.HasCardinalityConstraintType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.KeyConstraintType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.KeyConstraintType.KeyField;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.PropertyType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.RemarksType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedAllowedValuesType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedExpectConstraintType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedIndexConstraintType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedIndexHasKeyConstraintType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedKeyConstraintType;
import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedMatchesConstraintType;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import edu.umd.cs.findbugs.annotations.NonNull;
/**
* Provides support for parsing and maintaining a set of Metaschema constraints.
* Constraints are parsed from XML.
*/
@SuppressWarnings("PMD.CouplingBetweenObjects")
class AssemblyConstraintSupport
extends ValueConstraintSupport
implements IModelConstrained {
@NonNull
private static final String PATH = "declare namespace m='http://csrc.nist.gov/ns/oscal/metaschema/1.0';"
+ "$this/m:allowed-values|$this/m:index|$this/m:index-has-key|$this/m:is-unique|"
+ "$this/m:has-cardinality|$this/m:matches|$this/m:expect";
@NonNull
private final List<IIndexConstraint> indexConstraints = new LinkedList<>();
@NonNull
private final List<IUniqueConstraint> uniqueConstraints = new LinkedList<>();
@NonNull
private final List<ICardinalityConstraint> cardinalityConstraints = new LinkedList<>();
public static AssemblyConstraintSupport newInstance(
@NonNull GlobalAssemblyDefinitionType definition,
@NonNull ISource source) {
AssemblyConstraintSupport retval;
if (definition.isSetConstraint()) {
retval = new AssemblyConstraintSupport(ObjectUtils.notNull(definition.getConstraint()), source);
} else {
retval = new AssemblyConstraintSupport();
}
return retval;
}
public AssemblyConstraintSupport() {
// do nothing
}
/**
* Generate a set of constraints from the provided XMLBeans instance.
*
* @param xmlConstraints
* the XMLBeans instance
* @param source
* information about the source of the constraints
*/
public AssemblyConstraintSupport( // NOPMD - unavoidable
@NonNull DefineAssemblyConstraintsType xmlConstraints,
@NonNull ISource source) {
try (XmlCursor cursor = xmlConstraints.newCursor()) {
assert cursor != null;
parse(source, cursor, PATH);
}
}
@Override
protected boolean parseXmlObject(
@NonNull ISource source,
@NonNull XmlObject obj) {
boolean handled = false;
if (obj instanceof ScopedAllowedValuesType) {
DefaultAllowedValuesConstraint constraint
= ModelFactory.newAllowedValuesConstraint((ScopedAllowedValuesType) obj, source);
addConstraint(constraint);
handled = true;
} else if (obj instanceof ScopedIndexConstraintType) {
DefaultIndexConstraint constraint
= ModelFactory.newIndexConstraint((ScopedIndexConstraintType) obj, source);
addConstraint(constraint);
handled = true;
} else if (obj instanceof ScopedIndexHasKeyConstraintType) {
DefaultIndexHasKeyConstraint constraint
= ModelFactory.newIndexHasKeyConstraint((ScopedIndexHasKeyConstraintType) obj, source);
addConstraint(constraint);
handled = true;
} else if (obj instanceof ScopedKeyConstraintType) {
DefaultUniqueConstraint constraint
= ModelFactory.newUniqueConstraint((ScopedKeyConstraintType) obj, source);
addConstraint(constraint);
handled = true;
} else if (obj instanceof HasCardinalityConstraintType) {
DefaultCardinalityConstraint constraint
= ModelFactory.newCardinalityConstraint((HasCardinalityConstraintType) obj, source);
addConstraint(constraint);
handled = true;
} else if (obj instanceof ScopedMatchesConstraintType) {
DefaultMatchesConstraint constraint
= ModelFactory.newMatchesConstraint((ScopedMatchesConstraintType) obj, source);
addConstraint(constraint);
handled = true;
} else if (obj instanceof ScopedExpectConstraintType) {
DefaultExpectConstraint constraint
= ModelFactory.newExpectConstraint((ScopedExpectConstraintType) obj, source);
addConstraint(constraint);
handled = true;
}
return handled;
}
@Override
public List<IIndexConstraint> getIndexConstraints() {
synchronized (this) {
return indexConstraints;
}
}
@Override
public List<IUniqueConstraint> getUniqueConstraints() {
synchronized (this) {
return uniqueConstraints;
}
}
@Override
public List<ICardinalityConstraint> getHasCardinalityConstraints() {
synchronized (this) {
return cardinalityConstraints;
}
}
@Override
public final void addConstraint(@NonNull IIndexConstraint constraint) {
synchronized (this) {
getConstraints().add(constraint);
indexConstraints.add(constraint);
}
}
@Override
public final void addConstraint(@NonNull IUniqueConstraint constraint) {
synchronized (this) {
getConstraints().add(constraint);
uniqueConstraints.add(constraint);
}
}
@Override
public final void addConstraint(@NonNull ICardinalityConstraint constraint) {
synchronized (this) {
getConstraints().add(constraint);
cardinalityConstraints.add(constraint);
}
}
public final DefineAssemblyConstraintsType generate() {
DefineAssemblyConstraintsType retval = DefineAssemblyConstraintsType.Factory.newInstance();
XmlbeanGeneratingVisitor visitor = new XmlbeanGeneratingVisitor();
for (IConstraint constraint : getConstraints()) {
constraint.accept(visitor, retval);
}
return retval;
}
private static final class XmlbeanGeneratingVisitor
implements IConstraintVisitor<DefineAssemblyConstraintsType, Void> {
private static void applyCommonValues(@NonNull IConstraint constraint, @NonNull ConstraintType bean) {
MarkupLine description = constraint.getDescription();
if (description != null) {
bean.setDescription(MarkupStringConverter.toMarkupLineDatatype(description));
}
String formalName = constraint.getFormalName();
if (formalName != null) {
bean.setFormalName(formalName);
}
String id = constraint.getId();
if (id != null) {
bean.setId(constraint.getId());
}
IConstraint.Level level = constraint.getLevel();
if (!IConstraint.DEFAULT_LEVEL.equals(level)) {
bean.setLevel(level);
}
for (Map.Entry<QName, Set<String>> entry : constraint.getProperties().entrySet()) {
QName qname = entry.getKey();
Set<String> values = entry.getValue();
for (String value : values) {
PropertyType prop = bean.addNewProp();
prop.setName(qname.getLocalPart());
String namespace = qname.getNamespaceURI();
if (namespace != null && !namespace.isEmpty()) {
prop.setNamespace(namespace);
}
prop.setValue(value);
}
}
}
@Override
public Void visitAllowedValues(IAllowedValuesConstraint constraint, DefineAssemblyConstraintsType state) {
ScopedAllowedValuesType bean = state.addNewAllowedValues();
assert bean != null;
applyCommonValues(constraint, bean);
if (Boolean.compare(IAllowedValuesConstraint.DEFAULT_ALLOW_OTHER, constraint.isAllowedOther()) != 0) {
bean.setAllowOther(constraint.isAllowedOther());
}
bean.setTarget(constraint.getTarget());
bean.setExtensible(constraint.getExtensible());
for (Map.Entry<String, ? extends IAllowedValue> entry : constraint.getAllowedValues().entrySet()) {
String value = entry.getKey();
IAllowedValue allowedValue = entry.getValue();
assert value.equals(allowedValue.getValue());
MarkupLine description = allowedValue.getDescription();
EnumType enumType = bean.addNewEnum();
enumType.setValue(value);
XmlbeansMarkupVisitor.visit(description, IModule.METASCHEMA_XML_NS, enumType);
}
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
return null;
}
@Override
public Void visitCardinalityConstraint(ICardinalityConstraint constraint, DefineAssemblyConstraintsType state) {
HasCardinalityConstraintType bean = state.addNewHasCardinality();
assert bean != null;
applyCommonValues(constraint, bean);
Integer minOccurs = constraint.getMinOccurs();
if (minOccurs != null) {
bean.setMinOccurs(BigInteger.valueOf(minOccurs));
}
Integer maxOccurs = constraint.getMaxOccurs();
if (maxOccurs != null) {
bean.setMaxOccurs(BigInteger.valueOf(maxOccurs));
}
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
return null;
}
@Override
public Void visitExpectConstraint(IExpectConstraint constraint, DefineAssemblyConstraintsType state) {
ScopedExpectConstraintType bean = state.addNewExpect();
assert bean != null;
applyCommonValues(constraint, bean);
bean.setTest(constraint.getTest());
String message = constraint.getMessage();
if (message != null) {
bean.setMessage(message);
}
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
return null;
}
@Override
public Void visitMatchesConstraint(IMatchesConstraint constraint, DefineAssemblyConstraintsType state) {
ScopedMatchesConstraintType bean = state.addNewMatches();
assert bean != null;
applyCommonValues(constraint, bean);
Pattern pattern = constraint.getPattern();
if (pattern != null) {
bean.setRegex(pattern);
}
IDataTypeAdapter<?> dataType = constraint.getDataType();
if (dataType != null) {
bean.setDatatype(dataType);
}
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
return null;
}
private static void applyKeyFields(@NonNull IKeyConstraint constraint, @NonNull KeyConstraintType bean) {
for (IKeyField keyField : constraint.getKeyFields()) {
KeyField keyFieldBean = bean.addNewKeyField();
assert keyField != null;
assert keyFieldBean != null;
applyKeyField(keyField, keyFieldBean);
}
}
private static void applyKeyField(@NonNull IKeyField keyField, @NonNull KeyField bean) {
Pattern pattern = keyField.getPattern();
if (pattern != null) {
bean.setPattern(pattern);
}
bean.setTarget(keyField.getTarget());
MarkupMultiline remarks = keyField.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
}
@Override
public Void visitIndexConstraint(IIndexConstraint constraint, DefineAssemblyConstraintsType state) {
ScopedIndexConstraintType bean = state.addNewIndex();
assert bean != null;
applyCommonValues(constraint, bean);
applyKeyFields(constraint, bean);
bean.setName(constraint.getName());
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
return null;
}
@Override
public Void visitIndexHasKeyConstraint(IIndexHasKeyConstraint constraint, DefineAssemblyConstraintsType state) {
ScopedIndexHasKeyConstraintType bean = state.addNewIndexHasKey();
assert bean != null;
applyCommonValues(constraint, bean);
applyKeyFields(constraint, bean);
bean.setName(constraint.getIndexName());
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
return null;
}
@Override
public Void visitUniqueConstraint(IUniqueConstraint constraint, DefineAssemblyConstraintsType state) {
ScopedIndexHasKeyConstraintType bean = state.addNewIndexHasKey();
assert bean != null;
applyCommonValues(constraint, bean);
applyKeyFields(constraint, bean);
MarkupMultiline remarks = constraint.getRemarks();
if (remarks != null) {
RemarksType remarksType = bean.addNewRemarks();
assert remarksType != null;
XmlbeansMarkupVisitor.visit(remarks, IModule.METASCHEMA_XML_NS, remarksType);
}
return null;
}
}
}