Merge pull request #1729 from hapifhir/EllipseSupport

Added support for tracking whether elements are ellipsed and for rend…
This commit is contained in:
Grahame Grieve 2024-09-22 08:51:39 -04:00 committed by GitHub
commit 6314ad61bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1822 additions and 1660 deletions

View File

@ -162,6 +162,7 @@ public class Element extends Base implements NamedItem {
private FhirFormat format; private FhirFormat format;
private Object nativeObject; private Object nativeObject;
private List<SliceDefinition> sliceDefinitions; private List<SliceDefinition> sliceDefinitions;
private boolean elided;
public Element(String name) { public Element(String name) {
super(); super();
@ -1429,6 +1430,8 @@ public class Element extends Base implements NamedItem {
public Base copy() { public Base copy() {
Element element = new Element(this); Element element = new Element(this);
this.copyValues(element); this.copyValues(element);
if (this.isElided())
element.setElided(true);
return element; return element;
} }
@ -1638,4 +1641,11 @@ public class Element extends Base implements NamedItem {
return FhirPublication.fromCode(property.getStructure().getVersion()); return FhirPublication.fromCode(property.getStructure().getVersion());
} }
public void setElided(boolean elided) {
this.elided = elided;
}
public boolean isElided() {
return this.elided;
}
} }

View File

@ -62,6 +62,7 @@ import org.hl7.fhir.r5.formats.JsonCreatorDirect;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.StringPair; import org.hl7.fhir.utilities.StringPair;
import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TextFile;
@ -85,6 +86,8 @@ public class JsonParser extends ParserBase {
private JsonCreator json; private JsonCreator json;
private boolean allowComments; private boolean allowComments;
private boolean elideElements;
// private boolean suppressResourceType;
private Element baseElement; private Element baseElement;
private boolean markedXhtml; private boolean markedXhtml;
@ -782,7 +785,8 @@ public class JsonParser extends ParserBase {
} }
checkComposeComments(e); checkComposeComments(e);
json.beginObject(); json.beginObject();
prop("resourceType", e.getType(), null); // if (!isSuppressResourceType())
prop("resourceType", e.getType(), null);
Set<String> done = new HashSet<String>(); Set<String> done = new HashSet<String>();
for (Element child : e.getChildren()) { for (Element child : e.getChildren()) {
compose(e.getName(), e, done, child); compose(e.getName(), e, done, child);
@ -807,7 +811,8 @@ public class JsonParser extends ParserBase {
checkComposeComments(e); checkComposeComments(e);
json.beginObject(); json.beginObject();
prop("resourceType", e.getType(), linkResolver == null ? null : linkResolver.resolveProperty(e.getProperty())); // if (!isSuppressResourceType())
prop("resourceType", e.getType(), linkResolver == null ? null : linkResolver.resolveProperty(e.getProperty()));
Set<String> done = new HashSet<String>(); Set<String> done = new HashSet<String>();
for (Element child : e.getChildren()) { for (Element child : e.getChildren()) {
compose(e.getName(), e, done, child); compose(e.getName(), e, done, child);
@ -821,15 +826,50 @@ public class JsonParser extends ParserBase {
if (wantCompose(path, child)) { if (wantCompose(path, child)) {
boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList(); boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList();
if (!isList) {// for specials, ignore the cardinality of the stated type if (!isList) {// for specials, ignore the cardinality of the stated type
compose(path, child); if (child.isElided() && isElideElements() && json.canElide())
json.elide();
else
compose(path, child);
} else if (!done.contains(child.getName())) { } else if (!done.contains(child.getName())) {
done.add(child.getName()); done.add(child.getName());
List<Element> list = e.getChildrenByName(child.getName()); List<Element> list = e.getChildrenByName(child.getName());
composeList(path, list); if (child.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY))
composeKeyList(path, list);
else
composeList(path, list);
} }
} }
} }
private void composeKeyList(String path, List<Element> list) throws IOException {
String keyName = list.get(0).getProperty().getDefinition().getExtensionString(ToolingExtensions.EXT_JSON_PROP_KEY);
json.name(list.get(0).getName());
json.beginObject();
for (Element e: list) {
Element key = null;
Element value = null;
for (Element child: e.getChildren()) {
if (child.getName().equals(keyName))
key = child;
else
value = child;
}
if (value.isPrimitive())
primitiveValue(key.getValue(), value);
else {
json.name(key.getValue());
checkComposeComments(e);
json.beginObject();
Set<String> done = new HashSet<String>();
for (Element child : value.getChildren()) {
compose(value.getName(), value, done, child);
}
json.endObject();
compose(path + "." + key.getValue(), value);
}
}
json.endObject();
}
private void composeList(String path, List<Element> list) throws IOException { private void composeList(String path, List<Element> list) throws IOException {
// there will be at least one element // there will be at least one element
@ -847,7 +887,9 @@ public class JsonParser extends ParserBase {
if (prim) { if (prim) {
openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty()));
for (Element item : list) { for (Element item : list) {
if (item.hasValue()) { if (item.isElided() && json.canElide())
json.elide();
else if (item.hasValue()) {
if (linkResolver != null && item.getProperty().isReference()) { if (linkResolver != null && item.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(item)); String ref = linkResolver.resolveReference(getReferenceForElement(item));
if (ref != null) { if (ref != null) {
@ -866,7 +908,9 @@ public class JsonParser extends ParserBase {
openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty()));
int i = 0; int i = 0;
for (Element item : list) { for (Element item : list) {
if (item.hasChildren()) { if (item.isElided() && json.canElide())
json.elide();
else if (item.hasChildren()) {
open(null,null); open(null,null);
if (item.getProperty().isResource()) { if (item.getProperty().isResource()) {
prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType())); prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType()));
@ -933,9 +977,10 @@ public class JsonParser extends ParserBase {
json.externalLink(ref); json.externalLink(ref);
} }
} }
Set<String> done = new HashSet<String>(); Set<String> done = new HashSet<String>();
for (Element child : element.getChildren()) { for (Element child : element.getChildren()) {
compose(path+"."+element.getName(), element, done, child); compose(path + "." + element.getName(), element, done, child);
} }
close(); close();
} }
@ -951,5 +996,23 @@ public class JsonParser extends ParserBase {
return this; return this;
} }
public boolean isElideElements() {
return elideElements;
}
public JsonParser setElideElements(boolean elideElements) {
this.elideElements = elideElements;
return this;
}
/*
public boolean isSuppressResourceType() {
return suppressResourceType;
}
public JsonParser setSuppressResourceType(boolean suppressResourceType) {
this.suppressResourceType = suppressResourceType;
return this;
}
*/
} }

View File

@ -94,6 +94,7 @@ import org.xml.sax.XMLReader;
public class XmlParser extends ParserBase { public class XmlParser extends ParserBase {
private boolean allowXsiLocation; private boolean allowXsiLocation;
private String version; private String version;
private boolean elideElements;
public XmlParser(IWorkerContext context) { public XmlParser(IWorkerContext context) {
super(context); super(context);
@ -805,92 +806,125 @@ public class XmlParser extends ParserBase {
} }
private void composeElement(IXMLWriter xml, Element element, String elementName, boolean root) throws IOException, FHIRException { private void composeElement(IXMLWriter xml, Element element, String elementName, boolean root) throws IOException, FHIRException {
if (showDecorations) { if (!(isElideElements() && element.isElided())) {
@SuppressWarnings("unchecked") if (showDecorations) {
List<ElementDecoration> decorations = (List<ElementDecoration>) element.getUserData("fhir.decorations"); @SuppressWarnings("unchecked")
if (decorations != null) List<ElementDecoration> decorations = (List<ElementDecoration>) element.getUserData("fhir.decorations");
for (ElementDecoration d : decorations) if (decorations != null)
xml.decorate(d); for (ElementDecoration d : decorations)
} xml.decorate(d);
for (String s : element.getComments()) { }
xml.comment(s, true); for (String s : element.getComments()) {
xml.comment(s, true);
}
} }
if (isText(element.getProperty())) { if (isText(element.getProperty())) {
if (linkResolver != null) if (isElideElements() && element.isElided() && xml.canElide())
xml.link(linkResolver.resolveProperty(element.getProperty())); xml.elide();
xml.enter(element.getProperty().getXmlNamespace(),elementName); else {
if (linkResolver != null && element.getProperty().isReference()) { if (linkResolver != null)
String ref = linkResolver.resolveReference(getReferenceForElement(element)); xml.link(linkResolver.resolveProperty(element.getProperty()));
if (ref != null) { xml.enter(element.getProperty().getXmlNamespace(),elementName);
xml.externalLink(ref); if (linkResolver != null && element.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(element));
if (ref != null) {
xml.externalLink(ref);
}
} }
xml.text(element.getValue());
xml.exit(element.getProperty().getXmlNamespace(),elementName);
} }
xml.text(element.getValue());
xml.exit(element.getProperty().getXmlNamespace(),elementName);
} else if (!element.hasChildren() && !element.hasValue()) { } else if (!element.hasChildren() && !element.hasValue()) {
if (element.getExplicitType() != null) if (isElideElements() && element.isElided() && xml.canElide())
xml.attribute("xsi:type", element.getExplicitType()); xml.elide();
xml.element(elementName); else {
if (element.getExplicitType() != null)
xml.attribute("xsi:type", element.getExplicitType());
xml.element(elementName);
}
} else if (element.isPrimitive() || (element.hasType() && isPrimitive(element.getType()))) { } else if (element.isPrimitive() || (element.hasType() && isPrimitive(element.getType()))) {
if (element.getType().equals("xhtml")) { if (element.getType().equals("xhtml")) {
String rawXhtml = element.getValue(); if (isElideElements() && element.isElided() && xml.canElide())
if (isCdaText(element.getProperty())) { xml.elide();
new CDANarrativeFormat().convert(xml, new XhtmlParser().parseFragment(rawXhtml)); else {
} else { String rawXhtml = element.getValue();
xml.escapedText(rawXhtml); if (isCdaText(element.getProperty())) {
if (!markedXhtml) { new CDANarrativeFormat().convert(xml, new XhtmlParser().parseFragment(rawXhtml));
xml.anchor("end-xhtml"); } else {
markedXhtml = true; xml.escapedText(rawXhtml);
if (!markedXhtml) {
xml.anchor("end-xhtml");
markedXhtml = true;
}
} }
} }
} else if (isText(element.getProperty())) { } else if (isText(element.getProperty())) {
if (linkResolver != null) if (isElideElements() && element.isElided() && xml.canElide())
xml.link(linkResolver.resolveProperty(element.getProperty())); xml.elide();
xml.text(element.getValue()); else {
} else {
setXsiTypeIfIsTypeAttr(xml, element);
if (element.hasValue()) {
if (linkResolver != null) if (linkResolver != null)
xml.link(linkResolver.resolveType(element.getType())); xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.attribute("value", element.getValue()); xml.text(element.getValue());
} }
if (linkResolver != null) } else {
xml.link(linkResolver.resolveProperty(element.getProperty())); if (isElideElements() && element.isElided())
if (element.hasChildren()) { xml.attributeElide();
xml.enter(element.getProperty().getXmlNamespace(), elementName); else {
if (linkResolver != null && element.getProperty().isReference()) { setXsiTypeIfIsTypeAttr(xml, element);
String ref = linkResolver.resolveReference(getReferenceForElement(element)); if (element.hasValue()) {
if (ref != null) { if (linkResolver != null)
xml.externalLink(ref); xml.link(linkResolver.resolveType(element.getType()));
} xml.attribute("value", element.getValue());
} }
for (Element child : element.getChildren()) if (linkResolver != null)
composeElement(xml, child, child.getName(), false); xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.exit(element.getProperty().getXmlNamespace(),elementName); if (element.hasChildren()) {
} else xml.enter(element.getProperty().getXmlNamespace(), elementName);
xml.element(elementName); if (linkResolver != null && element.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(element));
if (ref != null) {
xml.externalLink(ref);
}
}
for (Element child : element.getChildren())
composeElement(xml, child, child.getName(), false);
xml.exit(element.getProperty().getXmlNamespace(),elementName);
} else
xml.element(elementName);
}
} }
} else { } else {
setXsiTypeIfIsTypeAttr(xml, element); if (isElideElements() && element.isElided() && xml.canElide())
Set<String> handled = new HashSet<>(); xml.elide();
else {
setXsiTypeIfIsTypeAttr(xml, element);
Set<String> handled = new HashSet<>();
for (Element child : element.getChildren()) { for (Element child : element.getChildren()) {
if (!handled.contains(child.getName()) && isAttr(child.getProperty()) && wantCompose(element.getPath(), child)) { if (!handled.contains(child.getName()) && isAttr(child.getProperty()) && wantCompose(element.getPath(), child)) {
handled.add(child.getName()); handled.add(child.getName());
String av = child.getValue(); if (isElideElements() && child.isElided())
if (child.getProperty().isList()) { xml.attributeElide();
for (Element c2 : element.getChildren()) { else {
if (c2 != child && c2.getName().equals(child.getName())) { String av = child.getValue();
av = av + " "+c2.getValue(); if (child.getProperty().isList()) {
for (Element c2 : element.getChildren()) {
if (c2 != child && c2.getName().equals(child.getName())) {
if (c2.isElided())
av = av + " ...";
else
av = av + " " + c2.getValue();
}
} }
} }
if (linkResolver != null)
xml.link(linkResolver.resolveType(child.getType()));
if (ToolingExtensions.hasExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT))
av = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av);
xml.attribute(child.getProperty().getXmlNamespace(), child.getProperty().getXmlName(), av);
} }
if (linkResolver != null)
xml.link(linkResolver.resolveType(child.getType()));
if (ToolingExtensions.hasExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT))
av = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av);
xml.attribute(child.getProperty().getXmlNamespace(),child.getProperty().getXmlName(), av);
} }
} }
}
if (!element.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) { if (!element.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) {
if (linkResolver != null) if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty())); xml.link(linkResolver.resolveProperty(element.getProperty()));
@ -914,12 +948,16 @@ public class XmlParser extends ParserBase {
} }
for (Element child : element.getChildren()) { for (Element child : element.getChildren()) {
if (wantCompose(element.getPath(), child)) { if (wantCompose(element.getPath(), child)) {
if (isText(child.getProperty())) { if (isElideElements() && child.isElided() && xml.canElide())
if (linkResolver != null) xml.elide();
xml.link(linkResolver.resolveProperty(element.getProperty())); else {
xml.text(child.getValue()); if (isText(child.getProperty())) {
} else if (!isAttr(child.getProperty())) { if (linkResolver != null)
composeElement(xml, child, child.getName(), false); xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.text(child.getValue());
} else if (!isAttr(child.getProperty())) {
composeElement(xml, child, child.getName(), false);
}
} }
} }
} }
@ -1034,4 +1072,13 @@ public class XmlParser extends ParserBase {
// do nothing // do nothing
} }
} }
public boolean isElideElements() {
return elideElements;
}
public void setElideElements(boolean elideElements) {
this.elideElements = elideElements;
}
} }

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats; package org.hl7.fhir.r5.formats;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -28,47 +28,49 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
* Facade to GSON writer, or something that imposes property ordering first * Facade to GSON writer, or something that imposes property ordering first
* *
* @author Grahame * @author Grahame
* *
*/ */
public interface JsonCreator { public interface JsonCreator {
void comment(String comment); void comment(String comment);
void beginObject() throws IOException; void beginObject() throws IOException;
void endObject() throws IOException; void endObject() throws IOException;
void nullValue() throws IOException; void nullValue() throws IOException;
void name(String name) throws IOException; void name(String name) throws IOException;
void value(String value) throws IOException; void value(String value) throws IOException;
void value(Boolean value) throws IOException; void value(Boolean value) throws IOException;
void value(BigDecimal value) throws IOException; void value(BigDecimal value) throws IOException;
void valueNum(String value) throws IOException; // allow full control of representation void valueNum(String value) throws IOException; // allow full control of representation
void value(Integer value) throws IOException; void value(Integer value) throws IOException;
void beginArray() throws IOException; void beginArray() throws IOException;
void endArray() throws IOException; void endArray() throws IOException;
void finish() throws IOException; void finish() throws IOException;
// only used by an creator that actually produces xhtml // only used by an creator that actually produces xhtml
void link(String href); void link(String href);
void anchor(String string); void anchor(String string);
void externalLink(String string); void externalLink(String string);
void elide();
boolean canElide();
} }

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats; package org.hl7.fhir.r5.formats;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -28,257 +28,264 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
public class JsonCreatorCanonical implements JsonCreator { public class JsonCreatorCanonical implements JsonCreator {
public class JsonCanValue { public class JsonCanValue {
String name; String name;
private JsonCanValue(String name) { private JsonCanValue(String name) {
this.name = name; this.name = name;
} }
} }
private class JsonCanNumberValue extends JsonCanValue { private class JsonCanNumberValue extends JsonCanValue {
private BigDecimal value; private BigDecimal value;
private JsonCanNumberValue(String name, BigDecimal value) { private JsonCanNumberValue(String name, BigDecimal value) {
super(name); super(name);
this.value = value; this.value = value;
} }
} }
private class JsonCanPresentedNumberValue extends JsonCanValue { private class JsonCanPresentedNumberValue extends JsonCanValue {
private String value; private String value;
private JsonCanPresentedNumberValue(String name, String value) { private JsonCanPresentedNumberValue(String name, String value) {
super(name); super(name);
this.value = value; this.value = value;
} }
} }
private class JsonCanIntegerValue extends JsonCanValue { private class JsonCanIntegerValue extends JsonCanValue {
private Integer value; private Integer value;
private JsonCanIntegerValue(String name, Integer value) { private JsonCanIntegerValue(String name, Integer value) {
super(name); super(name);
this.value = value; this.value = value;
} }
} }
private class JsonCanBooleanValue extends JsonCanValue { private class JsonCanBooleanValue extends JsonCanValue {
private Boolean value; private Boolean value;
private JsonCanBooleanValue(String name, Boolean value) { private JsonCanBooleanValue(String name, Boolean value) {
super(name); super(name);
this.value = value; this.value = value;
} }
} }
private class JsonCanStringValue extends JsonCanValue { private class JsonCanStringValue extends JsonCanValue {
private String value; private String value;
private JsonCanStringValue(String name, String value) { private JsonCanStringValue(String name, String value) {
super(name); super(name);
this.value = value; this.value = value;
} }
} }
private class JsonCanNullValue extends JsonCanValue { private class JsonCanNullValue extends JsonCanValue {
private JsonCanNullValue(String name) { private JsonCanNullValue(String name) {
super(name); super(name);
} }
} }
public class JsonCanObject extends JsonCanValue { public class JsonCanObject extends JsonCanValue {
boolean array; boolean array;
List<JsonCanValue> children = new ArrayList<JsonCanValue>(); List<JsonCanValue> children = new ArrayList<JsonCanValue>();
public JsonCanObject(String name, boolean array) { public JsonCanObject(String name, boolean array) {
super(name); super(name);
this.array = array; this.array = array;
} }
public void addProp(JsonCanValue obj) { public void addProp(JsonCanValue obj) {
children.add(obj); children.add(obj);
} }
} }
Stack<JsonCanObject> stack; Stack<JsonCanObject> stack;
JsonCanObject root; JsonCanObject root;
JsonCreatorDirect jj; JsonCreatorDirect jj;
String name; String name;
public JsonCreatorCanonical(OutputStreamWriter osw) { public JsonCreatorCanonical(OutputStreamWriter osw) {
stack = new Stack<JsonCreatorCanonical.JsonCanObject>(); stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
jj = new JsonCreatorDirect(osw, false, false); jj = new JsonCreatorDirect(osw, false, false);
name = null; name = null;
} }
private String takeName() { private String takeName() {
String res = name; String res = name;
name = null; name = null;
return res; return res;
} }
@Override @Override
public void beginObject() throws IOException { public void beginObject() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), false); JsonCanObject obj = new JsonCanObject(takeName(), false);
if (stack.isEmpty()) if (stack.isEmpty())
root = obj; root = obj;
else else
stack.peek().addProp(obj); stack.peek().addProp(obj);
stack.push(obj); stack.push(obj);
} }
@Override @Override
public void endObject() throws IOException { public void endObject() throws IOException {
stack.pop(); stack.pop();
} }
@Override @Override
public void nullValue() throws IOException { public void nullValue() throws IOException {
stack.peek().addProp(new JsonCanNullValue(takeName())); stack.peek().addProp(new JsonCanNullValue(takeName()));
} }
@Override @Override
public void name(String name) throws IOException { public void name(String name) throws IOException {
this.name = name; this.name = name;
} }
@Override @Override
public void value(String value) throws IOException { public void value(String value) throws IOException {
stack.peek().addProp(new JsonCanStringValue(takeName(), value)); stack.peek().addProp(new JsonCanStringValue(takeName(), value));
} }
@Override @Override
public void value(Boolean value) throws IOException { public void value(Boolean value) throws IOException {
stack.peek().addProp(new JsonCanBooleanValue(takeName(), value)); stack.peek().addProp(new JsonCanBooleanValue(takeName(), value));
} }
@Override @Override
public void value(BigDecimal value) throws IOException { public void value(BigDecimal value) throws IOException {
stack.peek().addProp(new JsonCanNumberValue(takeName(), value)); stack.peek().addProp(new JsonCanNumberValue(takeName(), value));
} }
@Override @Override
public void valueNum(String value) throws IOException { public void valueNum(String value) throws IOException {
stack.peek().addProp(new JsonCanPresentedNumberValue(takeName(), value)); stack.peek().addProp(new JsonCanPresentedNumberValue(takeName(), value));
} }
@Override @Override
public void value(Integer value) throws IOException { public void value(Integer value) throws IOException {
stack.peek().addProp(new JsonCanIntegerValue(takeName(), value)); stack.peek().addProp(new JsonCanIntegerValue(takeName(), value));
} }
@Override @Override
public void beginArray() throws IOException { public void beginArray() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), true); JsonCanObject obj = new JsonCanObject(takeName(), true);
if (!stack.isEmpty()) if (!stack.isEmpty())
stack.peek().addProp(obj); stack.peek().addProp(obj);
stack.push(obj); stack.push(obj);
} }
@Override @Override
public void endArray() throws IOException { public void endArray() throws IOException {
stack.pop(); stack.pop();
} }
@Override @Override
public void finish() throws IOException { public void finish() throws IOException {
writeObject(root); writeObject(root);
} }
private void writeObject(JsonCanObject obj) throws IOException { private void writeObject(JsonCanObject obj) throws IOException {
jj.beginObject(); jj.beginObject();
List<String> names = new ArrayList<String>(); List<String> names = new ArrayList<String>();
for (JsonCanValue v : obj.children) for (JsonCanValue v : obj.children)
names.add(v.name); names.add(v.name);
Collections.sort(names); Collections.sort(names);
for (String n : names) { for (String n : names) {
jj.name(n); jj.name(n);
JsonCanValue v = getPropForName(n, obj.children); JsonCanValue v = getPropForName(n, obj.children);
if (v instanceof JsonCanNumberValue) if (v instanceof JsonCanNumberValue)
jj.value(((JsonCanNumberValue) v).value); jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanPresentedNumberValue) else if (v instanceof JsonCanPresentedNumberValue)
jj.valueNum(((JsonCanPresentedNumberValue) v).value); jj.valueNum(((JsonCanPresentedNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue) else if (v instanceof JsonCanIntegerValue)
jj.value(((JsonCanIntegerValue) v).value); jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue) else if (v instanceof JsonCanBooleanValue)
jj.value(((JsonCanBooleanValue) v).value); jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue) else if (v instanceof JsonCanStringValue)
jj.value(((JsonCanStringValue) v).value); jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue) else if (v instanceof JsonCanNullValue)
jj.nullValue(); jj.nullValue();
else if (v instanceof JsonCanObject) { else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v; JsonCanObject o = (JsonCanObject) v;
if (o.array) if (o.array)
writeArray(o); writeArray(o);
else else
writeObject(o); writeObject(o);
} else } else
throw new Error("not possible"); throw new Error("not possible");
} }
jj.endObject(); jj.endObject();
} }
private JsonCanValue getPropForName(String name, List<JsonCanValue> children) { private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
for (JsonCanValue child : children) for (JsonCanValue child : children)
if (child.name.equals(name)) if (child.name.equals(name))
return child; return child;
return null; return null;
} }
private void writeArray(JsonCanObject arr) throws IOException { private void writeArray(JsonCanObject arr) throws IOException {
jj.beginArray(); jj.beginArray();
for (JsonCanValue v : arr.children) { for (JsonCanValue v : arr.children) {
if (v instanceof JsonCanNumberValue) if (v instanceof JsonCanNumberValue)
jj.value(((JsonCanNumberValue) v).value); jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue) else if (v instanceof JsonCanIntegerValue)
jj.value(((JsonCanIntegerValue) v).value); jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue) else if (v instanceof JsonCanBooleanValue)
jj.value(((JsonCanBooleanValue) v).value); jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue) else if (v instanceof JsonCanStringValue)
jj.value(((JsonCanStringValue) v).value); jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue) else if (v instanceof JsonCanNullValue)
jj.nullValue(); jj.nullValue();
else if (v instanceof JsonCanObject) { else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v; JsonCanObject o = (JsonCanObject) v;
if (o.array) if (o.array)
writeArray(o); writeArray(o);
else else
writeObject(o); writeObject(o);
} else } else
throw new Error("not possible"); throw new Error("not possible");
} }
jj.endArray(); jj.endArray();
} }
@Override @Override
public void comment(String content) { public void comment(String content) {
// canonical JSON ignores comments // canonical JSON ignores comments
} }
@Override @Override
public void link(String href) { public void link(String href) {
// not used // not used
} }
@Override @Override
public void anchor(String name) { public void anchor(String name) {
// not used // not used
} }
@Override @Override
public void externalLink(String string) { public void externalLink(String string) {
// not used // not used
} }
@Override
public boolean canElide() { return false; }
@Override
public void elide() {
// not used
}
} }

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats; package org.hl7.fhir.r5.formats;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -28,219 +28,227 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
/** /**
* A little implementation of a json write to replace Gson .... because Gson screws up decimal values, and *we care* * A little implementation of a json write to replace Gson .... because Gson screws up decimal values, and *we care*
* *
* @author Grahame Grieve * @author Grahame Grieve
* *
*/ */
public class JsonCreatorDirect implements JsonCreator { public class JsonCreatorDirect implements JsonCreator {
private Writer writer; private Writer writer;
private boolean pretty; private boolean pretty;
private boolean comments; private boolean comments;
private boolean named; private boolean named;
private List<Boolean> valued = new ArrayList<Boolean>(); private List<Boolean> valued = new ArrayList<Boolean>();
private int indent; private int indent;
private List<String> commentList = new ArrayList<>(); private List<String> commentList = new ArrayList<>();
public JsonCreatorDirect(Writer writer, boolean pretty, boolean comments) { public JsonCreatorDirect(Writer writer, boolean pretty, boolean comments) {
super(); super();
this.writer = writer; this.writer = writer;
this.pretty = pretty; this.pretty = pretty;
this.comments = pretty && comments; this.comments = pretty && comments;
} }
@Override @Override
public void comment(String content) { public void comment(String content) {
if (comments) { if (comments) {
commentList.add(content); commentList.add(content);
} }
} }
@Override @Override
public void beginObject() throws IOException { public void beginObject() throws IOException {
checkState(); checkState();
writer.write("{"); writer.write("{");
stepIn(); stepIn();
if (!valued.isEmpty()) { if (!valued.isEmpty()) {
valued.set(0, true); valued.set(0, true);
} }
valued.add(0, false); valued.add(0, false);
} }
private void commitComments() throws IOException { private void commitComments() throws IOException {
if (comments) { if (comments) {
for (String s : commentList) { for (String s : commentList) {
writer.write("// "); writer.write("// ");
writer.write(s); writer.write(s);
writer.write("\r\n"); writer.write("\r\n");
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++) {
writer.write(" "); writer.write(" ");
} }
} }
commentList.clear(); commentList.clear();
} }
} }
public void stepIn() throws IOException { public void stepIn() throws IOException {
if (pretty) { if (pretty) {
indent++; indent++;
writer.write("\r\n"); writer.write("\r\n");
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++) {
writer.write(" "); writer.write(" ");
} }
} }
} }
public void stepOut() throws IOException { public void stepOut() throws IOException {
if (pretty) { if (pretty) {
indent--; indent--;
writer.write("\r\n"); writer.write("\r\n");
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++) {
writer.write(" "); writer.write(" ");
} }
} }
} }
private void checkState() throws IOException { private void checkState() throws IOException {
commitComments(); commitComments();
if (named) { if (named) {
if (pretty) if (pretty)
writer.write(" : "); writer.write(" : ");
else else
writer.write(":"); writer.write(":");
named = false; named = false;
} }
if (!valued.isEmpty() && valued.get(0)) { if (!valued.isEmpty() && valued.get(0)) {
writer.write(","); writer.write(",");
if (pretty) { if (pretty) {
writer.write("\r\n"); writer.write("\r\n");
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++) {
writer.write(" "); writer.write(" ");
} }
} }
valued.set(0, false); valued.set(0, false);
} }
} }
@Override @Override
public void endObject() throws IOException { public void endObject() throws IOException {
stepOut(); stepOut();
writer.write("}"); writer.write("}");
valued.remove(0); valued.remove(0);
} }
@Override @Override
public void nullValue() throws IOException { public void nullValue() throws IOException {
checkState(); checkState();
writer.write("null"); writer.write("null");
valued.set(0, true); valued.set(0, true);
} }
@Override @Override
public void name(String name) throws IOException { public void name(String name) throws IOException {
checkState(); checkState();
writer.write("\""+name+"\""); writer.write("\""+name+"\"");
named = true; named = true;
} }
@Override @Override
public void value(String value) throws IOException { public void value(String value) throws IOException {
checkState(); checkState();
writer.write("\""+Utilities.escapeJson(value)+"\""); writer.write("\""+Utilities.escapeJson(value)+"\"");
valued.set(0, true); valued.set(0, true);
} }
@Override @Override
public void value(Boolean value) throws IOException { public void value(Boolean value) throws IOException {
checkState(); checkState();
if (value == null) if (value == null)
writer.write("null"); writer.write("null");
else if (value.booleanValue()) else if (value.booleanValue())
writer.write("true"); writer.write("true");
else else
writer.write("false"); writer.write("false");
valued.set(0, true); valued.set(0, true);
} }
@Override @Override
public void value(BigDecimal value) throws IOException { public void value(BigDecimal value) throws IOException {
checkState(); checkState();
if (value == null) if (value == null)
writer.write("null"); writer.write("null");
else else
writer.write(value.toString()); writer.write(value.toString());
valued.set(0, true); valued.set(0, true);
} }
@Override @Override
public void valueNum(String value) throws IOException { public void valueNum(String value) throws IOException {
checkState(); checkState();
if (value == null) if (value == null)
writer.write("null"); writer.write("null");
else else
writer.write(value); writer.write(value);
valued.set(0, true); valued.set(0, true);
} }
@Override @Override
public void value(Integer value) throws IOException { public void value(Integer value) throws IOException {
checkState(); checkState();
if (value == null) if (value == null)
writer.write("null"); writer.write("null");
else else
writer.write(value.toString()); writer.write(value.toString());
valued.set(0, true); valued.set(0, true);
} }
@Override @Override
public void beginArray() throws IOException { public void beginArray() throws IOException {
checkState(); checkState();
writer.write("["); writer.write("[");
if (!valued.isEmpty()) { if (!valued.isEmpty()) {
valued.set(0, true); valued.set(0, true);
} }
valued.add(0, false); valued.add(0, false);
} }
@Override @Override
public void endArray() throws IOException { public void endArray() throws IOException {
writer.write("]"); writer.write("]");
valued.remove(0); valued.remove(0);
} }
@Override @Override
public void finish() throws IOException { public void finish() throws IOException {
writer.flush(); writer.flush();
} }
@Override @Override
public void link(String href) { public void link(String href) {
// not used // not used
} }
@Override @Override
public void anchor(String name) { public void anchor(String name) {
// not used // not used
} }
@Override @Override
public void externalLink(String string) { public void externalLink(String string) {
// not used // not used
} }
@Override
public boolean canElide() { return false; }
@Override
public void elide() {
// not used
}
} }

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats; package org.hl7.fhir.r5.formats;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -28,103 +28,111 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
public class JsonCreatorGson implements JsonCreator { public class JsonCreatorGson implements JsonCreator {
JsonWriter gson; JsonWriter gson;
public JsonCreatorGson(OutputStreamWriter osw) { public JsonCreatorGson(OutputStreamWriter osw) {
gson = new JsonWriter(osw); gson = new JsonWriter(osw);
} }
@Override @Override
public void beginObject() throws IOException { public void beginObject() throws IOException {
gson.beginObject(); gson.beginObject();
} }
@Override @Override
public void endObject() throws IOException { public void endObject() throws IOException {
gson.endObject(); gson.endObject();
} }
@Override @Override
public void nullValue() throws IOException { public void nullValue() throws IOException {
gson.nullValue(); gson.nullValue();
} }
@Override @Override
public void name(String name) throws IOException { public void name(String name) throws IOException {
gson.name(name); gson.name(name);
} }
@Override @Override
public void value(String value) throws IOException { public void value(String value) throws IOException {
gson.value(value); gson.value(value);
} }
@Override @Override
public void value(Boolean value) throws IOException { public void value(Boolean value) throws IOException {
gson.value(value); gson.value(value);
} }
@Override @Override
public void value(BigDecimal value) throws IOException { public void value(BigDecimal value) throws IOException {
gson.value(value); gson.value(value);
} }
@Override @Override
public void value(Integer value) throws IOException { public void value(Integer value) throws IOException {
gson.value(value); gson.value(value);
} }
@Override @Override
public void beginArray() throws IOException { public void beginArray() throws IOException {
gson.beginArray(); gson.beginArray();
} }
@Override @Override
public void endArray() throws IOException { public void endArray() throws IOException {
gson.endArray(); gson.endArray();
} }
@Override @Override
public void finish() { public void finish() {
// nothing to do here // nothing to do here
} }
@Override @Override
public void link(String href) { public void link(String href) {
// not used // not used
} }
@Override @Override
public void valueNum(String value) throws IOException { public void valueNum(String value) throws IOException {
value(new BigDecimal(value)); value(new BigDecimal(value));
} }
@Override @Override
public void anchor(String name) { public void anchor(String name) {
// not used // not used
} }
@Override @Override
public void comment(String content) { public void comment(String content) {
// gson (dense json) ignores comments // gson (dense json) ignores comments
} }
@Override @Override
public void externalLink(String string) { public void externalLink(String string) {
// not used // not used
} }
@Override
public void elide() {
// not used
}
@Override
public boolean canElide() { return false;}
} }

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.utilities.xml; package org.hl7.fhir.utilities.xml;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -28,80 +28,85 @@ package org.hl7.fhir.utilities.xml;
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.io.IOException; import java.io.IOException;
import org.hl7.fhir.utilities.ElementDecoration; import org.hl7.fhir.utilities.ElementDecoration;
/** /**
* Generalize * Generalize
* @author dennisn * @author dennisn
*/ */
public interface IXMLWriter { public interface IXMLWriter {
public abstract void start() throws IOException; public abstract void start() throws IOException;
public abstract void end() throws IOException; public abstract void end() throws IOException;
public abstract void attribute(String namespace, String name, String value, boolean onlyIfNotEmpty) throws IOException; public abstract void attribute(String namespace, String name, String value, boolean onlyIfNotEmpty) throws IOException;
public abstract void attribute(String namespace, String name, String value) throws IOException; public abstract void attribute(String namespace, String name, String value) throws IOException;
public abstract void attribute(String name, String value, boolean onlyIfNotEmpty) throws IOException; public abstract void attribute(String name, String value, boolean onlyIfNotEmpty) throws IOException;
public abstract void attribute(String name, String value) throws IOException; public abstract void attribute(String name, String value) throws IOException;
public abstract void attributeNoLines(String name, String value) throws IOException; public abstract void attributeNoLines(String name, String value) throws IOException;
public abstract boolean namespaceDefined(String namespace); public abstract boolean namespaceDefined(String namespace);
public abstract boolean abbreviationDefined(String abbreviation); public abstract boolean abbreviationDefined(String abbreviation);
public abstract String getDefaultNamespace(); public abstract String getDefaultNamespace();
public abstract void namespace(String namespace) throws IOException; public abstract void namespace(String namespace) throws IOException;
public abstract void setDefaultNamespace(String namespace) throws IOException; public abstract void setDefaultNamespace(String namespace) throws IOException;
public abstract void namespace(String namespace, String abbreviation) throws IOException; public abstract void namespace(String namespace, String abbreviation) throws IOException;
public abstract void comment(String comment, boolean doPretty) throws IOException; public abstract void comment(String comment, boolean doPretty) throws IOException;
public abstract void decorate(ElementDecoration decoration) throws IOException; public abstract void decorate(ElementDecoration decoration) throws IOException;
public abstract void setSchemaLocation(String ns, String loc) throws IOException; public abstract void setSchemaLocation(String ns, String loc) throws IOException;
public abstract void enter(String name) throws IOException; public abstract void enter(String name) throws IOException;
public abstract void enter(String namespace, String name) throws IOException; public abstract void enter(String namespace, String name) throws IOException;
public abstract void enter(String namespace, String name, String comment) throws IOException; public abstract void enter(String namespace, String name, String comment) throws IOException;
public abstract void exit() throws IOException; public abstract void exit() throws IOException;
public abstract void exit(String name) throws IOException; public abstract void exit(String name) throws IOException;
public abstract void exit(String namespace, String name) throws IOException; public abstract void exit(String namespace, String name) throws IOException;
public abstract void exitToLevel(int count) throws IOException; public abstract void exitToLevel(int count) throws IOException;
public abstract void element(String namespace, String name, String content, boolean onlyIfNotEmpty) throws IOException; public abstract void element(String namespace, String name, String content, boolean onlyIfNotEmpty) throws IOException;
public abstract void element(String namespace, String name, String content, String comment) throws IOException; public abstract void element(String namespace, String name, String content, String comment) throws IOException;
public abstract void element(String namespace, String name, String content) throws IOException; public abstract void element(String namespace, String name, String content) throws IOException;
public abstract void element(String name, String content, boolean onlyIfNotEmpty) throws IOException; public abstract void element(String name, String content, boolean onlyIfNotEmpty) throws IOException;
public abstract void element(String name, String content) throws IOException; public abstract void element(String name, String content) throws IOException;
public abstract void element(String name) throws IOException; public abstract void element(String name) throws IOException;
public abstract void text(String content) throws IOException; public abstract void text(String content) throws IOException;
public abstract void text(String content, boolean dontEscape) throws IOException; public abstract void text(String content, boolean dontEscape) throws IOException;
public abstract void cData(String text) throws IOException; public abstract void cData(String text) throws IOException;
public abstract void writeBytes(byte[] bytes) throws IOException; public abstract void writeBytes(byte[] bytes) throws IOException;
public abstract boolean isPretty() throws IOException; public abstract boolean isPretty() throws IOException;
public abstract void setPretty(boolean pretty) throws IOException; public abstract void setPretty(boolean pretty) throws IOException;
/** /**
* Start comment inserts a <!-- in the stream, but allows the user to * Start comment inserts a <!-- in the stream, but allows the user to
* go on creating xml content as usual, with proper formatting applied etc. * go on creating xml content as usual, with proper formatting applied etc.
* Any comments inserted inside a comment will be terminated with -- > instead of --> * Any comments inserted inside a comment will be terminated with -- > instead of -->
* so the comment doesn't close prematurely. * so the comment doesn't close prematurely.
* @throws IOException * @throws IOException
*/ */
public abstract void startCommentBlock() throws IOException; public abstract void startCommentBlock() throws IOException;
public abstract void endCommentBlock() throws IOException; public abstract void endCommentBlock() throws IOException;
public abstract void escapedText(String content) throws IOException; public abstract void escapedText(String content) throws IOException;
// this is only implemented by an implementation that is producing an xhtml representation, and is able to render elements as hyperlinks // this is only implemented by an implementation that is producing an xhtml representation, and is able to render elements as hyperlinks
public abstract void link(String href); public abstract void link(String href);
public abstract void anchor(String name); public abstract void anchor(String name);
public abstract void externalLink(String ref) throws IOException; public abstract void externalLink(String ref) throws IOException;
// this is only implemented by an implementation that is producing an xhtml representation and handles ellipsing elements
public abstract boolean canElide();
public abstract void elide() throws IOException;
public abstract void attributeElide();
} }