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