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.impl;
28  
29  import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
30  import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
31  import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
32  import gov.nist.secauto.metaschema.core.model.IDefinition;
33  import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
34  import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
35  import gov.nist.secauto.metaschema.core.model.IModelElement;
36  import gov.nist.secauto.metaschema.core.model.IModule;
37  import gov.nist.secauto.metaschema.core.model.IValuedDefinition;
38  import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
39  import gov.nist.secauto.metaschema.core.util.AutoCloser;
40  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
41  import gov.nist.secauto.metaschema.schemagen.AbstractGenerationState;
42  import gov.nist.secauto.metaschema.schemagen.SchemaGenerationException;
43  import gov.nist.secauto.metaschema.schemagen.SchemaGenerationFeature;
44  import gov.nist.secauto.metaschema.schemagen.xml.datatype.XmlDatatypeManager;
45  import gov.nist.secauto.metaschema.schemagen.xml.schematype.IXmlComplexType;
46  import gov.nist.secauto.metaschema.schemagen.xml.schematype.IXmlSimpleType;
47  import gov.nist.secauto.metaschema.schemagen.xml.schematype.IXmlType;
48  import gov.nist.secauto.metaschema.schemagen.xml.schematype.XmlComplexTypeAssemblyDefinition;
49  import gov.nist.secauto.metaschema.schemagen.xml.schematype.XmlComplexTypeFieldDefinition;
50  import gov.nist.secauto.metaschema.schemagen.xml.schematype.XmlSimpleTypeDataTypeReference;
51  import gov.nist.secauto.metaschema.schemagen.xml.schematype.XmlSimpleTypeDataTypeRestriction;
52  import gov.nist.secauto.metaschema.schemagen.xml.schematype.XmlSimpleTypeUnion;
53  
54  import org.codehaus.stax2.XMLStreamWriter2;
55  
56  import java.io.IOException;
57  import java.util.List;
58  import java.util.Map;
59  import java.util.concurrent.ConcurrentHashMap;
60  
61  import javax.xml.namespace.QName;
62  import javax.xml.stream.XMLStreamException;
63  
64  import edu.umd.cs.findbugs.annotations.NonNull;
65  import edu.umd.cs.findbugs.annotations.Nullable;
66  
67  public class XmlGenerationState
68      extends AbstractGenerationState<AutoCloser<XMLStreamWriter2, SchemaGenerationException>, XmlDatatypeManager> {
69    @NonNull
70    private final String defaultNS;
71    @NonNull
72    private final Map<String, String> namespaceToPrefixMap = new ConcurrentHashMap<>();
73    @NonNull
74    private final Map<IDataTypeAdapter<?>, IXmlSimpleType> dataTypeToSimpleTypeMap = new ConcurrentHashMap<>();
75    @NonNull
76    private final Map<IValuedDefinition, IXmlSimpleType> definitionToSimpleTypeMap = new ConcurrentHashMap<>();
77    @NonNull
78    private final Map<IDefinition, IXmlType> definitionToTypeMap = new ConcurrentHashMap<>();
79  
80    private int prefixNum; // 0
81  
82    public XmlGenerationState(
83        @NonNull IModule module,
84        @NonNull AutoCloser<XMLStreamWriter2, SchemaGenerationException> writer,
85        @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration) {
86      super(module, writer, configuration, new XmlDatatypeManager());
87      this.defaultNS = ObjectUtils.notNull(module.getXmlNamespace().toASCIIString());
88    }
89  
90    @SuppressWarnings("resource")
91    @NonNull
92    public XMLStreamWriter2 getXMLStreamWriter() {
93      return getWriter().getResource();
94    }
95  
96    @NonNull
97    public String getDefaultNS() {
98      return defaultNS;
99    }
100 
101   @NonNull
102   public String getDatatypeNS() {
103     return getDefaultNS();
104   }
105 
106   @SuppressWarnings("null")
107   @NonNull
108   public String getNS(@NonNull IModelElement modelElement) {
109     return modelElement.getContainingModule().getXmlNamespace().toASCIIString();
110   }
111 
112   public String getNSPrefix(String namespace) {
113     String retval = null;
114     if (!getDefaultNS().equals(namespace)) {
115       synchronized (this) {
116         retval = namespaceToPrefixMap.get(namespace);
117         if (retval == null) {
118           retval = String.format("ns%d", ++prefixNum);
119           namespaceToPrefixMap.put(namespace, retval);
120         }
121       }
122     }
123     return retval;
124   }
125 
126   @NonNull
127   protected QName newQName(
128       @NonNull String localName,
129       @NonNull String namespace) {
130     String prefix = null;
131     if (!getDefaultNS().equals(namespace)) {
132       prefix = getNSPrefix(namespace);
133     }
134 
135     return ObjectUtils.notNull(
136         prefix == null ? new QName(namespace, localName) : new QName(namespace, localName, prefix));
137   }
138 
139   @NonNull
140   protected QName newQName(
141       @NonNull IDefinition definition,
142       @Nullable String suffix) {
143     return newQName(
144         getTypeNameForDefinition(definition, suffix),
145         getNS(definition));
146   }
147 
148   public IXmlType getTypeForDefinition(@NonNull IDefinition definition) {
149     IXmlType retval = definitionToTypeMap.get(definition);
150     if (retval == null) {
151       switch (definition.getModelType()) {
152       case FIELD: {
153         IFieldDefinition field = (IFieldDefinition) definition;
154         if (field.getFlagInstances().isEmpty()) {
155           retval = getSimpleType(field);
156         } else {
157           retval = newComplexType(field);
158         }
159         break;
160       }
161       case ASSEMBLY: {
162         retval = newComplexType((IAssemblyDefinition) definition);
163         break;
164       }
165       case FLAG:
166         retval = getSimpleType((IFlagDefinition) definition);
167         break;
168       default:
169         throw new UnsupportedOperationException(definition.getModelType().toString());
170       }
171       definitionToTypeMap.put(definition, retval);
172     }
173     return retval;
174   }
175 
176   @NonNull
177   public IXmlSimpleType getSimpleType(@NonNull IDataTypeAdapter<?> dataType) {
178     IXmlSimpleType type = dataTypeToSimpleTypeMap.get(dataType);
179     if (type == null) {
180       // lazy initialize and cache the type
181       QName qname = newQName(
182           getDatatypeManager().getTypeNameForDatatype(dataType),
183           getDatatypeNS());
184       type = new XmlSimpleTypeDataTypeReference(qname, dataType);
185       dataTypeToSimpleTypeMap.put(dataType, type);
186     }
187     return type;
188   }
189 
190   @NonNull
191   public IXmlSimpleType getSimpleType(@NonNull IValuedDefinition definition) {
192     IXmlSimpleType simpleType = definitionToSimpleTypeMap.get(definition);
193     if (simpleType == null) {
194       AllowedValueCollection allowedValuesCollection = getContextIndependentEnumeratedValues(definition);
195       List<IAllowedValue> allowedValues = allowedValuesCollection.getValues();
196 
197       IDataTypeAdapter<?> dataType = definition.getJavaTypeAdapter();
198       if (allowedValues.isEmpty()) {
199         // just use the built-in type
200         simpleType = getSimpleType(dataType);
201       } else {
202 
203         // generate a restriction on the built-in type for the enumerated values
204         simpleType = new XmlSimpleTypeDataTypeRestriction(
205             newQName(definition, null),
206             definition,
207             allowedValuesCollection);
208 
209         if (!allowedValuesCollection.isClosed()) {
210           // if other values are allowed, we need to make a union of the restriction type
211           // and the base
212           // built-in type
213           simpleType = new XmlSimpleTypeUnion(
214               newQName(definition, "Union"),
215               definition,
216               getSimpleType(dataType),
217               simpleType);
218         }
219       }
220 
221       definitionToSimpleTypeMap.put(definition, simpleType);
222     }
223     return simpleType;
224   }
225 
226   @NonNull
227   protected IXmlComplexType newComplexType(@NonNull IFieldDefinition definition) {
228     QName qname = newQName(definition, null);
229     return new XmlComplexTypeFieldDefinition(qname, definition);
230   }
231 
232   @NonNull
233   protected IXmlComplexType newComplexType(@NonNull IAssemblyDefinition definition) {
234     QName qname = newQName(definition, null);
235     return new XmlComplexTypeAssemblyDefinition(qname, definition);
236   }
237 
238   public void generateXmlTypes() throws XMLStreamException {
239 
240     for (IXmlType type : definitionToTypeMap.values()) {
241       if (!type.isInline(this) && type.isGeneratedType(this) && type.isReferenced(this)) {
242         type.generateType(this, false);
243       } else {
244         if (type.isGeneratedType(this)) {
245           assert type.isInline(this) || !type.isReferenced(this);
246         }
247       }
248     }
249     getDatatypeManager().generateDatatypes(getXMLStreamWriter());
250   }
251 
252   public void writeAttribute(@NonNull String localName, @NonNull String value) throws XMLStreamException {
253     getXMLStreamWriter().writeAttribute(localName, value);
254   }
255 
256   public void writeStartElement(@NonNull String namespaceUri, @NonNull String localName) throws XMLStreamException {
257     getXMLStreamWriter().writeStartElement(namespaceUri, localName);
258   }
259 
260   public void writeStartElement(
261       @NonNull String prefix,
262       @NonNull String localName,
263       @NonNull String namespaceUri) throws XMLStreamException {
264     getXMLStreamWriter().writeStartElement(prefix, localName, namespaceUri);
265 
266   }
267 
268   public void writeEndElement() throws XMLStreamException {
269     getXMLStreamWriter().writeEndElement();
270   }
271 
272   public void writeCharacters(@NonNull String text) throws XMLStreamException {
273     getXMLStreamWriter().writeCharacters(text);
274   }
275 
276   public void writeNamespace(String prefix, String namespaceUri) throws XMLStreamException {
277     getXMLStreamWriter().writeNamespace(prefix, namespaceUri);
278   }
279 
280   @Override
281   public void flushWriter() throws IOException {
282     try {
283       getWriter().getResource().flush();
284     } catch (XMLStreamException ex) {
285       throw new IOException(ex);
286     }
287   }
288 }