Smart Health Cards support in validator
This commit is contained in:
parent
899eb788f3
commit
8c148469d7
|
@ -36,6 +36,7 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -108,8 +109,9 @@ public class JsonParser extends ParserBase {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Element parse(InputStream stream) throws IOException, FHIRException {
|
public List<NamedElement> parse(InputStream stream) throws IOException, FHIRException {
|
||||||
// if we're parsing at this point, then we're going to use the custom parser
|
// if we're parsing at this point, then we're going to use the custom parser
|
||||||
|
List<NamedElement> res = new ArrayList<>();
|
||||||
map = new IdentityHashMap<JsonElement, LocationData>();
|
map = new IdentityHashMap<JsonElement, LocationData>();
|
||||||
String source = TextFile.streamToString(stream);
|
String source = TextFile.streamToString(stream);
|
||||||
if (policy == ValidationPolicy.EVERYTHING) {
|
if (policy == ValidationPolicy.EVERYTHING) {
|
||||||
|
@ -121,12 +123,19 @@ public class JsonParser extends ParserBase {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
assert (map.containsKey(obj));
|
assert (map.containsKey(obj));
|
||||||
return parse(obj);
|
Element e = parse(obj);
|
||||||
|
if (e != null) {
|
||||||
|
res.add(new NamedElement(null, e));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
JsonObject obj = JsonTrackingParser.parse(source, null); // (JsonObject) new com.google.gson.JsonParser().parse(source);
|
JsonObject obj = JsonTrackingParser.parse(source, null); // (JsonObject) new com.google.gson.JsonParser().parse(source);
|
||||||
// assert (map.containsKey(obj));
|
// assert (map.containsKey(obj));
|
||||||
return parse(obj);
|
Element e = parse(obj);
|
||||||
|
if (e != null) {
|
||||||
|
res.add(new NamedElement(null, e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element parse(JsonObject object, Map<JsonElement, LocationData> map) throws FHIRException {
|
public Element parse(JsonObject object, Map<JsonElement, LocationData> map) throws FHIRException {
|
||||||
|
|
|
@ -34,18 +34,21 @@ package org.hl7.fhir.r5.elementmodel;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.hl7.fhir.exceptions.DefinitionException;
|
import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
|
||||||
public class Manager {
|
public class Manager {
|
||||||
|
|
||||||
//TODO use EnumMap
|
//TODO use EnumMap
|
||||||
public enum FhirFormat { XML, JSON, TURTLE, TEXT, VBAR;
|
public enum FhirFormat { XML, JSON, TURTLE, TEXT, VBAR, SHC;
|
||||||
|
// SHC = smart health cards, including as text versions of QR codes
|
||||||
|
|
||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
@ -82,10 +85,15 @@ public class Manager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Element parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException {
|
public static List<NamedElement> parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException {
|
||||||
return makeParser(context, inputFormat).parse(source);
|
return makeParser(context, inputFormat).parse(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Element parseSingle(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException {
|
||||||
|
return makeParser(context, inputFormat).parseSingle(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void compose(IWorkerContext context, Element e, OutputStream destination, FhirFormat outputFormat, OutputStyle style, String base) throws FHIRException, IOException {
|
public static void compose(IWorkerContext context, Element e, OutputStream destination, FhirFormat outputFormat, OutputStyle style, String base) throws FHIRException, IOException {
|
||||||
makeParser(context, outputFormat).compose(e, destination, style, base);
|
makeParser(context, outputFormat).compose(e, destination, style, base);
|
||||||
}
|
}
|
||||||
|
@ -96,6 +104,7 @@ public class Manager {
|
||||||
case XML : return new XmlParser(context);
|
case XML : return new XmlParser(context);
|
||||||
case TURTLE : return new TurtleParser(context);
|
case TURTLE : return new TurtleParser(context);
|
||||||
case VBAR : return new VerticalBarParser(context);
|
case VBAR : return new VerticalBarParser(context);
|
||||||
|
case SHC : return new SHCParser(context);
|
||||||
case TEXT : throw new Error("Programming logic error: do not call makeParser for a text resource");
|
case TEXT : throw new Error("Programming logic error: do not call makeParser for a text resource");
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.List;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.r5.conformance.ProfileUtilities;
|
import org.hl7.fhir.r5.conformance.ProfileUtilities;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.model.Base;
|
import org.hl7.fhir.r5.model.Base;
|
||||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||||
|
@ -70,7 +71,11 @@ public class ObjectConverter {
|
||||||
org.hl7.fhir.r5.formats.JsonParser jp = new org.hl7.fhir.r5.formats.JsonParser();
|
org.hl7.fhir.r5.formats.JsonParser jp = new org.hl7.fhir.r5.formats.JsonParser();
|
||||||
jp.compose(bs, ig);
|
jp.compose(bs, ig);
|
||||||
ByteArrayInputStream bi = new ByteArrayInputStream(bs.toByteArray());
|
ByteArrayInputStream bi = new ByteArrayInputStream(bs.toByteArray());
|
||||||
return new JsonParser(context).parse(bi);
|
List<NamedElement> list = new JsonParser(context).parse(bi);
|
||||||
|
if (list.size() != 1) {
|
||||||
|
throw new FHIRException("Unable to convert because the source contains multieple resources");
|
||||||
|
}
|
||||||
|
return list.get(0).getElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element convert(Property property, DataType type) throws FHIRException {
|
public Element convert(Property property, DataType type) throws FHIRException {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
@ -54,6 +55,23 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||||
|
|
||||||
public abstract class ParserBase {
|
public abstract class ParserBase {
|
||||||
|
|
||||||
|
public class NamedElement {
|
||||||
|
private String name;
|
||||||
|
private Element element;
|
||||||
|
public NamedElement(String name, Element element) {
|
||||||
|
super();
|
||||||
|
this.name = name;
|
||||||
|
this.element = element;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public Element getElement() {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public interface ILinkResolver {
|
public interface ILinkResolver {
|
||||||
String resolveType(String type);
|
String resolveType(String type);
|
||||||
String resolveProperty(Property property);
|
String resolveProperty(Property property);
|
||||||
|
@ -86,7 +104,15 @@ public abstract class ParserBase {
|
||||||
this.errors = errors;
|
this.errors = errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Element parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException;
|
public abstract List<NamedElement> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException;
|
||||||
|
|
||||||
|
public Element parseSingle(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
|
||||||
|
List<NamedElement> res = parse(stream);
|
||||||
|
if (res.size() != 1) {
|
||||||
|
throw new FHIRException("Parsing FHIR content returned multiple elements in a context where only one element is allowed");
|
||||||
|
}
|
||||||
|
return res.get(0).getElement();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base) throws FHIRException, IOException;
|
public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base) throws FHIRException, IOException;
|
||||||
|
|
||||||
|
@ -161,5 +187,9 @@ public abstract class ParserBase {
|
||||||
this.showDecorations = showDecorations;
|
this.showDecorations = showDecorations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getImpliedProfile() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,309 @@
|
||||||
|
package org.hl7.fhir.r5.elementmodel;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.zip.DataFormatException;
|
||||||
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
|
import org.hl7.fhir.utilities.json.JSONUtil;
|
||||||
|
import org.hl7.fhir.utilities.json.JsonTrackingParser;
|
||||||
|
import org.hl7.fhir.utilities.json.JsonTrackingParser.LocationData;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonNull;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class is actually a smart health cards validator.
|
||||||
|
* It's going to parse the JWT and assume that it contains
|
||||||
|
* a smart health card, which has a nested bundle in it, and
|
||||||
|
* then validate the bundle.
|
||||||
|
*
|
||||||
|
* See https://spec.smarthealth.cards/#health-cards-are-encoded-as-compact-serialization-json-web-signatures-jws
|
||||||
|
*
|
||||||
|
* This parser dose the JWT work, and then passes the JsonObject through to the underlying JsonParser
|
||||||
|
*
|
||||||
|
* Error locations are in the decoded payload
|
||||||
|
*
|
||||||
|
* @author grahame
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SHCParser extends ParserBase {
|
||||||
|
|
||||||
|
private JsonParser jsonParser;
|
||||||
|
private Map<JsonElement, LocationData> map;
|
||||||
|
private List<String> types = new ArrayList<>();
|
||||||
|
|
||||||
|
public SHCParser(IWorkerContext context) {
|
||||||
|
super(context);
|
||||||
|
jsonParser = new JsonParser(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<NamedElement> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
|
||||||
|
List<NamedElement> res = new ArrayList<>();
|
||||||
|
String src = TextFile.streamToString(stream).trim();
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
String pfx = null;
|
||||||
|
if (src.startsWith("{")) {
|
||||||
|
JsonObject json = JsonTrackingParser.parseJson(src);
|
||||||
|
if (checkProperty(json, "$", "verifiableCredential", true, "Array")) {
|
||||||
|
pfx = "verifiableCredential";
|
||||||
|
JsonArray arr = json.getAsJsonArray("verifiableCredential");
|
||||||
|
int i = 0;
|
||||||
|
for (JsonElement e : arr) {
|
||||||
|
if (!(e instanceof JsonPrimitive)) {
|
||||||
|
logError(line(e), col(e), "$.verifiableCredential["+i+"]", IssueType.STRUCTURE, "Wrong Property verifiableCredential in JSON Payload. Expected : String but found "+JSONUtil.type(e), IssueSeverity.ERROR);
|
||||||
|
} else {
|
||||||
|
list.add(e.getAsString());
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.add(src);
|
||||||
|
}
|
||||||
|
int c = 0;
|
||||||
|
for (String ssrc : list) {
|
||||||
|
String prefix = pfx == null ? "" : pfx+"["+Integer.toString(c)+"].";
|
||||||
|
c++;
|
||||||
|
JWT jwt = null;
|
||||||
|
try {
|
||||||
|
jwt = decodeJWT(ssrc);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logError(1, 1, prefix+"JWT", IssueType.INVALID, "Unable to decode JWT token", IssueSeverity.ERROR);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
map = jwt.map;
|
||||||
|
JsonTrackingParser.write(jwt.payload, "c:\\temp\\payload.json");
|
||||||
|
logError(1, 1, prefix+"JWT", IssueType.INFORMATIONAL, "The FHIR Validator does not check the JWT signature (see https://demo-portals.smarthealth.cards/VerifierPortal.html or https://github.com/smart-on-fhir/health-cards-dev-tools)", IssueSeverity.INFORMATION);
|
||||||
|
checkNamedProperties(jwt.getPayload(), prefix+"payload", "iss", "nbf", "vc");
|
||||||
|
checkProperty(jwt.getPayload(), prefix+"payload", "iss", true, "String");
|
||||||
|
checkProperty(jwt.getPayload(), prefix+"payload", "nbf", true, "Number");
|
||||||
|
JsonObject vc = jwt.getPayload().getAsJsonObject("vc");
|
||||||
|
if (vc == null) {
|
||||||
|
logError(1, 1, "JWT", IssueType.STRUCTURE, "Unable to find property 'vc' in the payload", IssueSeverity.ERROR);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
String path = prefix+"payload.vc";
|
||||||
|
checkNamedProperties(vc, path, "type", "credentialSubject");
|
||||||
|
if (!checkProperty(vc, path, "type", true, "Array")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
JsonArray type = vc.getAsJsonArray("type");
|
||||||
|
int i = 0;
|
||||||
|
for (JsonElement e : type) {
|
||||||
|
if (!(e instanceof JsonPrimitive)) {
|
||||||
|
logError(line(e), col(e), path+".type["+i+"]", IssueType.STRUCTURE, "Wrong Property Type in JSON Payload. Expected : String but found "+JSONUtil.type(e), IssueSeverity.ERROR);
|
||||||
|
} else {
|
||||||
|
types.add(e.getAsString());
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (!types.contains("https://smarthealth.cards#health-card")) {
|
||||||
|
logError(line(vc), col(vc), path, IssueType.STRUCTURE, "Card does not claim to be of type https://smarthealth.cards#health-card, cannot validate", IssueSeverity.ERROR);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (!checkProperty(vc, path, "credentialSubject", true, "Object")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
JsonObject cs = vc.getAsJsonObject("credentialSubject");
|
||||||
|
path = path+".credentialSubject";
|
||||||
|
if (!checkProperty(cs, path, "fhirVersion", true, "String")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
JsonElement fv = cs.get("fhirVersion");
|
||||||
|
if (!VersionUtilities.versionsCompatible(context.getVersion(), fv.getAsString())) {
|
||||||
|
logError(line(fv), col(fv), path+".fhirVersion", IssueType.STRUCTURE, "Card claims to be of version "+fv.getAsString()+", cannot be validated against version "+context.getVersion(), IssueSeverity.ERROR);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (!checkProperty(cs, path, "fhirBundle", true, "Object")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// ok. all checks passed, we can now validate the bundle
|
||||||
|
Element e = jsonParser.parse(cs.getAsJsonObject("fhirBundle"), map);
|
||||||
|
if (e != null) {
|
||||||
|
res.add(new NamedElement(path, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getImpliedProfile() {
|
||||||
|
if (types.contains("https://smarthealth.cards#covid19") && types.contains("https://smarthealth.cards#immunization")) {
|
||||||
|
return "http://hl7.org/fhir/uv/shc-vaccination/StructureDefinition/shc-vaccination-bundle-dm";
|
||||||
|
}
|
||||||
|
if (types.contains("https://smarthealth.cards#covid19") && types.contains("https://smarthealth.cards#laboratory")) {
|
||||||
|
return "http://hl7.org/fhir/uv/shc-vaccination/StructureDefinition/shc-covid19-laboratory-bundle-dm";
|
||||||
|
}
|
||||||
|
if (types.contains("https://smarthealth.cards#laboratory")) {
|
||||||
|
return "http://hl7.org/fhir/uv/shc-vaccination/StructureDefinition/shc-infectious-disease-laboratory-bundle-dm";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean checkProperty(JsonObject obj, String path, String name, boolean required, String type) {
|
||||||
|
JsonElement e = obj.get(name);
|
||||||
|
if (e != null) {
|
||||||
|
String t = JSONUtil.type(e);
|
||||||
|
if (!type.equals(t)) {
|
||||||
|
logError(line(e), col(e), path+"."+name, IssueType.STRUCTURE, "Wrong Property Type in JSON Payload. Expected : "+type+" but found "+t, IssueSeverity.ERROR);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (required) {
|
||||||
|
logError(line(obj), col(obj), path, IssueType.STRUCTURE, "Missing Property in JSON Payload: "+name, IssueSeverity.ERROR);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNamedProperties(JsonObject obj, String path, String... names) {
|
||||||
|
for (Entry<String, JsonElement> e : obj.entrySet()) {
|
||||||
|
if (!Utilities.existsInList(e.getKey(), names)) {
|
||||||
|
logError(line(e.getValue()), col(e.getValue()), path+"."+e.getKey(), IssueType.STRUCTURE, "Unknown Property in JSON Payload", IssueSeverity.WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int line(JsonElement e) {
|
||||||
|
if (map == null|| !map.containsKey(e))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return map.get(e).getLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int col(JsonElement e) {
|
||||||
|
if (map == null|| !map.containsKey(e))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return map.get(e).getCol();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void compose(Element e, OutputStream destination, OutputStyle style, String base) throws FHIRException, IOException {
|
||||||
|
throw new FHIRFormatError("Writing resources is not supported for the SHC format");
|
||||||
|
// because then we'd have to try to sign, and we're just not going to be doing that from the element model
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class JWT {
|
||||||
|
|
||||||
|
private JsonObject header;
|
||||||
|
private JsonObject payload;
|
||||||
|
public Map<JsonElement, LocationData> map = new HashMap<>();
|
||||||
|
|
||||||
|
public JsonObject getHeader() {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
public void setHeader(JsonObject header) {
|
||||||
|
this.header = header;
|
||||||
|
}
|
||||||
|
public JsonObject getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
public void setPayload(JsonObject payload) {
|
||||||
|
this.payload = payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int BUFFER_SIZE = 1024;
|
||||||
|
public static final String CURRENT_PACKAGE = "hl7.fhir.uv.shc-vaccination#0.6.2";
|
||||||
|
|
||||||
|
// todo: deal with chunking
|
||||||
|
public static String decodeQRCode(String src) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
if (!src.startsWith("shc:/")) {
|
||||||
|
throw new FHIRException("Unable to process smart health card (didn't start with shc:/)");
|
||||||
|
}
|
||||||
|
for (int i = 5; i < src.length(); i = i + 2) {
|
||||||
|
String s = src.substring(i, i+2);
|
||||||
|
byte v = Byte.parseByte(s);
|
||||||
|
char c = (char) (45+v);
|
||||||
|
b.append(c);
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JWT decodeJWT(String jwt) throws IOException, DataFormatException {
|
||||||
|
if (jwt.startsWith("shc:/")) {
|
||||||
|
jwt = decodeQRCode(jwt);
|
||||||
|
}
|
||||||
|
String[] parts = splitToken(jwt);
|
||||||
|
byte[] headerJson;
|
||||||
|
byte[] payloadJson;
|
||||||
|
try {
|
||||||
|
headerJson = Base64.getUrlDecoder().decode(parts[0]);
|
||||||
|
payloadJson = Base64.getUrlDecoder().decode(parts[1]);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new FHIRException("The UTF-8 Charset isn't initialized.", e);
|
||||||
|
} catch (IllegalArgumentException e){
|
||||||
|
throw new FHIRException("The input is not a valid base 64 encoded string.", e);
|
||||||
|
}
|
||||||
|
JWT res = new JWT();
|
||||||
|
res.header = JsonTrackingParser.parseJson(headerJson);
|
||||||
|
if ("DEF".equals(JSONUtil.str(res.header, "zip"))) {
|
||||||
|
payloadJson = inflate(payloadJson);
|
||||||
|
}
|
||||||
|
res.payload = JsonTrackingParser.parse(TextFile.bytesToString(payloadJson), res.map, true);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String[] splitToken(String token) {
|
||||||
|
String[] parts = token.split("\\.");
|
||||||
|
if (parts.length == 2 && token.endsWith(".")) {
|
||||||
|
//Tokens with alg='none' have empty String as Signature.
|
||||||
|
parts = new String[]{parts[0], parts[1], ""};
|
||||||
|
}
|
||||||
|
if (parts.length != 3) {
|
||||||
|
throw new FHIRException(String.format("The token was expected to have 3 parts, but got %s.", parts.length));
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final byte[] inflate(byte[] data) throws IOException, DataFormatException {
|
||||||
|
final Inflater inflater = new Inflater(true);
|
||||||
|
inflater.setInput(data);
|
||||||
|
|
||||||
|
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length))
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
while (!inflater.finished())
|
||||||
|
{
|
||||||
|
final int count = inflater.inflate(buffer);
|
||||||
|
outputStream.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -34,11 +34,13 @@ package org.hl7.fhir.r5.elementmodel;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
@ -61,7 +63,7 @@ public class Tester {
|
||||||
// new FileOutputStream("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.json")), FhirFormat.JSON, OutputStyle.PRETTY);
|
// new FileOutputStream("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.json")), FhirFormat.JSON, OutputStyle.PRETTY);
|
||||||
// String src = normalise(TextFile.fileToString("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.json")));
|
// String src = normalise(TextFile.fileToString("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.json")));
|
||||||
// String tgt = normalise(TextFile.fileToString("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".json")));
|
// String tgt = normalise(TextFile.fileToString("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".json")));
|
||||||
Element e = Manager.parse(context, new FileInputStream("C:\\work\\org.hl7.fhir\\build\\publish\\"+f), FhirFormat.XML);
|
Element e = Manager.parseSingle(context, new FileInputStream("C:\\work\\org.hl7.fhir\\build\\publish\\"+f), FhirFormat.XML);
|
||||||
Manager.compose(context, e, new FileOutputStream("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.ttl")), FhirFormat.TURTLE, OutputStyle.PRETTY, null);
|
Manager.compose(context, e, new FileOutputStream("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.ttl")), FhirFormat.TURTLE, OutputStyle.PRETTY, null);
|
||||||
Manager.compose(context, e, new FileOutputStream("C:\\temp\\resource.xml"), FhirFormat.XML, OutputStyle.PRETTY, null);
|
Manager.compose(context, e, new FileOutputStream("C:\\temp\\resource.xml"), FhirFormat.XML, OutputStyle.PRETTY, null);
|
||||||
String src = TextFile.fileToString("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.ttl"));
|
String src = TextFile.fileToString("C:\\work\\org.hl7.fhir\\build\\publish\\"+Utilities.changeFileExt(f, ".mm.ttl"));
|
||||||
|
|
|
@ -34,6 +34,7 @@ package org.hl7.fhir.r5.elementmodel;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -42,6 +43,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
|
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
||||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
@ -74,7 +76,8 @@ public class TurtleParser extends ParserBase {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Element parse(InputStream input) throws IOException, FHIRException {
|
public List<NamedElement> parse(InputStream input) throws IOException, FHIRException {
|
||||||
|
List<NamedElement> res = new ArrayList<>();
|
||||||
Turtle src = new Turtle();
|
Turtle src = new Turtle();
|
||||||
if (policy == ValidationPolicy.EVERYTHING) {
|
if (policy == ValidationPolicy.EVERYTHING) {
|
||||||
try {
|
try {
|
||||||
|
@ -83,11 +86,18 @@ public class TurtleParser extends ParserBase {
|
||||||
logError(-1, -1, "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_TURTLE_, e.getMessage()), IssueSeverity.FATAL);
|
logError(-1, -1, "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_TURTLE_, e.getMessage()), IssueSeverity.FATAL);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return parse(src);
|
Element e = parse(src);
|
||||||
|
if (e != null) {
|
||||||
|
res.add(new NamedElement(null, e));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
src.parse(TextFile.streamToString(input));
|
src.parse(TextFile.streamToString(input));
|
||||||
return parse(src);
|
Element e = parse(src);
|
||||||
}
|
if (e != null) {
|
||||||
|
res.add(new NamedElement(null, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Element parse(Turtle src) throws FHIRException {
|
private Element parse(Turtle src) throws FHIRException {
|
||||||
|
|
|
@ -36,11 +36,14 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.hl7.fhir.exceptions.DefinitionException;
|
import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
|
||||||
|
@ -450,7 +453,7 @@ public class VerticalBarParser extends ParserBase {
|
||||||
private Delimiters delimiters = new Delimiters();
|
private Delimiters delimiters = new Delimiters();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Element parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
|
public List<NamedElement> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
|
||||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/v2/StructureDefinition/Message");
|
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/v2/StructureDefinition/Message");
|
||||||
Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd));
|
Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd));
|
||||||
VerticalBarParserReader reader = new VerticalBarParserReader(new BufferedInputStream(stream), charset);
|
VerticalBarParserReader reader = new VerticalBarParserReader(new BufferedInputStream(stream), charset);
|
||||||
|
@ -458,8 +461,9 @@ public class VerticalBarParser extends ParserBase {
|
||||||
preDecode(reader);
|
preDecode(reader);
|
||||||
while (!reader.isFinished()) // && (getOptions().getSegmentLimit() == 0 || getOptions().getSegmentLimit() > message.getSegments().size()))
|
while (!reader.isFinished()) // && (getOptions().getSegmentLimit() == 0 || getOptions().getSegmentLimit() > message.getSegments().size()))
|
||||||
readSegment(message, reader);
|
readSegment(message, reader);
|
||||||
|
List<NamedElement> res = new ArrayList<>();
|
||||||
return message;
|
res.add(new NamedElement(null, message));
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void preDecode(VerticalBarParserReader reader) throws FHIRException {
|
private void preDecode(VerticalBarParserReader reader) throws FHIRException {
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
import org.hl7.fhir.r5.conformance.ProfileUtilities;
|
import org.hl7.fhir.r5.conformance.ProfileUtilities;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
|
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.model.DateTimeType;
|
import org.hl7.fhir.r5.model.DateTimeType;
|
||||||
|
@ -106,7 +107,8 @@ public class XmlParser extends ParserBase {
|
||||||
this.allowXsiLocation = allowXsiLocation;
|
this.allowXsiLocation = allowXsiLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element parse(InputStream stream) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
|
public List<NamedElement> parse(InputStream stream) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
|
||||||
|
List<NamedElement> res = new ArrayList<>();
|
||||||
Document doc = null;
|
Document doc = null;
|
||||||
try {
|
try {
|
||||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
@ -157,10 +159,13 @@ public class XmlParser extends ParserBase {
|
||||||
logError(0, 0, "(syntax)", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL);
|
logError(0, 0, "(syntax)", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL);
|
||||||
doc = null;
|
doc = null;
|
||||||
}
|
}
|
||||||
if (doc == null)
|
if (doc != null) {
|
||||||
return null;
|
Element e = parse(doc);
|
||||||
else
|
if (e != null) {
|
||||||
return parse(doc);
|
res.add(new NamedElement(null, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -281,9 +281,9 @@ public interface IResourceValidator {
|
||||||
* in addition, you can pass one or more profiles ti validate beyond the base standard - as structure definitions or canonical URLs
|
* in addition, you can pass one or more profiles ti validate beyond the base standard - as structure definitions or canonical URLs
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element) throws FHIRException;
|
void validate(Object Context, List<ValidationMessage> errors, String initialPath, org.hl7.fhir.r5.elementmodel.Element element) throws FHIRException;
|
||||||
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element, String profile) throws FHIRException;
|
void validate(Object Context, List<ValidationMessage> errors, String initialPath, org.hl7.fhir.r5.elementmodel.Element element, String profile) throws FHIRException;
|
||||||
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element, List<StructureDefinition> profiles) throws FHIRException;
|
void validate(Object Context, List<ValidationMessage> errors, String initialPath, org.hl7.fhir.r5.elementmodel.Element element, List<StructureDefinition> profiles) throws FHIRException;
|
||||||
|
|
||||||
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format) throws FHIRException;
|
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format) throws FHIRException;
|
||||||
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, String profile) throws FHIRException;
|
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, String profile) throws FHIRException;
|
||||||
|
|
|
@ -207,14 +207,14 @@ public class CDARoundTripTests {
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void testClinicalDocumentXmlParser() throws IOException {
|
public void testClinicalDocumentXmlParser() throws IOException {
|
||||||
Element cda = Manager.parse(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example.xml"),
|
Element cda = Manager.parseSingle(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example.xml"),
|
||||||
FhirFormat.XML);
|
FhirFormat.XML);
|
||||||
|
|
||||||
assertsExample(cda);
|
assertsExample(cda);
|
||||||
|
|
||||||
ByteArrayOutputStream baosXml = new ByteArrayOutputStream();
|
ByteArrayOutputStream baosXml = new ByteArrayOutputStream();
|
||||||
Manager.compose(context, cda, baosXml, FhirFormat.XML, OutputStyle.PRETTY, null);
|
Manager.compose(context, cda, baosXml, FhirFormat.XML, OutputStyle.PRETTY, null);
|
||||||
Element cdaXmlRoundtrip = Manager.parse(context, new ByteArrayInputStream(baosXml.toString().getBytes()), FhirFormat.XML);
|
Element cdaXmlRoundtrip = Manager.parseSingle(context, new ByteArrayInputStream(baosXml.toString().getBytes()), FhirFormat.XML);
|
||||||
assertsExample(cdaXmlRoundtrip);
|
assertsExample(cdaXmlRoundtrip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,14 +226,14 @@ public class CDARoundTripTests {
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void testClinicalDocumentJsonParser() throws IOException {
|
public void testClinicalDocumentJsonParser() throws IOException {
|
||||||
Element cda = Manager.parse(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example.xml"),
|
Element cda = Manager.parseSingle(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example.xml"),
|
||||||
FhirFormat.XML);
|
FhirFormat.XML);
|
||||||
|
|
||||||
assertsExample(cda);
|
assertsExample(cda);
|
||||||
|
|
||||||
ByteArrayOutputStream baosJson = new ByteArrayOutputStream();
|
ByteArrayOutputStream baosJson = new ByteArrayOutputStream();
|
||||||
Manager.compose(context, cda, baosJson, FhirFormat.JSON, OutputStyle.PRETTY, null);
|
Manager.compose(context, cda, baosJson, FhirFormat.JSON, OutputStyle.PRETTY, null);
|
||||||
Element cdaJsonRoundtrip = Manager.parse(context, new ByteArrayInputStream(baosJson.toString().getBytes()),
|
Element cdaJsonRoundtrip = Manager.parseSingle(context, new ByteArrayInputStream(baosJson.toString().getBytes()),
|
||||||
FhirFormat.JSON);
|
FhirFormat.JSON);
|
||||||
|
|
||||||
assertsExample(cdaJsonRoundtrip);
|
assertsExample(cdaJsonRoundtrip);
|
||||||
|
@ -245,7 +245,7 @@ public class CDARoundTripTests {
|
||||||
* verify that umlaut like äö etc are not encoded in UTF-8 in attributes
|
* verify that umlaut like äö etc are not encoded in UTF-8 in attributes
|
||||||
*/
|
*/
|
||||||
public void testSerializeUmlaut() throws IOException {
|
public void testSerializeUmlaut() throws IOException {
|
||||||
Element xml = Manager.parse(context,
|
Element xml = Manager.parseSingle(context,
|
||||||
TestingUtilities.loadTestResourceStream("validator", "cda", "example.xml"), FhirFormat.XML);
|
TestingUtilities.loadTestResourceStream("validator", "cda", "example.xml"), FhirFormat.XML);
|
||||||
|
|
||||||
List<Element> title = xml.getChildrenByName("title");
|
List<Element> title = xml.getChildrenByName("title");
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class NarrativeGenerationTests {
|
||||||
Assertions.assertTrue(output.equals(target), "Output does not match expected");
|
Assertions.assertTrue(output.equals(target), "Output does not match expected");
|
||||||
|
|
||||||
if (test.isMeta()) {
|
if (test.isMeta()) {
|
||||||
org.hl7.fhir.r5.elementmodel.Element e = Manager.parse(context, TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".xml"), FhirFormat.XML);
|
org.hl7.fhir.r5.elementmodel.Element e = Manager.parseSingle(context, TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".xml"), FhirFormat.XML);
|
||||||
x = RendererFactory.factory(source, rc).render(new ElementWrappers.ResourceWrapperMetaElement(rc, e));
|
x = RendererFactory.factory(source, rc).render(new ElementWrappers.ResourceWrapperMetaElement(rc, e));
|
||||||
|
|
||||||
target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html"));
|
target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html"));
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class ResourceRoundTripTests {
|
||||||
* verify that umlaut like äö etc are not encoded in UTF-8 in attributes
|
* verify that umlaut like äö etc are not encoded in UTF-8 in attributes
|
||||||
*/
|
*/
|
||||||
public void testSerializeUmlaut() throws IOException {
|
public void testSerializeUmlaut() throws IOException {
|
||||||
Element xml = Manager.parse(TestingUtilities.context(), TestingUtilities.loadTestResourceStream("r5", "unicode.xml"),
|
Element xml = Manager.parseSingle(TestingUtilities.context(), TestingUtilities.loadTestResourceStream("r5", "unicode.xml"),
|
||||||
FhirFormat.XML);
|
FhirFormat.XML);
|
||||||
List<Element> concept = xml.getChildrenByName("concept");
|
List<Element> concept = xml.getChildrenByName("concept");
|
||||||
assertTrue(concept!=null && concept.size()==1);
|
assertTrue(concept!=null && concept.size()==1);
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class ValidationTestConvertor {
|
||||||
if (!t.exists()) {
|
if (!t.exists()) {
|
||||||
try {
|
try {
|
||||||
System.out.print("Process " + f.getAbsolutePath());
|
System.out.print("Process " + f.getAbsolutePath());
|
||||||
Element e = Manager.parse(context, new FileInputStream(f), FhirFormat.XML);
|
Element e = Manager.parseSingle(context, new FileInputStream(f), FhirFormat.XML);
|
||||||
Manager.compose(context, e, new FileOutputStream(t), FhirFormat.TURTLE, OutputStyle.PRETTY, null);
|
Manager.compose(context, e, new FileOutputStream(t), FhirFormat.TURTLE, OutputStyle.PRETTY, null);
|
||||||
System.out.println(" .... success");
|
System.out.println(" .... success");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -44,7 +44,7 @@ public class ValidationTestConvertor {
|
||||||
if (!t.exists()) {
|
if (!t.exists()) {
|
||||||
try {
|
try {
|
||||||
System.out.print("Process " + f.getAbsolutePath());
|
System.out.print("Process " + f.getAbsolutePath());
|
||||||
Element e = Manager.parse(context, new FileInputStream(f), FhirFormat.JSON);
|
Element e = Manager.parseSingle(context, new FileInputStream(f), FhirFormat.JSON);
|
||||||
Manager.compose(context, e, new FileOutputStream(t), FhirFormat.TURTLE, OutputStyle.PRETTY, null);
|
Manager.compose(context, e, new FileOutputStream(t), FhirFormat.TURTLE, OutputStyle.PRETTY, null);
|
||||||
System.out.println(" .... success");
|
System.out.println(" .... success");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class XmlParserTests {
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void testXsiDeserialiserXmlParser() throws IOException {
|
public void testXsiDeserialiserXmlParser() throws IOException {
|
||||||
Element cda = Manager.parse(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example-xsi.xml"),
|
Element cda = Manager.parseSingle(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example-xsi.xml"),
|
||||||
FhirFormat.XML);
|
FhirFormat.XML);
|
||||||
|
|
||||||
ByteArrayOutputStream baosXml = new ByteArrayOutputStream();
|
ByteArrayOutputStream baosXml = new ByteArrayOutputStream();
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class ResourceTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element testEM() throws Exception {
|
public Element testEM() throws Exception {
|
||||||
Element resource = Manager.parse(TestingUtilities.context(), new FileInputStream(source), isJson() ? FhirFormat.JSON : FhirFormat.XML);
|
Element resource = Manager.parseSingle(TestingUtilities.context(), new FileInputStream(source), isJson() ? FhirFormat.JSON : FhirFormat.XML);
|
||||||
Manager.compose(TestingUtilities.context(), resource, new FileOutputStream(source.getAbsoluteFile()+".out.json"), FhirFormat.JSON, OutputStyle.PRETTY, null);
|
Manager.compose(TestingUtilities.context(), resource, new FileOutputStream(source.getAbsoluteFile()+".out.json"), FhirFormat.JSON, OutputStyle.PRETTY, null);
|
||||||
Manager.compose(TestingUtilities.context(), resource, new FileOutputStream(source.getAbsoluteFile()+".out.json"), FhirFormat.XML, OutputStyle.PRETTY, null);
|
Manager.compose(TestingUtilities.context(), resource, new FileOutputStream(source.getAbsoluteFile()+".out.json"), FhirFormat.XML, OutputStyle.PRETTY, null);
|
||||||
return resource;
|
return resource;
|
||||||
|
|
|
@ -45,6 +45,7 @@ import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonNull;
|
import com.google.gson.JsonNull;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
public class JSONUtil {
|
public class JSONUtil {
|
||||||
|
|
||||||
|
@ -139,4 +140,27 @@ public class JSONUtil {
|
||||||
return (JsonObject) new com.google.gson.JsonParser().parse(TextFile.streamToString(c.getInputStream()));
|
return (JsonObject) new com.google.gson.JsonParser().parse(TextFile.streamToString(c.getInputStream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String type(JsonElement e) {
|
||||||
|
if (e == null) {
|
||||||
|
return "(null)";
|
||||||
|
}
|
||||||
|
if (e.isJsonObject()) {
|
||||||
|
return "Object";
|
||||||
|
}
|
||||||
|
if (e.isJsonArray()) {
|
||||||
|
return "Array";
|
||||||
|
}
|
||||||
|
if (e.isJsonNull()) {
|
||||||
|
return "Null";
|
||||||
|
}
|
||||||
|
JsonPrimitive p = (JsonPrimitive) e;
|
||||||
|
if (p.isBoolean()) {
|
||||||
|
return "Boolean";
|
||||||
|
}
|
||||||
|
if (p.isNumber()) {
|
||||||
|
return "Number";
|
||||||
|
}
|
||||||
|
return "String";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -189,7 +189,7 @@ public class IgLoader {
|
||||||
return readZip(new FileInputStream(src));
|
return readZip(new FileInputStream(src));
|
||||||
if (src.endsWith("igpack.zip"))
|
if (src.endsWith("igpack.zip"))
|
||||||
return readZip(new FileInputStream(src));
|
return readZip(new FileInputStream(src));
|
||||||
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), src);
|
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), TextFile.fileToBytes(f), src, true);
|
||||||
if (fmt != null) {
|
if (fmt != null) {
|
||||||
Map<String, byte[]> res = new HashMap<String, byte[]>();
|
Map<String, byte[]> res = new HashMap<String, byte[]>();
|
||||||
res.put(Utilities.changeFileExt(src, "." + fmt.getExtension()), TextFile.fileToBytesNCS(src));
|
res.put(Utilities.changeFileExt(src, "." + fmt.getExtension()), TextFile.fileToBytesNCS(src));
|
||||||
|
@ -321,7 +321,7 @@ public class IgLoader {
|
||||||
return readZip(new FileInputStream(src));
|
return readZip(new FileInputStream(src));
|
||||||
if (src.endsWith("igpack.zip"))
|
if (src.endsWith("igpack.zip"))
|
||||||
return readZip(new FileInputStream(src));
|
return readZip(new FileInputStream(src));
|
||||||
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), src);
|
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), TextFile.fileToBytes(f), src, true);
|
||||||
if (fmt != null) {
|
if (fmt != null) {
|
||||||
Map<String, byte[]> res = new HashMap<String, byte[]>();
|
Map<String, byte[]> res = new HashMap<String, byte[]>();
|
||||||
res.put(Utilities.changeFileExt(src, "." + fmt.getExtension()), TextFile.fileToBytesNCS(src));
|
res.put(Utilities.changeFileExt(src, "." + fmt.getExtension()), TextFile.fileToBytesNCS(src));
|
||||||
|
@ -470,7 +470,7 @@ public class IgLoader {
|
||||||
else
|
else
|
||||||
cnt = TextFile.streamToBytes(stream);
|
cnt = TextFile.streamToBytes(stream);
|
||||||
|
|
||||||
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), cnt, src);
|
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), cnt, src, true);
|
||||||
if (fmt != null) {
|
if (fmt != null) {
|
||||||
Map<String, byte[]> res = new HashMap<String, byte[]>();
|
Map<String, byte[]> res = new HashMap<String, byte[]>();
|
||||||
res.put(Utilities.changeFileExt(src, "." + fmt.getExtension()), cnt);
|
res.put(Utilities.changeFileExt(src, "." + fmt.getExtension()), cnt);
|
||||||
|
@ -569,7 +569,7 @@ public class IgLoader {
|
||||||
if (ff.isDirectory() && recursive) {
|
if (ff.isDirectory() && recursive) {
|
||||||
res.putAll(scanDirectory(ff, true));
|
res.putAll(scanDirectory(ff, true));
|
||||||
} else if (!ff.isDirectory() && !isIgnoreFile(ff)) {
|
} else if (!ff.isDirectory() && !isIgnoreFile(ff)) {
|
||||||
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), ff.getAbsolutePath());
|
Manager.FhirFormat fmt = ResourceChecker.checkIsResource(getContext(), isDebug(), TextFile.fileToBytes(ff), ff.getAbsolutePath(), true);
|
||||||
if (fmt != null) {
|
if (fmt != null) {
|
||||||
res.put(Utilities.changeFileExt(ff.getName(), "." + fmt.getExtension()), TextFile.fileToBytes(ff.getAbsolutePath()));
|
res.put(Utilities.changeFileExt(ff.getName(), "." + fmt.getExtension()), TextFile.fileToBytes(ff.getAbsolutePath()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,75 @@ package org.hl7.fhir.validation;
|
||||||
|
|
||||||
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.SHCParser;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.SHCParser.JWT;
|
||||||
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
|
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.json.JSONUtil;
|
||||||
|
import org.hl7.fhir.utilities.json.JsonTrackingParser;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class ResourceChecker {
|
public class ResourceChecker {
|
||||||
protected static Manager.FhirFormat checkIsResource(SimpleWorkerContext context, boolean debug, byte[] cnt, String filename) {
|
|
||||||
|
// protected static Manager.FhirFormat checkIsResource(SimpleWorkerContext context, boolean debug, String path) throws IOException {
|
||||||
|
//
|
||||||
|
// if (Utilities.existsInList(ext, "json"))
|
||||||
|
// return Manager.FhirFormat.JSON;
|
||||||
|
// if (Utilities.existsInList(ext, "map"))
|
||||||
|
// return Manager.FhirFormat.TEXT;
|
||||||
|
// if (Utilities.existsInList(ext, "txt"))
|
||||||
|
// return Manager.FhirFormat.TEXT;
|
||||||
|
// if (Utilities.existsInList(ext, "jwt", "jws"))
|
||||||
|
// return Manager.FhirFormat.SHC;
|
||||||
|
//
|
||||||
|
// return checkIsResource(context, debug, TextFile.fileToBytes(path), path);
|
||||||
|
// }
|
||||||
|
public static Manager.FhirFormat checkIsResource(SimpleWorkerContext context, boolean debug, byte[] cnt, String filename, boolean guessFromExtension) {
|
||||||
System.out.println(" ..Detect format for " + filename);
|
System.out.println(" ..Detect format for " + filename);
|
||||||
|
if (guessFromExtension) {
|
||||||
|
String ext = Utilities.getFileExtension(filename);
|
||||||
|
if (Utilities.existsInList(ext, "xml")) {
|
||||||
|
return FhirFormat.XML;
|
||||||
|
}
|
||||||
|
if (Utilities.existsInList(ext, "ttl")) {
|
||||||
|
return FhirFormat.TURTLE;
|
||||||
|
}
|
||||||
|
if (Utilities.existsInList(ext, "map")) {
|
||||||
|
return Manager.FhirFormat.TEXT;
|
||||||
|
}
|
||||||
|
if (Utilities.existsInList(ext, "jwt", "jws")) {
|
||||||
|
return Manager.FhirFormat.SHC;
|
||||||
|
}
|
||||||
|
if (Utilities.existsInList(ext, "json")) {
|
||||||
|
// no, we have to look inside, and decide.
|
||||||
|
try {
|
||||||
|
JsonObject json = JsonTrackingParser.parseJson(cnt);
|
||||||
|
if (json.has("verifiableCredential")) {
|
||||||
|
return FhirFormat.SHC;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return FhirFormat.JSON;
|
||||||
|
}
|
||||||
|
if (Utilities.existsInList(ext, "txt")) {
|
||||||
|
try {
|
||||||
|
String src = TextFile.bytesToString(cnt);
|
||||||
|
if (src.startsWith("shc:/")) {
|
||||||
|
return FhirFormat.SHC;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return Manager.FhirFormat.TEXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Manager.parse(context, new ByteArrayInputStream(cnt), Manager.FhirFormat.JSON);
|
Manager.parse(context, new ByteArrayInputStream(cnt), Manager.FhirFormat.JSON);
|
||||||
return Manager.FhirFormat.JSON;
|
return Manager.FhirFormat.JSON;
|
||||||
|
@ -36,6 +95,17 @@ public class ResourceChecker {
|
||||||
System.out.println("Not Turtle: " + e.getMessage());
|
System.out.println("Not Turtle: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
String s = new String(cnt, StandardCharsets.UTF_8);
|
||||||
|
if (s.startsWith("shc:/"))
|
||||||
|
s = SHCParser.decodeQRCode(s);
|
||||||
|
JWT jwt = SHCParser.decodeJWT(s);
|
||||||
|
return Manager.FhirFormat.SHC;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (debug) {
|
||||||
|
System.out.println("Not a smart health card: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
new StructureMapUtilities(context, null, null).parse(TextFile.bytesToString(cnt), null);
|
new StructureMapUtilities(context, null, null).parse(TextFile.bytesToString(cnt), null);
|
||||||
return Manager.FhirFormat.TEXT;
|
return Manager.FhirFormat.TEXT;
|
||||||
|
@ -49,19 +119,5 @@ public class ResourceChecker {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static Manager.FhirFormat checkIsResource(SimpleWorkerContext context, boolean debug, String path) throws IOException {
|
|
||||||
String ext = Utilities.getFileExtension(path);
|
|
||||||
if (Utilities.existsInList(ext, "xml"))
|
|
||||||
return Manager.FhirFormat.XML;
|
|
||||||
if (Utilities.existsInList(ext, "json"))
|
|
||||||
return Manager.FhirFormat.JSON;
|
|
||||||
if (Utilities.existsInList(ext, "ttl"))
|
|
||||||
return Manager.FhirFormat.TURTLE;
|
|
||||||
if (Utilities.existsInList(ext, "map"))
|
|
||||||
return Manager.FhirFormat.TEXT;
|
|
||||||
if (Utilities.existsInList(ext, "txt"))
|
|
||||||
return Manager.FhirFormat.TEXT;
|
|
||||||
|
|
||||||
return checkIsResource(context, debug, TextFile.fileToBytes(path), path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||||
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
|
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.SHCParser;
|
||||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.formats.JsonParser;
|
import org.hl7.fhir.r5.formats.JsonParser;
|
||||||
|
@ -293,7 +294,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
// testing entry point
|
// testing entry point
|
||||||
public OperationOutcome validate(FhirFormat format, InputStream stream, List<String> profiles) throws FHIRException, IOException, EOperationOutcome {
|
public OperationOutcome validate(FhirFormat format, InputStream stream, List<String> profiles) throws FHIRException, IOException, EOperationOutcome {
|
||||||
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
|
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
|
||||||
InstanceValidator validator = getValidator();
|
InstanceValidator validator = getValidator(format);
|
||||||
validator.validate(null, messages, stream, format, asSdList(profiles));
|
validator.validate(null, messages, stream, format, asSdList(profiles));
|
||||||
return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine);
|
return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine);
|
||||||
}
|
}
|
||||||
|
@ -350,7 +351,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
}
|
}
|
||||||
|
|
||||||
public OperationOutcome validate(byte[] source, FhirFormat cntType, List<String> profiles, List<ValidationMessage> messages) throws FHIRException, IOException, EOperationOutcome {
|
public OperationOutcome validate(byte[] source, FhirFormat cntType, List<String> profiles, List<ValidationMessage> messages) throws FHIRException, IOException, EOperationOutcome {
|
||||||
InstanceValidator validator = getValidator();
|
InstanceValidator validator = getValidator(cntType);
|
||||||
|
|
||||||
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
|
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
|
||||||
return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine);
|
return ValidatorUtils.messagesToOutcome(messages, context, fhirPathEngine);
|
||||||
|
@ -361,7 +362,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
if (doNative) {
|
if (doNative) {
|
||||||
SchemaValidator.validateSchema(location, cntType, messages);
|
SchemaValidator.validateSchema(location, cntType, messages);
|
||||||
}
|
}
|
||||||
InstanceValidator validator = getValidator();
|
InstanceValidator validator = getValidator(cntType);
|
||||||
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
|
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
|
||||||
if (showTimes) {
|
if (showTimes) {
|
||||||
System.out.println(location + ": " + validator.reportTimes());
|
System.out.println(location + ": " + validator.reportTimes());
|
||||||
|
@ -377,7 +378,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
if (doNative) {
|
if (doNative) {
|
||||||
SchemaValidator.validateSchema(location, cntType, messages);
|
SchemaValidator.validateSchema(location, cntType, messages);
|
||||||
}
|
}
|
||||||
InstanceValidator validator = getValidator();
|
InstanceValidator validator = getValidator(cntType);
|
||||||
validator.setResourceIdRule(resourceIdRule);
|
validator.setResourceIdRule(resourceIdRule);
|
||||||
validator.setBestPracticeWarningLevel(bpWarnings);
|
validator.setBestPracticeWarningLevel(bpWarnings);
|
||||||
validator.setCheckDisplay(displayOption);
|
validator.setCheckDisplay(displayOption);
|
||||||
|
@ -393,7 +394,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
public org.hl7.fhir.r5.elementmodel.Element transform(byte[] source, FhirFormat cntType, String mapUri) throws FHIRException, IOException {
|
public org.hl7.fhir.r5.elementmodel.Element transform(byte[] source, FhirFormat cntType, String mapUri) throws FHIRException, IOException {
|
||||||
List<Base> outputs = new ArrayList<>();
|
List<Base> outputs = new ArrayList<>();
|
||||||
StructureMapUtilities scu = new StructureMapUtilities(context, new TransformSupportServices(outputs, mapLog, context));
|
StructureMapUtilities scu = new StructureMapUtilities(context, new TransformSupportServices(outputs, mapLog, context));
|
||||||
org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(source), cntType);
|
org.hl7.fhir.r5.elementmodel.Element src = Manager.parseSingle(context, new ByteArrayInputStream(source), cntType);
|
||||||
StructureMap map = context.getTransform(mapUri);
|
StructureMap map = context.getTransform(mapUri);
|
||||||
if (map == null) throw new Error("Unable to find map " + mapUri + " (Known Maps = " + context.listMapUrls() + ")");
|
if (map == null) throw new Error("Unable to find map " + mapUri + " (Known Maps = " + context.listMapUrls() + ")");
|
||||||
org.hl7.fhir.r5.elementmodel.Element resource = getTargetResourceFromStructureMap(map);
|
org.hl7.fhir.r5.elementmodel.Element resource = getTargetResourceFromStructureMap(map);
|
||||||
|
@ -448,14 +449,14 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
|
|
||||||
public void convert(String source, String output) throws FHIRException, IOException {
|
public void convert(String source, String output) throws FHIRException, IOException {
|
||||||
Content cnt = igLoader.loadContent(source, "validate", false);
|
Content cnt = igLoader.loadContent(source, "validate", false);
|
||||||
Element e = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
|
Element e = Manager.parseSingle(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
|
||||||
Manager.compose(context, e, new FileOutputStream(output), (output.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML), OutputStyle.PRETTY, null);
|
Manager.compose(context, e, new FileOutputStream(output), (output.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML), OutputStyle.PRETTY, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String evaluateFhirPath(String source, String expression) throws FHIRException, IOException {
|
public String evaluateFhirPath(String source, String expression) throws FHIRException, IOException {
|
||||||
Content cnt = igLoader.loadContent(source, "validate", false);
|
Content cnt = igLoader.loadContent(source, "validate", false);
|
||||||
FHIRPathEngine fpe = this.getValidator().getFHIRPathEngine();
|
FHIRPathEngine fpe = this.getValidator(null).getFHIRPathEngine();
|
||||||
Element e = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
|
Element e = Manager.parseSingle(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
|
||||||
ExpressionNode exp = fpe.parse(expression);
|
ExpressionNode exp = fpe.parse(expression);
|
||||||
return fpe.evaluateToString(new ValidatorHostContext(context, e), e, e, e, exp);
|
return fpe.evaluateToString(new ValidatorHostContext(context, e), e, e, e, exp);
|
||||||
}
|
}
|
||||||
|
@ -490,7 +491,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
context.dropResource(type, id);
|
context.dropResource(type, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceValidator getValidator() {
|
public InstanceValidator getValidator(FhirFormat format) throws FHIRException, IOException {
|
||||||
InstanceValidator validator = new InstanceValidator(context, null, null);
|
InstanceValidator validator = new InstanceValidator(context, null, null);
|
||||||
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
|
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
|
||||||
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
|
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
|
||||||
|
@ -512,6 +513,10 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
validator.getBundleValidationRules().addAll(bundleValidationRules);
|
validator.getBundleValidationRules().addAll(bundleValidationRules);
|
||||||
validator.getValidationControl().putAll(validationControl);
|
validator.getValidationControl().putAll(validationControl);
|
||||||
validator.setQuestionnaireMode(questionnaireMode);
|
validator.setQuestionnaireMode(questionnaireMode);
|
||||||
|
if (format == FhirFormat.SHC) {
|
||||||
|
igLoader.loadIg(getIgs(), getBinaries(), SHCParser.CURRENT_PACKAGE, true);
|
||||||
|
}
|
||||||
|
|
||||||
return validator;
|
return validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,7 +622,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
||||||
|
|
||||||
public byte[] transformVersion(String source, String targetVer, FhirFormat format, Boolean canDoNative) throws FHIRException, IOException, Exception {
|
public byte[] transformVersion(String source, String targetVer, FhirFormat format, Boolean canDoNative) throws FHIRException, IOException, Exception {
|
||||||
Content cnt = igLoader.loadContent(source, "validate", false);
|
Content cnt = igLoader.loadContent(source, "validate", false);
|
||||||
org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
|
org.hl7.fhir.r5.elementmodel.Element src = Manager.parseSingle(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
|
||||||
|
|
||||||
// if the src has a url, we try to use the java code
|
// if the src has a url, we try to use the java code
|
||||||
if ((canDoNative == null && src.hasChild("url")) || (canDoNative != null && canDoNative)) {
|
if ((canDoNative == null && src.hasChild("url")) || (canDoNative != null && canDoNative)) {
|
||||||
|
|
|
@ -235,7 +235,7 @@ public class ValidatorCli {
|
||||||
}
|
}
|
||||||
System.out.println("Validating");
|
System.out.println("Validating");
|
||||||
if (cliContext.getMode() == EngineMode.SCAN) {
|
if (cliContext.getMode() == EngineMode.SCAN) {
|
||||||
Scanner validationScanner = new Scanner(validator.getContext(), validator.getValidator(), validator.getIgLoader(), validator.getFhirPathEngine());
|
Scanner validationScanner = new Scanner(validator.getContext(), validator.getValidator(null), validator.getIgLoader(), validator.getFhirPathEngine());
|
||||||
validationScanner.validateScan(cliContext.getOutput(), cliContext.getSources());
|
validationScanner.validateScan(cliContext.getOutput(), cliContext.getSources());
|
||||||
} else {
|
} else {
|
||||||
validationService.validateSources(cliContext, validator);
|
validationService.validateSources(cliContext, validator);
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.hl7.fhir.r5.elementmodel.Manager;
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||||
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
|
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
|
||||||
import org.hl7.fhir.r5.elementmodel.ParserBase;
|
import org.hl7.fhir.r5.elementmodel.ParserBase;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
|
||||||
import org.hl7.fhir.r5.elementmodel.ParserBase.ValidationPolicy;
|
import org.hl7.fhir.r5.elementmodel.ParserBase.ValidationPolicy;
|
||||||
import org.hl7.fhir.r5.elementmodel.XmlParser;
|
import org.hl7.fhir.r5.elementmodel.XmlParser;
|
||||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||||
|
@ -296,13 +297,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
try {
|
try {
|
||||||
Element e = new ObjectConverter(context).convert((Resource) item);
|
Element e = new ObjectConverter(context).convert((Resource) item);
|
||||||
setParents(e);
|
setParents(e);
|
||||||
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, e, validationLanguage));
|
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
throw new FHIRException(e1);
|
throw new FHIRException(e1);
|
||||||
}
|
}
|
||||||
} else if (item instanceof Element) {
|
} else if (item instanceof Element) {
|
||||||
Element e = (Element) item;
|
Element e = (Element) item;
|
||||||
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, e, validationLanguage));
|
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
|
||||||
} else
|
} else
|
||||||
throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT));
|
throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT));
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
|
@ -566,16 +567,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
((XmlParser) parser).setAllowXsiLocation(allowXsiLocation);
|
((XmlParser) parser).setAllowXsiLocation(allowXsiLocation);
|
||||||
parser.setupValidation(ValidationPolicy.EVERYTHING, errors);
|
parser.setupValidation(ValidationPolicy.EVERYTHING, errors);
|
||||||
long t = System.nanoTime();
|
long t = System.nanoTime();
|
||||||
Element e;
|
List<NamedElement> list = null;
|
||||||
try {
|
try {
|
||||||
e = parser.parse(stream);
|
list = parser.parse(stream);
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
throw new FHIRException(e1);
|
throw new FHIRException(e1);
|
||||||
}
|
}
|
||||||
timeTracker.load(t);
|
timeTracker.load(t);
|
||||||
if (e != null)
|
if (list != null && !list.isEmpty()) {
|
||||||
validate(appContext, errors, e, profiles);
|
String url = parser.getImpliedProfile();
|
||||||
return e;
|
if (url != null) {
|
||||||
|
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
|
||||||
|
if (sd == null) {
|
||||||
|
rule(errors, IssueType.NOTFOUND, "Payload", false, "Implied profile "+url+" not known to validator");
|
||||||
|
} else {
|
||||||
|
profiles.add(sd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (NamedElement ne : list) {
|
||||||
|
validate(appContext, errors, ne.getName(), ne.getElement(), profiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (list == null || list.isEmpty()) ? null : list.get(0).getElement(); // todo: this is broken, but fixing it really complicates things elsewhere, so we do this for now
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -602,7 +615,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
throw new FHIRException(e1);
|
throw new FHIRException(e1);
|
||||||
}
|
}
|
||||||
timeTracker.load(t);
|
timeTracker.load(t);
|
||||||
validate(appContext, errors, e, profiles);
|
validate(appContext, errors, null, e, profiles);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +646,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
timeTracker.load(t);
|
timeTracker.load(t);
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
validate(appContext, errors, e, profiles);
|
validate(appContext, errors, null, e, profiles);
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
@ -665,7 +678,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
timeTracker.load(t);
|
timeTracker.load(t);
|
||||||
if (e != null)
|
if (e != null)
|
||||||
validate(appContext, errors, e, profiles);
|
validate(appContext, errors, null, e, profiles);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,26 +704,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
Element e = parser.parse(object);
|
Element e = parser.parse(object);
|
||||||
timeTracker.load(t);
|
timeTracker.load(t);
|
||||||
if (e != null)
|
if (e != null)
|
||||||
validate(appContext, errors, e, profiles);
|
validate(appContext, errors, null, e, profiles);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate(Object appContext, List<ValidationMessage> errors, Element element) throws FHIRException {
|
public void validate(Object appContext, List<ValidationMessage> errors, String initialPath, Element element) throws FHIRException {
|
||||||
validate(appContext, errors, element, new ArrayList<>());
|
validate(appContext, errors, initialPath, element, new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate(Object appContext, List<ValidationMessage> errors, Element element, String profile) throws FHIRException {
|
public void validate(Object appContext, List<ValidationMessage> errors, String initialPath, Element element, String profile) throws FHIRException {
|
||||||
ArrayList<StructureDefinition> profiles = new ArrayList<>();
|
ArrayList<StructureDefinition> profiles = new ArrayList<>();
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
profiles.add(getSpecifiedProfile(profile));
|
profiles.add(getSpecifiedProfile(profile));
|
||||||
}
|
}
|
||||||
validate(appContext, errors, element, profiles);
|
validate(appContext, errors, initialPath, element, profiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate(Object appContext, List<ValidationMessage> errors, Element element, List<StructureDefinition> profiles) throws FHIRException {
|
public void validate(Object appContext, List<ValidationMessage> errors, String path, Element element, List<StructureDefinition> profiles) throws FHIRException {
|
||||||
// this is the main entry point; all the other public entry points end up here coming here...
|
// this is the main entry point; all the other public entry points end up here coming here...
|
||||||
// so the first thing to do is to clear the internal state
|
// so the first thing to do is to clear the internal state
|
||||||
fetchCache.clear();
|
fetchCache.clear();
|
||||||
|
@ -724,14 +737,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
|
|
||||||
long t = System.nanoTime();
|
long t = System.nanoTime();
|
||||||
if (profiles == null || profiles.isEmpty()) {
|
if (profiles == null || profiles.isEmpty()) {
|
||||||
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(context, element, validationLanguage).resetIds());
|
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(context, path, element, validationLanguage).resetIds());
|
||||||
} else {
|
} else {
|
||||||
for (StructureDefinition defn : profiles) {
|
for (StructureDefinition defn : profiles) {
|
||||||
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(context, element, validationLanguage).resetIds());
|
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(context, path, element, validationLanguage).resetIds());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hintAboutNonMustSupport) {
|
if (hintAboutNonMustSupport) {
|
||||||
checkElementUsage(errors, element, new NodeStack(context, element, validationLanguage));
|
checkElementUsage(errors, element, new NodeStack(context, path, element, validationLanguage));
|
||||||
}
|
}
|
||||||
errors.removeAll(messagesToRemove);
|
errors.removeAll(messagesToRemove);
|
||||||
timeTracker.overall(t);
|
timeTracker.overall(t);
|
||||||
|
@ -3387,7 +3400,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
rr.setResource(res.getMatch());
|
rr.setResource(res.getMatch());
|
||||||
rr.setFocus(res.getMatch());
|
rr.setFocus(res.getMatch());
|
||||||
rr.setExternal(false);
|
rr.setExternal(false);
|
||||||
rr.setStack(new NodeStack(context, hostContext, validationLanguage).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(),
|
rr.setStack(new NodeStack(context, null, hostContext, validationLanguage).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(),
|
||||||
res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1,
|
res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1,
|
||||||
res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition()));
|
res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition()));
|
||||||
rr.getStack().qualifyPath(".ofType("+rr.getResource().fhirType()+")");
|
rr.getStack().qualifyPath(".ofType("+rr.getResource().fhirType()+")");
|
||||||
|
@ -4873,6 +4886,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int last = -1;
|
int last = -1;
|
||||||
|
ElementInfo lastei = null;
|
||||||
int lastSlice = -1;
|
int lastSlice = -1;
|
||||||
for (ElementInfo ei : children) {
|
for (ElementInfo ei : children) {
|
||||||
String sliceInfo = "";
|
String sliceInfo = "";
|
||||||
|
@ -4911,13 +4925,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
|
|
||||||
if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) {
|
if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) {
|
||||||
boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr;
|
boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr;
|
||||||
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok, I18nConstants.VALIDATION_VAL_PROFILE_OUTOFORDER, profile.getUrl(), ei.getName());
|
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok, I18nConstants.VALIDATION_VAL_PROFILE_OUTOFORDER, profile.getUrl(), ei.getName(), lastei == null ? "(null)" : lastei.getName());
|
||||||
}
|
}
|
||||||
if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) {
|
if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) {
|
||||||
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr, I18nConstants.VALIDATION_VAL_PROFILE_SLICEORDER, profile.getUrl(), ei.getName());
|
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr, I18nConstants.VALIDATION_VAL_PROFILE_SLICEORDER, profile.getUrl(), ei.getName());
|
||||||
}
|
}
|
||||||
if (ei.definition == null || !isXmlAttr) {
|
if (ei.definition == null || !isXmlAttr) {
|
||||||
last = ei.index;
|
last = ei.index;
|
||||||
|
lastei = ei;
|
||||||
}
|
}
|
||||||
if (ei.slice != null) {
|
if (ei.slice != null) {
|
||||||
lastSlice = ei.sliceindex;
|
lastSlice = ei.sliceindex;
|
||||||
|
|
|
@ -30,11 +30,11 @@ public class NodeStack {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeStack(IWorkerContext context, Element element, String validationLanguage) {
|
public NodeStack(IWorkerContext context, String initialPath, Element element, String validationLanguage) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
ids = new HashMap<>();
|
ids = new HashMap<>();
|
||||||
this.element = element;
|
this.element = element;
|
||||||
literalPath = element.getPath();
|
literalPath = (initialPath == null ? "" : initialPath+".") + element.getPath();
|
||||||
workingLang = validationLanguage;
|
workingLang = validationLanguage;
|
||||||
if (!element.getName().equals(element.fhirType())) {
|
if (!element.getName().equals(element.fhirType())) {
|
||||||
logicalPaths = new ArrayList<>();
|
logicalPaths = new ArrayList<>();
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package org.hl7.fhir.validation.tests;
|
package org.hl7.fhir.validation.tests;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -31,6 +33,7 @@ import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||||
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
|
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.SHCParser;
|
||||||
import org.hl7.fhir.r5.formats.JsonParser;
|
import org.hl7.fhir.r5.formats.JsonParser;
|
||||||
import org.hl7.fhir.r5.formats.XmlParser;
|
import org.hl7.fhir.r5.formats.XmlParser;
|
||||||
import org.hl7.fhir.r5.model.Base;
|
import org.hl7.fhir.r5.model.Base;
|
||||||
|
@ -162,8 +165,11 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
if (content.has("use-test") && !content.get("use-test").getAsBoolean())
|
if (content.has("use-test") && !content.get("use-test").getAsBoolean())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
String testCaseContent = TestingUtilities.loadTestResource("validator", JSONUtil.str(content, "file"));
|
byte[] testCaseContent = TestingUtilities.loadTestResource("validator", JSONUtil.str(content, "file")).getBytes(StandardCharsets.UTF_8);
|
||||||
InstanceValidator val = vCurr.getValidator();
|
// load and process content
|
||||||
|
FhirFormat fmt = determineFormat(content, testCaseContent);
|
||||||
|
|
||||||
|
InstanceValidator val = vCurr.getValidator(fmt);
|
||||||
val.setWantCheckSnapshotUnchanged(true);
|
val.setWantCheckSnapshotUnchanged(true);
|
||||||
val.getContext().setClientRetryCount(4);
|
val.getContext().setClientRetryCount(4);
|
||||||
val.setDebug(false);
|
val.setDebug(false);
|
||||||
|
@ -236,13 +242,11 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
if (content.has("security-checks")) {
|
if (content.has("security-checks")) {
|
||||||
val.setSecurityChecks(content.get("security-checks").getAsBoolean());
|
val.setSecurityChecks(content.get("security-checks").getAsBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.has("logical")==false) {
|
if (content.has("logical")==false) {
|
||||||
val.setAssumeValidRestReferences(content.has("assumeValidRestReferences") ? content.get("assumeValidRestReferences").getAsBoolean() : false);
|
val.setAssumeValidRestReferences(content.has("assumeValidRestReferences") ? content.get("assumeValidRestReferences").getAsBoolean() : false);
|
||||||
System.out.println(String.format("Start Validating (%d to set up)", (System.nanoTime() - setup) / 1000000));
|
System.out.println(String.format("Start Validating (%d to set up)", (System.nanoTime() - setup) / 1000000));
|
||||||
if (JSONUtil.str(content, "file").endsWith(".json"))
|
val.validate(null, errors, new ByteArrayInputStream(testCaseContent), fmt);
|
||||||
val.validate(null, errors, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON);
|
|
||||||
else
|
|
||||||
val.validate(null, errors, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML);
|
|
||||||
System.out.println(val.reportTimes());
|
System.out.println(val.reportTimes());
|
||||||
checkOutcomes(errors, content, null, name);
|
checkOutcomes(errors, content, null, name);
|
||||||
}
|
}
|
||||||
|
@ -281,10 +285,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
}
|
}
|
||||||
val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false);
|
val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false);
|
||||||
List<ValidationMessage> errorsProfile = new ArrayList<ValidationMessage>();
|
List<ValidationMessage> errorsProfile = new ArrayList<ValidationMessage>();
|
||||||
if (JSONUtil.str(content, "file").endsWith(".json"))
|
val.validate(null, errorsProfile, new ByteArrayInputStream(testCaseContent), fmt, asSdList(sd));
|
||||||
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, asSdList(sd));
|
|
||||||
else
|
|
||||||
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML, asSdList(sd));
|
|
||||||
System.out.println(val.reportTimes());
|
System.out.println(val.reportTimes());
|
||||||
checkOutcomes(errorsProfile, profile, filename, name);
|
checkOutcomes(errorsProfile, profile, filename, name);
|
||||||
}
|
}
|
||||||
|
@ -309,7 +310,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<ValidationMessage> errorsLogical = new ArrayList<ValidationMessage>();
|
List<ValidationMessage> errorsLogical = new ArrayList<ValidationMessage>();
|
||||||
Element le = val.validate(null, errorsLogical, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), (name.endsWith(".json")) ? FhirFormat.JSON : FhirFormat.XML);
|
Element le = val.validate(null, errorsLogical, new ByteArrayInputStream(testCaseContent), fmt);
|
||||||
if (logical.has("expressions")) {
|
if (logical.has("expressions")) {
|
||||||
FHIRPathEngine fp = new FHIRPathEngine(val.getContext());
|
FHIRPathEngine fp = new FHIRPathEngine(val.getContext());
|
||||||
for (JsonElement e : logical.getAsJsonArray("expressions")) {
|
for (JsonElement e : logical.getAsJsonArray("expressions")) {
|
||||||
|
@ -321,6 +322,11 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FhirFormat determineFormat(JsonObject config, byte[] cnt) throws IOException {
|
||||||
|
String name = JSONUtil.str(config, "file");
|
||||||
|
return org.hl7.fhir.validation.ResourceChecker.checkIsResource(vCurr.getContext(), true, cnt, name, !JSONUtil.bool(config, "guess-format"));
|
||||||
|
}
|
||||||
|
|
||||||
private List<StructureDefinition> asSdList(StructureDefinition sd) {
|
private List<StructureDefinition> asSdList(StructureDefinition sd) {
|
||||||
List<StructureDefinition> res = new ArrayList<StructureDefinition>();
|
List<StructureDefinition> res = new ArrayList<StructureDefinition>();
|
||||||
res.add(sd);
|
res.add(sd);
|
||||||
|
@ -482,16 +488,16 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
if (url.equals("Patient/test")) {
|
if (url.equals("Patient/test")) {
|
||||||
res = new ObjectConverter(TestingUtilities.context(version)).convert(new Patient());
|
res = new ObjectConverter(TestingUtilities.context(version)).convert(new Patient());
|
||||||
} else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase() + ".json")) {
|
} else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase() + ".json")) {
|
||||||
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase() + ".json"));
|
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.JSON).parseSingle(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase() + ".json"));
|
||||||
} else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase() + ".xml")) {
|
} else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase() + ".xml")) {
|
||||||
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase() + ".xml"));
|
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.XML).parseSingle(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase() + ".xml"));
|
||||||
}
|
}
|
||||||
if (res == null && url.contains("/")) {
|
if (res == null && url.contains("/")) {
|
||||||
String tail = url.substring(url.indexOf("/") + 1);
|
String tail = url.substring(url.indexOf("/") + 1);
|
||||||
if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase() + ".json")) {
|
if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase() + ".json")) {
|
||||||
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase() + ".json"));
|
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.JSON).parseSingle(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase() + ".json"));
|
||||||
} else if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase() + ".xml")) {
|
} else if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase() + ".xml")) {
|
||||||
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase() + ".xml"));
|
res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.XML).parseSingle(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase() + ".xml"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
Loading…
Reference in New Issue