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.getParentQName();
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 parentQName) {
147 super(parentQName);
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 instance,
248 @NonNull IBoundObject parentItem,
249 @NonNull ObjectWriter<T> propertyWriter) throws IOException {
250
251 try {
252 QName wrapperQName = instance.getXmlQName();
253 NamespaceContext nsContext = writer.getNamespaceContext();
254 String prefix = nsContext.getPrefix(wrapperQName.getNamespaceURI());
255 if (prefix == null) {
256 prefix = "";
257 }
258
259 writer.writeStartElement(prefix, wrapperQName.getLocalPart(), wrapperQName.getNamespaceURI());
260
261 propertyWriter.accept(parentItem, instance);
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 = instance.getJavaTypeAdapter().asString(item);
272 QName name = instance.getXmlQName();
273 try {
274 if (name.getNamespaceURI().isEmpty()) {
275 writer.writeAttribute(name.getLocalPart(), itemString);
276 } else {
277 writer.writeAttribute(name.getNamespaceURI(), name.getLocalPart(), itemString);
278 }
279 } catch (XMLStreamException ex) {
280 throw new IOException(ex);
281 }
282 }
283
284 @Override
285 public void writeItemField(Object item, IBoundInstanceModelFieldScalar instance) throws IOException {
286 try {
287 if (instance.isEffectiveValueWrappedInXml()) {
288 QName wrapperQName = instance.getXmlQName();
289 writer.writeStartElement(wrapperQName.getNamespaceURI(), wrapperQName.getLocalPart());
290 instance.getJavaTypeAdapter().writeXmlValue(item, wrapperQName, writer);
291 writer.writeEndElement();
292 } else {
293 instance.getJavaTypeAdapter().writeXmlValue(item, getParentQName(), writer);
294 }
295 } catch (XMLStreamException ex) {
296 throw new IOException(ex);
297 }
298 }
299
300 @Override
301 public void writeItemField(IBoundObject item, IBoundInstanceModelFieldComplex instance) throws IOException {
302 ItemWriter itemWriter = new ItemWriter(instance.getXmlQName());
303 writeModelObject(
304 instance,
305 item,
306 ((ObjectWriter<IBoundInstanceModelFieldComplex>) this::writeFlags)
307 .andThen(itemWriter::writeFieldValue));
308 }
309
310 @Override
311 public void writeItemField(IBoundObject item, IBoundInstanceModelGroupedField instance) throws IOException {
312 ItemWriter itemWriter = new ItemWriter(instance.getXmlQName());
313 writeGroupedModelObject(
314 instance,
315 item,
316 ((ObjectWriter<IBoundInstanceModelGroupedField>) this::writeFlags)
317 .andThen(itemWriter::writeFieldValue));
318 }
319
320 @Override
321 public void writeItemField(IBoundObject item, IBoundDefinitionModelFieldComplex definition) throws IOException {
322 ItemWriter itemWriter = new ItemWriter(definition.getXmlQName());
323 writeDefinitionObject(
324 definition,
325 item,
326 ((ObjectWriter<IBoundDefinitionModelFieldComplex>) this::writeFlags)
327 .andThen(itemWriter::writeFieldValue));
328 }
329
330 @Override
331 public void writeItemFieldValue(Object parentItem, IBoundFieldValue fieldValue) throws IOException {
332 Object item = fieldValue.getValue(parentItem);
333 if (item != null) {
334 try {
335 fieldValue.getJavaTypeAdapter().writeXmlValue(item, getParentQName(), writer);
336 } catch (XMLStreamException ex) {
337 throw new IOException(ex);
338 }
339 }
340 }
341
342 @Override
343 public void writeItemAssembly(IBoundObject item, IBoundInstanceModelAssembly instance) throws IOException {
344 ItemWriter itemWriter = new ItemWriter(instance.getXmlQName());
345 writeModelObject(
346 instance,
347 item,
348 ((ObjectWriter<IBoundInstanceModelAssembly>) this::writeFlags)
349 .andThen(itemWriter::writeAssemblyModel));
350 }
351
352 @Override
353 public void writeItemAssembly(IBoundObject item, IBoundInstanceModelGroupedAssembly instance) throws IOException {
354 ItemWriter itemWriter = new ItemWriter(instance.getXmlQName());
355 writeGroupedModelObject(
356 instance,
357 item,
358 ((ObjectWriter<IBoundInstanceModelGroupedAssembly>) this::writeFlags)
359 .andThen(itemWriter::writeAssemblyModel));
360 }
361
362 @Override
363 public void writeItemAssembly(IBoundObject item, IBoundDefinitionModelAssembly definition) throws IOException {
364 ItemWriter itemWriter = new ItemWriter(definition.getXmlQName());
365 writeDefinitionObject(
366 definition,
367 item,
368 ((ObjectWriter<IBoundDefinitionModelAssembly>) this::writeFlags)
369 .andThen(itemWriter::writeAssemblyModel));
370 }
371
372 @Override
373 public void writeChoiceGroupItem(IBoundObject item, IBoundInstanceModelChoiceGroup instance) throws IOException {
374 IBoundInstanceModelGroupedNamed actualInstance = instance.getItemInstance(item);
375 assert actualInstance != null;
376 actualInstance.writeItem(item, this);
377 }
378 }
379
380 private abstract static class AbstractItemWriter implements IItemWriteHandler {
381 @NonNull
382 private final QName parentQName;
383
384 protected AbstractItemWriter(@NonNull QName parentQName) {
385 this.parentQName = parentQName;
386 }
387
388
389
390
391
392
393 @NonNull
394 protected QName getParentQName() {
395 return parentQName;
396 }
397 }
398 }