001package org.hl7.fhir.dstu2016may.formats;
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/*
034Copyright (c) 2011+, HL7, Inc
035All rights reserved.
036
037Redistribution and use in source and binary forms, with or without modification, 
038are permitted provided that the following conditions are met:
039
040 * Redistributions of source code must retain the above copyright notice, this 
041   list of conditions and the following disclaimer.
042 * Redistributions in binary form must reproduce the above copyright notice, 
043   this list of conditions and the following disclaimer in the documentation 
044   and/or other materials provided with the distribution.
045 * Neither the name of HL7 nor the names of its contributors may be used to 
046   endorse or promote products derived from this software without specific 
047   prior written permission.
048
049THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
050ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
051WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
052IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
053INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
054NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
055PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
056WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
057ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
058POSSIBILITY OF SUCH DAMAGE.
059
060*/
061
062import java.io.IOException;
063import java.io.InputStream;
064import java.io.OutputStream;
065import java.io.OutputStreamWriter;
066import java.math.BigDecimal;
067import java.util.List;
068
069import org.hl7.fhir.dstu2016may.model.DomainResource;
070import org.hl7.fhir.dstu2016may.model.Element;
071import org.hl7.fhir.dstu2016may.model.IdType;
072import org.hl7.fhir.dstu2016may.model.Resource;
073import org.hl7.fhir.dstu2016may.model.StringType;
074import org.hl7.fhir.dstu2016may.model.Type;
075import org.hl7.fhir.exceptions.FHIRFormatError;
076import org.hl7.fhir.instance.model.api.IIdType;
077import org.hl7.fhir.utilities.TextFile;
078import org.hl7.fhir.utilities.Utilities;
079import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
080import org.hl7.fhir.utilities.xhtml.XhtmlNode;
081import org.hl7.fhir.utilities.xhtml.XhtmlParser;
082
083import com.google.gson.JsonArray;
084import com.google.gson.JsonObject;
085import com.google.gson.JsonSyntaxException;
086/**
087 * General parser for JSON content. You instantiate an JsonParser of these, but you 
088 * actually use parse or parseGeneral defined on this class
089 * 
090 * The two classes are separated to keep generated and manually maintained code apart.
091 */
092public abstract class JsonParserBase extends ParserBase implements IParser {
093        
094  @Override
095  public ParserType getType() {
096          return ParserType.JSON;
097  }
098
099        private static com.google.gson.JsonParser  parser = new com.google.gson.JsonParser();
100
101  // -- in descendent generated code --------------------------------------
102  
103  abstract protected Resource parseResource(JsonObject json) throws IOException, FHIRFormatError;
104  abstract protected Type parseType(JsonObject json, String type) throws IOException, FHIRFormatError;
105  abstract protected Type parseType(String prefix, JsonObject json) throws IOException, FHIRFormatError;
106  abstract protected boolean hasTypeName(JsonObject json, String prefix);
107  abstract protected void composeResource(Resource resource) throws IOException;
108  abstract protected void composeTypeInner(Type type) throws IOException;
109
110  /* -- entry points --------------------------------------------------- */
111
112  /**
113   * @throws FHIRFormatError 
114   * Parse content that is known to be a resource
115   * @throws IOException 
116   * @throws  
117   */
118  @Override
119  public Resource parse(InputStream input) throws IOException, FHIRFormatError {
120    JsonObject json = loadJson(input);
121    return parseResource(json);
122  }
123
124  /**
125   * parse xml that is known to be a resource, and that has already been read into a JSON object  
126   * @throws IOException 
127   * @throws FHIRFormatError 
128   */
129  public Resource parse(JsonObject json) throws FHIRFormatError, IOException {
130    return parseResource(json);
131  }
132
133  @Override
134  public Type parseType(InputStream input, String type) throws IOException, FHIRFormatError {
135    JsonObject json = loadJson(input);
136    return parseType(json, type);
137  }
138
139  /**
140   * Compose a resource to a stream, possibly using pretty presentation for a human reader (used in the spec, for example, but not normally in production)
141   * @throws IOException 
142   */
143  @Override
144  public void compose(OutputStream stream, Resource resource) throws IOException {
145    OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
146    if (style == OutputStyle.CANONICAL)
147      json = new JsonCreatorCanonical(osw);
148    else
149      json = new JsonCreatorGson(osw);
150    json.setIndent(style == OutputStyle.PRETTY ? "  " : "");
151    json.beginObject();
152    composeResource(resource);
153    json.endObject();
154    json.finish();
155    osw.flush();
156  }
157
158  /**
159   * Compose a resource using a pre-existing JsonWriter
160   * @throws IOException 
161   */
162  public void compose(JsonCreator writer, Resource resource) throws IOException {
163    json = writer;
164    composeResource(resource);
165  }
166  
167  @Override
168  public void compose(OutputStream stream, Type type, String rootName) throws IOException {
169    OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
170    if (style == OutputStyle.CANONICAL)
171      json = new JsonCreatorCanonical(osw);
172    else
173      json = new JsonCreatorGson(osw);
174    json.setIndent(style == OutputStyle.PRETTY ? "  " : "");
175    json.beginObject();
176    composeTypeInner(type);
177    json.endObject();
178    json.finish();
179    osw.flush();
180  }
181    
182
183  
184  /* -- json routines --------------------------------------------------- */
185
186  protected JsonCreator json;
187  private boolean htmlPretty;
188  
189  private JsonObject loadJson(InputStream input) throws JsonSyntaxException, IOException {
190    return parser.parse(TextFile.streamToString(input)).getAsJsonObject();
191  }
192  
193//  private JsonObject loadJson(String input) {
194//    return parser.parse(input).getAsJsonObject();
195//  }
196//  
197  protected void parseElementProperties(JsonObject json, Element e) throws IOException, FHIRFormatError {
198    if (json != null && json.has("id"))
199      e.setId(json.get("id").getAsString());
200    if (!Utilities.noString(e.getId()))
201      idMap.put(e.getId(), e);
202    if (json.has("fhir_comments") && handleComments) {
203      JsonArray array = json.getAsJsonArray("fhir_comments");
204      for (int i = 0; i < array.size(); i++) {
205        e.getFormatCommentsPre().add(array.get(i).getAsString());
206      }
207    }
208  }
209  
210  protected XhtmlNode parseXhtml(String value) throws IOException, FHIRFormatError {
211    XhtmlParser prsr = new XhtmlParser();
212    try {
213                return prsr.parse(value, "div").getChildNodes().get(0);
214        } catch (org.hl7.fhir.exceptions.FHIRFormatError e) {
215                throw new FHIRFormatError(e.getMessage(), e);
216        }
217  }
218  
219  protected DomainResource parseDomainResource(JsonObject json) throws FHIRFormatError, IOException {
220          return (DomainResource) parseResource(json);
221  }
222
223        protected void writeNull(String name) throws IOException {
224                json.nullValue();
225        }
226        protected void prop(String name, String value) throws IOException {
227                if (name != null)
228                        json.name(name);
229                json.value(value);
230        }
231
232  protected void prop(String name, java.lang.Boolean value) throws IOException {
233    if (name != null)
234      json.name(name);
235    json.value(value);
236  }
237
238  protected void prop(String name, BigDecimal value) throws IOException {
239    if (name != null)
240      json.name(name);
241    json.value(value);
242  }
243
244  protected void prop(String name, java.lang.Integer value) throws IOException {
245    if (name != null)
246      json.name(name);
247    json.value(value);
248  }
249
250        protected void composeXhtml(String name, XhtmlNode html) throws IOException {
251                if (!Utilities.noString(xhtmlMessage)) {
252      prop(name, "<div>!-- "+xhtmlMessage+" --></div>");
253                } else {
254                XhtmlComposer comp = new XhtmlComposer(true, htmlPretty);
255                prop(name, comp.compose(html));
256                }
257        }
258
259        protected void open(String name) throws IOException {
260                if (name != null) 
261                        json.name(name);
262                json.beginObject();
263        }
264
265        protected void close() throws IOException {
266                json.endObject();
267        }
268
269        protected void openArray(String name) throws IOException {
270                if (name != null) 
271                        json.name(name);
272                json.beginArray();
273        }
274
275        protected void closeArray() throws IOException {
276                json.endArray();
277        }
278
279        protected void openObject(String name) throws IOException {
280                if (name != null) 
281                        json.name(name);
282                json.beginObject();
283        }
284
285        protected void closeObject() throws IOException {
286                json.endObject();
287        }
288
289//  protected void composeBinary(String name, Binary element) {
290//    if (element != null) {
291//      prop("resourceType", "Binary");
292//      if (element.getXmlId() != null)
293//        prop("id", element.getXmlId());
294//      prop("contentType", element.getContentType());
295//      prop("content", toString(element.getContent()));
296//    }    
297//    
298//  }
299
300  protected boolean anyHasExtras(List<? extends Element> list) {
301          for (Element e : list) {
302                if (e.hasExtension() || !Utilities.noString(e.getId()))
303                        return true;
304          }
305          return false;
306  }
307
308        protected boolean makeComments(Element element) {
309                return handleComments && (style != OutputStyle.CANONICAL) && !(element.getFormatCommentsPre().isEmpty() && element.getFormatCommentsPost().isEmpty());
310        }
311        
312  protected void composeDomainResource(String name, DomainResource e) throws IOException {
313          openObject(name);
314          composeResource(e);
315          close();
316          
317  }
318
319  protected abstract void composeType(String prefix, Type type) throws IOException;
320
321  
322  abstract void composeStringCore(String name, StringType value, boolean inArray) throws IOException;
323
324  protected void composeStringCore(String name, IIdType value, boolean inArray) throws IOException {
325          composeStringCore(name, new StringType(value.getValue()), inArray);
326  }    
327
328  abstract void composeStringExtras(String name, StringType value, boolean inArray) throws IOException;
329
330  protected void composeStringExtras(String name, IIdType value, boolean inArray) throws IOException {
331          composeStringExtras(name, new StringType(value.getValue()), inArray);
332  }    
333  
334  protected void parseElementProperties(JsonObject theAsJsonObject, IIdType theReferenceElement) throws FHIRFormatError, IOException {
335          parseElementProperties(theAsJsonObject, (Element)theReferenceElement);
336  }
337
338  protected void parseElementProperties(JsonObject theAsJsonObject, IdType theReferenceElement) throws FHIRFormatError, IOException {
339          parseElementProperties(theAsJsonObject, (Element)theReferenceElement);
340  }
341
342}