001package org.hl7.fhir.dstu3.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.dstu3.model.DomainResource;
070import org.hl7.fhir.dstu3.model.Element;
071import org.hl7.fhir.dstu3.model.IdType;
072import org.hl7.fhir.dstu3.model.Resource;
073import org.hl7.fhir.dstu3.model.StringType;
074import org.hl7.fhir.dstu3.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.JsonElement;
085import com.google.gson.JsonObject;
086import com.google.gson.JsonSyntaxException;
087/**
088 * General parser for JSON content. You instantiate an JsonParser of these, but you 
089 * actually use parse or parseGeneral defined on this class
090 * 
091 * The two classes are separated to keep generated and manually maintained code apart.
092 */
093public abstract class JsonParserBase extends ParserBase implements IParser {
094        
095  @Override
096  public ParserType getType() {
097          return ParserType.JSON;
098  }
099
100        private static com.google.gson.JsonParser  parser = new com.google.gson.JsonParser();
101  
102  // -- in descendent generated code --------------------------------------
103  
104  abstract protected Resource parseResource(JsonObject json) throws IOException, FHIRFormatError;
105  abstract protected Type parseType(JsonObject json, String type) throws IOException, FHIRFormatError;
106  abstract protected Type parseType(String prefix, JsonObject json) throws IOException, FHIRFormatError;
107  abstract protected boolean hasTypeName(JsonObject json, String prefix);
108  abstract protected void composeResource(Resource resource) throws IOException;
109  abstract protected void composeTypeInner(Type type) throws IOException;
110
111  /* -- entry points --------------------------------------------------- */
112
113  /**
114   * @throws FHIRFormatError 
115   * Parse content that is known to be a resource
116   * @throws IOException 
117   * @throws  
118   */
119  @Override
120  public Resource parse(InputStream input) throws IOException, FHIRFormatError {
121    JsonObject json = loadJson(input);
122    return parseResource(json);
123  }
124
125  /**
126   * parse xml that is known to be a resource, and that has already been read into a JSON object  
127   * @throws IOException 
128   * @throws FHIRFormatError 
129   */
130  public Resource parse(JsonObject json) throws FHIRFormatError, IOException {
131    return parseResource(json);
132  }
133
134  @Override
135  public Type parseType(InputStream input, String type) throws IOException, FHIRFormatError {
136    JsonObject json = loadJson(input);
137    return parseType(json, type);
138  }
139
140
141  protected JsonObject getJObject(JsonObject parent, String name) throws IOException {
142    JsonElement j = parent.get(name);
143    if (j == null) { 
144      return null;
145    }
146    if (!(j instanceof JsonObject)) {
147      throw new IOException("property "+name+" is a "+j.getClass()+" looking for an object");
148    }
149    return (JsonObject) j;
150  }
151  /**
152   * 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)
153   * @throws IOException 
154   */
155  @Override
156  public void compose(OutputStream stream, Resource resource) throws IOException {
157    OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
158    if (style == OutputStyle.CANONICAL)
159      json = new JsonCreatorCanonical(osw);
160    else
161      json = new JsonCreatorGson(osw);
162    json.setIndent(style == OutputStyle.PRETTY ? "  " : "");
163    json.beginObject();
164    composeResource(resource);
165    json.endObject();
166    json.finish();
167    osw.flush();
168  }
169
170  /**
171   * Compose a resource using a pre-existing JsonWriter
172   * @throws IOException 
173   */
174  public void compose(JsonCreator writer, Resource resource) throws IOException {
175    json = writer;
176    composeResource(resource);
177  }
178  
179  @Override
180  public void compose(OutputStream stream, Type type, String rootName) throws IOException {
181    OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
182    if (style == OutputStyle.CANONICAL)
183      json = new JsonCreatorCanonical(osw);
184    else
185      json = new JsonCreatorGson(osw);
186    json.setIndent(style == OutputStyle.PRETTY ? "  " : "");
187    json.beginObject();
188    composeTypeInner(type);
189    json.endObject();
190    json.finish();
191    osw.flush();
192  }
193    
194
195  
196  /* -- json routines --------------------------------------------------- */
197
198  protected JsonCreator json;
199  private boolean htmlPretty;
200  
201  private JsonObject loadJson(InputStream input) throws JsonSyntaxException, IOException {
202    return parser.parse(TextFile.streamToString(input)).getAsJsonObject();
203  }
204  
205//  private JsonObject loadJson(String input) {
206//    return parser.parse(input).getAsJsonObject();
207//  }
208//  
209  protected void parseElementProperties(JsonObject json, Element e) throws IOException, FHIRFormatError {
210    if (json != null && json.has("id"))
211      e.setId(json.get("id").getAsString());
212    if (!Utilities.noString(e.getId()))
213      idMap.put(e.getId(), e);
214    if (json.has("fhir_comments") && handleComments) {
215      JsonArray array = json.getAsJsonArray("fhir_comments");
216      for (int i = 0; i < array.size(); i++) {
217        e.getFormatCommentsPre().add(array.get(i).getAsString());
218      }
219    }
220  }
221  
222  protected XhtmlNode parseXhtml(String value) throws IOException, FHIRFormatError {
223    XhtmlParser prsr = new XhtmlParser();
224    try {
225                return prsr.parse(value, "div").getChildNodes().get(0);
226        } catch (org.hl7.fhir.exceptions.FHIRFormatError e) {
227                throw new FHIRFormatError(e.getMessage(), e);
228        }
229  }
230  
231  protected DomainResource parseDomainResource(JsonObject json) throws FHIRFormatError, IOException {
232          return (DomainResource) parseResource(json);
233  }
234
235        protected void writeNull(String name) throws IOException {
236                json.nullValue();
237        }
238        protected void prop(String name, String 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.Boolean value) throws IOException {
245    if (name != null)
246      json.name(name);
247    json.value(value);
248  }
249
250  protected void prop(String name, BigDecimal value) throws IOException {
251    if (name != null)
252      json.name(name);
253    json.value(value);
254  }
255
256  protected void prop(String name, java.lang.Integer value) throws IOException {
257    if (name != null)
258      json.name(name);
259    json.value(value);
260  }
261
262        protected void composeXhtml(String name, XhtmlNode html) throws IOException {
263                if (!Utilities.noString(xhtmlMessage)) {
264      prop(name, "<div>!-- "+xhtmlMessage+" --></div>");
265                } else {
266                XhtmlComposer comp = new XhtmlComposer(XhtmlComposer.XML, htmlPretty);
267                prop(name, comp.compose(html));
268                }
269        }
270
271        protected void open(String name) throws IOException {
272                if (name != null) 
273                        json.name(name);
274                json.beginObject();
275        }
276
277        protected void close() throws IOException {
278                json.endObject();
279        }
280
281        protected void openArray(String name) throws IOException {
282                if (name != null) 
283                        json.name(name);
284                json.beginArray();
285        }
286
287        protected void closeArray() throws IOException {
288                json.endArray();
289        }
290
291        protected void openObject(String name) throws IOException {
292                if (name != null) 
293                        json.name(name);
294                json.beginObject();
295        }
296
297        protected void closeObject() throws IOException {
298                json.endObject();
299        }
300
301//  protected void composeBinary(String name, Binary element) {
302//    if (element != null) {
303//      prop("resourceType", "Binary");
304//      if (element.getXmlId() != null)
305//        prop("id", element.getXmlId());
306//      prop("contentType", element.getContentType());
307//      prop("content", toString(element.getContent()));
308//    }    
309//    
310//  }
311
312  protected boolean anyHasExtras(List<? extends Element> list) {
313          for (Element e : list) {
314                if (e.hasExtension() || !Utilities.noString(e.getId()))
315                        return true;
316          }
317          return false;
318  }
319
320        protected boolean makeComments(Element element) {
321                return handleComments && (style != OutputStyle.CANONICAL) && !(element.getFormatCommentsPre().isEmpty() && element.getFormatCommentsPost().isEmpty());
322        }
323        
324  protected void composeDomainResource(String name, DomainResource e) throws IOException {
325          openObject(name);
326          composeResource(e);
327          close();
328          
329  }
330
331  protected abstract void composeType(String prefix, Type type) throws IOException;
332
333  
334  abstract void composeStringCore(String name, StringType value, boolean inArray) throws IOException;
335
336  protected void composeStringCore(String name, IIdType value, boolean inArray) throws IOException {
337          composeStringCore(name, new StringType(value.getValue()), inArray);
338  }    
339
340  abstract void composeStringExtras(String name, StringType value, boolean inArray) throws IOException;
341
342  protected void composeStringExtras(String name, IIdType value, boolean inArray) throws IOException {
343          composeStringExtras(name, new StringType(value.getValue()), inArray);
344  }    
345  
346  protected void parseElementProperties(JsonObject theAsJsonObject, IIdType theReferenceElement) throws FHIRFormatError, IOException {
347          parseElementProperties(theAsJsonObject, (Element)theReferenceElement);
348  }
349
350  protected void parseElementProperties(JsonObject theAsJsonObject, IdType theReferenceElement) throws FHIRFormatError, IOException {
351          parseElementProperties(theAsJsonObject, (Element)theReferenceElement);
352  }
353
354}