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}