001package org.hl7.fhir.dstu3.elementmodel; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.io.OutputStream; 006import java.io.OutputStreamWriter; 007import java.math.BigDecimal; 008import java.util.HashSet; 009import java.util.List; 010import java.util.Set; 011 012import org.apache.commons.lang3.NotImplementedException; 013import org.hl7.fhir.dstu3.formats.IParser.OutputStyle; 014import org.hl7.fhir.dstu3.formats.JsonCreator; 015import org.hl7.fhir.dstu3.formats.JsonCreatorCanonical; 016import org.hl7.fhir.dstu3.formats.JsonCreatorGson; 017import org.hl7.fhir.dstu3.context.IWorkerContext; 018import org.hl7.fhir.utilities.Utilities; 019 020public class JsonLDParser extends ParserBase { 021 022 private JsonCreator json; 023 private String base; 024 025 public JsonLDParser(IWorkerContext context) { 026 super(context); 027 } 028 029 @Override 030 public Element parse(InputStream stream) { 031 throw new NotImplementedException("not done yet"); 032 } 033 034 035 protected void prop(String name, String value) throws IOException { 036 if (name != null) 037 json.name(name); 038 json.value(value); 039 } 040 041 protected void open(String name) throws IOException { 042 if (name != null) 043 json.name(name); 044 json.beginObject(); 045 } 046 047 protected void close() throws IOException { 048 json.endObject(); 049 } 050 051 protected void openArray(String name) throws IOException { 052 if (name != null) 053 json.name(name); 054 json.beginArray(); 055 } 056 057 protected void closeArray() throws IOException { 058 json.endArray(); 059 } 060 061 062 @Override 063 public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws Exception { 064 this.base = base; 065 OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); 066 if (style == OutputStyle.CANONICAL) 067 json = new JsonCreatorCanonical(osw); 068 else 069 json = new JsonCreatorGson(osw); 070 json.setIndent(style == OutputStyle.PRETTY ? " " : ""); 071 json.beginObject(); 072 prop("@context", "http://hl7.org/fhir/jsonld/"+e.getType()); 073 prop("resourceType", e.getType()); 074 Set<String> done = new HashSet<String>(); 075 for (Element child : e.getChildren()) { 076 compose(e.getName(), e, done, child); 077 } 078 json.endObject(); 079 json.finish(); 080 osw.flush(); 081 } 082 083 private void compose(String path, Element e, Set<String> done, Element child) throws IOException { 084 if (!child.getProperty().isList()) { 085 compose(path, child); 086 } else if (!done.contains(child.getName())) { 087 done.add(child.getName()); 088 List<Element> list = e.getChildrenByName(child.getName()); 089 composeList(path, list); 090 } 091 } 092 093 private void composeList(String path, List<Element> list) throws IOException { 094 // there will be at least one element 095 String en = list.get(0).getProperty().getDefinition().getBase().getPath(); 096 if (en == null) 097 en = list.get(0).getProperty().getDefinition().getPath(); 098 boolean doType = false; 099 if (en.endsWith("[x]")) { 100 en = en.substring(0, en.length()-3); 101 doType = true; 102 } 103 if (doType) 104 en = en + Utilities.capitalize(list.get(0).getType()); 105 106 openArray(en); 107 for (Element item : list) { 108 open(null); 109 if (item.isPrimitive() || isPrimitive(item.getType())) { 110 if (item.hasValue()) 111 primitiveValue(item); 112 } 113 Set<String> done = new HashSet<String>(); 114 for (Element child : item.getChildren()) { 115 compose(path+"."+item.getName(), item, done, child); 116 } 117 close(); 118 } 119 closeArray(); 120 } 121 122 private void primitiveValue(Element item) throws IOException { 123 json.name("value"); 124 String type = item.getType(); 125 if (Utilities.existsInList(type, "boolean")) 126 json.value(item.getValue().equals("true") ? new Boolean(true) : new Boolean(false)); 127 else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt")) 128 json.value(new Integer(item.getValue())); 129 else if (Utilities.existsInList(type, "decimal")) 130 json.value(new BigDecimal(item.getValue())); 131 else 132 json.value(item.getValue()); 133 } 134 135 private void compose(String path, Element element) throws IOException { 136 String en = element.getProperty().getDefinition().getBase().getPath(); 137 if (en == null) 138 en = element.getProperty().getDefinition().getPath(); 139 boolean doType = false; 140 if (en.endsWith("[x]")) { 141 en = en.substring(0, en.length()-3); 142 doType = true; 143 } 144 if (doType) 145 en = en + Utilities.capitalize(element.getType()); 146 147 if (element.hasChildren() || element.hasComments() || element.hasValue()) { 148 open(en); 149 150 if (element.isPrimitive() || isPrimitive(element.getType())) { 151 if (element.hasValue()) 152 primitiveValue(element); 153 } 154 if (element.getProperty().isResource()) { 155 prop("resourceType", element.getType()); 156 element = element.getChildren().get(0); 157 } 158 Set<String> done = new HashSet<String>(); 159 for (Element child : element.getChildren()) { 160 compose(path+"."+element.getName(), element, done, child); 161 } 162 if ("Coding".equals(element.getType())) 163 decorateCoding(element); 164 if ("CodeableConcept".equals(element.getType())) 165 decorateCodeableConcept(element); 166 if ("Reference".equals(element.getType())) 167 decorateReference(element); 168 169 close(); 170 } 171 } 172 173 private void decorateReference(Element element) throws IOException { 174 String ref = element.getChildValue("reference"); 175 if (ref != null && (ref.startsWith("http://") || ref.startsWith("https://"))) { 176 json.name("reference"); 177 json.value(ref); 178 } else if (base != null && ref != null && ref.contains("/")) { 179 json.name("reference"); 180 json.value(base+"/"+ref); 181 } 182 } 183 184 protected void decorateCoding(Element coding) throws IOException { 185 String system = coding.getChildValue("system"); 186 String code = coding.getChildValue("code"); 187 188 if (system == null) 189 return; 190 if ("http://snomed.info/sct".equals(system)) { 191 json.name("concept"); 192 json.value("http://snomed.info/sct#"+code); 193 } else if ("http://loinc.org".equals(system)) { 194 json.name("concept"); 195 json.value("http://loinc.org/owl#"+code); 196 } 197 } 198 199 private void decorateCodeableConcept(Element element) throws IOException { 200 for (Element c : element.getChildren("coding")) { 201 decorateCoding(c); 202 } 203 } 204 205}