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