
001package org.hl7.fhir.r5.elementmodel; 002 003import java.io.ByteArrayInputStream; 004 005/* 006 Copyright (c) 2011+, HL7, Inc. 007 All rights reserved. 008 009 Redistribution and use in source and binary forms, with or without modification, 010 are permitted provided that the following conditions are met: 011 012 * Redistributions of source code must retain the above copyright notice, this 013 list of conditions and the following disclaimer. 014 * Redistributions in binary form must reproduce the above copyright notice, 015 this list of conditions and the following disclaimer in the documentation 016 and/or other materials provided with the distribution. 017 * Neither the name of HL7 nor the names of its contributors may be used to 018 endorse or promote products derived from this software without specific 019 prior written permission. 020 021 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 022 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 024 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 025 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 026 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 028 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 029 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 030 POSSIBILITY OF SUCH DAMAGE. 031 032 */ 033 034 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.OutputStream; 038import java.util.ArrayList; 039import java.util.Collections; 040import java.util.Comparator; 041import java.util.HashSet; 042import java.util.List; 043import java.util.Set; 044 045import javax.xml.parsers.DocumentBuilder; 046import javax.xml.parsers.DocumentBuilderFactory; 047import javax.xml.parsers.SAXParserFactory; 048import javax.xml.transform.Transformer; 049import javax.xml.transform.TransformerFactory; 050import javax.xml.transform.dom.DOMResult; 051import javax.xml.transform.sax.SAXSource; 052 053import org.hl7.fhir.exceptions.DefinitionException; 054import org.hl7.fhir.exceptions.FHIRException; 055import org.hl7.fhir.exceptions.FHIRFormatError; 056import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 057import org.hl7.fhir.r5.context.IWorkerContext; 058import org.hl7.fhir.r5.elementmodel.Element.SpecialElement; 059import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; 060import org.hl7.fhir.r5.formats.FormatUtilities; 061import org.hl7.fhir.r5.formats.IParser.OutputStyle; 062import org.hl7.fhir.r5.model.Constants; 063import org.hl7.fhir.r5.model.DateTimeType; 064import org.hl7.fhir.r5.model.ElementDefinition; 065import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; 066import org.hl7.fhir.r5.model.Enumeration; 067import org.hl7.fhir.r5.model.StructureDefinition; 068import org.hl7.fhir.r5.utils.ToolingExtensions; 069import org.hl7.fhir.r5.utils.UserDataNames; 070import org.hl7.fhir.r5.utils.formats.XmlLocationAnnotator; 071import org.hl7.fhir.r5.utils.formats.XmlLocationData; 072import org.hl7.fhir.utilities.ElementDecoration; 073import org.hl7.fhir.utilities.StringPair; 074import org.hl7.fhir.utilities.FileUtilities; 075import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 076import org.hl7.fhir.utilities.Utilities; 077import org.hl7.fhir.utilities.i18n.I18nConstants; 078import org.hl7.fhir.utilities.validation.ValidationMessage; 079import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 080import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 081import org.hl7.fhir.utilities.xhtml.CDANarrativeFormat; 082import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 083import org.hl7.fhir.utilities.xhtml.XhtmlNode; 084import org.hl7.fhir.utilities.xhtml.XhtmlParser; 085import org.hl7.fhir.utilities.xml.IXMLWriter; 086import org.hl7.fhir.utilities.xml.XMLUtil; 087import org.hl7.fhir.utilities.xml.XMLWriter; 088import org.w3c.dom.Document; 089import org.w3c.dom.Node; 090import org.xml.sax.ErrorHandler; 091import org.xml.sax.InputSource; 092import org.xml.sax.SAXParseException; 093import org.xml.sax.XMLReader; 094 095@MarkedToMoveToAdjunctPackage 096public class XmlParser extends ParserBase { 097 private boolean allowXsiLocation; 098 private String version; 099 private boolean elideElements; 100 101 public XmlParser(IWorkerContext context) { 102 super(context); 103 } 104 105 private String schemaPath; 106 private boolean markedXhtml; 107 108 public String getSchemaPath() { 109 return schemaPath; 110 } 111 public void setSchemaPath(String schemaPath) { 112 this.schemaPath = schemaPath; 113 } 114 115 public boolean isAllowXsiLocation() { 116 return allowXsiLocation; 117 } 118 119 public void setAllowXsiLocation(boolean allowXsiLocation) { 120 this.allowXsiLocation = allowXsiLocation; 121 } 122 123 public List<ValidatedFragment> parse(InputStream inStream) throws FHIRFormatError, DefinitionException, FHIRException, IOException { 124 125 byte[] content = FileUtilities.streamToBytes(inStream); 126 ValidatedFragment focusFragment = new ValidatedFragment(ValidatedFragment.FOCUS_NAME, "xml", content, false); 127 128 ByteArrayInputStream stream = new ByteArrayInputStream(content); 129 Document doc = null; 130 try { 131 DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory(); 132 // xxe protection 133 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 134 factory.setFeature("http://xml.org/sax/features/external-general-entities", false); 135 factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 136 factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 137 factory.setXIncludeAware(false); 138 factory.setExpandEntityReferences(false); 139 140 factory.setNamespaceAware(true); 141 if (policy == ValidationPolicy.EVERYTHING) { 142 // The SAX interface appears to not work when reporting the correct version/encoding. 143 // if we can, we'll inspect the header/encoding ourselves 144 145 stream.mark(1024); 146 version = checkHeader(focusFragment.getErrors(), stream); 147 stream.reset(); 148 149 // use a slower parser that keeps location data 150 TransformerFactory transformerFactory = XMLUtil.newXXEProtectedTransformerFactory(); 151 Transformer nullTransformer = transformerFactory.newTransformer(); 152 DocumentBuilder docBuilder = factory.newDocumentBuilder(); 153 doc = docBuilder.newDocument(); 154 DOMResult domResult = new DOMResult(doc); 155 SAXParserFactory spf = XMLUtil.newXXEProtectedSaxParserFactory(); 156 spf.setNamespaceAware(true); 157 spf.setValidating(false); 158 159 XMLReader xmlReader = XMLUtil.getXXEProtectedXMLReader(spf); 160 161 XmlLocationAnnotator locationAnnotator = new XmlLocationAnnotator(xmlReader, doc); 162 InputSource inputSource = new InputSource(stream); 163 SAXSource saxSource = new SAXSource(locationAnnotator, inputSource); 164 nullTransformer.transform(saxSource, domResult); 165 } else { 166 DocumentBuilder builder = factory.newDocumentBuilder(); 167 builder.setErrorHandler(new NullErrorHandler()); 168 doc = builder.parse(stream); 169 } 170 } catch (Exception e) { 171 if (e.getMessage().contains("lineNumber:") && e.getMessage().contains("columnNumber:")) { 172 int line = Utilities.parseInt(extractVal(e.getMessage(), "lineNumber"), 0); 173 int col = Utilities.parseInt(extractVal(e.getMessage(), "columnNumber"), 0); 174 logError(focusFragment.getErrors(), ValidationMessage.NO_RULE_DATE, line, col, "(xml)", IssueType.INVALID, e.getMessage().substring(e.getMessage().lastIndexOf(";")+1).trim(), IssueSeverity.FATAL); 175 } else { 176 logError(focusFragment.getErrors(), ValidationMessage.NO_RULE_DATE, 0, 0, "(xml)", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL); 177 } 178 doc = null; 179 } 180 if (doc != null) { 181 focusFragment.setElement(parse(focusFragment.getErrors(), doc)); 182 } 183 List<ValidatedFragment> res = new ArrayList<>(); 184 res.add(focusFragment); 185 return res; 186 } 187 188 189 private String extractVal(String src, String name) { 190 src = src.substring(src.indexOf(name)+name.length()+1); 191 src = src.substring(0, src.indexOf(";")).trim(); 192 return src; 193 } 194 private void checkForProcessingInstruction(List<ValidationMessage> errors, Document document) throws FHIRFormatError { 195 if (policy == ValidationPolicy.EVERYTHING && FormatUtilities.FHIR_NS.equals(document.getDocumentElement().getNamespaceURI())) { 196 Node node = document.getFirstChild(); 197 while (node != null) { 198 if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) 199 logError(errors, ValidationMessage.NO_RULE_DATE, line(document, false), col(document, false), "(document)", IssueType.INVALID, context.formatMessage( 200 I18nConstants.NO_PROCESSING_INSTRUCTIONS_ALLOWED_IN_RESOURCES), IssueSeverity.ERROR); 201 node = node.getNextSibling(); 202 } 203 } 204 } 205 206 207 private int line(Node node, boolean end) { 208 XmlLocationData loc = node == null ? null : (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); 209 return loc == null ? 0 : end ? loc.getEndLine() : loc.getStartLine(); 210 } 211 212 private int col(Node node, boolean end) { 213 XmlLocationData loc = node == null ? null : (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); 214 return loc == null ? 0 : end ? loc.getEndColumn() : loc.getStartColumn(); 215 } 216 217 public Element parse(List<ValidationMessage> errors, Document doc) throws FHIRFormatError, DefinitionException, FHIRException, IOException { 218 checkForProcessingInstruction(errors, doc); 219 org.w3c.dom.Element element = doc.getDocumentElement(); 220 return parse(errors, element); 221 } 222 223 public Element parse(List<ValidationMessage> errors, org.w3c.dom.Element element) throws FHIRFormatError, DefinitionException, FHIRException, IOException { 224 String ns = element.getNamespaceURI(); 225 String name = element.getLocalName(); 226 String path = "/"+pathPrefix(ns)+name; 227 228 StructureDefinition sd = getDefinition(errors, line(element, false), col(element, false), (ns == null ? "noNamespace" : ns), name); 229 if (sd == null) 230 return null; 231 232 Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.XML); 233 result.setPath(element.getLocalName()); 234 checkElement(errors, element, result, path, result.getProperty(), false); 235 result.markLocation(line(element, false), col(element, false)); 236 result.setType(element.getLocalName()); 237 parseChildren(errors, path, element, result); 238 result.numberChildren(); 239 return result; 240 } 241 242 private String pathPrefix(String ns) { 243 if (Utilities.noString(ns)) 244 return ""; 245 if (ns.equals(FormatUtilities.FHIR_NS)) 246 return "f:"; 247 if (ns.equals(FormatUtilities.XHTML_NS)) 248 return "h:"; 249 if (ns.equals("urn:hl7-org:v3")) 250 return "v3:"; 251 if (ns.equals("urn:hl7-org:sdtc")) 252 return "sdtc:"; 253 if (ns.equals("urn:ihe:pharm")) 254 return "pharm:"; 255 if (ns.equals("http://ns.electronichealth.net.au/Ci/Cda/Extensions/3.0")) 256 return "ext:"; 257 return "?:"; 258 } 259 260 private boolean empty(org.w3c.dom.Element element) { 261 for (int i = 0; i < element.getAttributes().getLength(); i++) { 262 String n = element.getAttributes().item(i).getNodeName(); 263 if (!n.equals("xmlns") && !n.startsWith("xmlns:")) 264 return false; 265 } 266 if (!Utilities.noString(element.getTextContent().trim())) 267 return false; 268 269 Node n = element.getFirstChild(); 270 while (n != null) { 271 if (n.getNodeType() == Node.ELEMENT_NODE) 272 return false; 273 n = n.getNextSibling(); 274 } 275 return true; 276 } 277 278 private void checkElement(List<ValidationMessage> errors, org.w3c.dom.Element element, Element e, String path, Property prop, boolean xsiTypeChecked) throws FHIRFormatError { 279 if (policy == ValidationPolicy.EVERYTHING) { 280 if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR Content 281 logError(errors, ValidationMessage.NO_RULE_DATE, line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.ELEMENT_MUST_HAVE_SOME_CONTENT), IssueSeverity.ERROR); 282 String ns = prop.getXmlNamespace(); 283 String elementNs = element.getNamespaceURI(); 284 if (elementNs == null) { 285 elementNs = "noNamespace"; 286 } 287 if (!elementNs.equals(ns)) { 288 logError(errors, ValidationMessage.NO_RULE_DATE, line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.WRONG_NAMESPACE__EXPECTED_, ns), IssueSeverity.ERROR); 289 } 290 if (!xsiTypeChecked) { 291 String xsiType = element.getAttributeNS(FormatUtilities.NS_XSI, "type"); 292 if (!Utilities.noString(xsiType)) { 293 String actualType = prop.getXmlTypeName(); 294 if (xsiType.equals(actualType)) { 295 logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_UNNECESSARY), IssueSeverity.INFORMATION); 296 } else { 297 StructureDefinition sd = findLegalConstraint(xsiType, actualType); 298 if (sd != null) { 299 e.setType(sd.getType()); 300 e.setExplicitType(xsiType); 301 } else { 302 logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_WRONG, xsiType, actualType), IssueSeverity.ERROR); 303 } 304 } 305 } 306 } 307 } 308 } 309 310 private StructureDefinition findLegalConstraint(String xsiType, String actualType) { 311 StructureDefinition sdA = context.fetchTypeDefinition(actualType); 312 StructureDefinition sd = context.fetchTypeDefinition(xsiType); 313 while (sd != null) { 314 if (sd == sdA) { 315 return sd; 316 } 317 sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 318 } 319 return null; 320 } 321 322 public Element parse(List<ValidationMessage> errors, org.w3c.dom.Element base, String type) throws Exception { 323 StructureDefinition sd = getDefinition(errors, 0, 0, FormatUtilities.FHIR_NS, type); 324 Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.XML).setNativeObject(base); 325 result.setPath(base.getLocalName()); 326 String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName(); 327 checkElement(errors, base, result, path, result.getProperty(), false); 328 result.setType(base.getLocalName()); 329 parseChildren(errors, path, base, result); 330 result.numberChildren(); 331 return result; 332 } 333 334 private void parseChildren(List<ValidationMessage> errors, String path, org.w3c.dom.Element node, Element element) throws FHIRFormatError, FHIRException, IOException, DefinitionException { 335 // this parsing routine retains the original order in a the XML file, to support validation 336 reapComments(node, element); 337 List<Property> properties = element.getProperty().getChildProperties(element.getName(), XMLUtil.getXsiType(node)); 338 Property cgProp = getChoiceGroupProp(properties); 339 Property mtProp = cgProp == null ? null : getTextProp(cgProp.getChildProperties(null, null)); 340 341 String text = mtProp == null ? XMLUtil.getDirectText(node).trim() : null; 342 int line = line(node, false); 343 int col = col(node, false); 344 if (!Utilities.noString(text)) { 345 Property property = getTextProp(properties); 346 if (property != null) { 347 if ("ED.data[x]".equals(property.getDefinition().getId()) || (property.getDefinition()!=null && property.getDefinition().getBase()!=null && "ED.data[x]".equals(property.getDefinition().getBase().getPath()))) { 348 if ("B64".equals(node.getAttribute("representation"))) { 349 Element n = new Element("dataBase64Binary", property, "base64Binary", text).markLocation(line, col).setFormat(FhirFormat.XML); 350 n.setPath(element.getPath()+"."+property.getName()); 351 element.getChildren().add(n); 352 } else { 353 Element n = new Element("dataString", property, "string", text).markLocation(line, col).setFormat(FhirFormat.XML); 354 n.setPath(element.getPath()+"."+property.getName()); 355 element.getChildren().add(n); 356 } 357 } else { 358 Element n = new Element(property.getName(), property, property.getType(), text).markLocation(line, col).setFormat(FhirFormat.XML); 359 n.setPath(element.getPath()+"."+property.getName()); 360 element.getChildren().add(n); 361 } 362 } else { 363 Node n = node.getFirstChild(); 364 while (n != null) { 365 if (n.getNodeType() == Node.TEXT_NODE && !Utilities.noString(n.getTextContent().trim())) { 366 Node nt = n; // try to find the nearest element for a line/col location 367 boolean end = false; 368 while (nt.getPreviousSibling() != null && nt.getNodeType() != Node.ELEMENT_NODE) { 369 nt = nt.getPreviousSibling(); 370 end = true; 371 } 372 while (nt.getNextSibling() != null && nt.getNodeType() != Node.ELEMENT_NODE) { 373 nt = nt.getNextSibling(); 374 end = false; 375 } 376 line = line(nt, end); 377 col = col(nt, end); 378 logError(errors, ValidationMessage.NO_RULE_DATE, line, col, path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.TEXT_SHOULD_NOT_BE_PRESENT, Utilities.makeSingleLine(n.getTextContent().trim())), IssueSeverity.ERROR); 379 } 380 n = n.getNextSibling(); 381 } 382 } 383 } 384 385 for (int i = 0; i < node.getAttributes().getLength(); i++) { 386 Node attr = node.getAttributes().item(i); 387 String value = attr.getNodeValue(); 388 if (!validAttrValue(value)) { 389 logError(errors, ValidationMessage.NO_RULE_DATE, line, col, path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.XML_ATTR_VALUE_INVALID, attr.getNodeName()), IssueSeverity.ERROR); 390 } 391 if (!(attr.getNodeName().equals("xmlns") || attr.getNodeName().startsWith("xmlns:"))) { 392 Property property = getAttrProp(properties, attr.getLocalName(), attr.getNamespaceURI()); 393 if (property != null) { 394 String av = attr.getNodeValue(); 395 if (ToolingExtensions.hasExtension(property.getDefinition(), ToolingExtensions.EXT_DATE_FORMAT)) 396 av = convertForDateFormatFromExternal(ToolingExtensions.readStringExtension(property.getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av); 397 if (property.getName().equals("value") && element.isPrimitive()) 398 element.setValue(av); 399 else { 400 String[] vl = {av}; 401 if (property.isList() && av.contains(" ")) { 402 vl = av.split(" "); 403 } 404 for (String v : vl) { 405 Element n = new Element(property.getName(), property, property.getType(), v).markLocation(line, col).setFormat(FhirFormat.XML); 406 n.setPath(element.getPath()+"."+property.getName()); 407 element.getChildren().add(n); 408 } 409 } 410 } else { 411 boolean ok = false; 412 if (FormatUtilities.FHIR_NS.equals(node.getNamespaceURI())) { 413 if (attr.getLocalName().equals("schemaLocation") && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())) { 414 ok = ok || allowXsiLocation; 415 } 416 } else 417 ok = ok || (attr.getLocalName().equals("schemaLocation")); // xsi:schemalocation allowed for non FHIR content 418 ok = ok || (hasTypeAttr(element) && attr.getLocalName().equals("type") && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())); // xsi:type allowed if element says so 419 if (!ok) { 420 logError(errors, ValidationMessage.NO_RULE_DATE, line(node, false), col(node, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNDEFINED_ATTRIBUTE__ON__FOR_TYPE__PROPERTIES__, attr.getNodeName(), node.getNodeName(), element.fhirType(), properties), IssueSeverity.ERROR); 421 } 422 } 423 } 424 } 425 426 String lastName = null; 427 int repeatCount = 0; 428 Node child = node.getFirstChild(); 429 while (child != null) { 430 if (child.getNodeType() == Node.ELEMENT_NODE) { 431 Property property = getElementProp(properties, child.getLocalName(), child.getNamespaceURI()); 432 433 if (property != null) { 434 if (property.getName().equals(lastName)) { 435 repeatCount++; 436 } else { 437 lastName = property.getName(); 438 repeatCount = 0; 439 } 440 if (!property.isChoice() && "xhtml".equals(property.getType())) { 441 XhtmlNode xhtml; 442 if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT)) 443 xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child); 444 else { 445 XhtmlParser xp = new XhtmlParser(); 446 xhtml = xp.parseHtmlNode((org.w3c.dom.Element) child); 447 if (policy == ValidationPolicy.EVERYTHING) { 448 for (StringPair s : xp.getValidationIssues()) { 449 logError(errors, "2022-11-17", line(child, false), col(child, false), path, IssueType.INVALID, context.formatMessage(s.getName(), s.getValue()), IssueSeverity.ERROR); 450 } 451 } 452 } 453 Element n = new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child); 454 n.setPath(element.getPath()+"."+property.getName()); 455 element.getChildren().add(n); 456 } else { 457 String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName(); 458 String name = child.getLocalName(); 459 if (!property.isChoice() && !name.equals(property.getName())) { 460 name = property.getName(); 461 } 462 Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child); 463 if (property.isList()) { 464 n.setPath(element.getPath()+"."+property.getName()+"["+repeatCount+"]"); 465 } else { 466 n.setPath(element.getPath()+"."+property.getName()); 467 } 468 boolean xsiTypeChecked = false; 469 boolean ok = true; 470 if (property.isChoice()) { 471 if (property.getDefinition().hasRepresentation(PropertyRepresentation.TYPEATTR)) { 472 String xsiType = ((org.w3c.dom.Element) child).getAttributeNS(FormatUtilities.NS_XSI, "type"); 473 if (Utilities.noString(xsiType)) { 474 if (ToolingExtensions.hasExtension(property.getDefinition(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) { 475 xsiType = ToolingExtensions.readStringExtension(property.getDefinition(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"); 476 n.setType(xsiType); 477 } else { 478 logError(errors, ValidationMessage.NO_RULE_DATE, line(child, false), col(child, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.NO_TYPE_FOUND_ON_, child.getLocalName()), IssueSeverity.ERROR); 479 ok = false; 480 } 481 } else { 482 if (xsiType.contains(":")) 483 xsiType = xsiType.substring(xsiType.indexOf(":")+1); 484 n.setType(xsiType); 485 n.setExplicitType(xsiType); 486 } 487 xsiTypeChecked = true; 488 } else 489 n.setType(n.getType()); 490 } 491 checkElement(errors, (org.w3c.dom.Element) child, n, npath, n.getProperty(), xsiTypeChecked); 492 element.getChildren().add(n); 493 if (ok) { 494 if (property.isResource()) 495 parseResource(errors, npath, (org.w3c.dom.Element) child, n, property); 496 else 497 parseChildren(errors, npath, (org.w3c.dom.Element) child, n); 498 } 499 } 500 } else { 501 if (cgProp != null) { 502 property = getElementProp(cgProp.getChildProperties(null, null), child.getLocalName(), child.getNamespaceURI()); 503 if (property != null) { 504 if (cgProp.getName().equals(lastName)) { 505 repeatCount++; 506 } else { 507 lastName = cgProp.getName(); 508 repeatCount = 0; 509 } 510 511 String npath = path+"/"+pathPrefix(cgProp.getXmlNamespace())+cgProp.getName(); 512 String name = cgProp.getName(); 513 Element cgn = new Element(cgProp.getName(), cgProp).setFormat(FhirFormat.XML); 514 cgn.setPath(element.getPath()+"."+cgProp.getName()+"["+repeatCount+"]"); 515 element.getChildren().add(cgn); 516 517 npath = npath+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName(); 518 name = child.getLocalName(); 519 Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child); 520 cgn.getChildren().add(n); 521 n.setPath(element.getPath()+"."+property.getName()); 522 checkElement(errors, (org.w3c.dom.Element) child, n, npath, n.getProperty(), false); 523 parseChildren(errors, npath, (org.w3c.dom.Element) child, n); 524 } 525 } 526 if (property == null) { 527 logError(errors, ValidationMessage.NO_RULE_DATE, line(child, false), col(child, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNDEFINED_ELEMENT_, child.getLocalName(), path), IssueSeverity.ERROR); 528 } 529 } 530 } else if (child.getNodeType() == Node.TEXT_NODE && !Utilities.noString(child.getTextContent().trim()) && mtProp != null) { 531 if (cgProp.getName().equals(lastName)) { 532 repeatCount++; 533 } else { 534 lastName = cgProp.getName(); 535 repeatCount = 0; 536 } 537 538 String npath = path+"/"+pathPrefix(cgProp.getXmlNamespace())+cgProp.getName(); 539 String name = cgProp.getName(); 540 Element cgn = new Element(cgProp.getName(), cgProp).setFormat(FhirFormat.XML); 541 cgn.setPath(element.getPath()+"."+cgProp.getName()+"["+repeatCount+"]"); 542 element.getChildren().add(cgn); 543 544 npath = npath+"/text()"; 545 name = mtProp.getName(); 546 Element n = new Element(name, mtProp, mtProp.getType(), child.getTextContent().trim()).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child); 547 cgn.getChildren().add(n); 548 n.setPath(element.getPath()+"."+mtProp.getName()); 549 550 551 } else if (child.getNodeType() == Node.CDATA_SECTION_NODE) { 552 logError(errors, ValidationMessage.NO_RULE_DATE, line(child, false), col(child, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.CDATA_IS_NOT_ALLOWED), IssueSeverity.ERROR); 553 } else if (!Utilities.existsInList(child.getNodeType(), 3, 8)) { 554 logError(errors, ValidationMessage.NO_RULE_DATE, line(child, false), col(child, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.NODE_TYPE__IS_NOT_ALLOWED, Integer.toString(child.getNodeType())), IssueSeverity.ERROR); 555 } 556 child = child.getNextSibling(); 557 } 558 } 559 560 private Property getChoiceGroupProp(List<Property> properties) { 561 for (Property p : properties) { 562 if (p.getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) { 563 return p; 564 } 565 } 566 return null; 567 } 568 569 private boolean validAttrValue(String value) { 570 if (version == null) { 571 return true; 572 } 573 if (version.equals("1.0")) { 574 boolean ok = true; 575 for (char ch : value.toCharArray()) { 576 if (ch <= 0x1F && !Utilities.existsInList(ch, '\r', '\n', '\t')) { 577 ok = false; 578 } 579 } 580 return ok; 581 } else 582 return true; 583 } 584 585 586 private Property getElementProp(List<Property> properties, String nodeName, String namespace) { 587 List<Property> propsSortedByLongestFirst = new ArrayList<Property>(properties); 588 // sort properties according to their name longest first, so .requestOrganizationReference comes first before .request[x] 589 // and therefore the longer property names get evaluated first 590 Collections.sort(propsSortedByLongestFirst, new Comparator<Property>() { 591 @Override 592 public int compare(Property o1, Property o2) { 593 return o2.getName().length() - o1.getName().length(); 594 } 595 }); 596 // first scan, by namespace 597 for (Property p : propsSortedByLongestFirst) { 598 if (!p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && !p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) { 599 if (p.getXmlName().equals(nodeName) && p.getXmlNamespace().equals(namespace)) 600 return p; 601 } 602 } 603 for (Property p : propsSortedByLongestFirst) { 604 if (!p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && !p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) { 605 if (p.getXmlName().equals(nodeName)) 606 return p; 607 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))) 608 return p; 609 } 610 } 611 612 613 return null; 614 } 615 616 private Property getAttrProp(List<Property> properties, String nodeName, String namespace) { 617 for (Property p : properties) { 618 if (p.getXmlName().equals(nodeName) && p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && p.getXmlNamespace().equals(namespace)) { 619 return p; 620 } 621 } 622 if (namespace == null) { 623 for (Property p : properties) { 624 if (p.getXmlName().equals(nodeName) && p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR)) { 625 return p; 626 } 627 } 628 } 629 return null; 630 } 631 632 private Property getTextProp(List<Property> properties) { 633 for (Property p : properties) 634 if (p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) 635 return p; 636 return null; 637 } 638 639 private String convertForDateFormatFromExternal(String fmt, String av) throws FHIRException { 640 if ("v3".equals(fmt) || "YYYYMMDDHHMMSS.UUUU[+|-ZZzz]".equals(fmt)) { 641 try { 642 DateTimeType d = DateTimeType.parseV3(av); 643 return d.asStringValue(); 644 } catch (Exception e) { 645 return av; // not at all clear what to do in this case. 646 } 647 } 648 throw new FHIRException(context.formatMessage(I18nConstants.UNKNOWN_DATA_FORMAT_, fmt)); 649 } 650 651 private String convertForDateFormatToExternal(String fmt, String av) throws FHIRException { 652 if ("v3".equals(fmt) || "YYYYMMDDHHMMSS.UUUU[+|-ZZzz]".equals(fmt)) { 653 DateTimeType d = new DateTimeType(av); 654 return d.getAsV3(); 655 } else 656 throw new FHIRException(context.formatMessage(I18nConstants.UNKNOWN_DATE_FORMAT_, fmt)); 657 } 658 659 private void parseResource(List<ValidationMessage> errors, String string, org.w3c.dom.Element container, Element parent, Property elementProperty) throws FHIRFormatError, DefinitionException, FHIRException, IOException { 660 org.w3c.dom.Element res = XMLUtil.getFirstChild(container); 661 String name = res.getLocalName(); 662 StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null)); 663 if (sd == null) 664 throw new FHIRFormatError(context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, res.getLocalName())); 665 parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(parent.getProperty()), elementProperty); 666 parent.setType(name); 667 parseChildren(errors, res.getLocalName(), res, parent); 668 } 669 670 private void reapComments(org.w3c.dom.Element element, Element context) { 671 Node node = element.getPreviousSibling(); 672 while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { 673 if (node.getNodeType() == Node.COMMENT_NODE) 674 context.getComments().add(0, node.getTextContent()); 675 node = node.getPreviousSibling(); 676 } 677 node = element.getLastChild(); 678 while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { 679 node = node.getPreviousSibling(); 680 } 681 while (node != null) { 682 if (node.getNodeType() == Node.COMMENT_NODE) 683 context.getComments().add(node.getTextContent()); 684 node = node.getNextSibling(); 685 } 686 } 687 688 private boolean isAttr(Property property) { 689 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 690 if (r.getValue() == PropertyRepresentation.XMLATTR) { 691 return true; 692 } 693 } 694 return false; 695 } 696 697 private boolean isCdaText(Property property) { 698 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 699 if (r.getValue() == PropertyRepresentation.CDATEXT) { 700 return true; 701 } 702 } 703 return false; 704 } 705 706 private boolean isTypeAttr(Property property) { 707 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 708 if (r.getValue() == PropertyRepresentation.TYPEATTR) { 709 return true; 710 } 711 } 712 return false; 713 } 714 715 private boolean isText(Property property) { 716 for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) { 717 if (r.getValue() == PropertyRepresentation.XMLTEXT) { 718 return true; 719 } 720 } 721 return false; 722 } 723 724 @Override 725 public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException, FHIRException { 726 markedXhtml = false; 727 XMLWriter xml = new XMLWriter(stream, "UTF-8"); 728 xml.setSortAttributes(false); 729 xml.setPretty(style == OutputStyle.PRETTY); 730 xml.start(); 731 if (e.getPath() == null) { 732 e.populatePaths(null); 733 } 734 String ns = e.getProperty().getXmlNamespace(); 735 if (ns!=null && !"noNamespace".equals(ns)) { 736 xml.setDefaultNamespace(ns); 737 } 738 if (hasTypeAttr(e)) 739 xml.namespace("http://www.w3.org/2001/XMLSchema-instance", "xsi"); 740 if (Utilities.isAbsoluteUrl(e.getType())) { 741 xml.namespace(urlRoot(e.getType()), "et"); 742 } 743 addNamespaces(xml, e); 744 composeElement(xml, e, e.getType(), true); 745 xml.end(); 746 } 747 748 private void addNamespaces(IXMLWriter xml, Element e) throws IOException { 749 String ns = e.getProperty().getXmlNamespace(); 750 if (ns!=null && xml.getDefaultNamespace()!=null && !xml.getDefaultNamespace().equals(ns)){ 751 if (!xml.namespaceDefined(ns)) { 752 String prefix = pathPrefix(ns); 753 if (prefix.endsWith(":")) { 754 prefix = prefix.substring(0, prefix.length()-1); 755 } 756 if ("?".equals(prefix)) { 757 xml.namespace(ns); 758 } else { 759 xml.namespace(ns, prefix); 760 } 761 } 762 } 763 for (Element c : e.getChildren()) { 764 addNamespaces(xml, c); 765 } 766 } 767 768 private boolean hasTypeAttr(Element e) { 769 if (isTypeAttr(e.getProperty())) 770 return true; 771 for (Element c : e.getChildren()) { 772 if (hasTypeAttr(c)) 773 return true; 774 } 775 // xsi_type is always allowed on CDA elements. right now, I'm not sure where to indicate this in the model, 776 // so it's just hardcoded here 777 if (e.getType() != null && e.getType().startsWith(Constants.NS_CDA_ROOT)) { 778 return true; 779 } 780 return false; 781 } 782 783 private void setXsiTypeIfIsTypeAttr(IXMLWriter xml, Element element) throws IOException, FHIRException { 784 if (isTypeAttr(element.getProperty()) && !Utilities.noString(element.getType())) { 785 String type = element.getType(); 786 if (Utilities.isAbsoluteUrl(type)) { 787 type = type.substring(type.lastIndexOf("/")+1); 788 } 789 xml.attribute("xsi:type",type); 790 } 791 } 792 793 public void compose(Element e, IXMLWriter xml) throws Exception { 794 if (e.getPath() == null) { 795 e.populatePaths(null); 796 } 797 markedXhtml = false; 798 xml.start(); 799 xml.setDefaultNamespace(e.getProperty().getXmlNamespace()); 800 if (Utilities.isAbsoluteUrl(e.getType())) { 801 xml.namespace(urlRoot(e.getType()), "et"); 802 } 803 804 if (schemaPath != null) { 805 xml.setSchemaLocation(FormatUtilities.FHIR_NS, Utilities.pathURL(schemaPath, e.fhirType()+".xsd")); 806 } 807 composeElement(xml, e, e.getType(), true); 808 xml.end(); 809 } 810 811 private void composeElement(IXMLWriter xml, Element element, String elementName, boolean root) throws IOException, FHIRException { 812 if (!(isElideElements() && element.isElided())) { 813 if (showDecorations) { 814 @SuppressWarnings("unchecked") 815 List<ElementDecoration> decorations = (List<ElementDecoration>) element.getUserData(UserDataNames.rendering_xml_decorations); 816 if (decorations != null) 817 for (ElementDecoration d : decorations) 818 xml.decorate(d); 819 } 820 for (String s : element.getComments()) { 821 xml.comment(s, true); 822 } 823 } 824 if (isText(element.getProperty())) { 825 if (isElideElements() && element.isElided() && xml.canElide()) 826 xml.elide(); 827 else { 828 if (linkResolver != null) 829 xml.link(linkResolver.resolveProperty(element.getProperty())); 830 xml.enter(element.getProperty().getXmlNamespace(),elementName); 831 if (linkResolver != null && element.getProperty().isReference()) { 832 String ref = linkResolver.resolveReference(getReferenceForElement(element)); 833 if (ref != null) { 834 xml.externalLink(ref); 835 } 836 } 837 xml.text(element.getValue()); 838 xml.exit(element.getProperty().getXmlNamespace(),elementName); 839 } 840 } else if (!element.hasChildren() && !element.hasValue()) { 841 if (isElideElements() && element.isElided() && xml.canElide()) 842 xml.elide(); 843 else { 844 if (element.getExplicitType() != null) 845 xml.attribute("xsi:type", element.getExplicitType()); 846 xml.element(elementName); 847 } 848 } else if (element.isPrimitive() || (element.hasType() && isPrimitive(element.getType()))) { 849 if (element.getType().equals("xhtml")) { 850 if (isElideElements() && element.isElided() && xml.canElide()) 851 xml.elide(); 852 else { 853 String rawXhtml = element.getValue(); 854 if (isCdaText(element.getProperty())) { 855 new CDANarrativeFormat().convert(xml, new XhtmlParser().parseFragment(rawXhtml)); 856 } else { 857 xml.escapedText(rawXhtml); 858 if (!markedXhtml) { 859 xml.anchor("end-xhtml"); 860 markedXhtml = true; 861 } 862 } 863 } 864 } else if (isText(element.getProperty())) { 865 if (isElideElements() && element.isElided() && xml.canElide()) 866 xml.elide(); 867 else { 868 if (linkResolver != null) 869 xml.link(linkResolver.resolveProperty(element.getProperty())); 870 xml.text(element.getValue()); 871 } 872 } else { 873 if (isElideElements() && element.isElided()) 874 xml.attributeElide(); 875 else { 876 setXsiTypeIfIsTypeAttr(xml, element); 877 if (element.hasValue()) { 878 if (linkResolver != null) 879 xml.link(linkResolver.resolveType(element.getType())); 880 xml.attribute("value", element.getValue()); 881 } 882 if (linkResolver != null) 883 xml.link(linkResolver.resolveProperty(element.getProperty())); 884 if (element.hasChildren()) { 885 xml.enter(element.getProperty().getXmlNamespace(), elementName); 886 if (linkResolver != null && element.getProperty().isReference()) { 887 String ref = linkResolver.resolveReference(getReferenceForElement(element)); 888 if (ref != null) { 889 xml.externalLink(ref); 890 } 891 } 892 for (Element child : element.getChildren()) 893 composeElement(xml, child, child.getName(), false); 894 xml.exit(element.getProperty().getXmlNamespace(),elementName); 895 } else 896 xml.element(elementName); 897 } 898 } 899 } else { 900 if (isElideElements() && element.isElided() && xml.canElide()) 901 xml.elide(); 902 else { 903 setXsiTypeIfIsTypeAttr(xml, element); 904 Set<String> handled = new HashSet<>(); 905 for (Element child : element.getChildren()) { 906 if (!handled.contains(child.getName()) && isAttr(child.getProperty()) && wantCompose(element.getPath(), child)) { 907 handled.add(child.getName()); 908 if (isElideElements() && child.isElided()) 909 xml.attributeElide(); 910 else { 911 String av = child.getValue(); 912 if (child.getProperty().isList()) { 913 for (Element c2 : element.getChildren()) { 914 if (c2 != child && c2.getName().equals(child.getName())) { 915 if (c2.isElided()) 916 av = av + " ..."; 917 else 918 av = av + " " + c2.getValue(); 919 } 920 } 921 } 922 if (linkResolver != null) 923 xml.link(linkResolver.resolveType(child.getType())); 924 if (ToolingExtensions.hasExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT)) 925 av = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av); 926 xml.attribute(child.getProperty().getXmlNamespace(), child.getProperty().getXmlName(), av); 927 } 928 } 929 } 930 } 931 if (!element.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) { 932 if (linkResolver != null) 933 xml.link(linkResolver.resolveProperty(element.getProperty())); 934 if (!xml.namespaceDefined(element.getProperty().getXmlNamespace())) { 935 String abbrev = makeNamespaceAbbrev(element.getProperty(), xml); 936 xml.namespace(element.getProperty().getXmlNamespace(), abbrev); 937 } 938 if (Utilities.isAbsoluteUrl(elementName)) { 939 xml.enter(urlRoot(elementName), urlTail(elementName)); 940 } else { 941 xml.enter(element.getProperty().getXmlNamespace(), elementName); 942 } 943 } 944 945 if (!root && element.getSpecial() != null) { 946 if (linkResolver != null) 947 xml.link(linkResolver.resolveProperty(element.getProperty())); 948 xml.enter(element.getProperty().getXmlNamespace(),element.getType()); 949 } 950 if (linkResolver != null && element.getProperty().isReference()) { 951 String ref = linkResolver.resolveReference(getReferenceForElement(element)); 952 if (ref != null) { 953 xml.externalLink(ref); 954 } 955 } 956 for (Element child : element.getChildren()) { 957 if (wantCompose(element.getPath(), child)) { 958 if (isElideElements() && child.isElided() && xml.canElide()) 959 xml.elide(); 960 else { 961 if (isText(child.getProperty())) { 962 if (linkResolver != null) 963 xml.link(linkResolver.resolveProperty(element.getProperty())); 964 xml.text(child.getValue()); 965 } else if (!isAttr(child.getProperty())) { 966 composeElement(xml, child, child.getName(), false); 967 } 968 } 969 } 970 } 971 if (!element.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) { 972 if (!root && element.getSpecial() != null) 973 xml.exit(element.getProperty().getXmlNamespace(),element.getType()); 974 if (Utilities.isAbsoluteUrl(elementName)) { 975 xml.exit(urlRoot(elementName), urlTail(elementName)); 976 } else { 977 xml.exit(element.getProperty().getXmlNamespace(),elementName); 978 } 979 } 980 } 981 } 982 983 private String urlRoot(String elementName) { 984 return elementName.substring(0, elementName.lastIndexOf("/")); 985 } 986 987 private String makeNamespaceAbbrev(Property property, IXMLWriter xml) { 988 // it's a cosmetic thing, but we're going to try to come up with a nice namespace 989 990 ElementDefinition ed = property.getDefinition(); 991 String ns = property.getXmlNamespace(); 992 String n = property.getXmlName(); 993 994 String diff = property.getName().toLowerCase().replace(n.toLowerCase(), ""); 995 if (!Utilities.noString(diff) && diff.length() <= 5 && Utilities.isToken(diff) && !xml.abbreviationDefined(diff)) { 996 return diff; 997 } 998 999 int i = ns.length()-1; 1000 while (i > 0) { 1001 if (Character.isAlphabetic(ns.charAt(i)) || Character.isDigit(ns.charAt(i))) { 1002 i--; 1003 } else { 1004 break; 1005 } 1006 } 1007 String tail = ns.substring(i+1); 1008 if (!Utilities.noString(tail) && tail.length() <= 5 && Utilities.isToken(tail) && !xml.abbreviationDefined(tail)) { 1009 return tail; 1010 } 1011 1012 i = 0; 1013 while (xml.abbreviationDefined("ns"+i)) { 1014 i++; 1015 } 1016 return "ns"+i; 1017 } 1018 private String checkHeader(List<ValidationMessage> errors, InputStream stream) throws IOException { 1019 try { 1020 // the stream will either start with the UTF-8 BOF or with <xml 1021 int i0 = stream.read(); 1022 int i1 = stream.read(); 1023 int i2 = stream.read(); 1024 1025 StringBuilder b = new StringBuilder(); 1026 if (i0 == 0xEF && i1 == 0xBB && i2 == 0xBF) { 1027 // ok, it's UTF-8 1028 } else if (i0 == 0x3C && i1 == 0x3F && i2 == 0x78) { // <xm 1029 b.append((char) i0); 1030 b.append((char) i1); 1031 b.append((char) i2); 1032 } else if (i0 == 60) { // just plain old XML with no header 1033 return "1.0"; 1034 } else { 1035 throw new Exception(context.formatMessage(I18nConstants.XML_ENCODING_INVALID)); 1036 } 1037 int i = stream.read(); 1038 do { 1039 b.append((char) i); 1040 i = stream.read(); 1041 } while (i != 0x3E); 1042 String header = b.toString(); 1043 String e = null; 1044 i = header.indexOf("encoding=\""); 1045 if (i > -1) { 1046 e = header.substring(i+10, i+15); 1047 } else { 1048 i = header.indexOf("encoding='"); 1049 if (i > -1) { 1050 e = header.substring(i+10, i+15); 1051 } 1052 } 1053 if (e != null && !"UTF-8".equalsIgnoreCase(e)) { 1054 logError(errors, ValidationMessage.NO_RULE_DATE, 0, 0, "XML", IssueType.INVALID, context.formatMessage(I18nConstants.XML_ENCODING_INVALID), IssueSeverity.ERROR); 1055 } 1056 1057 i = header.indexOf("version=\""); 1058 if (i > -1) { 1059 return header.substring(i+9, i+12); 1060 } else { 1061 i = header.indexOf("version='"); 1062 if (i > -1) { 1063 return header.substring(i+9, i+12); 1064 } 1065 } 1066 return "?xml-p1?"; 1067 } catch (Exception e) { 1068 // suppress this error 1069 logError(errors, ValidationMessage.NO_RULE_DATE, 0, 0, "XML", IssueType.INVALID, e.getMessage(), IssueSeverity.ERROR); 1070 } 1071 return "?xml-p2?"; 1072 } 1073 1074 class NullErrorHandler implements ErrorHandler { 1075 @Override 1076 public void fatalError(SAXParseException e) { 1077 // do nothing 1078 } 1079 1080 @Override 1081 public void error(SAXParseException e) { 1082 // do nothing 1083 } 1084 1085 @Override 1086 public void warning(SAXParseException e) { 1087 // do nothing 1088 } 1089 } 1090 1091 public boolean isElideElements() { 1092 return elideElements; 1093 } 1094 1095 public void setElideElements(boolean elideElements) { 1096 this.elideElements = elideElements; 1097 } 1098 1099}