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