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