001package ca.uhn.fhir.parser; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2017 University Health Network 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022import static org.apache.commons.lang3.StringUtils.defaultString; 023import static org.apache.commons.lang3.StringUtils.isBlank; 024import static org.apache.commons.lang3.StringUtils.isNotBlank; 025 026import java.io.IOException; 027import java.io.Reader; 028import java.io.StringWriter; 029import java.io.Writer; 030import java.util.ArrayList; 031import java.util.Iterator; 032import java.util.List; 033 034import javax.xml.namespace.QName; 035import javax.xml.stream.FactoryConfigurationError; 036import javax.xml.stream.XMLEventReader; 037import javax.xml.stream.XMLStreamConstants; 038import javax.xml.stream.XMLStreamException; 039import javax.xml.stream.XMLStreamWriter; 040import javax.xml.stream.events.Attribute; 041import javax.xml.stream.events.Characters; 042import javax.xml.stream.events.Comment; 043import javax.xml.stream.events.EntityReference; 044import javax.xml.stream.events.Namespace; 045import javax.xml.stream.events.StartElement; 046import javax.xml.stream.events.XMLEvent; 047 048import org.apache.commons.lang3.StringUtils; 049import org.hl7.fhir.instance.model.api.IAnyResource; 050import org.hl7.fhir.instance.model.api.IBase; 051import org.hl7.fhir.instance.model.api.IBaseBinary; 052import org.hl7.fhir.instance.model.api.IBaseDatatype; 053import org.hl7.fhir.instance.model.api.IBaseExtension; 054import org.hl7.fhir.instance.model.api.IBaseHasExtensions; 055import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; 056import org.hl7.fhir.instance.model.api.IBaseResource; 057import org.hl7.fhir.instance.model.api.IBaseXhtml; 058import org.hl7.fhir.instance.model.api.IDomainResource; 059import org.hl7.fhir.instance.model.api.IIdType; 060import org.hl7.fhir.instance.model.api.INarrative; 061import org.hl7.fhir.instance.model.api.IPrimitiveType; 062 063import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 064import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition; 065import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 066import ca.uhn.fhir.context.ConfigurationException; 067import ca.uhn.fhir.context.FhirContext; 068import ca.uhn.fhir.context.FhirVersionEnum; 069import ca.uhn.fhir.context.RuntimeChildContainedResources; 070import ca.uhn.fhir.context.RuntimeChildExtension; 071import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition; 072import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition; 073import ca.uhn.fhir.context.RuntimeResourceDefinition; 074import ca.uhn.fhir.model.api.BaseBundle; 075import ca.uhn.fhir.model.api.Bundle; 076import ca.uhn.fhir.model.api.BundleEntry; 077import ca.uhn.fhir.model.api.IResource; 078import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; 079import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; 080import ca.uhn.fhir.model.api.Tag; 081import ca.uhn.fhir.model.api.TagList; 082import ca.uhn.fhir.model.base.composite.BaseCodingDt; 083import ca.uhn.fhir.model.primitive.IdDt; 084import ca.uhn.fhir.model.primitive.InstantDt; 085import ca.uhn.fhir.model.primitive.StringDt; 086import ca.uhn.fhir.model.primitive.XhtmlDt; 087import ca.uhn.fhir.narrative.INarrativeGenerator; 088import ca.uhn.fhir.rest.server.Constants; 089import ca.uhn.fhir.rest.server.EncodingEnum; 090import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 091import ca.uhn.fhir.util.ElementUtil; 092import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper; 093import ca.uhn.fhir.util.PrettyPrintWriterWrapper; 094import ca.uhn.fhir.util.XmlUtil; 095 096/** 097 * This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use 098 * {@link FhirContext#newXmlParser()} to get an instance. 099 */ 100public class XmlParser extends BaseParser /*implements IParser */{ 101 102 static final String ATOM_NS = "http://www.w3.org/2005/Atom"; 103 static final String FHIR_NS = "http://hl7.org/fhir"; 104 static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/"; 105 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParser.class); 106 static final String RESREF_DISPLAY = "display"; 107 static final String RESREF_REFERENCE = "reference"; 108 static final String TOMBSTONES_NS = "http://purl.org/atompub/tombstones/1.0"; 109 static final String XHTML_NS = "http://www.w3.org/1999/xhtml"; 110 111 // private static final Set<String> RESOURCE_NAMESPACES; 112 113 private FhirContext myContext; 114 private boolean myPrettyPrint; 115 116 /** 117 * Do not use this constructor, the recommended way to obtain a new instance of the XML parser is to invoke 118 * {@link FhirContext#newXmlParser()}. 119 * 120 * @param theParserErrorHandler 121 */ 122 public XmlParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) { 123 super(theContext, theParserErrorHandler); 124 myContext = theContext; 125 } 126 127 private XMLEventReader createStreamReader(Reader theReader) { 128 try { 129 return XmlUtil.createXmlReader(theReader); 130 } catch (FactoryConfigurationError e1) { 131 throw new ConfigurationException("Failed to initialize STaX event factory", e1); 132 } catch (XMLStreamException e1) { 133 throw new DataFormatException(e1); 134 } 135 } 136 137 private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException { 138 XMLStreamWriter eventWriter; 139 eventWriter = XmlUtil.createXmlStreamWriter(theWriter); 140 eventWriter = decorateStreamWriter(eventWriter); 141 return eventWriter; 142 } 143 144 private XMLStreamWriter decorateStreamWriter(XMLStreamWriter eventWriter) { 145 if (myPrettyPrint) { 146 PrettyPrintWriterWrapper retVal = new PrettyPrintWriterWrapper(eventWriter); 147 return retVal; 148 } 149 NonPrettyPrintWriterWrapper retVal = new NonPrettyPrintWriterWrapper(eventWriter); 150 return retVal; 151 } 152 153 @Override 154 public void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException { 155 try { 156 XMLStreamWriter eventWriter = createXmlWriter(theWriter); 157 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 158 encodeBundleToWriterDstu2(theBundle, eventWriter); 159 } else { 160 encodeBundleToWriterDstu1(theBundle, eventWriter); 161 } 162 } catch (XMLStreamException e) { 163 throw new ConfigurationException("Failed to initialize STaX event factory", e); 164 } 165 } 166 167 @Override 168 public void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws DataFormatException { 169 XMLStreamWriter eventWriter; 170 try { 171 eventWriter = createXmlWriter(theWriter); 172 173 encodeResourceToXmlStreamWriter(theResource, eventWriter, false, false); 174 eventWriter.flush(); 175 } catch (XMLStreamException e) { 176 throw new ConfigurationException("Failed to initialize STaX event factory", e); 177 } 178 } 179 180 @Override 181 public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) { 182 XMLEventReader streamReader = createStreamReader(theReader); 183 return parseResource(theResourceType, streamReader); 184 } 185 186 private <T> T doXmlLoop(XMLEventReader streamReader, ParserState<T> parserState) { 187 ourLog.trace("Entering XML parsing loop with state: {}", parserState); 188 189 try { 190 List<String> heldComments = new ArrayList<String>(1); 191 192 while (streamReader.hasNext()) { 193 XMLEvent nextEvent = streamReader.nextEvent(); 194 try { 195 196 switch (nextEvent.getEventType()) { 197 case XMLStreamConstants.START_ELEMENT: { 198 StartElement elem = nextEvent.asStartElement(); 199 200 String namespaceURI = elem.getName().getNamespaceURI(); 201 202 if ("extension".equals(elem.getName().getLocalPart())) { 203 Attribute urlAttr = elem.getAttributeByName(new QName("url")); 204 String url; 205 if (urlAttr == null || isBlank(urlAttr.getValue())) { 206 getErrorHandler().missingRequiredElement(new ParseLocation("extension"), "url"); 207 url = null; 208 } else { 209 url = urlAttr.getValue(); 210 } 211 parserState.enteringNewElementExtension(elem, url, false, getServerBaseUrl()); 212 } else if ("modifierExtension".equals(elem.getName().getLocalPart())) { 213 Attribute urlAttr = elem.getAttributeByName(new QName("url")); 214 String url; 215 if (urlAttr == null || isBlank(urlAttr.getValue())) { 216 getErrorHandler().missingRequiredElement(new ParseLocation("modifierExtension"), "url"); 217 url = null; 218 } else { 219 url = urlAttr.getValue(); 220 } 221 parserState.enteringNewElementExtension(elem, url, true, getServerBaseUrl()); 222 } else { 223 String elementName = elem.getName().getLocalPart(); 224 parserState.enteringNewElement(namespaceURI, elementName); 225 } 226 227 if (!heldComments.isEmpty()) { 228 for (String next : heldComments) { 229 parserState.commentPre(next); 230 } 231 heldComments.clear(); 232 } 233 234 @SuppressWarnings("unchecked") 235 Iterator<Attribute> attributes = elem.getAttributes(); 236 for (Iterator<Attribute> iter = attributes; iter.hasNext();) { 237 Attribute next = iter.next(); 238 parserState.attributeValue(next.getName().getLocalPart(), next.getValue()); 239 } 240 241 break; 242 } 243 case XMLStreamConstants.END_DOCUMENT: 244 case XMLStreamConstants.END_ELEMENT: { 245 if (!heldComments.isEmpty()) { 246 for (String next : heldComments) { 247 parserState.commentPost(next); 248 } 249 heldComments.clear(); 250 } 251 parserState.endingElement(); 252// if (parserState.isComplete()) { 253// return parserState.getObject(); 254// } 255 break; 256 } 257 case XMLStreamConstants.CHARACTERS: { 258 parserState.string(nextEvent.asCharacters().getData()); 259 break; 260 } 261 case XMLStreamConstants.COMMENT: { 262 Comment comment = (Comment) nextEvent; 263 String commentText = comment.getText(); 264 heldComments.add(commentText); 265 break; 266 } 267 } 268 269 parserState.xmlEvent(nextEvent); 270 271 } catch (DataFormatException e) { 272 throw new DataFormatException("DataFormatException at [" + nextEvent.getLocation().toString() + "]: " + e.getMessage(), e); 273 } 274 } 275 return parserState.getObject(); 276 } catch (XMLStreamException e) { 277 throw new DataFormatException(e); 278 } 279 } 280 281 @Override 282 public String encodeBundleToString(Bundle theBundle) throws DataFormatException { 283 StringWriter stringWriter = new StringWriter(); 284 try { 285 encodeBundleToWriter(theBundle, stringWriter); 286 } catch (IOException e) { 287 throw new InternalErrorException("IOException writing to StringWriter - Should not happen", e); 288 } 289 290 return stringWriter.toString(); 291 } 292 293 private void encodeBundleToWriterDstu1(Bundle theBundle, XMLStreamWriter eventWriter) throws XMLStreamException { 294 eventWriter.writeStartElement("feed"); 295 eventWriter.writeDefaultNamespace(ATOM_NS); 296 297 writeTagWithTextNode(eventWriter, "title", theBundle.getTitle()); 298 writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId()); 299 300 writeAtomLink(eventWriter, Constants.LINK_SELF, theBundle.getLinkSelf()); 301 writeAtomLink(eventWriter, Constants.LINK_FIRST, theBundle.getLinkFirst()); 302 writeAtomLink(eventWriter, Constants.LINK_PREVIOUS, theBundle.getLinkPrevious()); 303 writeAtomLink(eventWriter, Constants.LINK_NEXT, theBundle.getLinkNext()); 304 writeAtomLink(eventWriter, Constants.LINK_LAST, theBundle.getLinkLast()); 305 writeAtomLink(eventWriter, Constants.LINK_FHIR_BASE, theBundle.getLinkBase()); 306 307 if (theBundle.getTotalResults().getValue() != null) { 308 eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS); 309 eventWriter.writeNamespace("os", OPENSEARCH_NS); 310 eventWriter.writeCharacters(theBundle.getTotalResults().getValue().toString()); 311 eventWriter.writeEndElement(); 312 } 313 314 writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated()); 315 316 writeAuthor(eventWriter, theBundle); 317 318 writeCategories(eventWriter, theBundle.getCategories()); 319 320 for (BundleEntry nextEntry : theBundle.getEntries()) { 321 boolean deleted = false; 322 if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false) { 323 deleted = true; 324 eventWriter.writeStartElement("at", "deleted-entry", TOMBSTONES_NS); 325 eventWriter.writeNamespace("at", TOMBSTONES_NS); 326 327 if (nextEntry.getDeletedResourceId().isEmpty()) { 328 //TODO: Use of a deprecated method should be resolved. 329 writeOptionalAttribute(eventWriter, "ref", nextEntry.getId().getValueAsString()); 330 } else { 331 writeOptionalAttribute(eventWriter, "ref", nextEntry.getDeletedResourceId().getValueAsString()); 332 } 333 334 writeOptionalAttribute(eventWriter, "when", nextEntry.getDeletedAt().getValueAsString()); 335 if (nextEntry.getDeletedByEmail().isEmpty() == false || nextEntry.getDeletedByName().isEmpty() == false) { 336 eventWriter.writeStartElement(TOMBSTONES_NS, "by"); 337 if (nextEntry.getDeletedByName().isEmpty() == false) { 338 eventWriter.writeStartElement(TOMBSTONES_NS, "name"); 339 eventWriter.writeCharacters(nextEntry.getDeletedByName().getValue()); 340 eventWriter.writeEndElement(); 341 } 342 if (nextEntry.getDeletedByEmail().isEmpty() == false) { 343 eventWriter.writeStartElement(TOMBSTONES_NS, "email"); 344 eventWriter.writeCharacters(nextEntry.getDeletedByEmail().getValue()); 345 eventWriter.writeEndElement(); 346 } 347 eventWriter.writeEndElement(); 348 } 349 if (nextEntry.getDeletedComment().isEmpty() == false) { 350 eventWriter.writeStartElement(TOMBSTONES_NS, "comment"); 351 eventWriter.writeCharacters(nextEntry.getDeletedComment().getValue()); 352 eventWriter.writeEndElement(); 353 } 354 } else { 355 eventWriter.writeStartElement("entry"); 356 } 357 358 writeOptionalTagWithTextNode(eventWriter, "title", nextEntry.getTitle()); 359 if (!deleted) { 360 //TODO: Use of a deprecated method should be resolved. 361 if (nextEntry.getId().isEmpty() == false) { 362 //TODO: Use of a deprecated method should be resolved. 363 writeTagWithTextNode(eventWriter, "id", nextEntry.getId()); 364 } else { 365 writeTagWithTextNode(eventWriter, "id", nextEntry.getResource().getId()); 366 } 367 } 368 //TODO: Use of a deprecated method should be resolved. 369 writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated()); 370 writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished()); 371 372 writeAuthor(eventWriter, nextEntry); 373 374 writeCategories(eventWriter, nextEntry.getCategories()); 375 376 if (!nextEntry.getLinkSelf().isEmpty()) { 377 writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf()); 378 } 379 380 if (!nextEntry.getLinkAlternate().isEmpty()) { 381 writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate()); 382 } 383 384 if (!nextEntry.getLinkSearch().isEmpty()) { 385 writeAtomLink(eventWriter, "search", nextEntry.getLinkSearch()); 386 } 387 388 IResource resource = nextEntry.getResource(); 389 if (resource != null && !resource.isEmpty() && !deleted) { 390 eventWriter.writeStartElement("content"); 391 eventWriter.writeAttribute("type", "text/xml"); 392 encodeResourceToXmlStreamWriter(resource, eventWriter, false, true); 393 eventWriter.writeEndElement(); // content 394 } else { 395 ourLog.debug("Bundle entry contains null resource"); 396 } 397 398 if (!nextEntry.getSummary().isEmpty()) { 399 eventWriter.writeStartElement("summary"); 400 eventWriter.writeAttribute("type", "xhtml"); 401 encodeXhtml(nextEntry.getSummary(), eventWriter); 402 eventWriter.writeEndElement(); 403 } 404 405 eventWriter.writeEndElement(); // entry 406 } 407 408 eventWriter.writeEndElement(); 409 eventWriter.close(); 410 } 411 412 private void encodeBundleToWriterDstu2(Bundle theBundle, XMLStreamWriter theEventWriter) throws XMLStreamException { 413 theEventWriter.writeStartElement("Bundle"); 414 theEventWriter.writeDefaultNamespace(FHIR_NS); 415 416 writeOptionalTagWithValue(theEventWriter, "id", theBundle.getId().getIdPart()); 417 418 InstantDt updated = (InstantDt) theBundle.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); 419 IdDt bundleId = theBundle.getId(); 420 if (bundleId != null && isNotBlank(bundleId.getVersionIdPart()) || (updated != null && !updated.isEmpty())) { 421 theEventWriter.writeStartElement("meta"); 422 //FIXME potential null acces bundleId may be null at this time due to the OR clause 423 writeOptionalTagWithValue(theEventWriter, "versionId", bundleId.getVersionIdPart()); 424 if (updated != null) { 425 writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString()); 426 } 427 theEventWriter.writeEndElement(); 428 } 429 430 writeOptionalTagWithValue(theEventWriter, "type", theBundle.getType().getValue()); 431 writeOptionalTagWithValue(theEventWriter, "total", theBundle.getTotalResults().getValueAsString()); 432 433 writeBundleResourceLink(theEventWriter, "first", theBundle.getLinkFirst()); 434 writeBundleResourceLink(theEventWriter, "previous", theBundle.getLinkPrevious()); 435 writeBundleResourceLink(theEventWriter, "next", theBundle.getLinkNext()); 436 writeBundleResourceLink(theEventWriter, "last", theBundle.getLinkLast()); 437 writeBundleResourceLink(theEventWriter, "self", theBundle.getLinkSelf()); 438 439 for (BundleEntry nextEntry : theBundle.getEntries()) { 440 theEventWriter.writeStartElement("entry"); 441 442 boolean deleted = false; 443 if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false) { 444 deleted = true; 445 } 446 447 writeBundleResourceLink(theEventWriter, "alternate", nextEntry.getLinkAlternate()); 448 449 if (nextEntry.getResource() != null && isNotBlank(nextEntry.getResource().getIdElement().getValue()) && (nextEntry.getResource().getId().getBaseUrl() != null || nextEntry.getResource().getId().getValueAsString().startsWith("urn:"))) { 450 writeOptionalTagWithValue(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue()); 451 } 452 453 IResource resource = nextEntry.getResource(); 454 if (resource != null && !resource.isEmpty() && !deleted) { 455 theEventWriter.writeStartElement("resource"); 456 encodeResourceToXmlStreamWriter(resource, theEventWriter, false, true); 457 theEventWriter.writeEndElement(); // content 458 } else { 459 ourLog.debug("Bundle entry contains null resource"); 460 } 461 462 if (nextEntry.getSearchMode().isEmpty() == false || nextEntry.getScore().isEmpty() == false) { 463 theEventWriter.writeStartElement("search"); 464 writeOptionalTagWithValue(theEventWriter, "mode", nextEntry.getSearchMode().getValueAsString()); 465 writeOptionalTagWithValue(theEventWriter, "score", nextEntry.getScore().getValueAsString()); 466 theEventWriter.writeEndElement(); 467 // IResource nextResource = nextEntry.getResource(); 468 } 469 470 if (nextEntry.getTransactionMethod().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) { 471 theEventWriter.writeStartElement("request"); 472 writeOptionalTagWithValue(theEventWriter, "method", nextEntry.getTransactionMethod().getValue()); 473 writeOptionalTagWithValue(theEventWriter, "url", nextEntry.getLinkSearch().getValue()); 474 theEventWriter.writeEndElement(); 475 } 476 477 if (deleted) { 478 theEventWriter.writeStartElement("deleted"); 479 //TODO: Use of a deprecated method should be resolved. 480 writeOptionalTagWithValue(theEventWriter, "type", nextEntry.getId().getResourceType()); 481 //TODO: Use of a deprecated method should be resolved. 482 writeOptionalTagWithValue(theEventWriter, "id", nextEntry.getId().getIdPart()); 483 //TODO: Use of a deprecated method should be resolved. 484 writeOptionalTagWithValue(theEventWriter, "versionId", nextEntry.getId().getVersionIdPart()); 485 writeOptionalTagWithValue(theEventWriter, "instant", nextEntry.getDeletedAt().getValueAsString()); 486 theEventWriter.writeEndElement(); 487 } 488 489 theEventWriter.writeEndElement(); // entry 490 } 491 492 theEventWriter.writeEndElement(); 493 theEventWriter.close(); 494 } 495 496 private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase theElement, String childName, BaseRuntimeElementDefinition<?> childDef, 497 String theExtensionUrl, boolean theIncludedResource, CompositeChildElement theParent) throws XMLStreamException, DataFormatException { 498 if (theElement == null || theElement.isEmpty()) { 499 if (isChildContained(childDef, theIncludedResource)) { 500 // We still want to go in.. 501 } else { 502 return; 503 } 504 } 505 506 writeCommentsPre(theEventWriter, theElement); 507 508 switch (childDef.getChildType()) { 509 case ID_DATATYPE: { 510 IIdType value = IIdType.class.cast(theElement); 511 String encodedValue = "id".equals(childName) ? value.getIdPart() : value.getValue(); 512 theEventWriter.writeStartElement(childName); 513 theEventWriter.writeAttribute("value", encodedValue); 514 encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); 515 theEventWriter.writeEndElement(); 516 break; 517 } 518 case PRIMITIVE_DATATYPE: { 519 IPrimitiveType<?> pd = IPrimitiveType.class.cast(theElement); 520 String value = pd.getValueAsString(); 521 if (value != null || super.hasExtensions(pd)) { 522 theEventWriter.writeStartElement(childName); 523 String elementId = getCompositeElementId(theElement); 524 if (isNotBlank(elementId)) { 525 theEventWriter.writeAttribute("id", elementId); 526 } 527 if (value != null) { 528 theEventWriter.writeAttribute("value", value); 529 } 530 encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); 531 theEventWriter.writeEndElement(); 532 } 533 break; 534 } 535 case RESOURCE_BLOCK: 536 case COMPOSITE_DATATYPE: { 537 theEventWriter.writeStartElement(childName); 538 String elementId = getCompositeElementId(theElement); 539 if (isNotBlank(elementId)) { 540 theEventWriter.writeAttribute("id", elementId); 541 } 542 if (isNotBlank(theExtensionUrl)) { 543 theEventWriter.writeAttribute("url", theExtensionUrl); 544 } 545 encodeCompositeElementToStreamWriter(theResource, theElement, theEventWriter, theIncludedResource, theParent); 546 theEventWriter.writeEndElement(); 547 break; 548 } 549 case CONTAINED_RESOURCE_LIST: 550 case CONTAINED_RESOURCES: { 551 /* 552 * Disable per #103 for (IResource next : value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; } 553 * theEventWriter.writeStartElement("contained"); encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue())); 554 * theEventWriter.writeEndElement(); } 555 */ 556 for (IBaseResource next : getContainedResources().getContainedResources()) { 557 IIdType resourceId = getContainedResources().getResourceId(next); 558 theEventWriter.writeStartElement("contained"); 559 encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(resourceId.getValue())); 560 theEventWriter.writeEndElement(); 561 } 562 break; 563 } 564 case RESOURCE: { 565 theEventWriter.writeStartElement(childName); 566 IBaseResource resource = (IBaseResource) theElement; 567 encodeResourceToXmlStreamWriter(resource, theEventWriter, false, true); 568 theEventWriter.writeEndElement(); 569 break; 570 } 571 case PRIMITIVE_XHTML: { 572 XhtmlDt dt = XhtmlDt.class.cast(theElement); 573 if (dt.hasContent()) { 574 encodeXhtml(dt, theEventWriter); 575 } 576 break; 577 } 578 case PRIMITIVE_XHTML_HL7ORG: { 579 IBaseXhtml dt = IBaseXhtml.class.cast(theElement); 580 if (!dt.isEmpty()) { 581 // TODO: this is probably not as efficient as it could be 582 XhtmlDt hdt = new XhtmlDt(); 583 hdt.setValueAsString(dt.getValueAsString()); 584 encodeXhtml(hdt, theEventWriter); 585 } 586 break; 587 } 588 case EXTENSION_DECLARED: 589 case UNDECL_EXT: { 590 throw new IllegalStateException("state should not happen: " + childDef.getName()); 591 } 592 } 593 594 writeCommentsPost(theEventWriter, theElement); 595 596 } 597 598 private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, boolean theContainedResource, CompositeChildElement theParent) 599 throws XMLStreamException, DataFormatException { 600 601 for (CompositeChildElement nextChildElem : super.compositeChildIterator(theElement, theContainedResource, theParent)) { 602 603 BaseRuntimeChildDefinition nextChild = nextChildElem.getDef(); 604 605 if (nextChild.getElementName().equals("url") && theElement instanceof IBaseExtension) { 606 /* 607 * XML encoding is a one-off for extensions. The URL element goes in an attribute 608 * instead of being encoded as a normal element, only for XML encoding 609 */ 610 continue; 611 } 612 613 if (nextChild instanceof RuntimeChildNarrativeDefinition) { 614 INarrativeGenerator gen = myContext.getNarrativeGenerator(); 615 INarrative narr; 616 if (theResource instanceof IResource) { 617 narr = ((IResource) theResource).getText(); 618 } else if (theResource instanceof IDomainResource) { 619 narr = ((IDomainResource) theResource).getText(); 620 } else { 621 narr = null; 622 } 623 //FIXME potential null access on narr see line 623 624 if (gen != null && narr.isEmpty()) { 625 gen.generateNarrative(myContext, theResource, narr); 626 } 627 if (narr != null && narr.isEmpty() == false) { 628 RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; 629 String childName = nextChild.getChildNameByDatatype(child.getDatatype()); 630 BaseRuntimeElementDefinition<?> type = child.getChildByName(childName); 631 encodeChildElementToStreamWriter(theResource, theEventWriter, narr, childName, type, null, theContainedResource, nextChildElem); 632 continue; 633 } 634 } 635 636 if (nextChild instanceof RuntimeChildContainedResources) { 637 encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource, nextChildElem); 638 } else { 639 640 List<? extends IBase> values = nextChild.getAccessor().getValues(theElement); 641 values = super.preProcessValues(nextChild, theResource, values, nextChildElem); 642 643 if (values == null || values.isEmpty()) { 644 continue; 645 } 646 for (IBase nextValue : values) { 647 if ((nextValue == null || nextValue.isEmpty())) { 648 continue; 649 } 650 651 BaseParser.ChildNameAndDef childNameAndDef = super.getChildNameAndDef(nextChild, nextValue); 652 if (childNameAndDef == null) { 653 continue; 654 } 655 656 String childName = childNameAndDef.getChildName(); 657 BaseRuntimeElementDefinition<?> childDef = childNameAndDef.getChildDef(); 658 String extensionUrl = getExtensionUrl(nextChild.getExtensionUrl()); 659 660 if (nextValue instanceof IBaseExtension && myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { 661 // This is called for the Query resource in DSTU1 only 662 extensionUrl = getExtensionUrl(((IBaseExtension<?, ?>) nextValue).getUrl()); 663 encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theContainedResource, nextChildElem); 664 665 } else if (extensionUrl != null && childName.equals("extension") == false) { 666 encodeExtension(theResource, theEventWriter, theContainedResource, nextChildElem, nextChild, nextValue, childName, extensionUrl, childDef); 667 } else if (nextChild instanceof RuntimeChildExtension) { 668 IBaseExtension<?, ?> extension = (IBaseExtension<?, ?>) nextValue; 669 if ((extension.getValue() == null || extension.getValue().isEmpty())) { 670 if (extension.getExtension().isEmpty()) { 671 continue; 672 } 673 } 674 encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, getExtensionUrl(extension.getUrl()), theContainedResource, nextChildElem); 675 } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) { 676 // suppress narratives from contained resources 677 } else { 678 encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theContainedResource, nextChildElem); 679 } 680 } 681 } 682 } 683 } 684 685 private void encodeExtension(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, CompositeChildElement nextChildElem, BaseRuntimeChildDefinition nextChild, IBase nextValue, String childName, String extensionUrl, BaseRuntimeElementDefinition<?> childDef) 686 throws XMLStreamException { 687 BaseRuntimeDeclaredChildDefinition extDef = (BaseRuntimeDeclaredChildDefinition) nextChild; 688 if (extDef.isModifier()) { 689 theEventWriter.writeStartElement("modifierExtension"); 690 } else { 691 theEventWriter.writeStartElement("extension"); 692 } 693 694 String elementId = getCompositeElementId(nextValue); 695 if (isNotBlank(elementId)) { 696 theEventWriter.writeAttribute("id", elementId); 697 } 698 699 theEventWriter.writeAttribute("url", extensionUrl); 700 encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, null, theContainedResource, nextChildElem); 701 theEventWriter.writeEndElement(); 702 } 703 704 private void encodeExtensionsIfPresent(IBaseResource theResource, XMLStreamWriter theWriter, IBase theElement, boolean theIncludedResource) throws XMLStreamException, DataFormatException { 705 if (theElement instanceof ISupportsUndeclaredExtensions) { 706 ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement; 707 encodeUndeclaredExtensions(theResource, theWriter, toBaseExtensionList(res.getUndeclaredExtensions()), "extension", theIncludedResource); 708 encodeUndeclaredExtensions(theResource, theWriter, toBaseExtensionList(res.getUndeclaredModifierExtensions()), "modifierExtension", theIncludedResource); 709 } 710 if (theElement instanceof IBaseHasExtensions) { 711 IBaseHasExtensions res = (IBaseHasExtensions) theElement; 712 encodeUndeclaredExtensions(theResource, theWriter, res.getExtension(), "extension", theIncludedResource); 713 } 714 if (theElement instanceof IBaseHasModifierExtensions) { 715 IBaseHasModifierExtensions res = (IBaseHasModifierExtensions) theElement; 716 encodeUndeclaredExtensions(theResource, theWriter, res.getModifierExtension(), "modifierExtension", theIncludedResource); 717 } 718 } 719 720 private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource, boolean theSubResource) throws XMLStreamException, DataFormatException { 721 IIdType resourceId = null; 722 723 if (StringUtils.isNotBlank(theResource.getIdElement().getIdPart())) { 724 resourceId = theResource.getIdElement(); 725 if (theResource.getIdElement().getValue().startsWith("urn:")) { 726 resourceId = null; 727 } 728 if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { 729 resourceId = null; 730 } 731 } 732 733 if (!theIncludedResource) { 734 if (super.shouldEncodeResourceId(theResource) == false) { 735 resourceId = null; 736 } else if (theSubResource == false && getEncodeForceResourceId() != null) { 737 resourceId = getEncodeForceResourceId(); 738 } 739 } 740 741 encodeResourceToXmlStreamWriter(theResource, theEventWriter, theIncludedResource, resourceId); 742 } 743 744 745 private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, IIdType theResourceId) throws XMLStreamException { 746 if (!theContainedResource) { 747 super.containResourcesForEncoding(theResource); 748 } 749 750 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); 751 if (resDef == null) { 752 throw new ConfigurationException("Unknown resource type: " + theResource.getClass()); 753 } 754 755 theEventWriter.writeStartElement(resDef.getName()); 756 theEventWriter.writeDefaultNamespace(FHIR_NS); 757 758 if (theResource instanceof IAnyResource) { 759 760 // HL7.org Structures 761 if (theResourceId != null) { 762 writeCommentsPre(theEventWriter, theResourceId); 763 writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart()); 764 writeCommentsPost(theEventWriter, theResourceId); 765 } 766 767 encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, theContainedResource, new CompositeChildElement(resDef)); 768 769 } else { 770 771 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 772 773 // DSTU2+ 774 775 IResource resource = (IResource) theResource; 776 if (theResourceId != null) { 777 writeCommentsPre(theEventWriter, theResourceId); 778 writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart()); 779 writeCommentsPost(theEventWriter, theResourceId); 780 } 781 782 InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); 783 IdDt resourceId = resource.getId(); 784 String versionIdPart = resourceId.getVersionIdPart(); 785 if (isBlank(versionIdPart)) { 786 versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource); 787 } 788 List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS); 789 List<? extends IIdType> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES); 790 profiles = super.getProfileTagsForEncoding(resource, profiles); 791 792 TagList tags = getMetaTagsForEncoding((resource)); 793 794 if (super.shouldEncodeResourceMeta(resource) && ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) { 795 theEventWriter.writeStartElement("meta"); 796 writeOptionalTagWithValue(theEventWriter, "versionId", versionIdPart); 797 if (updated != null) { 798 writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString()); 799 } 800 801 for (IIdType profile : profiles) { 802 theEventWriter.writeStartElement("profile"); 803 theEventWriter.writeAttribute("value", profile.getValue()); 804 theEventWriter.writeEndElement(); 805 } 806 for (BaseCodingDt securityLabel : securityLabels) { 807 theEventWriter.writeStartElement("security"); 808 encodeCompositeElementToStreamWriter(resource, securityLabel, theEventWriter, theContainedResource, null); 809 theEventWriter.writeEndElement(); 810 } 811 if (tags != null) { 812 for (Tag tag : tags) { 813 if (tag.isEmpty()) { 814 continue; 815 } 816 theEventWriter.writeStartElement("tag"); 817 writeOptionalTagWithValue(theEventWriter, "system", tag.getScheme()); 818 writeOptionalTagWithValue(theEventWriter, "code", tag.getTerm()); 819 writeOptionalTagWithValue(theEventWriter, "display", tag.getLabel()); 820 theEventWriter.writeEndElement(); 821 } 822 } 823 theEventWriter.writeEndElement(); 824 } 825 826 if (theResource instanceof IBaseBinary) { 827 IBaseBinary bin = (IBaseBinary) theResource; 828 writeOptionalTagWithValue(theEventWriter, "contentType", bin.getContentType()); 829 writeOptionalTagWithValue(theEventWriter, "content", bin.getContentAsBase64()); 830 } else { 831 encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, theContainedResource, new CompositeChildElement(resDef)); 832 } 833 834 } else { 835 836 // DSTU1 837 if (theResourceId != null && theContainedResource && theResourceId.hasIdPart()) { 838 theEventWriter.writeAttribute("id", theResourceId.getIdPart()); 839 } 840 841 if (theResource instanceof IBaseBinary) { 842 IBaseBinary bin = (IBaseBinary) theResource; 843 if (bin.getContentType() != null) { 844 theEventWriter.writeAttribute("contentType", bin.getContentType()); 845 } 846 theEventWriter.writeCharacters(bin.getContentAsBase64()); 847 } else { 848 encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, theContainedResource, new CompositeChildElement(resDef)); 849 } 850 851 } 852 853 } 854 855 theEventWriter.writeEndElement(); 856 } 857 858 859 @Override 860 public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException { 861 try { 862 XMLStreamWriter eventWriter = createXmlWriter(theWriter); 863 864 eventWriter.writeStartElement(TagList.ELEMENT_NAME_LC); 865 eventWriter.writeDefaultNamespace(FHIR_NS); 866 867 for (Tag next : theTagList) { 868 eventWriter.writeStartElement(TagList.ATTR_CATEGORY); 869 870 if (isNotBlank(next.getTerm())) { 871 eventWriter.writeAttribute(Tag.ATTR_TERM, next.getTerm()); 872 } 873 if (isNotBlank(next.getLabel())) { 874 eventWriter.writeAttribute(Tag.ATTR_LABEL, next.getLabel()); 875 } 876 if (isNotBlank(next.getScheme())) { 877 eventWriter.writeAttribute(Tag.ATTR_SCHEME, next.getScheme()); 878 } 879 880 eventWriter.writeEndElement(); 881 } 882 883 eventWriter.writeEndElement(); 884 eventWriter.close(); 885 } catch (XMLStreamException e) { 886 throw new ConfigurationException("Failed to initialize STaX event factory", e); 887 } 888 } 889 890 private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theEventWriter, List<? extends IBaseExtension<?, ?>> theExtensions, String tagName, boolean theIncludedResource) throws XMLStreamException, DataFormatException { 891 for (IBaseExtension<?, ?> next : theExtensions) { 892 if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) { 893 continue; 894 } 895 896 writeCommentsPre(theEventWriter, next); 897 898 theEventWriter.writeStartElement(tagName); 899 900 String elementId = getCompositeElementId(next); 901 if (isNotBlank(elementId)) { 902 theEventWriter.writeAttribute("id", elementId); 903 } 904 905 String url = getExtensionUrl(next.getUrl()); 906 theEventWriter.writeAttribute("url", url); 907 908 if (next.getValue() != null) { 909 IBaseDatatype value = next.getValue(); 910 RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition(); 911 String childName = extDef.getChildNameByDatatype(value.getClass()); 912 BaseRuntimeElementDefinition<?> childDef; 913 if (childName == null) { 914 childDef = myContext.getElementDefinition(value.getClass()); 915 if (childDef == null) { 916 throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName()); 917 } 918 childName = RuntimeChildUndeclaredExtensionDefinition.createExtensionChildName(childDef); 919 } else { 920 childDef = extDef.getChildElementDefinitionByDatatype(value.getClass()); 921 if (childDef == null) { 922 throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName()); 923 } 924 } 925 encodeChildElementToStreamWriter(theResource, theEventWriter, value, childName, childDef, null, theIncludedResource, null); 926 } 927 928 // child extensions 929 encodeExtensionsIfPresent(theResource, theEventWriter, next, theIncludedResource); 930 931 theEventWriter.writeEndElement(); 932 933 writeCommentsPost(theEventWriter, next); 934 935 } 936 } 937 938 private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException { 939 if (theDt == null || theDt.getValue() == null) { 940 return; 941 } 942 943 boolean firstElement = true; 944 for (XMLEvent event : theDt.getValue()) { 945 switch (event.getEventType()) { 946 case XMLStreamConstants.ATTRIBUTE: 947 Attribute attr = (Attribute) event; 948 if (isBlank(attr.getName().getPrefix())) { 949 if (isBlank(attr.getName().getNamespaceURI())) { 950 theEventWriter.writeAttribute(attr.getName().getLocalPart(), attr.getValue()); 951 } else { 952 theEventWriter.writeAttribute(attr.getName().getNamespaceURI(), attr.getName().getLocalPart(), attr.getValue()); 953 } 954 } else { 955 theEventWriter.writeAttribute(attr.getName().getPrefix(), attr.getName().getNamespaceURI(), attr.getName().getLocalPart(), attr.getValue()); 956 } 957 958 break; 959 case XMLStreamConstants.CDATA: 960 theEventWriter.writeCData(((Characters) event).getData()); 961 break; 962 case XMLStreamConstants.CHARACTERS: 963 case XMLStreamConstants.SPACE: 964 String data = ((Characters) event).getData(); 965 theEventWriter.writeCharacters(data); 966 break; 967 case XMLStreamConstants.COMMENT: 968 theEventWriter.writeComment(((Comment) event).getText()); 969 break; 970 case XMLStreamConstants.END_ELEMENT: 971 theEventWriter.writeEndElement(); 972 break; 973 case XMLStreamConstants.ENTITY_REFERENCE: 974 EntityReference er = (EntityReference) event; 975 theEventWriter.writeEntityRef(er.getName()); 976 break; 977 case XMLStreamConstants.NAMESPACE: 978 Namespace ns = (Namespace) event; 979 theEventWriter.writeNamespace(ns.getPrefix(), ns.getNamespaceURI()); 980 break; 981 case XMLStreamConstants.START_ELEMENT: 982 StartElement se = event.asStartElement(); 983 if (firstElement) { 984 if (StringUtils.isBlank(se.getName().getPrefix())) { 985 String namespaceURI = se.getName().getNamespaceURI(); 986 if (StringUtils.isBlank(namespaceURI)) { 987 namespaceURI = "http://www.w3.org/1999/xhtml"; 988 } 989 theEventWriter.writeStartElement(se.getName().getLocalPart()); 990 theEventWriter.writeDefaultNamespace(namespaceURI); 991 } else { 992 String prefix = se.getName().getPrefix(); 993 String namespaceURI = se.getName().getNamespaceURI(); 994 theEventWriter.writeStartElement(prefix, se.getName().getLocalPart(), namespaceURI); 995 theEventWriter.writeNamespace(prefix, namespaceURI); 996 } 997 firstElement = false; 998 } else { 999 if (isBlank(se.getName().getPrefix())) { 1000 if (isBlank(se.getName().getNamespaceURI())) { 1001 theEventWriter.writeStartElement(se.getName().getLocalPart()); 1002 } else { 1003 if (StringUtils.isBlank(se.getName().getPrefix())) { 1004 theEventWriter.writeStartElement(se.getName().getLocalPart()); 1005 // theEventWriter.writeDefaultNamespace(se.getName().getNamespaceURI()); 1006 } else { 1007 theEventWriter.writeStartElement(se.getName().getNamespaceURI(), se.getName().getLocalPart()); 1008 } 1009 } 1010 } else { 1011 theEventWriter.writeStartElement(se.getName().getPrefix(), se.getName().getLocalPart(), se.getName().getNamespaceURI()); 1012 } 1013 for (Iterator<?> attrIter = se.getAttributes(); attrIter.hasNext();) { 1014 Attribute next = (Attribute) attrIter.next(); 1015 theEventWriter.writeAttribute(next.getName().getLocalPart(), next.getValue()); 1016 } 1017 } 1018 break; 1019 case XMLStreamConstants.DTD: 1020 case XMLStreamConstants.END_DOCUMENT: 1021 case XMLStreamConstants.ENTITY_DECLARATION: 1022 case XMLStreamConstants.NOTATION_DECLARATION: 1023 case XMLStreamConstants.PROCESSING_INSTRUCTION: 1024 case XMLStreamConstants.START_DOCUMENT: 1025 break; 1026 } 1027 1028 } 1029 } 1030 1031 @Override 1032 public EncodingEnum getEncoding() { 1033 return EncodingEnum.XML; 1034 } 1035 1036 @Override 1037 public <T extends IBaseResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) { 1038 XMLEventReader streamReader = createStreamReader(theReader); 1039 1040 return parseBundle(streamReader, theResourceType); 1041 } 1042 1043 private Bundle parseBundle(XMLEventReader theStreamReader, Class<? extends IBaseResource> theResourceType) { 1044 ParserState<Bundle> parserState = ParserState.getPreAtomInstance(this, myContext, theResourceType, false, getErrorHandler()); 1045 return doXmlLoop(theStreamReader, parserState); 1046 } 1047 1048 private <T extends IBaseResource> T parseResource(Class<T> theResourceType, XMLEventReader theStreamReader) { 1049 ParserState<T> parserState = ParserState.getPreResourceInstance(this, theResourceType, myContext, false, getErrorHandler()); 1050 return doXmlLoop(theStreamReader, parserState); 1051 } 1052 1053 @Override 1054 public TagList parseTagList(Reader theReader) { 1055 XMLEventReader streamReader = createStreamReader(theReader); 1056 1057 ParserState<TagList> parserState = ParserState.getPreTagListInstance(this, myContext, false, getErrorHandler()); 1058 return doXmlLoop(streamReader, parserState); 1059 } 1060 1061 @Override 1062 public IParser setPrettyPrint(boolean thePrettyPrint) { 1063 myPrettyPrint = thePrettyPrint; 1064 return this; 1065 } 1066 1067 /** 1068 * This is just to work around the fact that casting java.util.List<ca.uhn.fhir.model.api.ExtensionDt> to 1069 * java.util.List<? extends org.hl7.fhir.instance.model.api.IBaseExtension<?, ?>> seems to be 1070 * rejected by the compiler some of the time. 1071 */ 1072 private <Q extends IBaseExtension<?, ?>> List<IBaseExtension<?, ?>> toBaseExtensionList(final List<Q> theList) { 1073 List<IBaseExtension<?, ?>> retVal = new ArrayList<IBaseExtension<?, ?>>(theList.size()); 1074 retVal.addAll(theList); 1075 return retVal; 1076 } 1077 1078 private void writeAuthor(XMLStreamWriter theEventWriter, BaseBundle theBundle) throws XMLStreamException { 1079 if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) { 1080 theEventWriter.writeStartElement("author"); 1081 writeTagWithTextNode(theEventWriter, "name", theBundle.getAuthorName()); 1082 writeOptionalTagWithTextNode(theEventWriter, "uri", theBundle.getAuthorUri()); 1083 theEventWriter.writeEndElement(); 1084 } 1085 } 1086 1087 private void writeAtomLink(XMLStreamWriter theEventWriter, String theRel, StringDt theStringDt) throws XMLStreamException { 1088 if (StringUtils.isNotBlank(theStringDt.getValue())) { 1089 theEventWriter.writeStartElement("link"); 1090 theEventWriter.writeAttribute("rel", theRel); 1091 theEventWriter.writeAttribute("href", theStringDt.getValue()); 1092 theEventWriter.writeEndElement(); 1093 } 1094 } 1095 1096 private void writeBundleResourceLink(XMLStreamWriter theEventWriter, String theRel, StringDt theUrl) throws XMLStreamException { 1097 if (theUrl.isEmpty() == false) { 1098 theEventWriter.writeStartElement("link"); 1099 theEventWriter.writeStartElement("relation"); 1100 theEventWriter.writeAttribute("value", theRel); 1101 theEventWriter.writeEndElement(); 1102 theEventWriter.writeStartElement("url"); 1103 theEventWriter.writeAttribute("value", theUrl.getValue()); 1104 theEventWriter.writeEndElement(); 1105 theEventWriter.writeEndElement(); 1106 } 1107 } 1108 1109 private void writeCategories(XMLStreamWriter eventWriter, TagList categories) throws XMLStreamException { 1110 if (categories != null) { 1111 for (Tag next : categories) { 1112 eventWriter.writeStartElement("category"); 1113 eventWriter.writeAttribute("term", defaultString(next.getTerm())); 1114 eventWriter.writeAttribute("label", defaultString(next.getLabel())); 1115 eventWriter.writeAttribute("scheme", defaultString(next.getScheme())); 1116 eventWriter.writeEndElement(); 1117 } 1118 } 1119 } 1120 1121 private void writeCommentsPost(XMLStreamWriter theEventWriter, IBase theElement) throws XMLStreamException { 1122 if (theElement != null && theElement.hasFormatComment()) { 1123 for (String next : theElement.getFormatCommentsPost()) { 1124 if (isNotBlank(next)) { 1125 theEventWriter.writeComment(next); 1126 } 1127 } 1128 } 1129 } 1130 1131 private void writeCommentsPre(XMLStreamWriter theEventWriter, IBase theElement) throws XMLStreamException { 1132 if (theElement != null && theElement.hasFormatComment()) { 1133 for (String next : theElement.getFormatCommentsPre()) { 1134 if (isNotBlank(next)) { 1135 theEventWriter.writeComment(next); 1136 } 1137 } 1138 } 1139 } 1140 1141 private void writeOptionalAttribute(XMLStreamWriter theEventWriter, String theName, String theValue) throws XMLStreamException { 1142 if (StringUtils.isNotBlank(theValue)) { 1143 theEventWriter.writeAttribute(theName, theValue); 1144 } 1145 } 1146 1147 private void writeOptionalTagWithTextNode(XMLStreamWriter theEventWriter, String theTagName, InstantDt theInstantDt) throws XMLStreamException { 1148 if (theInstantDt.getValue() != null) { 1149 theEventWriter.writeStartElement(theTagName); 1150 theEventWriter.writeCharacters(theInstantDt.getValueAsString()); 1151 theEventWriter.writeEndElement(); 1152 } 1153 } 1154 1155 private void writeOptionalTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, StringDt theTextValue) throws XMLStreamException { 1156 if (StringUtils.isNotBlank(theTextValue.getValue())) { 1157 theEventWriter.writeStartElement(theElementName); 1158 theEventWriter.writeCharacters(theTextValue.getValue()); 1159 theEventWriter.writeEndElement(); 1160 } 1161 } 1162 1163 private void writeOptionalTagWithValue(XMLStreamWriter theEventWriter, String theName, String theValue) throws XMLStreamException { 1164 if (StringUtils.isNotBlank(theValue)) { 1165 theEventWriter.writeStartElement(theName); 1166 theEventWriter.writeAttribute("value", theValue); 1167 theEventWriter.writeEndElement(); 1168 } 1169 } 1170 1171 private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, IdDt theIdDt) throws XMLStreamException { 1172 theEventWriter.writeStartElement(theElementName); 1173 if (StringUtils.isNotBlank(theIdDt.getValue())) { 1174 theEventWriter.writeCharacters(theIdDt.getValue()); 1175 } 1176 theEventWriter.writeEndElement(); 1177 } 1178 1179 private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, StringDt theStringDt) throws XMLStreamException { 1180 theEventWriter.writeStartElement(theElementName); 1181 if (StringUtils.isNotBlank(theStringDt.getValue())) { 1182 theEventWriter.writeCharacters(theStringDt.getValue()); 1183 } 1184 theEventWriter.writeEndElement(); 1185 } 1186 1187}