1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.io.xml;
7
8 import gov.nist.secauto.metaschema.core.model.IBoundObject;
9 import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
10 import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonProblemHandler;
11 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModel;
12 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
13 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelComplex;
14 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelFieldComplex;
15 import gov.nist.secauto.metaschema.databind.model.IBoundFieldValue;
16 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
17 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModel;
18 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelAssembly;
19 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelChoiceGroup;
20 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelFieldComplex;
21 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelFieldScalar;
22 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedAssembly;
23 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedField;
24 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedNamed;
25 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelNamed;
26 import gov.nist.secauto.metaschema.databind.model.info.AbstractModelInstanceWriteHandler;
27 import gov.nist.secauto.metaschema.databind.model.info.IFeatureComplexItemValueHandler;
28 import gov.nist.secauto.metaschema.databind.model.info.IItemWriteHandler;
29 import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
30
31 import org.codehaus.stax2.XMLStreamWriter2;
32
33 import java.io.IOException;
34
35 import javax.xml.namespace.NamespaceContext;
36 import javax.xml.stream.XMLStreamException;
37
38 import edu.umd.cs.findbugs.annotations.NonNull;
39
40 public class MetaschemaXmlWriter implements IXmlWritingContext {
41 @NonNull
42 private final XMLStreamWriter2 writer;
43
44
45
46
47
48
49
50
51 public MetaschemaXmlWriter(
52 @NonNull XMLStreamWriter2 writer) {
53 this.writer = writer;
54 }
55
56 @Override
57 public XMLStreamWriter2 getWriter() {
58 return writer;
59 }
60
61
62
63
64
65 @Override
66 public void write(
67 @NonNull IBoundDefinitionModelComplex definition,
68 @NonNull IBoundObject item) throws IOException {
69
70 IEnhancedQName qname = definition.getQName();
71
72 definition.writeItem(item, new ItemWriter(qname));
73 }
74
75 @Override
76 public void writeRoot(
77 @NonNull IBoundDefinitionModelAssembly definition,
78 @NonNull IBoundObject item) throws IOException {
79 IEnhancedQName rootEQName = definition.getRootQName();
80 if (rootEQName == null) {
81 throw new IllegalArgumentException(
82 String.format("The assembly definition '%s' does not have a root QName.",
83 definition.getQName()));
84 }
85
86 definition.writeItem(item, new ItemWriter(rootEQName));
87 }
88
89
90
91
92
93 private <T> void writeModelInstance(
94 @NonNull IBoundInstanceModel<T> instance,
95 @NonNull Object parentItem,
96 @NonNull ItemWriter itemWriter) throws IOException {
97 Object value = instance.getValue(parentItem);
98 if (value == null) {
99 return;
100 }
101
102
103
104
105
106 IModelInstanceCollectionInfo<T> collectionInfo = instance.getCollectionInfo();
107 if (!collectionInfo.isEmpty(value)) {
108 IEnhancedQName currentQName = itemWriter.getObjectQName();
109 IEnhancedQName groupAsEQName = instance.getEffectiveXmlGroupAsQName();
110 try {
111 if (groupAsEQName != null) {
112
113 writer.writeStartElement(groupAsEQName.getNamespace(), groupAsEQName.getLocalName());
114 currentQName = groupAsEQName;
115 }
116
117 collectionInfo.writeItems(
118 new ModelInstanceWriteHandler<>(instance, new ItemWriter(currentQName)),
119 value);
120
121 if (groupAsEQName != null) {
122 writer.writeEndElement();
123 }
124 } catch (XMLStreamException ex) {
125 throw new IOException(ex);
126 }
127 }
128 }
129
130 private static class ModelInstanceWriteHandler<ITEM>
131 extends AbstractModelInstanceWriteHandler<ITEM> {
132 @NonNull
133 private final ItemWriter itemWriter;
134
135 public ModelInstanceWriteHandler(
136 @NonNull IBoundInstanceModel<ITEM> instance,
137 @NonNull ItemWriter itemWriter) {
138 super(instance);
139 this.itemWriter = itemWriter;
140 }
141
142 @Override
143 public void writeItem(ITEM item) throws IOException {
144 IBoundInstanceModel<ITEM> instance = getInstance();
145 instance.writeItem(item, itemWriter);
146 }
147 }
148
149 private class ItemWriter
150 extends AbstractItemWriter {
151
152 public ItemWriter(@NonNull IEnhancedQName qname) {
153 super(qname);
154 }
155
156 private <T extends IBoundInstanceModelNamed<IBoundObject> & IFeatureComplexItemValueHandler> void writeFlags(
157 @NonNull IBoundObject parentItem,
158 @NonNull T instance) throws IOException {
159 writeFlags(parentItem, instance.getDefinition());
160 }
161
162 private <T extends IBoundInstanceModelGroupedNamed & IFeatureComplexItemValueHandler> void writeFlags(
163 @NonNull IBoundObject parentItem,
164 @NonNull T instance) throws IOException {
165 writeFlags(parentItem, instance.getDefinition());
166 }
167
168 private void writeFlags(
169 @NonNull IBoundObject parentItem,
170 @NonNull IBoundDefinitionModel<?> definition) throws IOException {
171 for (IBoundInstanceFlag flag : definition.getFlagInstances()) {
172 assert flag != null;
173
174 Object value = flag.getValue(parentItem);
175 if (value != null) {
176 writeItemFlag(value, flag);
177 }
178 }
179 }
180
181 private <T extends IBoundInstanceModelAssembly & IFeatureComplexItemValueHandler> void writeAssemblyModel(
182 @NonNull IBoundObject parentItem,
183 @NonNull T instance) throws IOException {
184 writeAssemblyModel(parentItem, instance.getDefinition());
185 }
186
187 private <T extends IBoundInstanceModelGroupedAssembly & IFeatureComplexItemValueHandler> void writeAssemblyModel(
188 @NonNull IBoundObject parentItem,
189 @NonNull T instance) throws IOException {
190 writeAssemblyModel(parentItem, instance.getDefinition());
191 }
192
193 private void writeAssemblyModel(
194 @NonNull IBoundObject parentItem,
195 @NonNull IBoundDefinitionModelAssembly definition) throws IOException {
196 for (IBoundInstanceModel<?> modelInstance : definition.getModelInstances()) {
197 assert modelInstance != null;
198 writeModelInstance(modelInstance, parentItem, this);
199 }
200 }
201
202 private void writeFieldValue(
203 @NonNull IBoundObject parentItem,
204 @NonNull IBoundInstanceModelFieldComplex instance) throws IOException {
205 writeFieldValue(parentItem, instance.getDefinition());
206 }
207
208 private void writeFieldValue(
209 @NonNull IBoundObject parentItem,
210 @NonNull IBoundInstanceModelGroupedField instance) throws IOException {
211 writeFieldValue(parentItem, instance.getDefinition());
212 }
213
214 private void writeFieldValue(
215 @NonNull IBoundObject parentItem,
216 @NonNull IBoundDefinitionModelFieldComplex definition) throws IOException {
217 definition.getFieldValue().writeItem(parentItem, this);
218 }
219
220 private <T extends IFeatureComplexItemValueHandler & IBoundInstanceModelNamed<IBoundObject>> void writeModelObject(
221 @NonNull T instance,
222 @NonNull IBoundObject parentItem,
223 @NonNull ObjectWriter<T> propertyWriter) throws IOException {
224 try {
225 IEnhancedQName wrapperQName = instance.getQName();
226 writer.writeStartElement(wrapperQName.getNamespace(), wrapperQName.getLocalName());
227
228 propertyWriter.accept(parentItem, instance);
229
230 writer.writeEndElement();
231 } catch (XMLStreamException ex) {
232 throw new IOException(ex);
233 }
234 }
235
236 private <T extends IFeatureComplexItemValueHandler & IBoundInstanceModelGroupedNamed> void writeGroupedModelObject(
237 @NonNull T instance,
238 @NonNull IBoundObject parentItem,
239 @NonNull ObjectWriter<T> propertyWriter) throws IOException {
240 try {
241 IEnhancedQName wrapperQName = instance.getQName();
242 writer.writeStartElement(wrapperQName.getNamespace(), wrapperQName.getLocalName());
243
244 propertyWriter.accept(parentItem, instance);
245
246 writer.writeEndElement();
247 } catch (XMLStreamException ex) {
248 throw new IOException(ex);
249 }
250 }
251
252 private <T extends IFeatureComplexItemValueHandler & IBoundDefinitionModelComplex> void writeDefinitionObject(
253 @NonNull T definition,
254 @NonNull IBoundObject parentItem,
255 @NonNull ObjectWriter<T> propertyWriter) throws IOException {
256
257 try {
258 IEnhancedQName qname = getObjectQName();
259 NamespaceContext nsContext = writer.getNamespaceContext();
260 String prefix = nsContext.getPrefix(qname.getNamespace());
261 if (prefix == null) {
262 prefix = "";
263 }
264
265 writer.writeStartElement(prefix, qname.getLocalName(), qname.getNamespace());
266
267 propertyWriter.accept(parentItem, definition);
268
269 writer.writeEndElement();
270 } catch (XMLStreamException ex) {
271 throw new IOException(ex);
272 }
273 }
274
275 @Override
276 public void writeItemFlag(Object item, IBoundInstanceFlag instance) throws IOException {
277 String itemString;
278 try {
279 itemString = instance.getJavaTypeAdapter().asString(item);
280 } catch (IllegalArgumentException ex) {
281 throw new IOException(ex);
282 }
283 IEnhancedQName name = instance.getQName();
284 try {
285 if (name.getNamespace().isEmpty()) {
286 writer.writeAttribute(name.getLocalName(), itemString);
287 } else {
288 writer.writeAttribute(name.getNamespace(), name.getLocalName(), itemString);
289 }
290 } catch (XMLStreamException ex) {
291 throw new IOException(ex);
292 }
293 }
294
295 @Override
296 public void writeItemField(Object item, IBoundInstanceModelFieldScalar instance) throws IOException {
297 try {
298 if (instance.isEffectiveValueWrappedInXml()) {
299 IEnhancedQName wrapperQName = instance.getQName();
300 writer.writeStartElement(wrapperQName.getNamespace(), wrapperQName.getLocalName());
301 instance.getJavaTypeAdapter().writeXmlValue(item, wrapperQName, writer);
302 writer.writeEndElement();
303 } else {
304 instance.getJavaTypeAdapter().writeXmlValue(item, getObjectQName(), writer);
305 }
306 } catch (XMLStreamException ex) {
307 throw new IOException(ex);
308 }
309 }
310
311 @Override
312 public void writeItemField(IBoundObject item, IBoundInstanceModelFieldComplex instance) throws IOException {
313 ItemWriter itemWriter = new ItemWriter(instance.getQName());
314 writeModelObject(
315 instance,
316 item,
317 ((ObjectWriter<IBoundInstanceModelFieldComplex>) this::writeFlags)
318 .andThen(itemWriter::writeFieldValue));
319 }
320
321 @Override
322 public void writeItemField(IBoundObject item, IBoundInstanceModelGroupedField instance) throws IOException {
323 ItemWriter itemWriter = new ItemWriter(instance.getQName());
324 writeGroupedModelObject(
325 instance,
326 item,
327 ((ObjectWriter<IBoundInstanceModelGroupedField>) this::writeFlags)
328 .andThen(itemWriter::writeFieldValue));
329 }
330
331 @Override
332 public void writeItemField(IBoundObject item, IBoundDefinitionModelFieldComplex definition) throws IOException {
333 ItemWriter itemWriter = new ItemWriter(definition.getQName());
334 writeDefinitionObject(
335 definition,
336 item,
337 ((ObjectWriter<IBoundDefinitionModelFieldComplex>) this::writeFlags)
338 .andThen(itemWriter::writeFieldValue));
339 }
340
341 @Override
342 public void writeItemFieldValue(Object parentItem, IBoundFieldValue fieldValue) throws IOException {
343 Object item = fieldValue.getValue(parentItem);
344 if (item != null) {
345 fieldValue.getJavaTypeAdapter().writeXmlValue(item, getObjectQName(), writer);
346 }
347 }
348
349 @Override
350 public void writeItemAssembly(IBoundObject item, IBoundInstanceModelAssembly instance) throws IOException {
351 ItemWriter itemWriter = new ItemWriter(instance.getQName());
352 writeModelObject(
353 instance,
354 item,
355 ((ObjectWriter<IBoundInstanceModelAssembly>) this::writeFlags)
356 .andThen(itemWriter::writeAssemblyModel));
357 }
358
359 @Override
360 public void writeItemAssembly(IBoundObject item, IBoundInstanceModelGroupedAssembly instance) throws IOException {
361 ItemWriter itemWriter = new ItemWriter(instance.getQName());
362 writeGroupedModelObject(
363 instance,
364 item,
365 ((ObjectWriter<IBoundInstanceModelGroupedAssembly>) this::writeFlags)
366 .andThen(itemWriter::writeAssemblyModel));
367 }
368
369 @Override
370 public void writeItemAssembly(IBoundObject item, IBoundDefinitionModelAssembly definition) throws IOException {
371
372
373 writeDefinitionObject(
374 definition,
375 item,
376 ((ObjectWriter<IBoundDefinitionModelAssembly>) this::writeFlags)
377 .andThen(this::writeAssemblyModel));
378 }
379
380 @Override
381 public void writeChoiceGroupItem(IBoundObject item, IBoundInstanceModelChoiceGroup instance) throws IOException {
382 IBoundInstanceModelGroupedNamed actualInstance = instance.getItemInstance(item);
383 assert actualInstance != null;
384 actualInstance.writeItem(item, this);
385 }
386 }
387
388 private abstract static class AbstractItemWriter implements IItemWriteHandler {
389 @NonNull
390 private final IEnhancedQName objectQName;
391
392 protected AbstractItemWriter(@NonNull IEnhancedQName qname) {
393 this.objectQName = qname;
394 }
395
396
397
398
399
400
401 @NonNull
402 protected IEnhancedQName getObjectQName() {
403 return objectQName;
404 }
405 }
406 }