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