001package org.hl7.fhir.dstu3.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.dstu3.model.DomainResource; 070import org.hl7.fhir.dstu3.model.Element; 071import org.hl7.fhir.dstu3.model.IdType; 072import org.hl7.fhir.dstu3.model.Resource; 073import org.hl7.fhir.dstu3.model.StringType; 074import org.hl7.fhir.dstu3.model.Type; 075import org.hl7.fhir.exceptions.FHIRFormatError; 076import org.hl7.fhir.instance.model.api.IIdType; 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.JsonElement; 085import com.google.gson.JsonObject; 086import com.google.gson.JsonSyntaxException; 087/** 088 * General parser for JSON content. You instantiate an JsonParser of these, but you 089 * actually use parse or parseGeneral defined on this class 090 * 091 * The two classes are separated to keep generated and manually maintained code 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 abstract protected Type parseType(JsonObject json, String type) throws IOException, FHIRFormatError; 106 abstract protected Type parseType(String prefix, JsonObject json) throws IOException, FHIRFormatError; 107 abstract protected boolean hasTypeName(JsonObject json, String prefix); 108 abstract protected void composeResource(Resource resource) throws IOException; 109 abstract protected void composeTypeInner(Type type) throws IOException; 110 111 /* -- entry points --------------------------------------------------- */ 112 113 /** 114 * @throws FHIRFormatError 115 * Parse content that is known to be a resource 116 * @throws IOException 117 * @throws 118 */ 119 @Override 120 public Resource parse(InputStream input) throws IOException, FHIRFormatError { 121 JsonObject json = loadJson(input); 122 return parseResource(json); 123 } 124 125 /** 126 * parse xml that is known to be a resource, and that has already been read into a JSON object 127 * @throws IOException 128 * @throws FHIRFormatError 129 */ 130 public Resource parse(JsonObject json) throws FHIRFormatError, IOException { 131 return parseResource(json); 132 } 133 134 @Override 135 public Type parseType(InputStream input, String type) throws IOException, FHIRFormatError { 136 JsonObject json = loadJson(input); 137 return parseType(json, type); 138 } 139 140 141 protected JsonObject getJObject(JsonObject parent, String name) throws IOException { 142 JsonElement j = parent.get(name); 143 if (j == null) { 144 return null; 145 } 146 if (!(j instanceof JsonObject)) { 147 throw new IOException("property "+name+" is a "+j.getClass()+" looking for an object"); 148 } 149 return (JsonObject) j; 150 } 151 /** 152 * 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) 153 * @throws IOException 154 */ 155 @Override 156 public void compose(OutputStream stream, Resource resource) throws IOException { 157 OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); 158 if (style == OutputStyle.CANONICAL) 159 json = new JsonCreatorCanonical(osw); 160 else 161 json = new JsonCreatorGson(osw); 162 json.setIndent(style == OutputStyle.PRETTY ? " " : ""); 163 json.beginObject(); 164 composeResource(resource); 165 json.endObject(); 166 json.finish(); 167 osw.flush(); 168 } 169 170 /** 171 * Compose a resource using a pre-existing JsonWriter 172 * @throws IOException 173 */ 174 public void compose(JsonCreator writer, Resource resource) throws IOException { 175 json = writer; 176 composeResource(resource); 177 } 178 179 @Override 180 public void compose(OutputStream stream, Type type, String rootName) throws IOException { 181 OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); 182 if (style == OutputStyle.CANONICAL) 183 json = new JsonCreatorCanonical(osw); 184 else 185 json = new JsonCreatorGson(osw); 186 json.setIndent(style == OutputStyle.PRETTY ? " " : ""); 187 json.beginObject(); 188 composeTypeInner(type); 189 json.endObject(); 190 json.finish(); 191 osw.flush(); 192 } 193 194 195 196 /* -- json routines --------------------------------------------------- */ 197 198 protected JsonCreator json; 199 private boolean htmlPretty; 200 201 private JsonObject loadJson(InputStream input) throws JsonSyntaxException, IOException { 202 return parser.parse(TextFile.streamToString(input)).getAsJsonObject(); 203 } 204 205// private JsonObject loadJson(String input) { 206// return parser.parse(input).getAsJsonObject(); 207// } 208// 209 protected void parseElementProperties(JsonObject json, Element e) throws IOException, FHIRFormatError { 210 if (json != null && json.has("id")) 211 e.setId(json.get("id").getAsString()); 212 if (!Utilities.noString(e.getId())) 213 idMap.put(e.getId(), e); 214 if (json.has("fhir_comments") && handleComments) { 215 JsonArray array = json.getAsJsonArray("fhir_comments"); 216 for (int i = 0; i < array.size(); i++) { 217 e.getFormatCommentsPre().add(array.get(i).getAsString()); 218 } 219 } 220 } 221 222 protected XhtmlNode parseXhtml(String value) throws IOException, FHIRFormatError { 223 XhtmlParser prsr = new XhtmlParser(); 224 try { 225 return prsr.parse(value, "div").getChildNodes().get(0); 226 } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { 227 throw new FHIRFormatError(e.getMessage(), e); 228 } 229 } 230 231 protected DomainResource parseDomainResource(JsonObject json) throws FHIRFormatError, IOException { 232 return (DomainResource) parseResource(json); 233 } 234 235 protected void writeNull(String name) throws IOException { 236 json.nullValue(); 237 } 238 protected void prop(String name, String value) throws IOException { 239 if (name != null) 240 json.name(name); 241 json.value(value); 242 } 243 244 protected void prop(String name, java.lang.Boolean value) throws IOException { 245 if (name != null) 246 json.name(name); 247 json.value(value); 248 } 249 250 protected void prop(String name, BigDecimal value) throws IOException { 251 if (name != null) 252 json.name(name); 253 json.value(value); 254 } 255 256 protected void prop(String name, java.lang.Integer value) throws IOException { 257 if (name != null) 258 json.name(name); 259 json.value(value); 260 } 261 262 protected void composeXhtml(String name, XhtmlNode html) throws IOException { 263 if (!Utilities.noString(xhtmlMessage)) { 264 prop(name, "<div>!-- "+xhtmlMessage+" --></div>"); 265 } else { 266 XhtmlComposer comp = new XhtmlComposer(XhtmlComposer.XML, htmlPretty); 267 prop(name, comp.compose(html)); 268 } 269 } 270 271 protected void open(String name) throws IOException { 272 if (name != null) 273 json.name(name); 274 json.beginObject(); 275 } 276 277 protected void close() throws IOException { 278 json.endObject(); 279 } 280 281 protected void openArray(String name) throws IOException { 282 if (name != null) 283 json.name(name); 284 json.beginArray(); 285 } 286 287 protected void closeArray() throws IOException { 288 json.endArray(); 289 } 290 291 protected void openObject(String name) throws IOException { 292 if (name != null) 293 json.name(name); 294 json.beginObject(); 295 } 296 297 protected void closeObject() throws IOException { 298 json.endObject(); 299 } 300 301// protected void composeBinary(String name, Binary element) { 302// if (element != null) { 303// prop("resourceType", "Binary"); 304// if (element.getXmlId() != null) 305// prop("id", element.getXmlId()); 306// prop("contentType", element.getContentType()); 307// prop("content", toString(element.getContent())); 308// } 309// 310// } 311 312 protected boolean anyHasExtras(List<? extends Element> list) { 313 for (Element e : list) { 314 if (e.hasExtension() || !Utilities.noString(e.getId())) 315 return true; 316 } 317 return false; 318 } 319 320 protected boolean makeComments(Element element) { 321 return handleComments && (style != OutputStyle.CANONICAL) && !(element.getFormatCommentsPre().isEmpty() && element.getFormatCommentsPost().isEmpty()); 322 } 323 324 protected void composeDomainResource(String name, DomainResource e) throws IOException { 325 openObject(name); 326 composeResource(e); 327 close(); 328 329 } 330 331 protected abstract void composeType(String prefix, Type type) throws IOException; 332 333 334 abstract void composeStringCore(String name, StringType value, boolean inArray) throws IOException; 335 336 protected void composeStringCore(String name, IIdType value, boolean inArray) throws IOException { 337 composeStringCore(name, new StringType(value.getValue()), inArray); 338 } 339 340 abstract void composeStringExtras(String name, StringType value, boolean inArray) throws IOException; 341 342 protected void composeStringExtras(String name, IIdType value, boolean inArray) throws IOException { 343 composeStringExtras(name, new StringType(value.getValue()), inArray); 344 } 345 346 protected void parseElementProperties(JsonObject theAsJsonObject, IIdType theReferenceElement) throws FHIRFormatError, IOException { 347 parseElementProperties(theAsJsonObject, (Element)theReferenceElement); 348 } 349 350 protected void parseElementProperties(JsonObject theAsJsonObject, IdType theReferenceElement) throws FHIRFormatError, IOException { 351 parseElementProperties(theAsJsonObject, (Element)theReferenceElement); 352 } 353 354}