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