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}