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