1 package ca.uhn.fhir.parser;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import static org.apache.commons.lang3.StringUtils.defaultString;
24 import static org.apache.commons.lang3.StringUtils.isBlank;
25 import static org.apache.commons.lang3.StringUtils.isNotBlank;
26
27 import java.io.IOException;
28 import java.io.Reader;
29 import java.io.StringWriter;
30 import java.io.Writer;
31 import java.util.Iterator;
32 import java.util.List;
33
34 import javax.xml.namespace.QName;
35 import javax.xml.stream.FactoryConfigurationError;
36 import javax.xml.stream.XMLEventReader;
37 import javax.xml.stream.XMLStreamConstants;
38 import javax.xml.stream.XMLStreamException;
39 import javax.xml.stream.XMLStreamWriter;
40 import javax.xml.stream.events.Attribute;
41 import javax.xml.stream.events.Characters;
42 import javax.xml.stream.events.Comment;
43 import javax.xml.stream.events.EntityReference;
44 import javax.xml.stream.events.Namespace;
45 import javax.xml.stream.events.StartElement;
46 import javax.xml.stream.events.XMLEvent;
47
48 import org.apache.commons.lang3.StringUtils;
49
50 import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
51 import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
52 import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
53 import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
54 import ca.uhn.fhir.context.ConfigurationException;
55 import ca.uhn.fhir.context.FhirContext;
56 import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
57 import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
58 import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
59 import ca.uhn.fhir.context.RuntimeResourceDefinition;
60 import ca.uhn.fhir.model.api.Bundle;
61 import ca.uhn.fhir.model.api.BundleEntry;
62 import ca.uhn.fhir.model.api.ExtensionDt;
63 import ca.uhn.fhir.model.api.IElement;
64 import ca.uhn.fhir.model.api.IPrimitiveDatatype;
65 import ca.uhn.fhir.model.api.IResource;
66 import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
67 import ca.uhn.fhir.model.api.Tag;
68 import ca.uhn.fhir.model.api.TagList;
69 import ca.uhn.fhir.model.dstu.composite.ContainedDt;
70 import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
71 import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
72 import ca.uhn.fhir.model.dstu.resource.Binary;
73 import ca.uhn.fhir.model.primitive.IdDt;
74 import ca.uhn.fhir.model.primitive.InstantDt;
75 import ca.uhn.fhir.model.primitive.StringDt;
76 import ca.uhn.fhir.model.primitive.XhtmlDt;
77 import ca.uhn.fhir.narrative.INarrativeGenerator;
78 import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
79 import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
80 import ca.uhn.fhir.util.XmlUtil;
81
82 public class XmlParser extends BaseParser implements IParser {
83 static final String RESREF_DISPLAY = "display";
84 static final String RESREF_REFERENCE = "reference";
85 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParser.class);
86 static final String ATOM_NS = "http://www.w3.org/2005/Atom";
87 static final String FHIR_NS = "http://hl7.org/fhir";
88 static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/";
89 static final String TOMBSTONES_NS = "http://purl.org/atompub/tombstones/1.0";
90 static final String XHTML_NS = "http://www.w3.org/1999/xhtml";
91
92
93
94 private FhirContext myContext;
95 private boolean myPrettyPrint;
96
97 public XmlParser(FhirContext theContext) {
98 super(theContext);
99 myContext = theContext;
100 }
101
102 @Override
103 public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
104 StringWriter stringWriter = new StringWriter();
105 encodeBundleToWriter(theBundle, stringWriter);
106
107 return stringWriter.toString();
108 }
109
110 @Override
111 public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
112 try {
113 XMLStreamWriter eventWriter = createXmlWriter(theWriter);
114
115 eventWriter.writeStartElement("feed");
116 eventWriter.writeDefaultNamespace(ATOM_NS);
117
118 writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
119 writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
120
121 writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
122 writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
123 writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
124 writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
125 writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
126 writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
127
128 if (theBundle.getTotalResults().getValue() != null) {
129 eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS);
130 eventWriter.writeNamespace("os", OPENSEARCH_NS);
131 eventWriter.writeCharacters(theBundle.getTotalResults().getValue().toString());
132 eventWriter.writeEndElement();
133 }
134
135 writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated());
136 writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
137
138 if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
139 eventWriter.writeStartElement("author");
140 writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
141 writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
142 eventWriter.writeEndElement();
143 }
144
145 writeCategories(eventWriter, theBundle.getCategories());
146
147 for (BundleEntry nextEntry : theBundle.getEntries()) {
148 boolean deleted = false;
149 if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false) {
150 deleted = true;
151 eventWriter.writeStartElement("at", "deleted-entry", TOMBSTONES_NS);
152 eventWriter.writeNamespace("at", TOMBSTONES_NS);
153 eventWriter.writeAttribute("ref", nextEntry.getId().getValueAsString());
154 eventWriter.writeAttribute("when", nextEntry.getDeletedAt().getValueAsString());
155 if (nextEntry.getDeletedByEmail().isEmpty() == false || nextEntry.getDeletedByName().isEmpty() == false) {
156 eventWriter.writeStartElement(TOMBSTONES_NS, "by");
157 if (nextEntry.getDeletedByName().isEmpty() == false) {
158 eventWriter.writeStartElement(TOMBSTONES_NS, "name");
159 eventWriter.writeCharacters(nextEntry.getDeletedByName().getValue());
160 eventWriter.writeEndElement();
161 }
162 if (nextEntry.getDeletedByEmail().isEmpty() == false) {
163 eventWriter.writeStartElement(TOMBSTONES_NS, "email");
164 eventWriter.writeCharacters(nextEntry.getDeletedByEmail().getValue());
165 eventWriter.writeEndElement();
166 }
167 eventWriter.writeEndElement();
168 }
169 if (nextEntry.getDeletedComment().isEmpty() == false) {
170 eventWriter.writeStartElement(TOMBSTONES_NS, "comment");
171 eventWriter.writeCharacters(nextEntry.getDeletedComment().getValue());
172 eventWriter.writeEndElement();
173 }
174 } else {
175 eventWriter.writeStartElement("entry");
176 }
177
178 writeOptionalTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
179 if (!deleted) {
180 writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
181 }
182 writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
183 writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
184
185 writeCategories(eventWriter, nextEntry.getCategories());
186
187 if (!nextEntry.getLinkSelf().isEmpty()) {
188 writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
189 }
190
191 if (!nextEntry.getLinkAlternate().isEmpty()) {
192 writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate());
193 }
194
195 if (!nextEntry.getLinkSearch().isEmpty()) {
196 writeAtomLink(eventWriter, "search", nextEntry.getLinkSearch());
197 }
198
199 IResource resource = nextEntry.getResource();
200 if (resource != null && !resource.isEmpty() && !deleted) {
201 eventWriter.writeStartElement("content");
202 eventWriter.writeAttribute("type", "text/xml");
203 encodeResourceToXmlStreamWriter(resource, eventWriter, false);
204 eventWriter.writeEndElement();
205 } else {
206 ourLog.debug("Bundle entry contains null resource");
207 }
208
209 if (!nextEntry.getSummary().isEmpty()) {
210 eventWriter.writeStartElement("summary");
211 eventWriter.writeAttribute("type", "xhtml");
212 encodeXhtml(nextEntry.getSummary(), eventWriter);
213 eventWriter.writeEndElement();
214 }
215
216 eventWriter.writeEndElement();
217 }
218
219 eventWriter.writeEndElement();
220 eventWriter.close();
221 } catch (XMLStreamException e) {
222 throw new ConfigurationException("Failed to initialize STaX event factory", e);
223 }
224 }
225
226 private void writeCategories(XMLStreamWriter eventWriter, TagList categories) throws XMLStreamException {
227 if (categories != null) {
228 for (Tag next : categories) {
229 eventWriter.writeStartElement("category");
230 eventWriter.writeAttribute("term", defaultString(next.getTerm()));
231 eventWriter.writeAttribute("label", defaultString(next.getLabel()));
232 eventWriter.writeAttribute("scheme", defaultString(next.getScheme()));
233 eventWriter.writeEndElement();
234 }
235 }
236 }
237
238 @Override
239 public String encodeResourceToString(IResource theResource) throws DataFormatException {
240 if (theResource == null) {
241 throw new NullPointerException("Resource can not be null");
242 }
243
244 Writer stringWriter = new StringWriter();
245 encodeResourceToWriter(theResource, stringWriter);
246 return stringWriter.toString();
247 }
248
249 @Override
250 public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws DataFormatException {
251 XMLStreamWriter eventWriter;
252 try {
253 eventWriter = createXmlWriter(theWriter);
254
255 encodeResourceToXmlStreamWriter(theResource, eventWriter, false);
256 eventWriter.flush();
257 } catch (XMLStreamException e) {
258 throw new ConfigurationException("Failed to initialize STaX event factory", e);
259 }
260 }
261
262 private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException {
263 XMLStreamWriter eventWriter;
264 eventWriter = XmlUtil.createXmlStreamWriter(theWriter);
265 eventWriter = decorateStreamWriter(eventWriter);
266 return eventWriter;
267 }
268
269 @Override
270 public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
271 try {
272 XMLStreamWriter eventWriter = createXmlWriter(theWriter);
273
274 eventWriter.writeStartElement(TagList.ELEMENT_NAME_LC);
275 eventWriter.writeDefaultNamespace(FHIR_NS);
276
277 for (Tag next : theTagList) {
278 eventWriter.writeStartElement(TagList.ATTR_CATEGORY);
279
280 if (isNotBlank(next.getTerm())) {
281 eventWriter.writeAttribute(Tag.ATTR_TERM, next.getTerm());
282 }
283 if (isNotBlank(next.getLabel())) {
284 eventWriter.writeAttribute(Tag.ATTR_LABEL, next.getLabel());
285 }
286 if (isNotBlank(next.getScheme())) {
287 eventWriter.writeAttribute(Tag.ATTR_SCHEME, next.getScheme());
288 }
289
290 eventWriter.writeEndElement();
291 }
292
293 eventWriter.writeEndElement();
294 eventWriter.close();
295 } catch (XMLStreamException e) {
296 throw new ConfigurationException("Failed to initialize STaX event factory", e);
297 }
298 }
299
300 @Override
301 public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
302 XMLEventReader streamReader = createStreamReader(theReader);
303
304 return parseBundle(streamReader, theResourceType);
305 }
306
307 @Override
308 public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
309 XMLEventReader streamReader = createStreamReader(theReader);
310
311 return parseResource(theResourceType, streamReader);
312 }
313
314 @Override
315 public TagList parseTagList(Reader theReader) {
316 XMLEventReader streamReader = createStreamReader(theReader);
317
318 ParserState<TagList> parserState = ParserState.getPreTagListInstance(myContext, false);
319 return doXmlLoop(streamReader, parserState);
320 }
321
322 @Override
323 public IParser setPrettyPrint(boolean thePrettyPrint) {
324 myPrettyPrint = thePrettyPrint;
325 return this;
326 }
327
328 private XMLEventReader createStreamReader(Reader theReader) {
329 try {
330 return XmlUtil.createXmlReader(theReader);
331 } catch (FactoryConfigurationError e1) {
332 throw new ConfigurationException("Failed to initialize STaX event factory", e1);
333 } catch (XMLStreamException e1) {
334 throw new DataFormatException(e1);
335 }
336
337
338
339
340
341
342
343
344
345
346 }
347
348 private XMLStreamWriter decorateStreamWriter(XMLStreamWriter eventWriter) {
349 if (myPrettyPrint) {
350 PrettyPrintWriterWrapper retVal = new PrettyPrintWriterWrapper(eventWriter);
351 return retVal;
352 } else {
353 NonPrettyPrintWriterWrapper retVal = new NonPrettyPrintWriterWrapper(eventWriter);
354 return retVal;
355 }
356 }
357
358 private <T> T doXmlLoop(XMLEventReader streamReader, ParserState<T> parserState) {
359 ourLog.trace("Entering XML parsing loop with state: {}", parserState);
360
361 try {
362 while (streamReader.hasNext()) {
363 XMLEvent nextEvent = streamReader.nextEvent();
364 try {
365 if (nextEvent.isStartElement()) {
366 StartElement elem = nextEvent.asStartElement();
367
368 String namespaceURI = elem.getName().getNamespaceURI();
369
370 if ("extension".equals(elem.getName().getLocalPart())) {
371 Attribute urlAttr = elem.getAttributeByName(new QName("url"));
372 if (urlAttr == null || isBlank(urlAttr.getValue())) {
373 throw new DataFormatException("Extension element has no 'url' attribute");
374 }
375 parserState.enteringNewElementExtension(elem, urlAttr.getValue(), false);
376 } else if ("modifierExtension".equals(elem.getName().getLocalPart())) {
377 Attribute urlAttr = elem.getAttributeByName(new QName("url"));
378 if (urlAttr == null || isBlank(urlAttr.getValue())) {
379 throw new DataFormatException("Extension element has no 'url' attribute");
380 }
381 parserState.enteringNewElementExtension(elem, urlAttr.getValue(), true);
382
383 } else {
384
385 String elementName = elem.getName().getLocalPart();
386 parserState.enteringNewElement(namespaceURI, elementName);
387
388 }
389
390 for (@SuppressWarnings("unchecked")
391 Iterator<Attribute> iter = elem.getAttributes(); iter.hasNext();) {
392 Attribute next = iter.next();
393
394
395 parserState.attributeValue(next.getName().getLocalPart(), next.getValue());
396
397 }
398
399 } else if (nextEvent.isAttribute()) {
400 Attribute elem = (Attribute) nextEvent;
401 String name = (elem.getName().getLocalPart());
402 parserState.attributeValue(name, elem.getValue());
403 } else if (nextEvent.isEndElement()) {
404 parserState.endingElement();
405 if (parserState.isComplete()) {
406 return parserState.getObject();
407 }
408 } else if (nextEvent.isCharacters()) {
409 parserState.string(nextEvent.asCharacters().getData());
410 }
411
412 parserState.xmlEvent(nextEvent);
413
414 } catch (DataFormatException e) {
415 throw new DataFormatException("DataFormatException at [" + nextEvent.getLocation().toString() + "]: " + e.getMessage(), e);
416 }
417 }
418 return null;
419 } catch (XMLStreamException e) {
420 throw new DataFormatException(e);
421 }
422 }
423
424 private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theEventWriter, IElement nextValue, String childName,
425 BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
426 if (nextValue.isEmpty()) {
427 if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty()==false && theIncludedResource == false) {
428
429 } else {
430 return;
431 }
432 }
433
434 switch (childDef.getChildType()) {
435 case PRIMITIVE_DATATYPE: {
436 IPrimitiveDatatype<?> pd = (IPrimitiveDatatype<?>) nextValue;
437 String value = pd.getValueAsString();
438 if (value != null) {
439 theEventWriter.writeStartElement(childName);
440 theEventWriter.writeAttribute("value", value);
441 encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, nextValue, theIncludedResource);
442 theEventWriter.writeEndElement();
443 }
444 break;
445 }
446 case RESOURCE_BLOCK:
447 case COMPOSITE_DATATYPE: {
448 theEventWriter.writeStartElement(childName);
449 if (isNotBlank(theExtensionUrl)) {
450 theEventWriter.writeAttribute("url", theExtensionUrl);
451 }
452 BaseRuntimeElementCompositeDefinition<?> childCompositeDef = (BaseRuntimeElementCompositeDefinition<?>) childDef;
453 encodeCompositeElementToStreamWriter(theResDef, theResource, nextValue, theEventWriter, childCompositeDef, theIncludedResource);
454 theEventWriter.writeEndElement();
455 break;
456 }
457 case RESOURCE_REF: {
458 ResourceReferenceDt ref = (ResourceReferenceDt) nextValue;
459 if (!ref.isEmpty()) {
460 theEventWriter.writeStartElement(childName);
461 encodeResourceReferenceToStreamWriter(theEventWriter, ref);
462 theEventWriter.writeEndElement();
463 }
464 break;
465 }
466 case CONTAINED_RESOURCES: {
467 ContainedDt value = (ContainedDt) nextValue;
468 theEventWriter.writeStartElement("contained");
469 for (IResource next : value.getContainedResources()) {
470 if (getContainedResources().getResourceId(next)!=null) {
471 continue;
472 }
473 encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue()));
474 }
475 for (IResource next : getContainedResources().getContainedResources()) {
476 IdDt resourceId = getContainedResources().getResourceId(next);
477 encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(resourceId.getValue()));
478 }
479 theEventWriter.writeEndElement();
480 break;
481 }
482 case RESOURCE: {
483 throw new IllegalStateException();
484 }
485 case PRIMITIVE_XHTML: {
486 XhtmlDt dt = (XhtmlDt) nextValue;
487 if (dt.hasContent()) {
488 encodeXhtml(dt, theEventWriter);
489 }
490 break;
491 }
492 case EXTENSION_DECLARED:
493 case UNDECL_EXT: {
494 throw new IllegalStateException("state should not happen: " + childDef.getName());
495 }
496 }
497
498 }
499
500
501 private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter,
502 List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
503 for (BaseRuntimeChildDefinition nextChild : children) {
504 if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) {
505 INarrativeGenerator gen = myContext.getNarrativeGenerator();
506 NarrativeDt narr = theResource.getText();
507 if (gen != null && narr.isEmpty()) {
508 narr = gen.generateNarrative(theResDef.getResourceProfile(), theResource);
509 }
510 if (narr != null) {
511 RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
512 String childName = nextChild.getChildNameByDatatype(child.getDatatype());
513 BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
514 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, childName, type, null, theIncludedResource);
515 continue;
516 }
517 }
518
519 List<? extends IElement> values = nextChild.getAccessor().getValues(theElement);
520 if (values == null || values.isEmpty()) {
521 continue;
522 }
523
524 for (IElement nextValue : values) {
525 if ((nextValue == null || nextValue.isEmpty()) && !(nextValue instanceof ContainedDt)) {
526 continue;
527 }
528 Class<? extends IElement> type = nextValue.getClass();
529 String childName = nextChild.getChildNameByDatatype(type);
530 String extensionUrl = nextChild.getExtensionUrl();
531 BaseRuntimeElementDefinition<?> childDef = nextChild.getChildElementDefinitionByDatatype(type);
532 if (childDef == null) {
533 super.throwExceptionForUnknownChildType(nextChild, type);
534 }
535
536 if (nextValue instanceof ExtensionDt) {
537
538 extensionUrl = ((ExtensionDt) nextValue).getUrlAsString();
539 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource);
540
541 } else if (extensionUrl != null && childName.equals("extension") == false) {
542 RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild;
543 if (extDef.isModifier()) {
544 theEventWriter.writeStartElement("modifierExtension");
545 } else {
546 theEventWriter.writeStartElement("extension");
547 }
548
549 theEventWriter.writeAttribute("url", extensionUrl);
550 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, null, theIncludedResource);
551 theEventWriter.writeEndElement();
552 } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theIncludedResource) {
553
554 } else {
555 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource);
556 }
557 }
558 }
559 }
560
561 private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter,
562 BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
563 encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement, theIncludedResource);
564 encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource);
565 encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIncludedResource);
566 }
567
568 private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, IElement theElement, boolean theIncludedResource)
569 throws XMLStreamException, DataFormatException {
570 if (theElement instanceof ISupportsUndeclaredExtensions) {
571 ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement;
572 encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredExtensions(), "extension", theIncludedResource);
573 encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredModifierExtensions(), "modifierExtension", theIncludedResource);
574 }
575 }
576
577 private void encodeResourceReferenceToStreamWriter(XMLStreamWriter theEventWriter, ResourceReferenceDt theRef) throws XMLStreamException {
578 String reference = determineReferenceText(theRef);
579
580 if (StringUtils.isNotBlank(reference)) {
581 theEventWriter.writeStartElement(RESREF_REFERENCE);
582 theEventWriter.writeAttribute("value", reference);
583 theEventWriter.writeEndElement();
584 }
585 if (!(theRef.getDisplay().isEmpty())) {
586 theEventWriter.writeStartElement(RESREF_DISPLAY);
587 theEventWriter.writeAttribute("value", theRef.getDisplay().getValue());
588 theEventWriter.writeEndElement();
589 }
590 }
591
592
593 private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
594 String resourceId = null;
595 if (theIncludedResource && StringUtils.isNotBlank(theResource.getId().getValue())) {
596 resourceId = theResource.getId().getValue();
597 }
598
599 encodeResourceToXmlStreamWriter(theResource, theEventWriter, theIncludedResource, resourceId);
600 }
601
602 private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource, String theResourceId) throws XMLStreamException {
603 if (!theIncludedResource) {
604 super.containResourcesForEncoding(theResource);
605 }
606
607 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
608 if (resDef == null) {
609 throw new ConfigurationException("Unknown resource type: " + theResource.getClass());
610 }
611
612 theEventWriter.writeStartElement(resDef.getName());
613 theEventWriter.writeDefaultNamespace(FHIR_NS);
614
615 if (theResourceId != null) {
616 theEventWriter.writeAttribute("id", theResourceId);
617 }
618
619 if (theResource instanceof Binary) {
620 Binary bin = (Binary) theResource;
621 theEventWriter.writeAttribute("contentType", bin.getContentType());
622 theEventWriter.writeCharacters(bin.getContentAsBase64());
623 } else {
624 encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theIncludedResource);
625 }
626
627 theEventWriter.writeEndElement();
628 }
629
630 private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName,
631 boolean theIncludedResource) throws XMLStreamException, DataFormatException {
632 for (ExtensionDt next : extensions) {
633 theWriter.writeStartElement(tagName);
634 theWriter.writeAttribute("url", next.getUrl().getValue());
635
636 if (next.getValue() != null) {
637 IElement nextValue = next.getValue();
638 RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
639 String childName = extDef.getChildNameByDatatype(nextValue.getClass());
640 if (childName == null) {
641 throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + nextValue.getClass().getCanonicalName());
642 }
643 BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(nextValue.getClass());
644 encodeChildElementToStreamWriter(theResDef, theResource, theWriter, nextValue, childName, childDef, null, theIncludedResource);
645 }
646
647
648 encodeExtensionsIfPresent(theResDef, theResource, theWriter, next, theIncludedResource);
649
650 theWriter.writeEndElement();
651 }
652 }
653
654 private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
655 if (theDt == null || theDt.getValue() == null || getSuppressNarratives()) {
656 return;
657 }
658
659 boolean firstElement = true;
660 for (XMLEvent event : theDt.getValue()) {
661 switch (event.getEventType()) {
662 case XMLStreamConstants.ATTRIBUTE:
663 Attribute attr = (Attribute) event;
664 if (isBlank(attr.getName().getPrefix())) {
665 if (isBlank(attr.getName().getNamespaceURI())) {
666 theEventWriter.writeAttribute(attr.getName().getLocalPart(), attr.getValue());
667 } else {
668 theEventWriter.writeAttribute(attr.getName().getNamespaceURI(), attr.getName().getLocalPart(), attr.getValue());
669 }
670 } else {
671 theEventWriter.writeAttribute(attr.getName().getPrefix(), attr.getName().getNamespaceURI(), attr.getName().getLocalPart(), attr.getValue());
672 }
673
674 break;
675 case XMLStreamConstants.CDATA:
676 theEventWriter.writeCData(((Characters) event).getData());
677 break;
678 case XMLStreamConstants.CHARACTERS:
679 case XMLStreamConstants.SPACE:
680 String data = ((Characters) event).getData();
681 theEventWriter.writeCharacters(data);
682 break;
683 case XMLStreamConstants.COMMENT:
684 theEventWriter.writeComment(((Comment) event).getText());
685 break;
686 case XMLStreamConstants.END_ELEMENT:
687 theEventWriter.writeEndElement();
688 break;
689 case XMLStreamConstants.ENTITY_REFERENCE:
690 EntityReference er = (EntityReference) event;
691 theEventWriter.writeEntityRef(er.getName());
692 break;
693 case XMLStreamConstants.NAMESPACE:
694 Namespace ns = (Namespace) event;
695 theEventWriter.writeNamespace(ns.getPrefix(), ns.getNamespaceURI());
696 break;
697 case XMLStreamConstants.START_ELEMENT:
698 StartElement se = event.asStartElement();
699 if (firstElement) {
700 if (StringUtils.isBlank(se.getName().getPrefix())) {
701 String namespaceURI = se.getName().getNamespaceURI();
702 if (StringUtils.isBlank(namespaceURI)) {
703 namespaceURI = "http://www.w3.org/1999/xhtml";
704 }
705 theEventWriter.writeStartElement(se.getName().getLocalPart());
706 theEventWriter.writeDefaultNamespace(namespaceURI);
707 } else {
708 String prefix = se.getName().getPrefix();
709 String namespaceURI = se.getName().getNamespaceURI();
710 theEventWriter.writeStartElement(prefix, se.getName().getLocalPart(), namespaceURI);
711 theEventWriter.writeNamespace(prefix, namespaceURI);
712 }
713 firstElement = false;
714 } else {
715 if (isBlank(se.getName().getPrefix())) {
716 if (isBlank(se.getName().getNamespaceURI())) {
717 theEventWriter.writeStartElement(se.getName().getLocalPart());
718 } else {
719 if (StringUtils.isBlank(se.getName().getPrefix())) {
720 theEventWriter.writeStartElement(se.getName().getLocalPart());
721
722 } else {
723 theEventWriter.writeStartElement(se.getName().getNamespaceURI(), se.getName().getLocalPart());
724 }
725 }
726 } else {
727 theEventWriter.writeStartElement(se.getName().getPrefix(), se.getName().getLocalPart(), se.getName().getNamespaceURI());
728 }
729 for (Iterator<?> attrIter = se.getAttributes(); attrIter.hasNext();) {
730 Attribute next = (Attribute) attrIter.next();
731 theEventWriter.writeAttribute(next.getName().getLocalPart(), next.getValue());
732 }
733 }
734 break;
735 case XMLStreamConstants.DTD:
736 case XMLStreamConstants.END_DOCUMENT:
737 case XMLStreamConstants.ENTITY_DECLARATION:
738 case XMLStreamConstants.NOTATION_DECLARATION:
739 case XMLStreamConstants.PROCESSING_INSTRUCTION:
740 case XMLStreamConstants.START_DOCUMENT:
741 break;
742 }
743
744 }
745 }
746
747 private Bundle parseBundle(XMLEventReader theStreamReader, Class<? extends IResource> theResourceType) {
748 ParserState<Bundle> parserState = ParserState.getPreAtomInstance(myContext, theResourceType, false);
749 return doXmlLoop(theStreamReader, parserState);
750 }
751
752 private <T extends IResource> T parseResource(Class<T> theResourceType, XMLEventReader theStreamReader) {
753 ParserState<T> parserState = ParserState.getPreResourceInstance(theResourceType, myContext, false);
754 return doXmlLoop(theStreamReader, parserState);
755 }
756
757 private void writeAtomLink(XMLStreamWriter theEventWriter, String theRel, StringDt theStringDt) throws XMLStreamException {
758 if (StringUtils.isNotBlank(theStringDt.getValue())) {
759 theEventWriter.writeStartElement("link");
760 theEventWriter.writeAttribute("rel", theRel);
761 theEventWriter.writeAttribute("href", theStringDt.getValue());
762 theEventWriter.writeEndElement();
763 }
764 }
765
766 private void writeOptionalTagWithTextNode(XMLStreamWriter theEventWriter, String theTagName, InstantDt theInstantDt) throws XMLStreamException {
767 if (theInstantDt.getValue() != null) {
768 theEventWriter.writeStartElement(theTagName);
769 theEventWriter.writeCharacters(theInstantDt.getValueAsString());
770 theEventWriter.writeEndElement();
771 }
772 }
773
774 private void writeOptionalTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, StringDt theTextValue) throws XMLStreamException {
775 if (StringUtils.isNotBlank(theTextValue.getValue())) {
776 theEventWriter.writeStartElement(theElementName);
777 theEventWriter.writeCharacters(theTextValue.getValue());
778 theEventWriter.writeEndElement();
779 }
780 }
781
782 private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, IdDt theIdDt) throws XMLStreamException {
783 theEventWriter.writeStartElement(theElementName);
784 if (StringUtils.isNotBlank(theIdDt.getValue())) {
785 theEventWriter.writeCharacters(theIdDt.getValue());
786 }
787 theEventWriter.writeEndElement();
788 }
789
790 private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, StringDt theStringDt) throws XMLStreamException {
791 theEventWriter.writeStartElement(theElementName);
792 if (StringUtils.isNotBlank(theStringDt.getValue())) {
793 theEventWriter.writeCharacters(theStringDt.getValue());
794 }
795 theEventWriter.writeEndElement();
796 }
797 }