001package org.hl7.fhir.dstu2.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.dstu2.model.DomainResource;
070import org.hl7.fhir.dstu2.model.Element;
071import org.hl7.fhir.dstu2.model.IdType;
072import org.hl7.fhir.dstu2.model.Resource;
073import org.hl7.fhir.dstu2.model.StringType;
074import org.hl7.fhir.dstu2.model.Type;
075import org.hl7.fhir.instance.model.api.IIdType;
076import org.hl7.fhir.exceptions.FHIRFormatError;
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    return prsr.parse(value, "div").getChildNodes().get(0);
213  }
214  
215  protected DomainResource parseDomainResource(JsonObject json) throws FHIRFormatError, IOException {
216          return (DomainResource) parseResource(json);
217  }
218
219        protected void writeNull(String name) throws IOException {
220                json.nullValue();
221        }
222        protected void prop(String name, String value) throws IOException {
223                if (name != null)
224                        json.name(name);
225                json.value(value);
226        }
227
228  protected void prop(String name, java.lang.Boolean value) throws IOException {
229    if (name != null)
230      json.name(name);
231    json.value(value);
232  }
233
234  protected void prop(String name, BigDecimal value) throws IOException {
235    if (name != null)
236      json.name(name);
237    json.value(value);
238  }
239
240  protected void prop(String name, java.lang.Integer value) throws IOException {
241    if (name != null)
242      json.name(name);
243    json.value(value);
244  }
245
246        protected void composeXhtml(String name, XhtmlNode html) throws IOException {
247                if (!Utilities.noString(xhtmlMessage)) {
248      prop(name, "<div>!-- "+xhtmlMessage+" --></div>");
249                } else {
250                XhtmlComposer comp = new XhtmlComposer(true, htmlPretty);
251                prop(name, comp.compose(html));
252                }
253        }
254
255        protected void open(String name) throws IOException {
256                if (name != null) 
257                        json.name(name);
258                json.beginObject();
259        }
260
261        protected void close() throws IOException {
262                json.endObject();
263        }
264
265        protected void openArray(String name) throws IOException {
266                if (name != null) 
267                        json.name(name);
268                json.beginArray();
269        }
270
271        protected void closeArray() throws IOException {
272                json.endArray();
273        }
274
275        protected void openObject(String name) throws IOException {
276                if (name != null) 
277                        json.name(name);
278                json.beginObject();
279        }
280
281        protected void closeObject() throws IOException {
282                json.endObject();
283        }
284
285//  protected void composeBinary(String name, Binary element) {
286//    if (element != null) {
287//      prop("resourceType", "Binary");
288//      if (element.getXmlId() != null)
289//        prop("id", element.getXmlId());
290//      prop("contentType", element.getContentType());
291//      prop("content", toString(element.getContent()));
292//    }    
293//    
294//  }
295
296  protected boolean anyHasExtras(List<? extends Element> list) {
297          for (Element e : list) {
298                if (e.hasExtension() || !Utilities.noString(e.getId()))
299                        return true;
300          }
301          return false;
302  }
303
304        protected boolean makeComments(Element element) {
305                return handleComments && (style != OutputStyle.CANONICAL) && !(element.getFormatCommentsPre().isEmpty() && element.getFormatCommentsPost().isEmpty());
306        }
307        
308  protected void composeDomainResource(String name, DomainResource e) throws IOException {
309          openObject(name);
310          composeResource(e);
311          close();
312          
313  }
314
315  protected abstract void composeType(String prefix, Type type) throws IOException;
316
317  
318  abstract void composeStringCore(String name, StringType value, boolean inArray) throws IOException;
319
320  protected void composeStringCore(String name, IIdType value, boolean inArray) throws IOException {
321          composeStringCore(name, new StringType(value.getValue()), inArray);
322  }    
323
324  abstract void composeStringExtras(String name, StringType value, boolean inArray) throws IOException;
325
326  protected void composeStringExtras(String name, IIdType value, boolean inArray) throws IOException {
327          composeStringExtras(name, new StringType(value.getValue()), inArray);
328  }    
329  
330  protected void parseElementProperties(JsonObject theAsJsonObject, IIdType theReferenceElement) throws FHIRFormatError, IOException {
331          parseElementProperties(theAsJsonObject, (Element)theReferenceElement);
332  }
333
334  protected void parseElementProperties(JsonObject theAsJsonObject, IdType theReferenceElement) throws FHIRFormatError, IOException {
335          parseElementProperties(theAsJsonObject, (Element)theReferenceElement);
336  }
337
338}