
001package org.hl7.fhir.r4.elementmodel; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.OutputStream; 035import java.util.ArrayList; 036import java.util.Collections; 037import java.util.Comparator; 038import java.util.List; 039 040import javax.xml.parsers.DocumentBuilder; 041import javax.xml.parsers.DocumentBuilderFactory; 042import javax.xml.parsers.SAXParserFactory; 043import javax.xml.transform.Transformer; 044import javax.xml.transform.TransformerFactory; 045import javax.xml.transform.dom.DOMResult; 046import javax.xml.transform.sax.SAXSource; 047 048import org.hl7.fhir.exceptions.DefinitionException; 049import org.hl7.fhir.exceptions.FHIRException; 050import org.hl7.fhir.exceptions.FHIRFormatError; 051import org.hl7.fhir.r4.conformance.ProfileUtilities; 052import org.hl7.fhir.r4.context.IWorkerContext; 053import org.hl7.fhir.r4.elementmodel.Element.SpecialElement; 054import org.hl7.fhir.r4.formats.FormatUtilities; 055import org.hl7.fhir.r4.formats.IParser.OutputStyle; 056import org.hl7.fhir.r4.model.DateTimeType; 057import org.hl7.fhir.r4.model.ElementDefinition.PropertyRepresentation; 058import org.hl7.fhir.r4.model.Enumeration; 059import org.hl7.fhir.r4.model.StructureDefinition; 060import org.hl7.fhir.r4.utils.ToolingExtensions; 061import org.hl7.fhir.r4.utils.formats.XmlLocationAnnotator; 062import org.hl7.fhir.r4.utils.formats.XmlLocationData; 063import org.hl7.fhir.utilities.ElementDecoration; 064import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 065import org.hl7.fhir.utilities.StringPair; 066import org.hl7.fhir.utilities.Utilities; 067import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 068import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 069import org.hl7.fhir.utilities.xhtml.CDANarrativeFormat; 070import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 071import org.hl7.fhir.utilities.xhtml.XhtmlNode; 072import org.hl7.fhir.utilities.xhtml.XhtmlParser; 073import org.hl7.fhir.utilities.xml.IXMLWriter; 074import org.hl7.fhir.utilities.xml.XMLUtil; 075import org.hl7.fhir.utilities.xml.XMLWriter; 076import org.w3c.dom.Document; 077import org.w3c.dom.Node; 078import org.xml.sax.InputSource; 079import org.xml.sax.XMLReader; 080 081 082@Deprecated 083public class XmlParser extends ParserBase { 084 private boolean allowXsiLocation; 085 086 public XmlParser(IWorkerContext context) { 087 super(context); 088 } 089 090 public boolean isAllowXsiLocation() { 091 return allowXsiLocation; 092 } 093 094 public void setAllowXsiLocation(boolean allowXsiLocation) { 095 this.allowXsiLocation = allowXsiLocation; 096 } 097 098 public Element parse(InputStream stream) throws FHIRFormatError, DefinitionException, FHIRException, IOException { 099 Document doc = null; 100 try { 101 DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory(); 102 // xxe protection 103 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 104 factory.setFeature("http://xml.org/sax/features/external-general-entities", false); 105 factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 106 factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 107 factory.setXIncludeAware(false); 108 factory.setExpandEntityReferences(false); 109 110 factory.setNamespaceAware(true); 111 if (policy == ValidationPolicy.EVERYTHING) { 112 // use a slower parser that keeps location data 113 TransformerFactory transformerFactory = XMLUtil.newXXEProtectedTransformerFactory(); 114 Transformer nullTransformer = transformerFactory.newTransformer(); 115 DocumentBuilder docBuilder = factory.newDocumentBuilder(); 116 doc = docBuilder.newDocument(); 117 DOMResult domResult = new DOMResult(doc); 118 SAXParserFactory spf = XMLUtil.newXXEProtectedSaxParserFactory(); 119 spf.setNamespaceAware(true); 120 spf.setValidating(false); 121 XMLReader xmlReader = XMLUtil.getXXEProtectedXMLReader(spf); 122 123 XmlLocationAnnotator locationAnnotator = new XmlLocationAnnotator(xmlReader, doc); 124 InputSource inputSource = new InputSource(stream); 125 SAXSource saxSource = new SAXSource(locationAnnotator, inputSource); 126 nullTransformer.transform(saxSource, domResult); 127 } else { 128 DocumentBuilder builder = factory.newDocumentBuilder(); 129 doc = builder.parse(stream); 130 } 131 } catch (Exception e) { 132 logError(0, 0, "(syntax)", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL); 133 doc = null; 134 } 135 if (doc == null) 136 return null; 137 else 138 return parse(doc); 139 } 140 141 private void checkForProcessingInstruction(Document document) throws FHIRFormatError { 142 if (policy == ValidationPolicy.EVERYTHING 143 && FormatUtilities.FHIR_NS.equals(document.getDocumentElement().getNamespaceURI())) { 144 Node node = document.getFirstChild(); 145 while (node != null) { 146 if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) 147 logError(line(document), col(document), "(document)", IssueType.INVALID, 148 "No processing instructions allowed in resources", IssueSeverity.ERROR); 149 node = node.getNextSibling(); 150 } 151 } 152 } 153 154 private int line(Node node) { 155 XmlLocationData loc = (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); 156 return loc == null ? 0 : loc.getStartLine(); 157 } 158 159 private int col(Node node) { 160 XmlLocationData loc = (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); 161 return loc == null ? 0 : loc.getStartColumn(); 162 } 163 164 public Element parse(Document doc) throws FHIRFormatError, DefinitionException, FHIRException, IOException { 165 checkForProcessingInstruction(doc); 166 org.w3c.dom.Element element = doc.getDocumentElement(); 167 return parse(element); 168 } 169 170 public Element parse(org.w3c.dom.Element element) 171 throws FHIRFormatError, DefinitionException, FHIRException, IOException { 172 String ns = element.getNamespaceURI(); 173 String name = element.getLocalName(); 174 String path = "/" + pathPrefix(ns) + name; 175 176 StructureDefinition sd = getDefinition(line(element), col(element), ns, name); 177 if (sd == null) 178 return null; 179 180 Element result = new Element(element.getLocalName(), 181 new Property(context, sd.getSnapshot().getElement().get(0), sd)); 182 checkElement(element, path, result.getProperty()); 183 result.markLocation(line(element), col(element)); 184 result.setType(element.getLocalName()); 185 parseChildren(path, element, result); 186 result.numberChildren(); 187 return result; 188 } 189 190 private String pathPrefix(String ns) { 191 if (Utilities.noString(ns)) 192 return ""; 193 if (ns.equals(FormatUtilities.FHIR_NS)) 194 return "f:"; 195 if (ns.equals(FormatUtilities.XHTML_NS)) 196 return "h:"; 197 if (ns.equals("urn:hl7-org:v3")) 198 return "v3:"; 199 return "?:"; 200 } 201 202 private boolean empty(org.w3c.dom.Element element) { 203 for (int i = 0; i < element.getAttributes().getLength(); i++) { 204 String n = element.getAttributes().item(i).getNodeName(); 205 if (!n.equals("xmlns") && !n.startsWith("xmlns:")) 206 return false; 207 } 208 if (!Utilities.noString(element.getTextContent().trim())) 209 return false; 210 211 Node n = element.getFirstChild(); 212 while (n != null) { 213 if (n.getNodeType() == Node.ELEMENT_NODE) 214 return false; 215 n = n.getNextSibling(); 216 } 217 return true; 218 } 219 220 private void checkElement(org.w3c.dom.Element element, String path, Property prop) throws FHIRFormatError { 221 if (policy == ValidationPolicy.EVERYTHING) { 222 if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR 223 // Content 224 logError(line(element), col(element), path, IssueType.INVALID, "Element must have some content", 225 IssueSeverity.ERROR); 226 String ns = FormatUtilities.FHIR_NS; 227 if (ToolingExtensions.hasExtension(prop.getDefinition(), 228 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) 229 ns = ToolingExtensions.readStringExtension(prop.getDefinition(), 230 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 231 else if (ToolingExtensions.hasExtension(prop.getStructure(), 232 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) 233 ns = ToolingExtensions.readStringExtension(prop.getStructure(), 234 "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 235 if (!element.getNamespaceURI().equals(ns)) 236 logError(line(element), col(element), path, IssueType.INVALID, "Wrong namespace - expected '" + ns + "'", 237 IssueSeverity.ERROR); 238 } 239 } 240 241 public Element parse(org.w3c.dom.Element base, String type) throws Exception { 242 StructureDefinition sd = getDefinition(0, 0, FormatUtilities.FHIR_NS, type); 243 Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)); 244 String path = "/" + pathPrefix(base.getNamespaceURI()) + base.getLocalName(); 245 checkElement(base, path, result.getProperty()); 246 result.setType(base.getLocalName()); 247 parseChildren(path, base, result); 248 result.numberChildren(); 249 return result; 250 } 251 252 private void parseChildren(String path, org.w3c.dom.Element node, Element context) 253 throws FHIRFormatError, FHIRException, IOException, DefinitionException { 254 // this parsing routine retains the original order in a the XML file, to support 255 // validation 256 reapComments(node, context); 257 List<Property> properties = context.getProperty().getChildProperties(context.getName(), XMLUtil.getXsiType(node)); 258 259 String text = XMLUtil.getDirectText(node).trim(); 260 if (!Utilities.noString(text)) { 261 Property property = getTextProp(properties); 262 if (property != null) { 263 context.getChildren().add( 264 new Element(property.getName(), property, property.getType(), text).markLocation(line(node), col(node))); 265 } else { 266 logError(line(node), col(node), path, IssueType.STRUCTURE, "Text should not be present", IssueSeverity.ERROR); 267 } 268 } 269 270 for (int i = 0; i < node.getAttributes().getLength(); i++) { 271 Node attr = node.getAttributes().item(i); 272 if (!(attr.getNodeName().equals("xmlns") || attr.getNodeName().startsWith("xmlns:"))) { 273 Property property = getAttrProp(properties, attr.getNodeName()); 274 if (property != null) { 275 String av = attr.getNodeValue(); 276 if (ToolingExtensions.hasExtension(property.getDefinition(), 277 "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat")) 278 av = convertForDateFormatFromExternal(ToolingExtensions.readStringExtension(property.getDefinition(), 279 "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), av); 280 if (property.getName().equals("value") && context.isPrimitive()) 281 context.setValue(av); 282 else 283 context.getChildren().add( 284 new Element(property.getName(), property, property.getType(), av).markLocation(line(node), col(node))); 285 } else { 286 boolean ok = false; 287 if (FormatUtilities.FHIR_NS.equals(node.getNamespaceURI())) { 288 if (attr.getLocalName().equals("schemaLocation") && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())) { 289 ok = ok || allowXsiLocation; 290 } 291 } else 292 ok = ok || (attr.getLocalName().equals("schemaLocation")); // xsi:schemalocation allowed for non FHIR 293 // content 294 ok = ok || (hasTypeAttr(context) && attr.getLocalName().equals("type") 295 && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())); // xsi:type allowed if element says so 296 if (!ok) 297 logError(line(node), col(node), path, IssueType.STRUCTURE, 298 "Undefined attribute '@" + attr.getNodeName() + "' on " + node.getNodeName() + " for type " 299 + context.fhirType() + " (properties = " + properties + ")", 300 IssueSeverity.ERROR); 301 } 302 } 303 } 304 305 Node child = node.getFirstChild(); 306 while (child != null) { 307 if (child.getNodeType() == Node.ELEMENT_NODE) { 308 Property property = getElementProp(properties, child.getLocalName()); 309 if (property != null) { 310 if (!property.isChoice() && "xhtml".equals(property.getType())) { 311 XhtmlNode xhtml; 312 if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT)) 313 xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child); 314 else { 315 XhtmlParser xp = new XhtmlParser(); 316 xhtml = xp.parseHtmlNode((org.w3c.dom.Element) child); 317 if (policy == ValidationPolicy.EVERYTHING) { 318 for (StringPair s : xp.getValidationIssues()) { 319 logError(line(child), col(child), path, IssueType.INVALID, s.getName() + " " + s.getValue(), 320 IssueSeverity.ERROR); 321 } 322 } 323 } 324 context.getChildren() 325 .add(new Element(property.getName(), property, "xhtml", 326 new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml) 327 .markLocation(line(child), col(child))); 328 } else { 329 String npath = path + "/" + pathPrefix(child.getNamespaceURI()) + child.getLocalName(); 330 Element n = new Element(child.getLocalName(), property).markLocation(line(child), col(child)); 331 checkElement((org.w3c.dom.Element) child, npath, n.getProperty()); 332 boolean ok = true; 333 if (property.isChoice()) { 334 if (property.getDefinition().hasRepresentation(PropertyRepresentation.TYPEATTR)) { 335 String xsiType = ((org.w3c.dom.Element) child).getAttributeNS(FormatUtilities.NS_XSI, "type"); 336 if (Utilities.noString(xsiType)) { 337 logError(line(child), col(child), path, IssueType.STRUCTURE, 338 "No type found on '" + child.getLocalName() + '"', IssueSeverity.ERROR); 339 ok = false; 340 } else { 341 if (xsiType.contains(":")) 342 xsiType = xsiType.substring(xsiType.indexOf(":") + 1); 343 n.setType(xsiType); 344 n.setExplicitType(xsiType); 345 } 346 } else 347 n.setType(n.getType()); 348 } 349 context.getChildren().add(n); 350 if (ok) { 351 if (property.isResource()) 352 parseResource(npath, (org.w3c.dom.Element) child, n, property); 353 else 354 parseChildren(npath, (org.w3c.dom.Element) child, n); 355 } 356 } 357 } else 358 logError(line(child), col(child), path, IssueType.STRUCTURE, 359 "Undefined element '" + child.getLocalName() + "'", IssueSeverity.ERROR); 360 } else if (child.getNodeType() == Node.CDATA_SECTION_NODE) { 361 logError(line(child), col(child), path, IssueType.STRUCTURE, "CDATA is not allowed", IssueSeverity.ERROR); 362 } else if (!Utilities.existsInList(child.getNodeType(), 3, 8)) { 363 logError(line(child), col(child), path, IssueType.STRUCTURE, 364 "Node type " + Integer.toString(child.getNodeType()) + " is not allowed", IssueSeverity.ERROR); 365 } 366 child = child.getNextSibling(); 367 } 368 } 369 370 private Property getElementProp(List<Property> properties, String nodeName) { 371 List<Property> propsSortedByLongestFirst = new ArrayList<Property>(properties); 372 // sort properties according to their name longest first, so 373 // .requestOrganizationReference comes first before .request[x] 374 // and therefore the longer property names get evaluated first 375 Collections.sort(propsSortedByLongestFirst, new Comparator<Property>() { 376 @Override 377 public int compare(Property o1, Property o2) { 378 return o2.getName().length() - o1.getName().length(); 379 } 380 }); 381 for (Property p : propsSortedByLongestFirst) 382 if (!p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) 383 && !p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) { 384 if (p.getName().equals(nodeName)) 385 return p; 386 if (p.getName().endsWith("[x]") && nodeName.length() > p.getName().length() - 3 && p.getName() 387 .substring(0, p.getName().length() - 3).equals(nodeName.substring(0, p.getName().length() - 3))) 388 return p; 389 } 390 return null; 391 } 392 393 private Property getAttrProp(List<Property> properties, String nodeName) { 394 for (Property p : properties) 395 if (p.getName().equals(nodeName) && p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR)) 396 return p; 397 return null; 398 } 399 400 private Property getTextProp(List<Property> properties) { 401 for (Property p : properties) 402 if (p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) 403 return p; 404 return null; 405 } 406 407 private String convertForDateFormatFromExternal(String fmt, String av) throws FHIRException { 408 if ("v3".equals(fmt)) { 409 DateTimeType d = DateTimeType.parseV3(av); 410 return d.asStringValue(); 411 } else 412 throw new FHIRException("Unknown Data format '" + fmt + "'"); 413 } 414 415 private String convertForDateFormatToExternal(String fmt, String av) throws FHIRException { 416 if ("v3".equals(fmt)) { 417 DateTimeType d = new DateTimeType(av); 418 return d.getAsV3(); 419 } else 420 throw new FHIRException("Unknown Date format '" + fmt + "'"); 421 } 422 423 private void parseResource(String string, org.w3c.dom.Element container, Element parent, Property elementProperty) 424 throws FHIRFormatError, DefinitionException, FHIRException, IOException { 425 org.w3c.dom.Element res = XMLUtil.getFirstChild(container); 426 String name = res.getLocalName(); 427 StructureDefinition sd = context.fetchResource(StructureDefinition.class, 428 ProfileUtilities.sdNs(name, context.getOverrideVersionNs())); 429 if (sd == null) 430 throw new FHIRFormatError( 431 "Contained resource does not appear to be a FHIR resource (unknown name '" + res.getLocalName() + "')"); 432 parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), 433 SpecialElement.fromProperty(parent.getProperty()), elementProperty); 434 parent.setType(name); 435 parseChildren(res.getLocalName(), res, parent); 436 } 437 438 private void reapComments(org.w3c.dom.Element element, Element context) { 439 Node node = element.getPreviousSibling(); 440 while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { 441 if (node.getNodeType() == Node.COMMENT_NODE) 442 context.getComments().add(0, node.getTextContent()); 443 node = node.getPreviousSibling(); 444 } 445 node = element.getLastChild(); 446 while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { 447 node = node.getPreviousSibling(); 448 } 449 while (node != null) { 450 if (node.getNodeType() == Node.COMMENT_NODE) 451 context.getComments().add(node.getTextContent()); 452 node = node.getNextSibling(); 453 } 454 } 455 456 private boolean isAttr(Property property) { 457 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 458 if (r.getValue() == PropertyRepresentation.XMLATTR) { 459 return true; 460 } 461 } 462 return false; 463 } 464 465 private boolean isCdaText(Property property) { 466 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 467 if (r.getValue() == PropertyRepresentation.CDATEXT) { 468 return true; 469 } 470 } 471 return false; 472 } 473 474 private boolean isTypeAttr(Property property) { 475 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 476 if (r.getValue() == PropertyRepresentation.TYPEATTR) { 477 return true; 478 } 479 } 480 return false; 481 } 482 483 private boolean isText(Property property) { 484 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 485 if (r.getValue() == PropertyRepresentation.XMLTEXT) { 486 return true; 487 } 488 } 489 return false; 490 } 491 492 @Override 493 public void compose(Element e, OutputStream stream, OutputStyle style, String base) 494 throws IOException, FHIRException { 495 XMLWriter xml = new XMLWriter(stream, "UTF-8"); 496 xml.setSortAttributes(false); 497 xml.setPretty(style == OutputStyle.PRETTY); 498 xml.start(); 499 xml.setDefaultNamespace(e.getProperty().getNamespace()); 500 if (hasTypeAttr(e)) 501 xml.namespace("http://www.w3.org/2001/XMLSchema-instance", "xsi"); 502 composeElement(xml, e, e.getType(), true); 503 xml.end(); 504 505 } 506 507 private boolean hasTypeAttr(Element e) { 508 if (isTypeAttr(e.getProperty())) 509 return true; 510 for (Element c : e.getChildren()) { 511 if (hasTypeAttr(c)) 512 return true; 513 } 514 return false; 515 } 516 517 public void compose(Element e, IXMLWriter xml) throws Exception { 518 xml.start(); 519 xml.setDefaultNamespace(e.getProperty().getNamespace()); 520 composeElement(xml, e, e.getType(), true); 521 xml.end(); 522 } 523 524 private void composeElement(IXMLWriter xml, Element element, String elementName, boolean root) 525 throws IOException, FHIRException { 526 if (showDecorations) { 527 @SuppressWarnings("unchecked") 528 List<ElementDecoration> decorations = (List<ElementDecoration>) element.getUserData("fhir.decorations"); 529 if (decorations != null) 530 for (ElementDecoration d : decorations) 531 xml.decorate(d); 532 } 533 for (String s : element.getComments()) { 534 xml.comment(s, true); 535 } 536 if (isText(element.getProperty())) { 537 if (linkResolver != null) 538 xml.link(linkResolver.resolveProperty(element.getProperty())); 539 xml.enter(elementName); 540 xml.text(element.getValue()); 541 xml.exit(elementName); 542 } else if (!element.hasChildren() && !element.hasValue()) { 543 if (element.getExplicitType() != null) 544 xml.attribute("xsi:type", element.getExplicitType()); 545 xml.element(elementName); 546 } else if (element.isPrimitive() || (element.hasType() && isPrimitive(element.getType()))) { 547 if (element.getType().equals("xhtml")) { 548 String rawXhtml = element.getValue(); 549 if (isCdaText(element.getProperty())) { 550 new CDANarrativeFormat().convert(xml, element.getXhtml()); 551 } else 552 xml.escapedText(rawXhtml); 553 } else if (isText(element.getProperty())) { 554 if (linkResolver != null) 555 xml.link(linkResolver.resolveProperty(element.getProperty())); 556 xml.text(element.getValue()); 557 } else { 558 if (isTypeAttr(element.getProperty()) && !Utilities.noString(element.getType())) { 559 xml.attribute("xsi:type", element.getType()); 560 } 561 if (element.hasValue()) { 562 if (linkResolver != null) 563 xml.link(linkResolver.resolveType(element.getType())); 564 xml.attribute("value", element.getValue()); 565 } 566 if (linkResolver != null) 567 xml.link(linkResolver.resolveProperty(element.getProperty())); 568 if (element.hasChildren()) { 569 xml.enter(elementName); 570 for (Element child : element.getChildren()) 571 composeElement(xml, child, child.getName(), false); 572 xml.exit(elementName); 573 } else 574 xml.element(elementName); 575 } 576 } else { 577 if (isTypeAttr(element.getProperty()) && !Utilities.noString(element.getType())) { 578 xml.attribute("xsi:type", element.getType()); 579 } 580 for (Element child : element.getChildren()) { 581 if (isAttr(child.getProperty())) { 582 if (linkResolver != null) 583 xml.link(linkResolver.resolveType(child.getType())); 584 String av = child.getValue(); 585 if (ToolingExtensions.hasExtension(child.getProperty().getDefinition(), 586 "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat")) 587 av = convertForDateFormatToExternal( 588 ToolingExtensions.readStringExtension(child.getProperty().getDefinition(), 589 "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), 590 av); 591 xml.attribute(child.getName(), av); 592 } 593 } 594 if (linkResolver != null) 595 xml.link(linkResolver.resolveProperty(element.getProperty())); 596 xml.enter(elementName); 597 if (!root && element.getSpecial() != null) { 598 if (linkResolver != null) 599 xml.link(linkResolver.resolveProperty(element.getProperty())); 600 xml.enter(element.getType()); 601 } 602 for (Element child : element.getChildren()) { 603 if (isText(child.getProperty())) { 604 if (linkResolver != null) 605 xml.link(linkResolver.resolveProperty(element.getProperty())); 606 xml.text(child.getValue()); 607 } else if (!isAttr(child.getProperty())) 608 composeElement(xml, child, child.getName(), false); 609 } 610 if (!root && element.getSpecial() != null) 611 xml.exit(element.getType()); 612 xml.exit(elementName); 613 } 614 } 615 616}