1
2
3
4
5
6 package dev.metaschema.schemagen.xml.impl;
7
8 import org.codehaus.stax2.XMLStreamWriter2;
9
10 import java.io.IOException;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.concurrent.ConcurrentHashMap;
14 import java.util.concurrent.atomic.AtomicInteger;
15
16 import javax.xml.namespace.QName;
17 import javax.xml.stream.XMLStreamException;
18
19 import dev.metaschema.core.configuration.IConfiguration;
20 import dev.metaschema.core.datatype.IDataTypeAdapter;
21 import dev.metaschema.core.model.IAssemblyDefinition;
22 import dev.metaschema.core.model.IDefinition;
23 import dev.metaschema.core.model.IFieldDefinition;
24 import dev.metaschema.core.model.IFlagDefinition;
25 import dev.metaschema.core.model.IModelElement;
26 import dev.metaschema.core.model.IModule;
27 import dev.metaschema.core.model.IValuedDefinition;
28 import dev.metaschema.core.model.constraint.IAllowedValue;
29 import dev.metaschema.core.util.AutoCloser;
30 import dev.metaschema.core.util.ObjectUtils;
31 import dev.metaschema.schemagen.AbstractGenerationState;
32 import dev.metaschema.schemagen.SchemaGenerationException;
33 import dev.metaschema.schemagen.SchemaGenerationFeature;
34 import dev.metaschema.schemagen.xml.impl.schematype.IXmlComplexType;
35 import dev.metaschema.schemagen.xml.impl.schematype.IXmlSimpleType;
36 import dev.metaschema.schemagen.xml.impl.schematype.IXmlType;
37 import dev.metaschema.schemagen.xml.impl.schematype.XmlComplexTypeAssemblyDefinition;
38 import dev.metaschema.schemagen.xml.impl.schematype.XmlComplexTypeFieldDefinition;
39 import dev.metaschema.schemagen.xml.impl.schematype.XmlSimpleTypeDataTypeReference;
40 import dev.metaschema.schemagen.xml.impl.schematype.XmlSimpleTypeDataTypeRestriction;
41 import dev.metaschema.schemagen.xml.impl.schematype.XmlSimpleTypeUnion;
42 import edu.umd.cs.findbugs.annotations.NonNull;
43 import edu.umd.cs.findbugs.annotations.Nullable;
44
45
46
47
48
49
50
51 public class XmlGenerationState
52 extends AbstractGenerationState<AutoCloser<XMLStreamWriter2, SchemaGenerationException>, XmlDatatypeManager>
53 implements IXmlGenerationState {
54 @NonNull
55 private final String defaultNS;
56 @NonNull
57 private final Map<String, String> namespaceToPrefixMap = new ConcurrentHashMap<>();
58 @NonNull
59 private final Map<IDataTypeAdapter<?>, IXmlSimpleType> dataTypeToSimpleTypeMap = new ConcurrentHashMap<>();
60 @NonNull
61 private final Map<IValuedDefinition, IXmlSimpleType> definitionToSimpleTypeMap = new ConcurrentHashMap<>();
62 @NonNull
63 private final Map<IDefinition, IXmlType> definitionToTypeMap = new ConcurrentHashMap<>();
64
65 private final AtomicInteger prefixNum = new AtomicInteger();
66
67
68
69
70
71
72
73
74
75
76
77 public XmlGenerationState(
78 @NonNull IModule module,
79 @NonNull AutoCloser<XMLStreamWriter2, SchemaGenerationException> writer,
80 @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration) {
81 super(module, writer, configuration, new XmlDatatypeManager());
82 this.defaultNS = ObjectUtils.notNull(module.getXmlNamespace().toASCIIString());
83 }
84
85
86
87
88
89
90 @Override
91 @NonNull
92 public XMLStreamWriter2 getXMLStreamWriter() {
93 return getWriter().getResource();
94 }
95
96
97
98
99
100
101 @Override
102 @NonNull
103 public String getDefaultNS() {
104 return defaultNS;
105 }
106
107
108
109
110
111
112 @NonNull
113 public String getDatatypeNS() {
114 return getDefaultNS();
115 }
116
117
118
119
120
121
122
123
124 @SuppressWarnings("null")
125 @Override
126 @NonNull
127 public String getNS(@NonNull IModelElement modelElement) {
128 return modelElement.getContainingModule().getXmlNamespace().toASCIIString();
129 }
130
131
132
133
134
135
136
137
138
139
140
141 public String getNSPrefix(String namespace) {
142 String retval = null;
143 if (!getDefaultNS().equals(namespace)) {
144 retval = namespaceToPrefixMap.computeIfAbsent(
145 namespace,
146 key -> String.format("ns%d", prefixNum.incrementAndGet()));
147 }
148 return retval;
149 }
150
151
152
153
154
155
156
157
158
159
160 @NonNull
161 protected QName newQName(
162 @NonNull String localName,
163 @NonNull String namespace) {
164 String prefix = null;
165 if (!getDefaultNS().equals(namespace)) {
166 prefix = getNSPrefix(namespace);
167 }
168
169 return ObjectUtils.notNull(
170 prefix == null ? new QName(namespace, localName) : new QName(namespace, localName, prefix));
171 }
172
173
174
175
176
177
178
179
180
181
182 @NonNull
183 protected QName newQName(
184 @NonNull IDefinition definition,
185 @Nullable String suffix) {
186 return newQName(
187 getTypeNameForDefinition(definition, suffix),
188 getNS(definition));
189 }
190
191
192
193
194
195
196
197
198
199
200
201
202
203 @Override
204 public IXmlType getXmlForDefinition(@NonNull IDefinition definition) {
205 IXmlType retval = definitionToTypeMap.get(definition);
206 if (retval == null) {
207 switch (definition.getModelType()) {
208 case FIELD: {
209 IFieldDefinition field = (IFieldDefinition) definition;
210 if (field.getFlagInstances().isEmpty()) {
211 retval = getSimpleType(field);
212 } else {
213 retval = newComplexType(field);
214 }
215 break;
216 }
217 case ASSEMBLY: {
218 retval = newComplexType((IAssemblyDefinition) definition);
219 break;
220 }
221 case FLAG:
222 retval = getSimpleType((IFlagDefinition) definition);
223 break;
224 case CHOICE_GROUP:
225 case CHOICE:
226 throw new UnsupportedOperationException(definition.getModelType().toString());
227 }
228 assert retval != null : definition.getModelType();
229 definitionToTypeMap.put(definition, retval);
230 }
231 return retval;
232 }
233
234
235
236
237
238
239
240
241 @Override
242 @NonNull
243 public IXmlSimpleType getSimpleType(@NonNull IDataTypeAdapter<?> dataType) {
244 IXmlSimpleType type = dataTypeToSimpleTypeMap.get(dataType);
245 if (type == null) {
246
247 QName qname = newQName(
248 getDatatypeManager().getTypeNameForDatatype(dataType),
249 getDatatypeNS());
250 type = new XmlSimpleTypeDataTypeReference(qname, dataType);
251 dataTypeToSimpleTypeMap.put(dataType, type);
252 }
253 return type;
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267 @Override
268 @NonNull
269 public IXmlSimpleType getSimpleType(@NonNull IValuedDefinition definition) {
270 IXmlSimpleType simpleType = definitionToSimpleTypeMap.get(definition);
271 if (simpleType == null) {
272 AllowedValueCollection allowedValuesCollection = getContextIndependentEnumeratedValues(definition);
273 List<IAllowedValue> allowedValues = allowedValuesCollection.getValues();
274
275 IDataTypeAdapter<?> dataType = definition.getJavaTypeAdapter();
276 if (allowedValues.isEmpty()) {
277
278 simpleType = getSimpleType(dataType);
279 } else {
280
281
282 simpleType = new XmlSimpleTypeDataTypeRestriction(
283 newQName(definition, null),
284 definition,
285 allowedValuesCollection);
286
287 if (!allowedValuesCollection.isClosed()) {
288
289
290
291 simpleType = new XmlSimpleTypeUnion(
292 newQName(definition, "Union"),
293 definition,
294 getSimpleType(dataType),
295 simpleType);
296 }
297 }
298
299 definitionToSimpleTypeMap.put(definition, simpleType);
300 }
301 return simpleType;
302 }
303
304
305
306
307
308
309
310
311 @NonNull
312 protected IXmlComplexType newComplexType(@NonNull IFieldDefinition definition) {
313 QName qname = newQName(definition, null);
314 return new XmlComplexTypeFieldDefinition(qname, definition);
315 }
316
317
318
319
320
321
322
323
324 @NonNull
325 protected IXmlComplexType newComplexType(@NonNull IAssemblyDefinition definition) {
326 QName qname = newQName(definition, null);
327 return new XmlComplexTypeAssemblyDefinition(qname, definition);
328 }
329
330
331
332
333
334
335
336
337
338
339 public void generateXmlTypes() throws XMLStreamException {
340
341 for (IXmlType type : definitionToTypeMap.values()) {
342 if (!type.isInline(this) && type.isGeneratedType(this) && type.isReferenced(this)) {
343 type.generate(this);
344 } else {
345 assert !type.isGeneratedType(this) || type.isInline(this) || !type.isReferenced(this);
346 }
347 }
348 getDatatypeManager().generateDatatypes(getXMLStreamWriter());
349 }
350
351
352
353
354
355
356
357
358
359
360
361 @Override
362 public void writeAttribute(@NonNull String localName, @NonNull String value) throws XMLStreamException {
363 getXMLStreamWriter().writeAttribute(localName, value);
364 }
365
366
367
368
369
370
371
372
373
374
375
376 @Override
377 public void writeStartElement(@NonNull String namespaceUri, @NonNull String localName) throws XMLStreamException {
378 getXMLStreamWriter().writeStartElement(namespaceUri, localName);
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392
393 @Override
394 public void writeStartElement(
395 @NonNull String prefix,
396 @NonNull String localName,
397 @NonNull String namespaceUri) throws XMLStreamException {
398 getXMLStreamWriter().writeStartElement(prefix, localName, namespaceUri);
399 }
400
401
402
403
404
405
406
407 @Override
408 public void writeEndElement() throws XMLStreamException {
409 getXMLStreamWriter().writeEndElement();
410 }
411
412
413
414
415
416
417
418
419
420 @Override
421 public void writeCharacters(@NonNull String text) throws XMLStreamException {
422 getXMLStreamWriter().writeCharacters(text);
423 }
424
425
426
427
428
429
430
431
432
433
434
435 @Override
436 public void writeNamespace(String prefix, String namespaceUri) throws XMLStreamException {
437 getXMLStreamWriter().writeNamespace(prefix, namespaceUri);
438 }
439
440 @Override
441 public void flushWriter() throws IOException {
442 try {
443 getWriter().getResource().flush();
444 } catch (XMLStreamException ex) {
445 throw new IOException(ex);
446 }
447 }
448 }