001package org.hl7.fhir.dstu3.elementmodel;
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.util.HashSet;
039import java.util.List;
040import java.util.Set;
041
042import org.apache.commons.lang3.NotImplementedException;
043import org.hl7.fhir.dstu3.context.IWorkerContext;
044import org.hl7.fhir.dstu3.elementmodel.Element.SpecialElement;
045import org.hl7.fhir.dstu3.formats.IParser.OutputStyle;
046import org.hl7.fhir.dstu3.formats.JsonCreator;
047import org.hl7.fhir.dstu3.formats.JsonCreatorCanonical;
048import org.hl7.fhir.dstu3.formats.JsonCreatorGson;
049import org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent;
050import org.hl7.fhir.utilities.Utilities;
051
052@Deprecated
053public class JsonLDParser extends ParserBase {
054
055        private JsonCreator json;
056  private String base;
057  private String jsonLDBase = "http://build.fhir.org/";
058
059        public JsonLDParser(IWorkerContext context) {
060                super(context);
061        }
062
063        @Override
064        public Element parse(InputStream stream) {
065                throw new NotImplementedException("not done yet");
066        }
067
068
069        protected void prop(String name, String value) throws IOException {
070                if (name != null)
071                        json.name(name);
072                json.value(value);
073        }
074
075        protected void open(String name) throws IOException {
076                if (name != null) 
077                        json.name(name);
078                json.beginObject();
079        }
080
081        protected void close() throws IOException {
082                json.endObject();
083        }
084
085        protected void openArray(String name) throws IOException {
086                if (name != null) 
087                        json.name(name);
088                json.beginArray();
089        }
090
091        protected void closeArray() throws IOException {
092                json.endArray();
093        }
094
095
096        @Override
097        public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException {
098          this.base = base;
099                OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
100                if (style == OutputStyle.CANONICAL)
101                        json = new JsonCreatorCanonical(osw);
102                else
103                        json = new JsonCreatorGson(osw);
104                json.setIndent(style == OutputStyle.PRETTY ? "  " : "");
105                json.beginObject();
106    prop("@type", "fhir:"+e.getType());
107    prop("@context", jsonLDBase+"fhir.jsonld");
108    prop("role", "fhir:treeRoot");
109    String id = e.getChildValue("id");
110    if (base != null && id != null) {
111       if (base.endsWith("#"))
112         prop("@id", base + e.getType() + "-" + id + ">");
113      else
114        prop("@id", Utilities.pathURL(base, e.getType(), id));
115    }
116                Set<String> done = new HashSet<String>();
117                for (Element child : e.getChildren()) {
118                        compose(e.getName(), e, done, child);
119                }
120                json.endObject();
121                json.finish();
122                osw.flush();
123        }
124
125        private void compose(String path, Element e, Set<String> done, Element child) throws IOException {
126                if (!child.isList()) {
127                        compose(path, child);
128                } else if (!done.contains(child.getName())) {
129                        done.add(child.getName());
130                        List<Element> list = e.getChildrenByName(child.getName());
131                        composeList(path, list);
132                }
133        }
134
135        private void composeList(String path, List<Element> list) throws IOException {
136                // there will be at least one element
137    String en = getFormalName(list.get(0));
138
139    openArray(en);
140    for (Element item : list) { 
141      open(null);
142      json.name("index");
143      json.value(item.getIndex());
144      if (item.isPrimitive() || isPrimitive(item.getType())) {
145        if (item.hasValue())
146          primitiveValue(item);
147      }
148      if (item.getProperty().isResource()) {
149        prop("@type", "fhir:"+item.getType());
150      }
151      Set<String> done = new HashSet<String>();
152      for (Element child : item.getChildren()) {
153        compose(path+"."+item.getName(), item, done, child);
154      }
155      if ("Coding".equals(item.getType()))
156        decorateCoding(item);
157      if ("CodeableConcept".equals(item.getType()))
158        decorateCodeableConcept(item);
159      if ("Reference".equals(item.getType()))
160        decorateReference(item);
161      
162      close();
163    }
164    closeArray();
165        }
166
167        private void primitiveValue(Element item) throws IOException {
168          String type = item.getType();
169          if (Utilities.existsInList(type, "date", "dateTime", "instant")) {
170      String v = item.getValue();
171      if (v.length() > 10) {
172        int i = v.substring(10).indexOf("-");
173        if (i == -1)
174          i = v.substring(10).indexOf("+");
175        v = i == -1 ? v : v.substring(0,  10+i);
176      }
177      if (v.length() > 10)
178        json.name("dateTime");
179      else if (v.length() == 10)
180        json.name("date");
181      else if (v.length() == 7)
182        json.name("gYearMonth");
183      else if (v.length() == 4)
184        json.name("gYear");
185      json.value(item.getValue());
186          } else if (Utilities.existsInList(type, "boolean")) {
187      json.name("boolean");
188      json.value(item.getValue().equals("true") ? new Boolean(true) : new Boolean(false));
189          } else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt")) {
190      json.name("integer");
191      json.value(new Integer(item.getValue()));
192    } else if (Utilities.existsInList(type, "decimal")) {
193      json.name("decimal");
194      json.value(item.getValue());
195    } else if (Utilities.existsInList(type, "base64Binary")) {
196      json.name("binary");
197      json.value(item.getValue());
198    } else {
199      json.name("value");
200      json.value(item.getValue());
201    }
202        }
203
204        private void compose(String path, Element element) throws IOException {
205          Property p = element.hasElementProperty() ? element.getElementProperty() : element.getProperty();
206    String en = getFormalName(element);
207
208    if (element.fhirType().equals("xhtml")) {
209      json.name(en);
210      json.value(element.getValue());
211    } else if (element.hasChildren() || element.hasComments() || element.hasValue()) {
212                        open(en);
213      if (element.getProperty().isResource()) {
214              prop("@type", "fhir:"+element.getType());
215//        element = element.getChildren().get(0);
216      }
217            if (element.isPrimitive() || isPrimitive(element.getType())) {
218              if (element.hasValue())
219                primitiveValue(element);
220            }
221            
222                        Set<String> done = new HashSet<String>();
223                        for (Element child : element.getChildren()) {
224                                compose(path+"."+element.getName(), element, done, child);
225                        }
226            if ("Coding".equals(element.getType()))
227              decorateCoding(element);
228      if ("CodeableConcept".equals(element.getType()))
229        decorateCodeableConcept(element);
230      if ("Reference".equals(element.getType()))
231        decorateReference(element);
232                        
233                        close();
234                }
235        }
236
237  private void decorateReference(Element element) throws IOException {
238    String ref = element.getChildValue("reference");
239    if (ref != null && (ref.startsWith("http://") || ref.startsWith("https://"))) {
240      json.name("link");
241      json.value(ref);
242    } else if (base != null && ref != null && ref.contains("/")) {
243      json.name("link");
244      json.value(Utilities.pathURL(base, ref));
245    }
246  }
247
248  protected void decorateCoding(Element coding) throws IOException {
249    String system = coding.getChildValue("system");
250    String code = coding.getChildValue("code");
251    
252    if (system == null)
253      return;
254    if ("http://snomed.info/sct".equals(system)) {
255      json.name("concept");
256      json.value("http://snomed.info/id/"+code);
257    } else if ("http://loinc.org".equals(system)) {
258      json.name("concept");
259      json.value("http://loinc.org/rdf#"+code);
260    }  
261  }
262
263  private void decorateCodeableConcept(Element element) throws IOException {
264    // nothing here; ITS committee decision
265  }
266
267  private String getFormalName(Element element) {
268    String en = null;
269    if (element.getSpecial() == null) {
270      if (element.getProperty().getDefinition().hasBase())
271        en = element.getProperty().getDefinition().getBase().getPath();
272    }
273    else if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY)
274      en = "Bundle.entry.resource";
275    else if (element.getSpecial() == SpecialElement.BUNDLE_OUTCOME)
276      en = "Bundle.entry.response.outcome";
277    else if (element.getSpecial() == SpecialElement.PARAMETER)
278      en = element.getElementProperty().getDefinition().getPath();
279    else // CONTAINED
280      en = "DomainResource.contained";
281    
282    if (en == null) 
283      en = element.getProperty().getDefinition().getPath();
284    boolean doType = false;
285      if (en.endsWith("[x]")) {
286        en = en.substring(0, en.length()-3);
287        doType = true;        
288      }
289     if (doType || (element.getProperty().getDefinition().getType().size() > 1 && !allReference(element.getProperty().getDefinition().getType())))
290       en = en + Utilities.capitalize(element.getType());
291    return en;
292  }
293
294  private boolean allReference(List<TypeRefComponent> types) {
295    for (TypeRefComponent t : types) {
296      if (!t.getCode().equals("Reference"))
297        return false;
298    }
299    return true;
300  }
301
302}