001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.model.xml; 028 029import gov.nist.secauto.metaschema.core.metapath.MetapathExpression; 030import gov.nist.secauto.metaschema.core.model.MetaschemaException; 031import gov.nist.secauto.metaschema.core.model.constraint.DefaultConstraintSet; 032import gov.nist.secauto.metaschema.core.model.constraint.DefaultScopedContraints; 033import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.ExternalSource; 034import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.ISource; 035import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet; 036import gov.nist.secauto.metaschema.core.model.constraint.IScopedContraints; 037import gov.nist.secauto.metaschema.core.model.constraint.ITargetedConstaints; 038import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.METASCHEMACONSTRAINTSDocument; 039import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.METASCHEMACONSTRAINTSDocument.METASCHEMACONSTRAINTS.Scope; 040import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.ScopedIndexHasKeyConstraintType; 041import gov.nist.secauto.metaschema.core.util.CollectionUtil; 042import gov.nist.secauto.metaschema.core.util.ObjectUtils; 043 044import org.apache.xmlbeans.XmlCursor; 045import org.apache.xmlbeans.XmlException; 046import org.apache.xmlbeans.XmlObject; 047import org.apache.xmlbeans.XmlOptions; 048 049import java.io.IOException; 050import java.net.URI; 051import java.util.Collection; 052import java.util.Collections; 053import java.util.Deque; 054import java.util.LinkedHashMap; 055import java.util.LinkedHashSet; 056import java.util.LinkedList; 057import java.util.List; 058import java.util.Map; 059 060import edu.umd.cs.findbugs.annotations.NonNull; 061 062/** 063 * Provides methods to load a constraint set expressed in XML. 064 * <p> 065 * Loaded constraint instances are cached to avoid the need to load them for 066 * every use. Any constraint set imported is also loaded and cached 067 * automatically. 068 */ 069public class ConstraintLoader 070 extends AbstractLoader<IConstraintSet> { 071 072 @Override 073 protected IConstraintSet parseResource(@NonNull URI resource, @NonNull Deque<URI> visitedResources) 074 throws IOException { 075 076 // parse this metaschema 077 METASCHEMACONSTRAINTSDocument xmlObject = parseConstraintSet(resource); 078 079 // now check if this constraint set imports other constraint sets 080 int size = xmlObject.getMETASCHEMACONSTRAINTS().sizeOfImportArray(); 081 @NonNull Map<URI, IConstraintSet> importedConstraints; 082 if (size == 0) { 083 importedConstraints = ObjectUtils.notNull(Collections.emptyMap()); 084 } else { 085 try { 086 importedConstraints = new LinkedHashMap<>(); 087 for (METASCHEMACONSTRAINTSDocument.METASCHEMACONSTRAINTS.Import imported : xmlObject.getMETASCHEMACONSTRAINTS() 088 .getImportList()) { 089 URI importedResource = URI.create(imported.getHref()); 090 importedResource = ObjectUtils.notNull(resource.resolve(importedResource)); 091 importedConstraints.put(importedResource, loadInternal(importedResource, visitedResources)); 092 } 093 } catch (MetaschemaException ex) { 094 throw new IOException(ex); 095 } 096 } 097 098 // now create this metaschema 099 Collection<IConstraintSet> values = importedConstraints.values(); 100 return new DefaultConstraintSet(resource, parseScopedConstraints(xmlObject, resource), new LinkedHashSet<>(values)); 101 } 102 103 /** 104 * Parse the provided XML resource as a Metaschema constraints. 105 * 106 * @param resource 107 * the resource to parse 108 * @return the XMLBeans representation of the Metaschema contraints 109 * @throws IOException 110 * if a parsing error occurred 111 */ 112 @NonNull 113 protected METASCHEMACONSTRAINTSDocument parseConstraintSet(@NonNull URI resource) throws IOException { 114 try { 115 XmlOptions options = new XmlOptions(); 116 options.setBaseURI(resource); 117 options.setLoadLineNumbers(); 118 return ObjectUtils.notNull(METASCHEMACONSTRAINTSDocument.Factory.parse(resource.toURL(), options)); 119 } catch (XmlException ex) { 120 throw new IOException(ex); 121 } 122 } 123 124 /** 125 * Parse individual constraint definitions from the provided XMLBeans object. 126 * 127 * @param xmlObject 128 * the XMLBeans object 129 * @param source 130 * the source of the constraint content 131 * @return the scoped constraint definitions 132 */ 133 @NonNull 134 protected List<IScopedContraints> parseScopedConstraints( 135 @NonNull METASCHEMACONSTRAINTSDocument xmlObject, 136 @NonNull URI source) { 137 List<IScopedContraints> scopedConstraints = new LinkedList<>(); 138 ISource constraintSource = ExternalSource.instance(source); 139 140 for (Scope scope : xmlObject.getMETASCHEMACONSTRAINTS().getScopeList()) { 141 URI namespace = ObjectUtils.notNull(URI.create(scope.getMetaschemaNamespace())); 142 String shortName = ObjectUtils.requireNonNull(scope.getMetaschemaShortName()); 143 144 try (XmlCursor cursor = scope.newCursor()) { 145 cursor.selectPath("declare namespace m='http://csrc.nist.gov/ns/oscal/metaschema/1.0';" 146 + "$this/m:assembly|$this/m:field|$this/m:flag"); 147 148 List<ITargetedConstaints> targetedConstraints = new LinkedList<>(); // NOPMD - intentional 149 while (cursor.toNextSelection()) { 150 XmlObject obj = cursor.getObject(); 151 if (obj instanceof Scope.Assembly) { 152 Scope.Assembly assembly = (Scope.Assembly) obj; 153 MetapathExpression expression = ObjectUtils.requireNonNull(assembly.getTarget()); 154 AssemblyConstraintSupport constraints 155 = new AssemblyConstraintSupport(assembly, constraintSource); // NOPMD - intentional 156 targetedConstraints.add(new AssemblyTargetedConstraints(expression, constraints)); 157 } else if (obj instanceof Scope.Field) { 158 Scope.Field field = (Scope.Field) obj; 159 MetapathExpression expression = ObjectUtils.requireNonNull(field.getTarget()); 160 ValueConstraintSupport constraints 161 = new ValueConstraintSupport(field, constraintSource); // NOPMD - intentional 162 targetedConstraints.add(new FieldTargetedConstraints(expression, constraints)); 163 } else if (obj instanceof ScopedIndexHasKeyConstraintType) { 164 Scope.Flag flag = (Scope.Flag) obj; 165 MetapathExpression expression = ObjectUtils.requireNonNull(flag.getTarget()); 166 ValueConstraintSupport constraints 167 = new ValueConstraintSupport(flag, constraintSource); // NOPMD - intentional 168 targetedConstraints.add(new FlagTargetedConstraints(expression, constraints)); 169 } 170 } 171 scopedConstraints.add( 172 new DefaultScopedContraints(namespace, shortName, CollectionUtil.unmodifiableList(targetedConstraints))); 173 } 174 } 175 return CollectionUtil.unmodifiableList(scopedConstraints); 176 } 177 178}