From 49f798de489b1f2149d13d88e3bca3740038cab2 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 26 Nov 2022 22:24:17 -0300 Subject: [PATCH] Consistency around rendering comments in examples in IGs --- .../fhir/r4b/elementmodel/TurtleParser.java | 12 +- .../hl7/fhir/r5/elementmodel/JsonParser.java | 36 +- .../fhir/r5/elementmodel/TurtleParser.java | 39 +- .../hl7/fhir/r5/elementmodel/XmlParser.java | 438 +++++++++--------- .../org/hl7/fhir/r5/formats/JsonCreator.java | 4 +- .../fhir/r5/formats/JsonCreatorCanonical.java | 15 +- .../fhir/r5/formats/JsonCreatorDirect.java | 28 +- .../hl7/fhir/r5/formats/JsonCreatorGson.java | 11 +- .../hl7/fhir/r5/formats/JsonParserBase.java | 20 +- .../org/hl7/fhir/r5/test/JsonDirectTests.java | 2 +- .../org/hl7/fhir/utilities/turtle/Turtle.java | 46 +- .../org/hl7/fhir/utilities/xml/XMLWriter.java | 4 +- 12 files changed, 365 insertions(+), 290 deletions(-) diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/TurtleParser.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/TurtleParser.java index 68d1037e9..3ffac94e4 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/TurtleParser.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/TurtleParser.java @@ -333,7 +333,7 @@ public class TurtleParser extends ParserBase { ontology.triple(ontologyId, "owl:versionIRI", ontologyId.replace(FHIR_URI_BASE, FHIR_VERSION_BASE)); Subject subject = section.triple(subjId, "a", "fhir:" + e.getType()); - subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root")); + subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root"), null); for (Element child : e.getChildren()) { composeElement(section, subject, child, null); @@ -398,17 +398,17 @@ public class TurtleParser extends ParserBase { Complex t; if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY && parent != null && parent.getNamedChildValue("fullUrl") != null) { String url = "<"+parent.getNamedChildValue("fullUrl")+">"; - ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); + ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), null); t = section.subject(url); } else { - t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); + t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), null); } if (element.getSpecial() != null) - t.linkedPredicate("a", "fhir:"+element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType())); + t.linkedPredicate("a", "fhir:"+element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType()), null); if (element.hasValue()) - t.linkedPredicate("fhir:value", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType())); + t.linkedPredicate("fhir:value", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType()), null); if (element.getProperty().isList() && (!element.isResource() || element.getSpecial() == SpecialElement.CONTAINED)) - t.linkedPredicate("fhir:index", Integer.toString(element.getIndex()), linkResolver == null ? null : linkResolver.resolvePage("rdf.html#index")); + t.linkedPredicate("fhir:index", Integer.toString(element.getIndex()), linkResolver == null ? null : linkResolver.resolvePage("rdf.html#index"), null); if ("Coding".equals(element.getType())) decorateCoding(t, element, section); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java index 429eb2f92..ae250a62d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java @@ -54,6 +54,7 @@ import org.hl7.fhir.r5.elementmodel.Element.SpecialElement; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonCreator; import org.hl7.fhir.r5.formats.JsonCreatorCanonical; +import org.hl7.fhir.r5.formats.JsonCreatorDirect; import org.hl7.fhir.r5.formats.JsonCreatorGson; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; @@ -182,10 +183,10 @@ public class JsonParser extends ParserBase { } private void checkComments(JsonElement element, Element b, String path) throws FHIRFormatError { - if (element.hasComments()) { + if (element != null && element.hasComments()) { if (allowComments) { for (JsonComment c : element.getComments()) { - b.getFormatCommentsPre().add(c.getContent()); + b.getComments().add(c.getContent()); } } else { for (JsonComment c : element.getComments()) { @@ -322,7 +323,7 @@ public class JsonParser extends ParserBase { } int c = 0; for (JsonElement am : arr) { - parseChildComplexInstance(npath+"["+c+"]", fpath+"["+c+"]", element, property, name, am); + parseChildComplexInstance(npath+"["+c+"]", fpath+"["+c+"]", element, property, name, am, c == 0 ? arr : null, path); c++; } } else if (property.isJsonKeyArray()) { @@ -394,7 +395,7 @@ public class JsonParser extends ParserBase { if (propV.isPrimitive(pvl.getType(null))) { parseChildPrimitiveInstance(n, pvl, pvl.getName(), npathV, fpathV, pv.getValue(), null); } else if (pv.getValue() instanceof JsonObject || pv.getValue() instanceof JsonNull) { - parseChildComplexInstance(npathV, fpathV, n, pvl, pvl.getName(), pv.getValue()); + parseChildComplexInstance(npathV, fpathV, n, pvl, pvl.getName(), pv.getValue(), null, null); } else { logError(ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(pv.getValue())), IssueSeverity.ERROR); } @@ -407,7 +408,7 @@ public class JsonParser extends ParserBase { if (property.isList()) { logError(ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(e), name, path), IssueSeverity.ERROR); } - parseChildComplexInstance(npath, fpath, element, property, name, e); + parseChildComplexInstance(npath, fpath, element, property, name, e, null, null); } } @@ -419,7 +420,7 @@ public class JsonParser extends ParserBase { return b.toString(); } - private void parseChildComplexInstance(String npath, String fpath, Element element, Property property, String name, JsonElement e) throws FHIRException { + private void parseChildComplexInstance(String npath, String fpath, Element element, Property property, String name, JsonElement e, JsonElement commentContext, String commentPath) throws FHIRException { if (property.hasTypeSpecifier()) { FHIRPathEngine fpe = new FHIRPathEngine(context); String type = null; @@ -454,6 +455,7 @@ public class JsonParser extends ParserBase { JsonObject child = (JsonObject) e; Element n = new Element(name, property).markLocation(line(child), col(child)); n.setPath(fpath); + checkComments(commentContext, n, commentPath); checkObject(child, n, npath); element.getChildren().add(n); if (property.isResource()) { @@ -465,6 +467,7 @@ public class JsonParser extends ParserBase { // we create an element marked as a null element so we know something was present JsonNull child = (JsonNull) e; Element n = new Element(name, property).markLocation(line(child), col(child)); + checkComments(commentContext, n, commentPath); checkComments(child, n, fpath); n.setPath(fpath); element.getChildren().add(n); @@ -675,13 +678,16 @@ public class JsonParser extends ParserBase { if (e.getPath() == null) { e.populatePaths(null); } - + OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); - if (style == OutputStyle.CANONICAL) + if (style == OutputStyle.CANONICAL) { json = new JsonCreatorCanonical(osw); - else - json = new JsonCreatorGson(osw); - json.setIndent(style == OutputStyle.PRETTY ? " " : ""); + } else if (style == OutputStyle.PRETTY) { + json = new JsonCreatorDirect(osw, true, allowComments); + } else { + json = new JsonCreatorDirect(osw, false, allowComments); + } + checkComposeComments(e); json.beginObject(); prop("resourceType", e.getType(), null); Set done = new HashSet(); @@ -693,12 +699,19 @@ public class JsonParser extends ParserBase { osw.flush(); } + private void checkComposeComments(Element e) { + for (String s : e.getComments()) { + json.comment(s); + } + } + public void compose(Element e, JsonCreator json) throws Exception { if (e.getPath() == null) { e.populatePaths(null); } this.json = json; + checkComposeComments(e); json.beginObject(); prop("resourceType", e.getType(), linkResolver == null ? null : linkResolver.resolveProperty(e.getProperty())); @@ -711,6 +724,7 @@ public class JsonParser extends ParserBase { } private void compose(String path, Element e, Set done, Element child) throws IOException { + checkComposeComments(child); if (wantCompose(path, child)) { boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList(); if (!isList) {// for specials, ignore the cardinality of the stated type diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/TurtleParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/TurtleParser.java index 96c2a3bd5..a9a47932c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/TurtleParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/TurtleParser.java @@ -69,6 +69,8 @@ public class TurtleParser extends ParserBase { private String base; + private OutputStyle style; + public static String FHIR_URI_BASE = "http://hl7.org/fhir/"; public static String FHIR_VERSION_BASE = "http://build.fhir.org/"; @@ -303,18 +305,16 @@ public class TurtleParser extends ParserBase { return en.substring(0, en.lastIndexOf(".")+1)+elementName; } - @Override public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException, FHIRException { this.base = base; + this.style = style; Turtle ttl = new Turtle(); compose(e, ttl, base); ttl.commit(stream, false); } - - public void compose(Element e, Turtle ttl, String base) throws FHIRException { if (e.getPath() == null) { e.populatePaths(null); @@ -325,8 +325,12 @@ public class TurtleParser extends ParserBase { ttl.prefix("owl", "http://www.w3.org/2002/07/owl#"); ttl.prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); - Section section = ttl.section("resource"); + if (style == OutputStyle.PRETTY) { + for (String s : e.getComments()) { + section.stringComment(s); + } + } String subjId = genSubjectId(e); String ontologyId = subjId.replace(">", ".ttl>"); @@ -337,7 +341,7 @@ public class TurtleParser extends ParserBase { ontology.triple(ontologyId, "owl:versionIRI", ontologyId.replace(FHIR_URI_BASE, FHIR_VERSION_BASE)); Subject subject = section.triple(subjId, "a", "fhir:" + e.getType()); - subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root")); + subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root"), null); for (Element child : e.getChildren()) { composeElement(section, subject, child, null); @@ -364,13 +368,13 @@ public class TurtleParser extends ParserBase { protected void decorateReference(Complex t, Element coding) { String refURI = getReferenceURI(coding.getChildValue("reference")); if(refURI != null) - t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference")); + t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference"), null); } protected void decorateCanonical(Complex t, Element canonical) { String refURI = getReferenceURI(canonical.primitiveValue()); if(refURI != null) - t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference")); + t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference"), null); } private String genSubjectId(Element e) { @@ -397,26 +401,31 @@ public class TurtleParser extends ParserBase { private void composeElement(Section section, Complex ctxt, Element element, Element parent) throws FHIRException { // "Extension".equals(element.getType())? // (element.getProperty().getDefinition().getIsModifier()? "modifierExtension" : "extension") ; + String en = getFormalName(element); if (!wantCompose(parent == null ? "" : parent.getPath(), element)) { return; } + String comment = null; + if (style == OutputStyle.PRETTY) { + comment = String.join(", ", element.getComments()); + } Complex t; if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY && parent != null && parent.getNamedChildValue("fullUrl") != null) { String url = "<"+parent.getNamedChildValue("fullUrl")+">"; - ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); + ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment); t = section.subject(url); } else { - t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); + t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment); } if (element.getSpecial() != null) - t.linkedPredicate("a", "fhir:"+element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType())); + t.linkedPredicate("a", "fhir:"+element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType()), null); if (element.hasValue()) - t.linkedPredicate("fhir:value", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType())); + t.linkedPredicate("fhir:value", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType()), null); if (element.getProperty().isList() && (!element.isResource() || element.getSpecial() == SpecialElement.CONTAINED)) - t.linkedPredicate("fhir:index", Integer.toString(element.getIndex()), linkResolver == null ? null : linkResolver.resolvePage("rdf.html#index")); + t.linkedPredicate("fhir:index", Integer.toString(element.getIndex()), linkResolver == null ? null : linkResolver.resolvePage("rdf.html#index"), null); if ("Coding".equals(element.getType())) decorateCoding(t, element, section); @@ -549,6 +558,12 @@ public class TurtleParser extends ParserBase { Expression expression = SnomedExpressions.parse(code); } + public OutputStyle getStyle() { + return style; + } + public void setStyle(OutputStyle style) { + this.style = style; + } // 128045006|cellulitis (disorder)|:{363698007|finding site|=56459004|foot structure|} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java index 890c31b65..d431b6fc4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java @@ -3,19 +3,19 @@ package org.hl7.fhir.r5.elementmodel; /* Copyright (c) 2011+, HL7, Inc. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to + * Neither the name of HL7 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. @@ -26,7 +26,7 @@ package org.hl7.fhir.r5.elementmodel; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + */ @@ -89,9 +89,9 @@ public class XmlParser extends ParserBase { public XmlParser(IWorkerContext context) { super(context); } - + private String schemaPath; - + public String getSchemaPath() { return schemaPath; } @@ -100,7 +100,7 @@ public class XmlParser extends ParserBase { } - + public boolean isAllowXsiLocation() { return allowXsiLocation; } @@ -111,69 +111,69 @@ public class XmlParser extends ParserBase { public List parse(InputStream stream) throws FHIRFormatError, DefinitionException, FHIRException, IOException { List res = new ArrayList<>(); - Document doc = null; - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - // xxe protection - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - factory.setFeature("http://xml.org/sax/features/external-general-entities", false); - factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - factory.setXIncludeAware(false); - factory.setExpandEntityReferences(false); - - factory.setNamespaceAware(true); - if (policy == ValidationPolicy.EVERYTHING) { - // The SAX interface appears to not work when reporting the correct version/encoding. - // if we can, we'll inspect the header/encoding ourselves - if (stream.markSupported()) { - stream.mark(1024); - version = checkHeader(stream); - stream.reset(); - } - // use a slower parser that keeps location data - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer nullTransformer = transformerFactory.newTransformer(); - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - doc = docBuilder.newDocument(); - DOMResult domResult = new DOMResult(doc); - SAXParserFactory spf = SAXParserFactory.newInstance(); - spf.setNamespaceAware(true); - spf.setValidating(false); - // xxe protection - spf.setFeature("http://xml.org/sax/features/external-general-entities", false); + Document doc = null; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + // xxe protection + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + + factory.setNamespaceAware(true); + if (policy == ValidationPolicy.EVERYTHING) { + // The SAX interface appears to not work when reporting the correct version/encoding. + // if we can, we'll inspect the header/encoding ourselves + if (stream.markSupported()) { + stream.mark(1024); + version = checkHeader(stream); + stream.reset(); + } + // use a slower parser that keeps location data + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer nullTransformer = transformerFactory.newTransformer(); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + doc = docBuilder.newDocument(); + DOMResult domResult = new DOMResult(doc); + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + spf.setValidating(false); + // xxe protection + spf.setFeature("http://xml.org/sax/features/external-general-entities", false); spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - SAXParser saxParser = spf.newSAXParser(); - XMLReader xmlReader = saxParser.getXMLReader(); - // xxe protection - xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); - xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - - XmlLocationAnnotator locationAnnotator = new XmlLocationAnnotator(xmlReader, doc); - InputSource inputSource = new InputSource(stream); - SAXSource saxSource = new SAXSource(locationAnnotator, inputSource); - nullTransformer.transform(saxSource, domResult); - } else { - DocumentBuilder builder = factory.newDocumentBuilder(); - doc = builder.parse(stream); - } - } catch (Exception e) { - if (e.getMessage().contains("lineNumber:") && e.getMessage().contains("columnNumber:")) { + SAXParser saxParser = spf.newSAXParser(); + XMLReader xmlReader = saxParser.getXMLReader(); + // xxe protection + xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); + xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + + XmlLocationAnnotator locationAnnotator = new XmlLocationAnnotator(xmlReader, doc); + InputSource inputSource = new InputSource(stream); + SAXSource saxSource = new SAXSource(locationAnnotator, inputSource); + nullTransformer.transform(saxSource, domResult); + } else { + DocumentBuilder builder = factory.newDocumentBuilder(); + doc = builder.parse(stream); + } + } catch (Exception e) { + if (e.getMessage().contains("lineNumber:") && e.getMessage().contains("columnNumber:")) { int line = Utilities.parseInt(extractVal(e.getMessage(), "lineNumber"), 0); int col = Utilities.parseInt(extractVal(e.getMessage(), "columnNumber"), 0); logError(ValidationMessage.NO_RULE_DATE, line, col, "(xml)", IssueType.INVALID, e.getMessage().substring(e.getMessage().lastIndexOf(";")+1).trim(), IssueSeverity.FATAL); - } else { + } else { logError(ValidationMessage.NO_RULE_DATE, 0, 0, "(xml)", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL); - } + } doc = null; - } - if (doc != null) { - Element e = parse(doc); + } + if (doc != null) { + Element e = parse(doc); if (e != null) { res.add(new NamedElement(null, e)); } - } - return res; + } + return res; } @@ -188,21 +188,21 @@ public class XmlParser extends ParserBase { while (node != null) { if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) logError(ValidationMessage.NO_RULE_DATE, line(document, false), col(document, false), "(document)", IssueType.INVALID, context.formatMessage( - I18nConstants.NO_PROCESSING_INSTRUCTIONS_ALLOWED_IN_RESOURCES), IssueSeverity.ERROR); + I18nConstants.NO_PROCESSING_INSTRUCTIONS_ALLOWED_IN_RESOURCES), IssueSeverity.ERROR); node = node.getNextSibling(); } } } - + private int line(Node node, boolean end) { - XmlLocationData loc = node == null ? null : (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); - return loc == null ? 0 : end ? loc.getEndLine() : loc.getStartLine(); + XmlLocationData loc = node == null ? null : (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); + return loc == null ? 0 : end ? loc.getEndLine() : loc.getStartLine(); } private int col(Node node, boolean end) { - XmlLocationData loc = node == null ? null : (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); - return loc == null ? 0 : end ? loc.getEndColumn() : loc.getStartColumn(); + XmlLocationData loc = node == null ? null : (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY); + return loc == null ? 0 : end ? loc.getEndColumn() : loc.getStartColumn(); } public Element parse(Document doc) throws FHIRFormatError, DefinitionException, FHIRException, IOException { @@ -210,12 +210,12 @@ public class XmlParser extends ParserBase { org.w3c.dom.Element element = doc.getDocumentElement(); return parse(element); } - + public Element parse(org.w3c.dom.Element element) throws FHIRFormatError, DefinitionException, FHIRException, IOException { String ns = element.getNamespaceURI(); String name = element.getLocalName(); String path = "/"+pathPrefix(ns)+name; - + StructureDefinition sd = getDefinition(line(element, false), col(element, false), (ns == null ? "noNamespace" : ns), name); if (sd == null) return null; @@ -254,7 +254,7 @@ public class XmlParser extends ParserBase { } if (!Utilities.noString(element.getTextContent().trim())) return false; - + Node n = element.getFirstChild(); while (n != null) { if (n.getNodeType() == Node.ELEMENT_NODE) @@ -263,7 +263,7 @@ public class XmlParser extends ParserBase { } return true; } - + private void checkElement(org.w3c.dom.Element element, String path, Property prop) throws FHIRFormatError { if (policy == ValidationPolicy.EVERYTHING) { if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR Content @@ -291,16 +291,16 @@ public class XmlParser extends ParserBase { } private void parseChildren(String path, org.w3c.dom.Element node, Element element) throws FHIRFormatError, FHIRException, IOException, DefinitionException { - // this parsing routine retains the original order in a the XML file, to support validation - reapComments(node, element); + // this parsing routine retains the original order in a the XML file, to support validation + reapComments(node, element); List properties = element.getProperty().getChildProperties(element.getName(), XMLUtil.getXsiType(node)); - String text = XMLUtil.getDirectText(node).trim(); + String text = XMLUtil.getDirectText(node).trim(); int line = line(node, false); int col = col(node, false); if (!Utilities.noString(text)) { - Property property = getTextProp(properties); - if (property != null) { + Property property = getTextProp(properties); + if (property != null) { if ("ED.data[x]".equals(property.getDefinition().getId()) || (property.getDefinition()!=null && property.getDefinition().getBase()!=null && "ED.data[x]".equals(property.getDefinition().getBase().getPath()))) { if ("B64".equals(node.getAttribute("representation"))) { Element n = new Element("dataBase64Binary", property, "base64Binary", text).markLocation(line, col); @@ -317,8 +317,8 @@ public class XmlParser extends ParserBase { element.getChildren().add(n); } } - else { - Node n = node.getFirstChild(); + else { + Node n = node.getFirstChild(); while (n != null) { if (n.getNodeType() == Node.TEXT_NODE && !Utilities.noString(n.getTextContent().trim())) { Node nt = n; // try to find the nearest element for a line/col location @@ -337,28 +337,28 @@ public class XmlParser extends ParserBase { } n = n.getNextSibling(); } - } + } } - + for (int i = 0; i < node.getAttributes().getLength(); i++) { - Node attr = node.getAttributes().item(i); - String value = attr.getNodeValue(); - if (!validAttrValue(value)) { + Node attr = node.getAttributes().item(i); + String value = attr.getNodeValue(); + if (!validAttrValue(value)) { logError(ValidationMessage.NO_RULE_DATE, line, col, path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.XML_ATTR_VALUE_INVALID, attr.getNodeName()), IssueSeverity.ERROR); - } - if (!(attr.getNodeName().equals("xmlns") || attr.getNodeName().startsWith("xmlns:"))) { - Property property = getAttrProp(properties, attr.getLocalName(), attr.getNamespaceURI()); - if (property != null) { - String av = attr.getNodeValue(); - if (ToolingExtensions.hasExtension(property.getDefinition(), ToolingExtensions.EXT_DATE_FORMAT)) - av = convertForDateFormatFromExternal(ToolingExtensions.readStringExtension(property.getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av); - if (property.getName().equals("value") && element.isPrimitive()) - element.setValue(av); - else { - Element n = new Element(property.getName(), property, property.getType(), av).markLocation(line, col); + } + if (!(attr.getNodeName().equals("xmlns") || attr.getNodeName().startsWith("xmlns:"))) { + Property property = getAttrProp(properties, attr.getLocalName(), attr.getNamespaceURI()); + if (property != null) { + String av = attr.getNodeValue(); + if (ToolingExtensions.hasExtension(property.getDefinition(), ToolingExtensions.EXT_DATE_FORMAT)) + av = convertForDateFormatFromExternal(ToolingExtensions.readStringExtension(property.getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av); + if (property.getName().equals("value") && element.isPrimitive()) + element.setValue(av); + else { + Element n = new Element(property.getName(), property, property.getType(), av).markLocation(line, col); n.setPath(element.getPath()+"."+property.getName()); element.getChildren().add(n); - } + } } else { boolean ok = false; if (FormatUtilities.FHIR_NS.equals(node.getNamespaceURI())) { @@ -371,28 +371,28 @@ public class XmlParser extends ParserBase { if (!ok) { logError(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); } - } - } + } + } } - + String lastName = null; int repeatCount = 0; Node child = node.getFirstChild(); while (child != null) { - if (child.getNodeType() == Node.ELEMENT_NODE) { - Property property = getElementProp(properties, child.getLocalName(), child.getNamespaceURI()); - if (property != null) { - if (property.getName().equals(lastName)) { - repeatCount++; - } else { - lastName = property.getName(); - repeatCount = 0; - } - if (!property.isChoice() && "xhtml".equals(property.getType())) { - XhtmlNode xhtml; - if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT)) - xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child); - else { + if (child.getNodeType() == Node.ELEMENT_NODE) { + Property property = getElementProp(properties, child.getLocalName(), child.getNamespaceURI()); + if (property != null) { + if (property.getName().equals(lastName)) { + repeatCount++; + } else { + lastName = property.getName(); + repeatCount = 0; + } + if (!property.isChoice() && "xhtml".equals(property.getType())) { + XhtmlNode xhtml; + if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT)) + xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child); + else { XhtmlParser xp = new XhtmlParser(); xhtml = xp.parseHtmlNode((org.w3c.dom.Element) child); if (policy == ValidationPolicy.EVERYTHING) { @@ -400,56 +400,56 @@ public class XmlParser extends ParserBase { logError("2022-11-17", line(child, false), col(child, false), path, IssueType.INVALID, context.formatMessage(s.getName(), s.getValue()), IssueSeverity.ERROR); } } - } - Element n = new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child, false), col(child, false)); + } + Element n = new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child, false), col(child, false)); n.setPath(element.getPath()+"."+property.getName()); element.getChildren().add(n); - } else { - String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName(); - Element n = new Element(child.getLocalName(), property).markLocation(line(child, false), col(child, false)); - if (property.isList()) { + } else { + String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName(); + Element n = new Element(child.getLocalName(), property).markLocation(line(child, false), col(child, false)); + if (property.isList()) { n.setPath(element.getPath()+"."+property.getName()+"["+repeatCount+"]"); - } else { + } else { n.setPath(element.getPath()+"."+property.getName()); - } - checkElement((org.w3c.dom.Element) child, npath, n.getProperty()); - boolean ok = true; - if (property.isChoice()) { - if (property.getDefinition().hasRepresentation(PropertyRepresentation.TYPEATTR)) { - String xsiType = ((org.w3c.dom.Element) child).getAttributeNS(FormatUtilities.NS_XSI, "type"); - if (Utilities.noString(xsiType)) { + } + checkElement((org.w3c.dom.Element) child, npath, n.getProperty()); + boolean ok = true; + if (property.isChoice()) { + if (property.getDefinition().hasRepresentation(PropertyRepresentation.TYPEATTR)) { + String xsiType = ((org.w3c.dom.Element) child).getAttributeNS(FormatUtilities.NS_XSI, "type"); + if (Utilities.noString(xsiType)) { if (ToolingExtensions.hasExtension(property.getDefinition(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) { xsiType = ToolingExtensions.readStringExtension(property.getDefinition(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"); n.setType(xsiType); } else { logError(ValidationMessage.NO_RULE_DATE, line(child, false), col(child, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.NO_TYPE_FOUND_ON_, child.getLocalName()), IssueSeverity.ERROR); - ok = false; + ok = false; } - } else { - if (xsiType.contains(":")) - xsiType = xsiType.substring(xsiType.indexOf(":")+1); - n.setType(xsiType); - n.setExplicitType(xsiType); - } - } else - n.setType(n.getType()); - } - element.getChildren().add(n); - if (ok) { - if (property.isResource()) + } else { + if (xsiType.contains(":")) + xsiType = xsiType.substring(xsiType.indexOf(":")+1); + n.setType(xsiType); + n.setExplicitType(xsiType); + } + } else + n.setType(n.getType()); + } + element.getChildren().add(n); + if (ok) { + if (property.isResource()) parseResource(npath, (org.w3c.dom.Element) child, n, property); - else - parseChildren(npath, (org.w3c.dom.Element) child, n); - } - } - } else + else + parseChildren(npath, (org.w3c.dom.Element) child, n); + } + } + } else logError(ValidationMessage.NO_RULE_DATE, line(child, false), col(child, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNDEFINED_ELEMENT_, child.getLocalName()), IssueSeverity.ERROR); - } else if (child.getNodeType() == Node.CDATA_SECTION_NODE){ + } else if (child.getNodeType() == Node.CDATA_SECTION_NODE){ logError(ValidationMessage.NO_RULE_DATE, line(child, false), col(child, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.CDATA_IS_NOT_ALLOWED), IssueSeverity.ERROR); - } else if (!Utilities.existsInList(child.getNodeType(), 3, 8)) { + } else if (!Utilities.existsInList(child.getNodeType(), 3, 8)) { logError(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); - } - child = child.getNextSibling(); + } + child = child.getNextSibling(); } } @@ -471,16 +471,16 @@ public class XmlParser extends ParserBase { private Property getElementProp(List properties, String nodeName, String namespace) { - List propsSortedByLongestFirst = new ArrayList(properties); - // sort properties according to their name longest first, so .requestOrganizationReference comes first before .request[x] - // and therefore the longer property names get evaluated first - Collections.sort(propsSortedByLongestFirst, new Comparator() { - @Override - public int compare(Property o1, Property o2) { - return o2.getName().length() - o1.getName().length(); - } - }); - // first scan, by namespace + List propsSortedByLongestFirst = new ArrayList(properties); + // sort properties according to their name longest first, so .requestOrganizationReference comes first before .request[x] + // and therefore the longer property names get evaluated first + Collections.sort(propsSortedByLongestFirst, new Comparator() { + @Override + public int compare(Property o1, Property o2) { + return o2.getName().length() - o1.getName().length(); + } + }); + // first scan, by namespace for (Property p : propsSortedByLongestFirst) { if (!p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && !p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) { if (p.getXmlName().equals(nodeName) && p.getXmlNamespace().equals(namespace)) @@ -495,8 +495,8 @@ public class XmlParser extends ParserBase { return p; } } - return null; - } + return null; + } private Property getAttrProp(List properties, String nodeName, String namespace) { for (Property p : properties) { @@ -511,27 +511,27 @@ public class XmlParser extends ParserBase { } } } - return null; + return null; } - private Property getTextProp(List properties) { - for (Property p : properties) - if (p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) - return p; - return null; - } + private Property getTextProp(List properties) { + for (Property p : properties) + if (p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) + return p; + return null; + } - private String convertForDateFormatFromExternal(String fmt, String av) throws FHIRException { - if ("v3".equals(fmt) || "YYYYMMDDHHMMSS.UUUU[+|-ZZzz]".equals(fmt)) { - try { - DateTimeType d = DateTimeType.parseV3(av); - return d.asStringValue(); - } catch (Exception e) { - return av; // not at all clear what to do in this case. - } - } - throw new FHIRException(context.formatMessage(I18nConstants.UNKNOWN_DATA_FORMAT_, fmt)); - } + private String convertForDateFormatFromExternal(String fmt, String av) throws FHIRException { + if ("v3".equals(fmt) || "YYYYMMDDHHMMSS.UUUU[+|-ZZzz]".equals(fmt)) { + try { + DateTimeType d = DateTimeType.parseV3(av); + return d.asStringValue(); + } catch (Exception e) { + return av; // not at all clear what to do in this case. + } + } + throw new FHIRException(context.formatMessage(I18nConstants.UNKNOWN_DATA_FORMAT_, fmt)); + } private String convertForDateFormatToExternal(String fmt, String av) throws FHIRException { if ("v3".equals(fmt)) { @@ -542,7 +542,7 @@ public class XmlParser extends ParserBase { } private void parseResource(String string, org.w3c.dom.Element container, Element parent, Property elementProperty) throws FHIRFormatError, DefinitionException, FHIRException, IOException { - org.w3c.dom.Element res = XMLUtil.getFirstChild(container); + org.w3c.dom.Element res = XMLUtil.getFirstChild(container); String name = res.getLocalName(); StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null)); if (sd == null) @@ -550,25 +550,25 @@ public class XmlParser extends ParserBase { parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(parent.getProperty()), elementProperty); parent.setType(name); parseChildren(res.getLocalName(), res, parent); - } + } - private void reapComments(org.w3c.dom.Element element, Element context) { - Node node = element.getPreviousSibling(); - while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { - if (node.getNodeType() == Node.COMMENT_NODE) - context.getComments().add(0, node.getTextContent()); - node = node.getPreviousSibling(); - } - node = element.getLastChild(); - while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { - node = node.getPreviousSibling(); - } - while (node != null) { - if (node.getNodeType() == Node.COMMENT_NODE) - context.getComments().add(node.getTextContent()); - node = node.getNextSibling(); - } - } + private void reapComments(org.w3c.dom.Element element, Element context) { + Node node = element.getPreviousSibling(); + while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { + if (node.getNodeType() == Node.COMMENT_NODE) + context.getComments().add(0, node.getTextContent()); + node = node.getPreviousSibling(); + } + node = element.getLastChild(); + while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { + node = node.getPreviousSibling(); + } + while (node != null) { + if (node.getNodeType() == Node.COMMENT_NODE) + context.getComments().add(node.getTextContent()); + node = node.getNextSibling(); + } + } private boolean isAttr(Property property) { for (Enumeration r : property.getDefinition().getRepresentation()) { @@ -598,17 +598,17 @@ public class XmlParser extends ParserBase { } private boolean isText(Property property) { - for (Enumeration r : property.getDefinition().getRepresentation()) { - if (r.getValue() == PropertyRepresentation.XMLTEXT) { - return true; - } - } - return false; + for (Enumeration r : property.getDefinition().getRepresentation()) { + if (r.getValue() == PropertyRepresentation.XMLTEXT) { + return true; + } + } + return false; } - @Override + @Override public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException, FHIRException { - XMLWriter xml = new XMLWriter(stream, "UTF-8"); + XMLWriter xml = new XMLWriter(stream, "UTF-8"); xml.setSortAttributes(false); xml.setPretty(style == OutputStyle.PRETTY); xml.start(); @@ -645,7 +645,7 @@ public class XmlParser extends ParserBase { addNamespaces(xml, c); } } - + private boolean hasTypeAttr(Element e) { if (isTypeAttr(e.getProperty())) return true; @@ -665,7 +665,7 @@ public class XmlParser extends ParserBase { xml.attribute("xsi:type",type); } } - + public void compose(Element e, IXMLWriter xml) throws Exception { if (e.getPath() == null) { e.populatePaths(null); @@ -718,16 +718,16 @@ public class XmlParser extends ParserBase { if (element.hasValue()) { if (linkResolver != null) xml.link(linkResolver.resolveType(element.getType())); - xml.attribute("value", element.getValue()); + xml.attribute("value", element.getValue()); } if (linkResolver != null) xml.link(linkResolver.resolveProperty(element.getProperty())); - if (element.hasChildren()) { - xml.enter(element.getProperty().getXmlNamespace(), elementName); - for (Element child : element.getChildren()) - composeElement(xml, child, child.getName(), false); - xml.exit(element.getProperty().getXmlNamespace(),elementName); - } else + if (element.hasChildren()) { + xml.enter(element.getProperty().getXmlNamespace(), elementName); + for (Element child : element.getChildren()) + composeElement(xml, child, child.getName(), false); + xml.exit(element.getProperty().getXmlNamespace(),elementName); + } else xml.element(elementName); } } else { @@ -760,7 +760,7 @@ public class XmlParser extends ParserBase { composeElement(xml, child, child.getName(), false); } } - if (!root && element.getSpecial() != null) + if (!root && element.getSpecial() != null) xml.exit(element.getProperty().getXmlNamespace(),element.getType()); xml.exit(element.getProperty().getXmlNamespace(),elementName); } @@ -772,7 +772,7 @@ public class XmlParser extends ParserBase { int i0 = stream.read(); int i1 = stream.read(); int i2 = stream.read(); - + StringBuilder b = new StringBuilder(); if (i0 == 0xEF && i1 == 0xBB && i2 == 0xBF) { // ok, it's UTF-8 diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreator.java index 651cd9f7c..7e835d9df 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreator.java @@ -42,8 +42,8 @@ import java.math.BigDecimal; */ public interface JsonCreator { - void setIndent(String string); - + void comment(String comment); + void beginObject() throws IOException; void endObject() throws IOException; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorCanonical.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorCanonical.java index e03c58c26..114cc3613 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorCanonical.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorCanonical.java @@ -116,7 +116,7 @@ public class JsonCreatorCanonical implements JsonCreator { public JsonCreatorCanonical(OutputStreamWriter osw) { stack = new Stack(); - jj = new JsonCreatorDirect(osw); + jj = new JsonCreatorDirect(osw, false, false); name = null; } @@ -125,13 +125,6 @@ public class JsonCreatorCanonical implements JsonCreator { name = null; return res; } - - @Override - public void setIndent(String indent) { - if (!indent.equals("")) - throw new Error("do not use pretty when canonical is set"); - jj.setIndent(indent); - } @Override public void beginObject() throws IOException { @@ -276,6 +269,12 @@ public class JsonCreatorCanonical implements JsonCreator { public void anchor(String name) { // not used } + + @Override + public void comment(String content) { + // canonical JSON ignores comments + + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorDirect.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorDirect.java index a7a62e890..a1ea85bc0 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorDirect.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorDirect.java @@ -49,18 +49,24 @@ public class JsonCreatorDirect implements JsonCreator { private Writer writer; private boolean pretty; + private boolean comments; private boolean named; private List valued = new ArrayList(); private int indent; + private List commentList = new ArrayList<>(); - public JsonCreatorDirect(Writer writer) { + public JsonCreatorDirect(Writer writer, boolean pretty, boolean comments) { super(); this.writer = writer; + this.pretty = pretty; + this.comments = pretty && comments; } @Override - public void setIndent(String indent) { - this.pretty = !Utilities.noString(indent); + public void comment(String content) { + if (comments) { + commentList.add(content); + } } @Override @@ -74,6 +80,21 @@ public class JsonCreatorDirect implements JsonCreator { valued.add(0, false); } + private void commitComments() throws IOException { + if (comments) { + for (String s : commentList) { + writer.write("// "); + writer.write(s); + writer.write("\r\n"); + for (int i = 0; i < indent; i++) { + writer.write(" "); + } + } + commentList.clear(); + } + } + + public void stepIn() throws IOException { if (pretty) { indent++; @@ -95,6 +116,7 @@ public class JsonCreatorDirect implements JsonCreator { } private void checkState() throws IOException { + commitComments(); if (named) { if (pretty) writer.write(" : "); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorGson.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorGson.java index c2603b12e..7e8b71393 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorGson.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonCreatorGson.java @@ -45,11 +45,6 @@ public class JsonCreatorGson implements JsonCreator { gson = new JsonWriter(osw); } - @Override - public void setIndent(String indent) { - gson.setIndent(indent); - } - @Override public void beginObject() throws IOException { gson.beginObject(); @@ -120,5 +115,11 @@ public class JsonCreatorGson implements JsonCreator { public void anchor(String name) { // not used } + + @Override + public void comment(String content) { + // gson (dense json) ignores comments + + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonParserBase.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonParserBase.java index 0061bb7f2..7db5a8228 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonParserBase.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/formats/JsonParserBase.java @@ -183,11 +183,13 @@ public abstract class JsonParserBase extends ParserBase implements IParser { @Override public void compose(OutputStream stream, Resource resource) throws IOException { OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); - if (style == OutputStyle.CANONICAL) + if (style == OutputStyle.CANONICAL) { json = new JsonCreatorCanonical(osw); - else - json = new JsonCreatorDirect(osw); // use this instead of Gson because this preserves decimal formatting - json.setIndent(style == OutputStyle.PRETTY ? " " : ""); + } else if (style == OutputStyle.PRETTY) { + json = new JsonCreatorDirect(osw, true, false); + } else { + json = new JsonCreatorDirect(osw, false, false); // use this instead of Gson because this preserves decimal formatting + } json.beginObject(); composeResource(resource); json.endObject(); @@ -207,11 +209,13 @@ public abstract class JsonParserBase extends ParserBase implements IParser { @Override public void compose(OutputStream stream, DataType type, String rootName) throws IOException { OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); - if (style == OutputStyle.CANONICAL) + if (style == OutputStyle.CANONICAL) { json = new JsonCreatorCanonical(osw); - else - json = new JsonCreatorDirect(osw);// use this instead of Gson because this preserves decimal formatting - json.setIndent(style == OutputStyle.PRETTY ? " " : ""); + } else if (style == OutputStyle.PRETTY) { + json = new JsonCreatorDirect(osw, true, false); + } else { + json = new JsonCreatorDirect(osw, false, false); // use this instead of Gson because this preserves decimal formatting + } json.beginObject(); composeTypeInner(type); json.endObject(); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/JsonDirectTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/JsonDirectTests.java index 03ac51d8b..5ee798ac7 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/JsonDirectTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/JsonDirectTests.java @@ -41,7 +41,7 @@ public class JsonDirectTests { @Test public void testEmptyObject() throws FHIRFormatError, FileNotFoundException, IOException { ByteArrayOutputStream bs = new ByteArrayOutputStream(); - JsonCreatorDirect json = new JsonCreatorDirect(new OutputStreamWriter(bs, "UTF-8")); + JsonCreatorDirect json = new JsonCreatorDirect(new OutputStreamWriter(bs, "UTF-8"), false, false); json.beginObject(); json.name("a"); json.beginObject(); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/turtle/Turtle.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/turtle/Turtle.java index 122399b0e..bc2c83a79 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/turtle/Turtle.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/turtle/Turtle.java @@ -77,10 +77,10 @@ public class Turtle { return predicate(predicate, new StringType(object)); } - public Complex linkedPredicate(String predicate, String object, String link) { + public Complex linkedPredicate(String predicate, String object, String link, String comment) { predicateSet.add(predicate); objectSet.add(object); - return linkedPredicate(predicate, new StringType(object), link); + return linkedPredicate(predicate, new StringType(object), link, comment); } public Complex predicate(String predicate, Triple object) { @@ -104,12 +104,13 @@ public class Turtle { return null; } - public Complex linkedPredicate(String predicate, Triple object, String link) { + public Complex linkedPredicate(String predicate, Triple object, String link, String comment) { Predicate p = getPredicate(predicate); if (p == null) { p = new Predicate(); p.predicate = predicate; p.link = link; + p.comment = comment; predicateSet.add(predicate); predicates.add(p); } @@ -126,10 +127,10 @@ public class Turtle { return c; } - public Complex linkedPredicate(String predicate, String link) { + public Complex linkedPredicate(String predicate, String link, String comment) { predicateSet.add(predicate); Complex c = complex(); - linkedPredicate(predicate, c, link); + linkedPredicate(predicate, c, link, comment); return c; } @@ -197,6 +198,7 @@ public class Turtle { } public class Section { + private List comments = new ArrayList<>(); private String name; private List subjects = new ArrayList(); @@ -244,6 +246,10 @@ public class Turtle { return true; return false; } + + public void stringComment(String cnt) { + comments.add(cnt); + } } private List
sections = new ArrayList
(); @@ -435,8 +441,14 @@ public class Turtle { // private String lastComment = ""; private void commitSection(LineOutputStreamWriter writer, Section section) throws IOException { - writer.ln("# - "+section.name+" "+Utilities.padLeft("", '-', 75-section.name.length())); - writer.ln(); + writer.ln("# - "+section.name+" "+Utilities.padLeft("", '-', 75-section.name.length())); + writer.ln(); + if (!section.comments.isEmpty()) { + for (String s : section.comments) { + writer.ln("# "+s); + } + writer.ln(); + } for (Subject sbj : section.subjects) { if (Utilities.noString(sbj.id)) { writer.write("["); @@ -447,6 +459,7 @@ public class Turtle { int i = 0; for (Predicate p : sbj.predicates) { + // writer.write(p.getPredicate()); writer.write(" "); boolean first = true; @@ -481,12 +494,19 @@ public class Turtle { private void commitSection(StringBuilder b, Section section) throws Exception { b.append("# - "+section.name+" "+Utilities.padLeft("", '-', 75-section.name.length())+"\r\n"); b.append("\r\n"); + if (!section.comments.isEmpty()) { + for (String s : section.comments) { + b.append("# "+s+"\r\n"); + } + b.append("\r\n"); + } for (Subject sbj : section.subjects) { b.append(Utilities.escapeXml(sbj.id)); b.append(" "); int i = 0; for (Predicate p : sbj.predicates) { + // b.append("# test\r\n "); b.append(p.makelink()); b.append(" "); boolean first = true; @@ -497,13 +517,13 @@ public class Turtle { b.append(", "); if (o instanceof StringType) b.append(Utilities.escapeXml(((StringType) o).value)); - else { - b.append("["); + else { + b.append("["); if (write((Complex) o, b, 4)) - b.append("\r\n ]"); - else - b.append("]"); - } + b.append("\r\n ]"); + else + b.append("]"); + } } String comment = p.comment == null? "" : " # "+p.comment; i++; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java index aa1f06679..b271da9a1 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java @@ -480,9 +480,9 @@ public class XMLWriter extends OutputStreamWriter implements IXMLWriter { writePretty(); } if (levels.inComment()) - write(""); + write(" "); if (doPretty && !isPretty()) writePretty(); }