001package org.hl7.fhir.r5.formats; 002 003import java.io.FileNotFoundException; 004import java.io.IOException; 005import java.io.InputStream; 006 007/* 008 Copyright (c) 2011+, HL7, Inc. 009 All rights reserved. 010 011 Redistribution and use in source and binary forms, with or without modification, 012 are permitted provided that the following conditions are met: 013 014 * Redistributions of source code must retain the above copyright notice, this 015 list of conditions and the following disclaimer. 016 * Redistributions in binary form must reproduce the above copyright notice, 017 this list of conditions and the following disclaimer in the documentation 018 and/or other materials provided with the distribution. 019 * Neither the name of HL7 nor the names of its contributors may be used to 020 endorse or promote products derived from this software without specific 021 prior written permission. 022 023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 024 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 026 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 027 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 028 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 029 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 030 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 031 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 032 POSSIBILITY OF SUCH DAMAGE. 033 034 */ 035 036 037 038/* 039Copyright (c) 2011+, HL7, Inc 040All rights reserved. 041 042Redistribution and use in source and binary forms, with or without modification, 043are permitted provided that the following conditions are met: 044 045 * Redistributions of source code must retain the above copyright notice, this 046 list of conditions and the following disclaimer. 047 * Redistributions in binary form must reproduce the above copyright notice, 048 this list of conditions and the following disclaimer in the documentation 049 and/or other materials provided with the distribution. 050 * Neither the name of HL7 nor the names of its contributors may be used to 051 endorse or promote products derived from this software without specific 052 prior written permission. 053 054THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 055ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 056WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 057IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 058INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 059NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 060PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 061WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 062ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 063POSSIBILITY OF SUCH DAMAGE. 064 065*/ 066 067import java.lang.reflect.InvocationTargetException; 068import java.math.BigDecimal; 069import java.net.URI; 070 071import org.apache.commons.codec.binary.Base64; 072import org.hl7.fhir.exceptions.FHIRException; 073import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; 074import org.hl7.fhir.r5.model.Resource; 075import org.hl7.fhir.utilities.TextFile; 076 077public abstract class FormatUtilities { 078 public static final String ID_REGEX = "[A-Za-z0-9\\-\\.]{1,64}"; 079 public static final String FHIR_NS = "http://hl7.org/fhir"; 080 public static final String XHTML_NS = "http://www.w3.org/1999/xhtml"; 081 public static final String NS_XSI = "http://www.w3.org/2001/XMLSchema-instance"; 082 private static final int MAX_SCAN_LENGTH = 1000; // how many characters to scan into content when autodetermining format 083 084 protected String toString(String value) { 085 return value; 086 } 087 088 protected String toString(int value) { 089 return java.lang.Integer.toString(value); 090 } 091 092 protected String toString(boolean value) { 093 return java.lang.Boolean.toString(value); 094 } 095 096 protected String toString(BigDecimal value) { 097 return value.toString(); 098 } 099 100 protected String toString(URI value) { 101 return value.toString(); 102 } 103 104 public static String toString(byte[] value) { 105 byte[] encodeBase64 = Base64.encodeBase64(value); 106 return new String(encodeBase64); 107 } 108 109 public static boolean isValidId(String tail) { 110 return tail == null ? false : tail.matches(ID_REGEX); 111 } 112 113 public static String makeId(String candidate) { 114 StringBuilder b = new StringBuilder(); 115 for (char c : candidate.toCharArray()) 116 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '.' || c == '-') 117 b.append(c); 118 return b.toString(); 119 } 120 121 public static ParserBase makeParser(FhirFormat format) { 122 return makeParser(format.name()); 123 } 124 125 public static ParserBase makeParser(String format) { 126 /* 127 * Note: Use fully qualified references to the parsers here in order to avoid adding 128 * a class-level import statement for them. This is because the 129 * XmlParser and JsonParser are huuuuuge classes and classloading them is quite expensive 130 * in cases where they won't actually ever be instantiated (such as when using the 131 * validator in HAPI FHIR). 132 * 133 * See https://github.com/hapifhir/hapi-fhir/issues/3268 134 */ 135 try { 136 if ("XML".equalsIgnoreCase(format)) 137 return (ParserBase) Class.forName("org.hl7.fhir.r5.formats.XmlParser").getConstructor().newInstance(); 138 if ("JSON".equalsIgnoreCase(format)) 139 return (ParserBase) Class.forName("org.hl7.fhir.r5.formats.JsonParser").getConstructor().newInstance(); 140 if ("TURTLE".equalsIgnoreCase(format)) 141 throw new Error("unsupported Format " + format.toString()); // return new TurtleParser(); 142 if ("JSONLD".equalsIgnoreCase(format)) 143 throw new Error("unsupported Format " + format.toString()); // return new JsonLdParser(); 144 if ("VBAR".equalsIgnoreCase(format)) throw new Error("unsupported Format " + format.toString()); // 145 if ("TEXT".equalsIgnoreCase(format)) throw new Error("unsupported Format " + format.toString()); // 146 throw new Error("unsupported Format " + format); 147 } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) { 148 throw new Error("Could not instantiate", e); 149 } 150 } 151 152 public static FhirFormat determineFormat(byte[] source) throws FHIRException { 153 return determineFormat(source, MAX_SCAN_LENGTH); 154 } 155 156 public static FhirFormat determineFormat(byte[] source, int scanLength) throws FHIRException { 157 if (scanLength == -1) 158 scanLength = source.length; 159 int lt = firstIndexOf(source, '<', scanLength); 160 int ps = firstIndexOf(source, '{', scanLength); 161 int at = firstIndexOf(source, '@', scanLength); 162 if (at < ps && at < lt) return FhirFormat.TURTLE; 163 if (ps < lt) return FhirFormat.JSON; 164 if (lt < ps) return FhirFormat.XML; 165 throw new FHIRException("unable to determine format"); 166 } 167 168 private static int firstIndexOf(byte[] source, char c, int scanLength) { 169 for (int i = 0; i < Math.min(source.length, scanLength); i++) { 170 if (source[i] == c) 171 return i; 172 } 173 return Integer.MAX_VALUE; 174 } 175 176 public static Resource loadFile(String path) throws FileNotFoundException, IOException, FHIRException { 177 byte[] src = TextFile.fileToBytes(path); 178 FhirFormat fmt = determineFormat(src); 179 ParserBase parser = makeParser(fmt); 180 return parser.parse(src); 181 } 182 183 184 public static Resource loadFile(InputStream source) throws FileNotFoundException, IOException, FHIRException { 185 byte[] src = TextFile.streamToBytes(source); 186 FhirFormat fmt = determineFormat(src); 187 ParserBase parser = makeParser(fmt); 188 return parser.parse(src); 189 } 190 191 192 public static Resource loadFileTight(String path) throws FileNotFoundException, IOException, FHIRException { 193 byte[] src = TextFile.fileToBytes(path); 194 FhirFormat fmt = determineFormat(src); 195 ParserBase parser = makeParser(fmt); 196 parser.setAllowUnknownContent(false); 197 return parser.parse(src); 198 } 199 200 public static Resource loadFileTight(InputStream source) throws FileNotFoundException, IOException, FHIRException { 201 byte[] src = TextFile.streamToBytes(source); 202 FhirFormat fmt = determineFormat(src); 203 ParserBase parser = makeParser(fmt); 204 parser.setAllowUnknownContent(false); 205 return parser.parse(src); 206 } 207 208 209}