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.schemagen.xml.schematype;
28  
29  import gov.nist.secauto.metaschema.core.datatype.markup.MarkupDataTypeProvider;
30  import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
31  import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
32  import gov.nist.secauto.metaschema.core.model.IFieldInstance;
33  import gov.nist.secauto.metaschema.core.model.IFlagContainer;
34  import gov.nist.secauto.metaschema.core.model.IFlagInstance;
35  import gov.nist.secauto.metaschema.core.model.IModelInstance;
36  import gov.nist.secauto.metaschema.core.model.INamedModelInstance;
37  import gov.nist.secauto.metaschema.core.model.XmlGroupAsBehavior;
38  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
39  import gov.nist.secauto.metaschema.schemagen.SchemaGenerationException;
40  import gov.nist.secauto.metaschema.schemagen.xml.XmlSchemaGenerator;
41  import gov.nist.secauto.metaschema.schemagen.xml.impl.DocumentationGenerator;
42  import gov.nist.secauto.metaschema.schemagen.xml.impl.XmlGenerationState;
43  
44  import java.util.Collection;
45  
46  import javax.xml.namespace.QName;
47  import javax.xml.stream.XMLStreamException;
48  
49  import edu.umd.cs.findbugs.annotations.NonNull;
50  
51  public class XmlComplexTypeAssemblyDefinition
52      extends AbstractXmlComplexType<IAssemblyDefinition> {
53  
54    public XmlComplexTypeAssemblyDefinition(
55        @NonNull QName qname,
56        @NonNull IAssemblyDefinition definition) {
57      super(qname, definition);
58    }
59  
60    @Override
61    protected void generateTypeBody(XmlGenerationState state) throws XMLStreamException {
62      IAssemblyDefinition definition = getDefinition();
63  
64      Collection<? extends IModelInstance> modelInstances = definition.getModelInstances();
65      if (!modelInstances.isEmpty()) {
66        state.writeStartElement(XmlSchemaGenerator.PREFIX_XML_SCHEMA, "sequence", XmlSchemaGenerator.NS_XML_SCHEMA);
67        for (IModelInstance modelInstance : modelInstances) {
68          assert modelInstance != null;
69          generateModelInstance(modelInstance, state);
70        }
71        state.writeEndElement();
72      }
73  
74      Collection<? extends IFlagInstance> flagInstances = definition.getFlagInstances();
75      if (!flagInstances.isEmpty()) {
76        for (IFlagInstance flagInstance : flagInstances) {
77          assert flagInstance != null;
78          generateFlagInstance(flagInstance, state);
79        }
80      }
81    }
82  
83    protected void generateModelInstance( // NOPMD acceptable complexity
84        @NonNull IModelInstance modelInstance,
85        @NonNull XmlGenerationState state)
86        throws XMLStreamException {
87  
88      boolean grouped = false;
89      if (XmlGroupAsBehavior.GROUPED.equals(modelInstance.getXmlGroupAsBehavior())) {
90        // handle grouping
91        state.writeStartElement(XmlSchemaGenerator.PREFIX_XML_SCHEMA, "element", XmlSchemaGenerator.NS_XML_SCHEMA);
92  
93        QName groupAsQName = ObjectUtils.requireNonNull(modelInstance.getXmlGroupAsQName());
94  
95        if (state.getDefaultNS().equals(groupAsQName.getNamespaceURI())) {
96          state.writeAttribute("name", ObjectUtils.requireNonNull(groupAsQName.getLocalPart()));
97        } else {
98          throw new SchemaGenerationException(
99              String.format("Attempt to create element '%s' on definition '%s' with different namespace", groupAsQName,
100                 getDefinition().toCoordinates()));
101       }
102 
103       if (modelInstance.getMinOccurs() == 0) {
104         // this is an optional instance group
105         state.writeAttribute("minOccurs", "0");
106       }
107 
108       // now generate the child elements of the group
109       state.writeStartElement(XmlSchemaGenerator.PREFIX_XML_SCHEMA, "complexType", XmlSchemaGenerator.NS_XML_SCHEMA);
110       state.writeStartElement(XmlSchemaGenerator.PREFIX_XML_SCHEMA, "sequence", XmlSchemaGenerator.NS_XML_SCHEMA);
111 
112       // mark that we need to close these elements
113       grouped = true;
114     }
115 
116     switch (modelInstance.getModelType()) {
117     case ASSEMBLY:
118       generateNamedModelInstance((INamedModelInstance) modelInstance, grouped, state);
119       break;
120     case FIELD: {
121       IFieldInstance fieldInstance = (IFieldInstance) modelInstance;
122       if (!fieldInstance.isInXmlWrapped()
123           && fieldInstance.getDefinition().getJavaTypeAdapter().isUnrappedValueAllowedInXml()) {
124         generateUnwrappedFieldInstance(fieldInstance, grouped, state);
125       } else {
126         generateNamedModelInstance(fieldInstance, grouped, state);
127       }
128       break;
129     }
130     case CHOICE:
131       generateChoiceModelInstance((IChoiceInstance) modelInstance, state);
132       break;
133     default:
134       throw new UnsupportedOperationException();
135     }
136 
137     if (grouped) {
138       state.writeEndElement(); // xs:sequence
139       state.writeEndElement(); // xs:complexType
140       state.writeEndElement(); // xs:element
141     }
142   }
143 
144   protected void generateNamedModelInstance(
145       @NonNull INamedModelInstance modelInstance,
146       boolean grouped,
147       @NonNull XmlGenerationState state) throws XMLStreamException {
148     state.writeStartElement(XmlSchemaGenerator.PREFIX_XML_SCHEMA, "element", XmlSchemaGenerator.NS_XML_SCHEMA);
149 
150     state.writeAttribute("name", modelInstance.getEffectiveName());
151 
152     // state.generateElementNameOrRef(modelInstance);
153 
154     if (!grouped && modelInstance.getMinOccurs() != 1) {
155       state.writeAttribute("minOccurs", ObjectUtils.notNull(Integer.toString(modelInstance.getMinOccurs())));
156     }
157 
158     if (modelInstance.getMaxOccurs() != 1) {
159       state.writeAttribute("maxOccurs",
160           modelInstance.getMaxOccurs() == -1 ? "unbounded"
161               : ObjectUtils.notNull(Integer.toString(modelInstance.getMaxOccurs())));
162     }
163 
164     IFlagContainer definition = modelInstance.getDefinition();
165     IXmlType type = state.getTypeForDefinition(definition);
166 
167     if (state.isInline(definition)) {
168       DocumentationGenerator.generateDocumentation(modelInstance, state);
169       type.generateType(state, true);
170     } else {
171       state.writeAttribute("type", type.getTypeReference());
172       DocumentationGenerator.generateDocumentation(modelInstance, state);
173     }
174     state.writeEndElement(); // xs:element
175   }
176 
177   protected static void generateUnwrappedFieldInstance(
178       @NonNull IFieldInstance fieldInstance,
179       boolean grouped,
180       @NonNull XmlGenerationState state) throws XMLStreamException {
181 
182     if (!MarkupDataTypeProvider.MARKUP_MULTILINE.equals(fieldInstance.getDefinition().getJavaTypeAdapter())) {
183       throw new IllegalStateException();
184     }
185 
186     state.writeStartElement(XmlSchemaGenerator.PREFIX_XML_SCHEMA, "group", XmlSchemaGenerator.NS_XML_SCHEMA);
187 
188     state.writeAttribute("ref", "blockElementGroup");
189 
190     // minOccurs=1 is the schema default
191     if (!grouped && fieldInstance.getMinOccurs() != 1) {
192       state.writeAttribute("minOccurs", ObjectUtils.notNull(Integer.toString(fieldInstance.getMinOccurs())));
193     }
194 
195     // if (fieldInstance.getMaxOccurs() != 1) {
196     // state.writeAttribute("maxOccurs",
197     // fieldInstance.getMaxOccurs() == -1 ? "unbounded"
198     // : ObjectUtils.notNull(Integer.toString(fieldInstance.getMaxOccurs())));
199     // }
200 
201     // unwrapped fields always have a max-occurance of 1. Since the markup multiline
202     // is unbounded, this
203     // value is unbounded.
204     state.writeAttribute("maxOccurs", "unbounded");
205 
206     DocumentationGenerator.generateDocumentation(fieldInstance, state);
207 
208     state.writeEndElement(); // xs:group
209   }
210 
211   protected void generateChoiceModelInstance(@NonNull IChoiceInstance choice,
212       @NonNull XmlGenerationState state) throws XMLStreamException {
213     state.writeStartElement(XmlSchemaGenerator.PREFIX_XML_SCHEMA, "choice", XmlSchemaGenerator.NS_XML_SCHEMA);
214 
215     for (IModelInstance instance : choice.getModelInstances()) {
216       assert instance != null;
217 
218       if (instance instanceof IChoiceInstance) {
219         generateChoiceModelInstance((IChoiceInstance) instance, state);
220       } else {
221         generateModelInstance(instance, state);
222       }
223     }
224 
225     state.writeEndElement(); // xs:choice
226   }
227 }