001package org.hl7.fhir.dstu2016may.metamodel; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032 033 034import java.io.IOException; 035import java.io.InputStream; 036import java.io.OutputStream; 037import java.io.OutputStreamWriter; 038import java.math.BigDecimal; 039import java.util.HashSet; 040import java.util.List; 041import java.util.Set; 042 043import org.apache.commons.lang3.NotImplementedException; 044import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle; 045import org.hl7.fhir.dstu2016may.formats.JsonCreator; 046import org.hl7.fhir.dstu2016may.formats.JsonCreatorCanonical; 047import org.hl7.fhir.dstu2016may.formats.JsonCreatorGson; 048import org.hl7.fhir.dstu2016may.utils.IWorkerContext; 049import org.hl7.fhir.utilities.Utilities; 050 051public class JsonLDParser extends ParserBase { 052 053 private JsonCreator json; 054 private String base; 055 056 public JsonLDParser(IWorkerContext context) { 057 super(context); 058 } 059 060 @Override 061 public Element parse(InputStream stream) throws Exception { 062 throw new NotImplementedException("not done yet"); 063 } 064 065 066 protected void prop(String name, String value) throws IOException { 067 if (name != null) 068 json.name(name); 069 json.value(value); 070 } 071 072 protected void open(String name) throws IOException { 073 if (name != null) 074 json.name(name); 075 json.beginObject(); 076 } 077 078 protected void close() throws IOException { 079 json.endObject(); 080 } 081 082 protected void openArray(String name) throws IOException { 083 if (name != null) 084 json.name(name); 085 json.beginArray(); 086 } 087 088 protected void closeArray() throws IOException { 089 json.endArray(); 090 } 091 092 093 @Override 094 public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws Exception { 095 this.base = base; 096 OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); 097 if (style == OutputStyle.CANONICAL) 098 json = new JsonCreatorCanonical(osw); 099 else 100 json = new JsonCreatorGson(osw); 101 json.setIndent(style == OutputStyle.PRETTY ? " " : ""); 102 json.beginObject(); 103 prop("@context", "http://hl7.org/fhir/jsonld/"+e.getType()); 104 prop("resourceType", e.getType()); 105 Set<String> done = new HashSet<String>(); 106 for (Element child : e.getChildren()) { 107 compose(e.getName(), e, done, child); 108 } 109 json.endObject(); 110 json.finish(); 111 osw.flush(); 112 } 113 114 private void compose(String path, Element e, Set<String> done, Element child) throws IOException { 115 if (!child.getProperty().isList()) { 116 compose(path, child); 117 } else if (!done.contains(child.getName())) { 118 done.add(child.getName()); 119 List<Element> list = e.getChildrenByName(child.getName()); 120 composeList(path, list); 121 } 122 } 123 124 private void composeList(String path, List<Element> list) throws IOException { 125 // there will be at least one element 126 String en = list.get(0).getProperty().getDefinition().getBase().getPath(); 127 if (en == null) 128 en = list.get(0).getProperty().getDefinition().getPath(); 129 boolean doType = false; 130 if (en.endsWith("[x]")) { 131 en = en.substring(0, en.length()-3); 132 doType = true; 133 } 134 if (doType) 135 en = en + Utilities.capitalize(list.get(0).getType()); 136 137 openArray(en); 138 for (Element item : list) { 139 open(null); 140 if (item.isPrimitive() || ParserBase.isPrimitive(item.getType())) { 141 if (item.hasValue()) 142 primitiveValue(item); 143 } 144 Set<String> done = new HashSet<String>(); 145 for (Element child : item.getChildren()) { 146 compose(path+"."+item.getName(), item, done, child); 147 } 148 close(); 149 } 150 closeArray(); 151 } 152 153 private void primitiveValue(Element item) throws IOException { 154 json.name("value"); 155 String type = item.getType(); 156 if (Utilities.existsInList(type, "boolean")) 157 json.value(item.getValue().equals("true") ? new Boolean(true) : new Boolean(false)); 158 else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt")) 159 json.value(new Integer(item.getValue())); 160 else if (Utilities.existsInList(type, "decimal")) 161 json.value(new BigDecimal(item.getValue())); 162 else 163 json.value(item.getValue()); 164 } 165 166 private void compose(String path, Element element) throws IOException { 167 String en = element.getProperty().getDefinition().getBase().getPath(); 168 if (en == null) 169 en = element.getProperty().getDefinition().getPath(); 170 boolean doType = false; 171 if (en.endsWith("[x]")) { 172 en = en.substring(0, en.length()-3); 173 doType = true; 174 } 175 if (doType) 176 en = en + Utilities.capitalize(element.getType()); 177 178 if (element.hasChildren() || element.hasComments() || element.hasValue()) { 179 open(en); 180 181 if (element.isPrimitive() || ParserBase.isPrimitive(element.getType())) { 182 if (element.hasValue()) 183 primitiveValue(element); 184 } 185 if (element.getProperty().isResource()) { 186 prop("resourceType", element.getType()); 187 element = element.getChildren().get(0); 188 } 189 Set<String> done = new HashSet<String>(); 190 for (Element child : element.getChildren()) { 191 compose(path+"."+element.getName(), element, done, child); 192 } 193 if ("Coding".equals(element.getType())) 194 decorateCoding(element); 195 if ("CodeableConcept".equals(element.getType())) 196 decorateCodeableConcept(element); 197 if ("Reference".equals(element.getType())) 198 decorateReference(element); 199 200 close(); 201 } 202 } 203 204 private void decorateReference(Element element) throws IOException { 205 String ref = element.getChildValue("reference"); 206 if (ref != null && (ref.startsWith("http://") || ref.startsWith("https://"))) { 207 json.name("reference"); 208 json.value(ref); 209 } else if (base != null && ref != null && ref.contains("/")) { 210 json.name("reference"); 211 json.value(base+"/"+ref); 212 } 213 } 214 215 protected void decorateCoding(Element coding) throws IOException { 216 String system = coding.getChildValue("system"); 217 String code = coding.getChildValue("code"); 218 219 if (system == null) 220 return; 221 if ("http://snomed.info/sct".equals(system)) { 222 json.name("concept"); 223 json.value("http://snomed.info/sct#"+code); 224 } else if ("http://loinc.org".equals(system)) { 225 json.name("concept"); 226 json.value("http://loinc.org/rdf#"+code); 227 } 228 } 229 230 private void decorateCodeableConcept(Element element) throws IOException { 231 for (Element c : element.getChildren("coding")) { 232 decorateCoding(c); 233 } 234 } 235 236}