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