Merge branch 'hapifhir:master' into master

This commit is contained in:
Bryn Rhodes 2021-12-16 14:09:30 -07:00 committed by GitHub
commit 34d8dfa24d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
151 changed files with 3854 additions and 1624 deletions

28
.github/workflows/bidi-checker.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: BIDI CHECK
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
bidi_checker_job:
runs-on: ubuntu-latest
name: Check for bidi unicode characters in repo
steps:
# Checkout the repo code. IMPORTANT, this step is needed to populate the directory defined by GITHUB_WORKSPACE
- name: Checkout repo
uses: actions/checkout@v1
id: checkout
# Run the check for bidi characters.
- name: Check for bidi characters
id: bidi_check
uses: HL7/bidi-checker-action@v1.5
- name: Get the output time
run: echo "The time was ${{ steps.bidi_check.outputs.time }}"

View File

@ -4,7 +4,7 @@
| :---: |
| [![Build Status][Badge-BuildPipeline]][Link-BuildPipeline] |
This is the core object handling code, with utilities (including validator), for the FHIR specification.
This is the java core object handling code, with utilities (including validator), for the FHIR specification.
included in this repo:
* org.fhir.fhir.utilities: Shared code used by all the other projects - including the internationalization code
@ -17,6 +17,11 @@ included in this repo:
* org.fhir.fhir.validation: The FHIR Java validator (note: based on R5 internally, but validates all the above versions)
* org.fhir.fhir.validation.cli: Holder project for releasing the FHIR validator as as single fat jar (will be removed in the future)
This code is used in all HAPI servers and clients, and also is the HL7 maintained
FHIR Validator. In addition, this is the core code for the HL7 maintained IG publisher
and FHIR main build publisher. As such, this code is considered an authoritatively
correct implementation of the core FHIR specification that it implements.
### CI/CD
All integration and delivery done on Azure pipelines. Azure project can be viewed [here][Link-AzureProject].

View File

@ -1,2 +0,0 @@
Code changes:
* remove erroneous logging to c:\temp

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.5.12-SNAPSHOT</version>
<version>5.6.16-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -89,7 +89,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<version>${lombok_version}</version>
<scope>provided</scope>
</dependency>

View File

@ -613,10 +613,10 @@ public class ElementDefinition30_40 {
Type t = ConversionContext30_40.INSTANCE.getVersionConvertor_30_40().convertType(src.getValueSet());
if (t instanceof org.hl7.fhir.r4.model.Reference) {
tgt.setValueSet(((org.hl7.fhir.r4.model.Reference) t).getReference());
tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new UriType("Reference"));
tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new org.hl7.fhir.r4.model.UrlType("Reference"));
} else {
tgt.setValueSet(t.primitiveValue());
tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new UriType("uri"));
tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new org.hl7.fhir.r4.model.UrlType("uri"));
}
tgt.setValueSet(VersionConvertorConstants.refToVS(tgt.getValueSet()));
}

View File

@ -625,10 +625,10 @@ public class ElementDefinition30_50 {
DataType t = ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().convertType(src.getValueSet());
if (t instanceof org.hl7.fhir.r5.model.Reference) {
tgt.setValueSet(((org.hl7.fhir.r5.model.Reference) t).getReference());
tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new UriType("Reference"));
tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new org.hl7.fhir.r5.model.UrlType("Reference"));
} else {
tgt.setValueSet(t.primitiveValue());
tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new UriType("uri"));
tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new org.hl7.fhir.r5.model.UrlType("uri"));
}
tgt.setValueSet(VersionConvertorConstants.refToVS(tgt.getValueSet()));
}

View File

@ -54,7 +54,7 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader {
if (VersionUtilities.isR5Ver(npm.fhirVersion())) {
return new R5ToR5Loader(types, lkp.forNewPackage(npm));
} else if (VersionUtilities.isR4Ver(npm.fhirVersion())) {
return new R4ToR5Loader(types, lkp.forNewPackage(npm));
return new R4ToR5Loader(types, lkp.forNewPackage(npm), npm.version());
} else if (VersionUtilities.isR3Ver(npm.fhirVersion())) {
return new R3ToR5Loader(types, lkp.forNewPackage(npm));
} else if (VersionUtilities.isR2Ver(npm.fhirVersion())) {

View File

@ -36,12 +36,14 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r5.conformance.StructureDefinitionHacker;
import org.hl7.fhir.r5.context.IWorkerContext.IContextResourceLoader;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Bundle.BundleType;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.utilities.VersionUtilities;
import java.io.IOException;
import java.io.InputStream;
@ -52,9 +54,11 @@ import java.util.UUID;
public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader {
private final BaseAdvisor_40_50 advisor = new BaseAdvisor_40_50();
private String version;
public R4ToR5Loader(String[] types, ILoaderKnowledgeProviderR5 lkp) {
public R4ToR5Loader(String[] types, ILoaderKnowledgeProviderR5 lkp, String version) { // might be 4B
super(types, lkp);
this.version = version;
}
@Override
@ -123,6 +127,9 @@ public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
if (killPrimitives) {
throw new FHIRException("Cannot kill primitives when using deferred loading");
}
if (r5 instanceof StructureDefinition && VersionUtilities.isR4BVer(version)) {
r5 = new StructureDefinitionHacker(version).fixSD((StructureDefinition) r5);
}
if (patchUrls) {
if (r5 instanceof StructureDefinition) {
StructureDefinition sd = (StructureDefinition) r5;

View File

@ -80,7 +80,7 @@ public class DicomPackageBuilder {
vs.setId(vs.getId().substring(0, 64));
}
if (ids.contains(vs.getId())) {
throw new Error("Duplicate Id once Ids cut off at 64 char: "+vs.getId());
throw new Error("Duplicate Id (note Ids cut off at 64 char): "+vs.getId());
}
ids.add(vs.getId());
gen.addFile(Category.RESOURCE, "ValueSet-"+vs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(vs));
@ -113,7 +113,7 @@ public class DicomPackageBuilder {
private JsonObject buildPackage() {
JsonObject npm = new JsonObject();
npm.addProperty("tools-version", 3);
npm.addProperty("type", "fhir.ig");
npm.addProperty("type", "Conformance");
npm.addProperty("license", "free");
npm.addProperty("author", "FHIR Project for DICOM");
npm.addProperty("name", "fhir.dicom");

View File

@ -0,0 +1,387 @@
package org.hl7.fhir.convertors.misc;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.naming.ldap.StartTlsRequest;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.JsonTrackingParser;
import org.xml.sax.SAXException;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class XVerPackegeFixer {
private static final String R5_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r5.core\\package";
private static final String R4_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r4.core\\package";
private static final String R3_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r3.core\\package";
private static final String R2B_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2b.core\\package";
private static final String R2_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2.core\\package";
private static int mod;
private static Map<String, org.hl7.fhir.r5.model.StructureDefinition> map5 = new HashMap<>();
private static Map<String, org.hl7.fhir.r4.model.StructureDefinition> map4 = new HashMap<>();
private static Map<String, org.hl7.fhir.dstu3.model.StructureDefinition> map3 = new HashMap<>();
private static Map<String, org.hl7.fhir.dstu2.model.StructureDefinition> map2 = new HashMap<>();
private static Map<String, org.hl7.fhir.dstu2016may.model.StructureDefinition> map2b = new HashMap<>();
public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
mod = 0;
for (File f : new File(args[0]).listFiles()) {
if (f.getName().startsWith("xver-")) {
JsonObject j = JsonTrackingParser.parseJson(f);
fixUp(j, f.getName());
JsonTrackingParser.write(j, f, true);
}
}
System.out.println("all done: "+mod+" modifiers");
}
private static void fixUp(JsonObject j, String name) throws FHIRFormatError, FileNotFoundException, IOException {
name = name.replace(".json", "");
System.out.println("Process "+name);
String version = name.substring(name.lastIndexOf("-")+1);
int i = 0;
for (Entry<String, JsonElement> e : j.entrySet()) {
if (i == 50) {
i = 0;
System.out.print(".");
}
i++;
String n = e.getKey();
JsonObject o = ((JsonObject) e.getValue());
boolean ok = (o.has("types") && o.getAsJsonArray("types").size() > 0) || (o.has("elements") && o.getAsJsonArray("elements").size() > 0);
if (!ok) {
List<String> types = new ArrayList<>();
List<String> elements = new ArrayList<>();
getElementInfo(version, n, types, elements);
if (elements.size() > 0) {
JsonArray arr = o.getAsJsonArray("elements");
if (arr == null) {
arr = new JsonArray();
o.add("elements", arr);
}
for (String s : types) {
arr.add(s);
}
} else if (types.size() > 0) {
JsonArray arr = o.getAsJsonArray("types");
if (arr == null) {
arr = new JsonArray();
o.add("types", arr);
}
for (String s : types) {
arr.add(s);
}
}
}
}
System.out.println("done");
}
private static boolean getElementInfo(String version, String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
if ("contained".equals(n.substring(n.indexOf(".")+1))) {
return false;
}
switch (version) {
case "4.6": return getElementInfoR5(n, types, elements);
case "4.0": return getElementInfoR4(n, types, elements);
case "3.0": return getElementInfoR3(n, types, elements);
case "1.4": return getElementInfoR2B(n, types, elements);
case "1.0": return getElementInfoR2(n, types, elements);
}
return false;
}
private static Object tail(String value) {
return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value;
}
private static boolean getElementInfoR5(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.r5.model.StructureDefinition sd = null;
if (map5.containsKey(tn)) {
sd = map5.get(tn);
} else {
sd = (org.hl7.fhir.r5.model.StructureDefinition) new org.hl7.fhir.r5.formats.JsonParser().parse(new FileInputStream(Utilities.path(R5_FOLDER, "StructureDefinition-"+tn+".json")));
map5.put(tn, sd);
}
for (org.hl7.fhir.r5.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.r5.model.ElementDefinition> children = listChildrenR5(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.r5.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasTargetProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getWorkingCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.r5.model.CanonicalType u : t.getTargetProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getWorkingCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.r5.model.ElementDefinition> listChildrenR5(List<org.hl7.fhir.r5.model.ElementDefinition> list, org.hl7.fhir.r5.model.ElementDefinition ed) {
List<org.hl7.fhir.r5.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.r5.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR4(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.r4.model.StructureDefinition sd = null;
if (map4.containsKey(tn)) {
sd = map4.get(tn);
} else {
sd = (org.hl7.fhir.r4.model.StructureDefinition) new org.hl7.fhir.r4.formats.JsonParser().parse(new FileInputStream(Utilities.path(R4_FOLDER, "StructureDefinition-"+tn+".json")));
map4.put(tn, sd);
}
for (org.hl7.fhir.r4.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.r4.model.ElementDefinition> children = listChildrenR4(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.r4.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasTargetProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getWorkingCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.r4.model.CanonicalType u : t.getTargetProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getWorkingCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.r4.model.ElementDefinition> listChildrenR4(List<org.hl7.fhir.r4.model.ElementDefinition> list, org.hl7.fhir.r4.model.ElementDefinition ed) {
List<org.hl7.fhir.r4.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.r4.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR3(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.dstu3.model.StructureDefinition sd = null;
if (map3.containsKey(tn)) {
sd = map3.get(tn);
} else {
sd = (org.hl7.fhir.dstu3.model.StructureDefinition) new org.hl7.fhir.dstu3.formats.JsonParser().parse(new FileInputStream(Utilities.path(R3_FOLDER, "StructureDefinition-"+tn+".json")));
map3.put(tn, sd);
}
for (org.hl7.fhir.dstu3.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.dstu3.model.ElementDefinition> children = listChildrenR3(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.dstu3.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasTargetProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getCode());
b.append("(");
b.append(tail(t.getTargetProfile()));
b.append(")");
types.add(b.toString());
} else {
types.add(t.getCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.dstu3.model.ElementDefinition> listChildrenR3(List<org.hl7.fhir.dstu3.model.ElementDefinition> list, org.hl7.fhir.dstu3.model.ElementDefinition ed) {
List<org.hl7.fhir.dstu3.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.dstu3.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR2(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.dstu2.model.StructureDefinition sd = null;
if (map2.containsKey(tn)) {
sd = map2.get(tn);
} else {
sd = (org.hl7.fhir.dstu2.model.StructureDefinition) new org.hl7.fhir.dstu2.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2_FOLDER, "StructureDefinition-"+tn+".json")));
map2.put(tn, sd);
}
for (org.hl7.fhir.dstu2.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.dstu2.model.ElementDefinition> children = listChildrenR2(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.dstu2.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.dstu2.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.dstu2.model.ElementDefinition> listChildrenR2(List<org.hl7.fhir.dstu2.model.ElementDefinition> list, org.hl7.fhir.dstu2.model.ElementDefinition ed) {
List<org.hl7.fhir.dstu2.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.dstu2.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR2B(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.dstu2016may.model.StructureDefinition sd = null;
if (map2b.containsKey(tn)) {
sd = map2b.get(tn);
} else {
sd = (org.hl7.fhir.dstu2016may.model.StructureDefinition) new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2B_FOLDER, "StructureDefinition-"+tn+".json")));
map2b.put(tn, sd);
}
for (org.hl7.fhir.dstu2016may.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.dstu2016may.model.ElementDefinition> children = listChildrenR2B(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.dstu2016may.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.dstu2016may.model.UriType u : t.getProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.dstu2016may.model.ElementDefinition> listChildrenR2B(List<org.hl7.fhir.dstu2016may.model.ElementDefinition> list, org.hl7.fhir.dstu2016may.model.ElementDefinition ed) {
List<org.hl7.fhir.dstu2016may.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.dstu2016may.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.5.12-SNAPSHOT</version>
<version>5.6.16-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -46,6 +46,7 @@ import org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu2.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.dstu2.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;

View File

@ -59,6 +59,7 @@ import org.hl7.fhir.dstu2.model.ValueSet;
import org.hl7.fhir.dstu2.terminologies.ValueSetExpansionCache;
import org.hl7.fhir.dstu2.utils.ProfileUtilities.ProfileKnowledgeProvider;
import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient;
import org.hl7.fhir.dstu2.utils.validation.IResourceValidator;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.CSFileInputStream;

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.dstu2.utils;
package org.hl7.fhir.dstu2.utils.validation;
/*
Copyright (c) 2011+, HL7, Inc.
@ -34,6 +34,9 @@ package org.hl7.fhir.dstu2.utils;
import java.util.List;
import org.hl7.fhir.dstu2.model.StructureDefinition;
import org.hl7.fhir.dstu2.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.dstu2.utils.validation.constants.CheckDisplayOption;
import org.hl7.fhir.dstu2.utils.validation.constants.IdStatus;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@ -42,29 +45,6 @@ import com.google.gson.JsonObject;
public interface IResourceValidator {
/**
* whether the validator should enforce best practice guidelines
* as defined by various HL7 committees
*
*
* @author Grahame Grieve
*
*/
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}
/**
* how much to check displays for coded elements
* @return
@ -77,10 +57,6 @@ public interface IResourceValidator {
*/
void setCheckDisplay(CheckDisplayOption checkDisplay);
enum IdStatus {
OPTIONAL, REQUIRED, PROHIBITED
}
/**
* whether the resource must have an id or not (depends on context)
*
@ -93,39 +69,26 @@ public interface IResourceValidator {
BestPracticeWarningLevel getBasePracticeWarningLevel();
void setBestPracticeWarningLevel(BestPracticeWarningLevel value);
/**
* Given a DOM element, return a list of errors in the resource
*
* @param errors
* @param elem
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, Element element) throws Exception;
/**
* Given a JSON Object, return a list of errors in the resource
*
* @param errors
* @param elem
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, JsonObject object) throws Exception;
/**
* Given a DOM element, return a list of errors in the resource
*
* @param errors
* @param elem
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
List<ValidationMessage> validate(Element element) throws Exception;
/**
* Given a DOM element, return a list of errors in the resource
*
* @param errors
* @param elem
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
List<ValidationMessage> validate(JsonObject object) throws Exception;
@ -133,10 +96,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile (by logical identifier)
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, Element element, String profile) throws Exception;
@ -144,10 +103,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile (by logical identifier)
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid)
*/
List<ValidationMessage> validate(Element element, String profile) throws Exception;
@ -155,10 +110,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile (by logical identifier)
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid)
*/
List<ValidationMessage> validate(JsonObject object, StructureDefinition profile) throws Exception;
@ -166,10 +117,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile (by logical identifier)
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid)
*/
List<ValidationMessage> validate(JsonObject object, String profile) throws Exception;
@ -177,10 +124,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, Element element, StructureDefinition profile) throws Exception;
@ -188,10 +131,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, JsonObject object, StructureDefinition profile) throws Exception;
@ -199,10 +138,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, JsonObject object, String profile) throws Exception;
@ -210,10 +145,6 @@ public interface IResourceValidator {
/**
* Given a DOM element, return a list of errors in the resource
* with regard to the specified profile
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
List<ValidationMessage> validate(Element element, StructureDefinition profile) throws Exception;
@ -221,18 +152,12 @@ public interface IResourceValidator {
/**
* Given a DOM document, return a list of errors in the resource
*
* @param errors
* @param elem
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, Document document) throws Exception;
/**
* Given a DOM document, return a list of errors in the resource
*
* @param errors
* @param elem
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
List<ValidationMessage> validate(Document document) throws Exception;
@ -240,10 +165,6 @@ public interface IResourceValidator {
/**
* Given a DOM document, return a list of errors in the resource
* with regard to the specified profile (by logical identifier)
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, Document document, String profile) throws Exception;
@ -251,10 +172,6 @@ public interface IResourceValidator {
/**
* Given a DOM document, return a list of errors in the resource
* with regard to the specified profile (by logical identifier)
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid)
*/
List<ValidationMessage> validate(Document document, String profile) throws Exception;
@ -262,10 +179,6 @@ public interface IResourceValidator {
/**
* Given a DOM document, return a list of errors in the resource
* with regard to the specified profile
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
void validate(List<ValidationMessage> errors, Document document, StructureDefinition profile) throws Exception;
@ -273,10 +186,6 @@ public interface IResourceValidator {
/**
* Given a DOM document, return a list of errors in the resource
* with regard to the specified profile
*
* @param errors
* @param element
* @param profile
* @- if the underlying infrastructure fails (not if the resource is invalid)
*/
List<ValidationMessage> validate(Document document, StructureDefinition profile) throws Exception;

View File

@ -0,0 +1,12 @@
package org.hl7.fhir.dstu2.utils.validation.constants;
/**
* whether the validator should enforce best practice guidelines
* as defined by various HL7 committees
*/
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}

View File

@ -0,0 +1,9 @@
package org.hl7.fhir.dstu2.utils.validation.constants;
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}

View File

@ -0,0 +1,7 @@
package org.hl7.fhir.dstu2.utils.validation.constants;
public enum IdStatus {
OPTIONAL,
REQUIRED,
PROHIBITED
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.5.12-SNAPSHOT</version>
<version>5.6.16-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.5.12-SNAPSHOT</version>
<version>5.6.16-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -52,7 +52,7 @@ import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.dstu3.utils.IResourceValidator;
import org.hl7.fhir.dstu3.utils.validation.IResourceValidator;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;

View File

@ -80,7 +80,7 @@ import org.hl7.fhir.dstu3.model.StructureMap.StructureMapStructureComponent;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpansionCache;
import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.dstu3.utils.IResourceValidator;
import org.hl7.fhir.dstu3.utils.validation.IResourceValidator;
import org.hl7.fhir.dstu3.utils.NarrativeGenerator;
import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient;
import org.hl7.fhir.exceptions.DefinitionException;

View File

@ -206,14 +206,19 @@ public class FhirRequestBuilder {
public <T extends Resource> ResourceRequest<T> execute() throws IOException {
formatHeaders(httpRequest, resourceFormat, headers);
Response response = getHttpClient().newCall(httpRequest.build()).execute();
final Request request = httpRequest.build();
log(request.method(), request.url().toString(), request.headers(), request.body() != null ? request.body().toString().getBytes() : null);
Response response = getHttpClient().newCall(request).execute();
T resource = unmarshalReference(response, resourceFormat);
return new ResourceRequest<T>(resource, response.code(), getLocationHeader(response.headers()));
}
public Bundle executeAsBatch() throws IOException {
formatHeaders(httpRequest, resourceFormat, null);
Response response = getHttpClient().newCall(httpRequest.build()).execute();
final Request request = httpRequest.build();
log(request.method(), request.url().toString(), request.headers(), request.body() != null ? request.body().toString().getBytes() : null);
Response response = getHttpClient().newCall(request).execute();
return unmarshalFeed(response, resourceFormat);
}
@ -302,6 +307,26 @@ public class FhirRequestBuilder {
}
}
/**
* Logs the given {@link Request}, using the current {@link ToolingClientLogger}. If the current
* {@link FhirRequestBuilder#logger} is null, no action is taken.
*
* @param method HTTP request method
* @param url request URL
* @param requestHeaders {@link Headers} for request
* @param requestBody Byte array request
*/
protected void log(String method, String url, Headers requestHeaders, byte[] requestBody) {
if (logger != null) {
List<String> headerList = new ArrayList<>(Collections.emptyList());
Map<String, List<String>> headerMap = requestHeaders.toMultimap();
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
logger.logRequest(method, url, headerList, requestBody);
}
}
/**
* Logs the given {@link Response}, using the current {@link ToolingClientLogger}. If the current
* {@link FhirRequestBuilder#logger} is null, no action is taken.

View File

@ -34,8 +34,8 @@ public class RetryInterceptor implements Interceptor {
try {
// If we are retrying a failed request that failed due to a bad response from the server, we must close it first
if (response != null) {
System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
+ "> from url -> " + chain.request().url() + ".");
// System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
// + "> from url -> " + chain.request().url() + ".");
response.close();
}
// System.out.println(chain.request().method() + " attempt <" + (retryCounter + 1) + "> to url -> " + chain.request().url());

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.dstu3.utils;
package org.hl7.fhir.dstu3.utils.validation;
/*
Copyright (c) 2011+, HL7, Inc.
@ -30,21 +30,20 @@ package org.hl7.fhir.dstu3.utils;
*/
import com.google.gson.JsonObject;
import org.hl7.fhir.dstu3.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.utils.ValidationProfileSet;
import org.hl7.fhir.dstu3.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.dstu3.utils.validation.constants.CheckDisplayOption;
import org.hl7.fhir.dstu3.utils.validation.constants.IdStatus;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.hl7.fhir.dstu3.elementmodel.Element;
import org.hl7.fhir.dstu3.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import com.google.gson.JsonObject;
/**
* Interface to the instance validator. This takes a resource, in one of many forms, and
* checks whether it is valid
@ -54,49 +53,6 @@ import com.google.gson.JsonObject;
*/
public interface IResourceValidator {
public enum ReferenceValidationPolicy {
IGNORE, CHECK_TYPE_IF_EXISTS, CHECK_EXISTS, CHECK_EXISTS_AND_TYPE, CHECK_VALID;
public boolean checkExists() {
return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID;
}
public boolean checkType() {
return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
}
public boolean checkValid() {
return this == CHECK_VALID;
}
}
public interface IValidatorResourceFetcher {
Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, IOException, FHIRException;
ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url);
boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException;
}
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}
enum IdStatus {
OPTIONAL, REQUIRED, PROHIBITED
}
/**
* how much to check displays for coded elements
* @return
@ -124,21 +80,24 @@ public interface IResourceValidator {
IValidatorResourceFetcher getFetcher();
IResourceValidator setFetcher(IValidatorResourceFetcher value);
IValidationPolicyAdvisor getPolicyAdvisor();
IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor);
boolean isNoBindingMsgSuppressed();
IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
public boolean isNoInvariantChecks();
public IResourceValidator setNoInvariantChecks(boolean value) ;
boolean isNoInvariantChecks();
IResourceValidator setNoInvariantChecks(boolean value) ;
public boolean isNoTerminologyChecks();
public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
boolean isNoTerminologyChecks();
IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
/**
* Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning
* @return
*/
public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
boolean isErrorForUnknownProfiles();
void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
/**
* Validate suite

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.dstu3.utils.validation;
import org.hl7.fhir.dstu3.elementmodel.Element;
import org.hl7.fhir.dstu3.utils.validation.constants.ReferenceValidationPolicy;
public interface IValidationPolicyAdvisor {
ReferenceValidationPolicy policyForReference(IResourceValidator validator,
Object appContext,
String path,
String url);
ReferenceValidationPolicy policyForContained(IResourceValidator validator,
Object appContext,
String containerType,
String containerId,
Element.SpecialElement containingResourceType,
String path,
String url);
}

View File

@ -0,0 +1,13 @@
package org.hl7.fhir.dstu3.utils.validation;
import org.hl7.fhir.dstu3.elementmodel.Element;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import java.io.IOException;
public interface IValidatorResourceFetcher {
Element fetch(Object appContext, String url) throws IOException, FHIRException;
boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException;
}

View File

@ -0,0 +1,8 @@
package org.hl7.fhir.dstu3.utils.validation.constants;
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}

View File

@ -0,0 +1,9 @@
package org.hl7.fhir.dstu3.utils.validation.constants;
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}

View File

@ -0,0 +1,7 @@
package org.hl7.fhir.dstu3.utils.validation.constants;
public enum IdStatus {
OPTIONAL,
REQUIRED,
PROHIBITED
}

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.dstu3.utils.validation.constants;
public enum ReferenceValidationPolicy {
IGNORE,
CHECK_TYPE_IF_EXISTS,
CHECK_EXISTS,
CHECK_EXISTS_AND_TYPE,
CHECK_VALID;
public boolean checkExists() {
return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID;
}
public boolean checkType() {
return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
}
public boolean checkValid() {
return this == CHECK_VALID;
}
}

View File

@ -0,0 +1,101 @@
package org.hl7.fhir.dstu3.utils.client.network;
import okhttp3.*;
import org.hl7.fhir.dstu3.formats.IParser;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.AdditionalMatchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.ArgumentMatchers;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
@ExtendWith(MockitoExtension.class)
public class FhirRequestBuilderTests {
private static final String DUMMY_URL = "https://some-url.com/";
Request mockRequest = new Request.Builder()
.url(DUMMY_URL)
.build();
final String RESPONSE_BODY_STRING = "{}";
Response response = new Response.Builder()
.request(mockRequest)
.protocol(Protocol.HTTP_2)
.code(200) // status code
.message("")
.body(ResponseBody.create(RESPONSE_BODY_STRING,
MediaType.get("application/json; charset=utf-8")
))
.addHeader("Content-Type", "")
.build();
final Request.Builder requestBuilder = new Request.Builder()
.url(DUMMY_URL);
final FhirRequestBuilder fhirRequestBuilder = Mockito.spy(new FhirRequestBuilder(requestBuilder));
@Mock
OkHttpClient client;
@Mock
Call mockCall;
@Mock
ToolingClientLogger logger;
@BeforeEach
public void beforeEach() {
Mockito.doReturn(client).when(fhirRequestBuilder).getHttpClient();
fhirRequestBuilder.withLogger(logger);
}
@Nested
class RequestLoggingTests {
@BeforeEach
public void beforeEach() throws IOException {
Mockito.doReturn(response).when(mockCall).execute();
Mockito.doReturn(mockCall).when(client).newCall(ArgumentMatchers.any());
Mockito.doReturn(null).when(fhirRequestBuilder).unmarshalReference(ArgumentMatchers.any(), ArgumentMatchers.isNull());
}
@Test
public void testExecuteLogging() throws IOException {
fhirRequestBuilder.execute();
Mockito.verify(logger).logRequest(ArgumentMatchers.eq("GET"), ArgumentMatchers.eq(DUMMY_URL), ArgumentMatchers.anyList(), ArgumentMatchers.isNull());
}
@Test
public void testExecuteBatchLogging() throws IOException {
fhirRequestBuilder.executeAsBatch();
Mockito.verify(logger).logRequest(ArgumentMatchers.eq("GET"), ArgumentMatchers.eq(DUMMY_URL), ArgumentMatchers.anyList(), ArgumentMatchers.isNull());
}
}
@Test
public void testUnmarshallReferenceLogging() {
IParser parser = Mockito.mock(IParser.class);
Mockito.doReturn(parser).when(fhirRequestBuilder).getParser(ArgumentMatchers.eq("json"));
fhirRequestBuilder.unmarshalReference(response, "json");
Mockito.verify(logger).logResponse(ArgumentMatchers.eq("200"), ArgumentMatchers.anyList(), AdditionalMatchers.aryEq(RESPONSE_BODY_STRING.getBytes()));
}
@Test
public void testUnmarshallFeedLogging() {
fhirRequestBuilder.unmarshalFeed(response, "application/json");
Mockito.verify(logger).logResponse(ArgumentMatchers.eq("200"), ArgumentMatchers.anyList(), AdditionalMatchers.aryEq(RESPONSE_BODY_STRING.getBytes()));
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.5.12-SNAPSHOT</version>
<version>5.6.16-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -33,7 +33,6 @@ package org.hl7.fhir.r4.context;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.fhir.ucum.UcumService;
@ -58,8 +57,7 @@ import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.INarrativeGenerator;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.r4.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationOptions;

View File

@ -75,7 +75,7 @@ import org.hl7.fhir.r4.model.StructureMap.StructureMapModelMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapStructureComponent;
import org.hl7.fhir.r4.terminologies.TerminologyClient;
import org.hl7.fhir.r4.utils.INarrativeGenerator;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.validation.IResourceValidator;
import org.hl7.fhir.r4.utils.NarrativeGenerator;
import org.hl7.fhir.utilities.CSFileInputStream;
import org.hl7.fhir.utilities.Utilities;

View File

@ -10036,6 +10036,7 @@ The primary difference between a medication statement and a medication administr
* R4B - manually added
*/
_4_1_0,
_4_3_0_CIBUILD,
NULL;
public static FHIRVersion fromCode(String codeString) throws FHIRException {
if (codeString == null || "".equals(codeString))
@ -10088,6 +10089,8 @@ The primary difference between a medication statement and a medication administr
return _4_0_1;
if ("4.1.0".equals(codeString))
return _4_1_0;
if ("4.3.0-CIBUILD".equals(codeString))
return _4_3_0_CIBUILD;
throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'");
}
@Override
@ -10120,6 +10123,8 @@ The primary difference between a medication statement and a medication administr
case _4_0_0: return "4.0.0";
case _4_0_1: return "4.0.1";
case _4_1_0: return "4.1.0";
case _4_3_0_CIBUILD: return "4.3.0-CIBUILD";
case NULL: return null;
default: return "?";
}
@ -10150,6 +10155,7 @@ The primary difference between a medication statement and a medication administr
case _4_0_0: return "http://hl7.org/fhir/FHIR-version";
case _4_0_1: return "http://hl7.org/fhir/FHIR-version";
case _4_1_0: return "http://hl7.org/fhir/FHIR-version";
case _4_3_0_CIBUILD: return "http://hl7.org/fhir/FHIR-version";
case NULL: return null;
default: return "?";
}
@ -10179,7 +10185,8 @@ The primary difference between a medication statement and a medication administr
case _3_5_0: return "R4 Ballot #2.";
case _4_0_0: return "FHIR Release 4 (Normative + STU).";
case _4_0_1: return "FHIR Release 4 Technical Correction #1.";
case _4_1_0: return "FHIR Release 4B";
case _4_1_0: return "FHIR Release 4B Ballot #1";
case _4_3_0_CIBUILD: return "FHIR Release 4B CI-Builld";
case NULL: return null;
default: return "?";
}
@ -10210,6 +10217,7 @@ The primary difference between a medication statement and a medication administr
case _4_0_0: return "4.0.0";
case _4_0_1: return "4.0.1";
case _4_1_0: return "4.1.0";
case _4_3_0_CIBUILD: return "4.3.0-CIBUILD";
case NULL: return null;
default: return "?";
}
@ -10275,6 +10283,8 @@ The primary difference between a medication statement and a medication administr
return FHIRVersion._4_0_1;
if ("4.1.0".equals(codeString))
return FHIRVersion._4_1_0;
if ("4.3.0-CIBUILD".equals(codeString))
return FHIRVersion._4_3_0_CIBUILD;
throw new IllegalArgumentException("Unknown FHIRVersion code '"+codeString+"'");
}
public Enumeration<FHIRVersion> fromType(Base code) throws FHIRException {
@ -10333,6 +10343,8 @@ The primary difference between a medication statement and a medication administr
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_0_1);
if ("4.1.0".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_1_0);
if ("4.3.0-CIBUILD".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_3_0_CIBUILD);
throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'");
}
public String toCode(FHIRVersion code) {
@ -10384,6 +10396,8 @@ The primary difference between a medication statement and a medication administr
return "4.0.1";
if (code == FHIRVersion._4_1_0)
return "4.1.0";
if (code == FHIRVersion._4_3_0_CIBUILD)
return "4.3.0_CIBUILD";
return "?";
}
public String toSystem(FHIRVersion code) {

View File

@ -115,6 +115,7 @@ import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r4.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.Utilities;

View File

@ -2,8 +2,8 @@ package org.hl7.fhir.r4.utils.client.network;
import okhttp3.*;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -22,9 +22,8 @@ public class FhirLoggingInterceptor implements Interceptor {
return this;
}
@NotNull
@Override
public Response intercept(@NotNull Interceptor.Chain chain) throws IOException {
public Response intercept(@Nonnull Interceptor.Chain chain) throws IOException {
// Log Request
Request request = chain.request();
logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()),

View File

@ -34,8 +34,8 @@ public class RetryInterceptor implements Interceptor {
try {
// If we are retrying a failed request that failed due to a bad response from the server, we must close it first
if (response != null) {
System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
+ "> from url -> " + chain.request().url() + ".");
// System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
// + "> from url -> " + chain.request().url() + ".");
response.close();
}
// System.out.println(chain.request().method() + " attempt <" + (retryCounter + 1) + "> to url -> " + chain.request().url());

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r4.utils;
package org.hl7.fhir.r4.utils.validation;
/*
Copyright (c) 2011+, HL7, Inc.
@ -29,18 +29,17 @@ package org.hl7.fhir.r4.utils;
*/
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.utils.ValidationProfileSet;
import org.hl7.fhir.r4.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r4.utils.validation.constants.CheckDisplayOption;
import org.hl7.fhir.r4.utils.validation.constants.IdStatus;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import com.google.gson.JsonObject;
@ -54,69 +53,21 @@ import com.google.gson.JsonObject;
*/
public interface IResourceValidator {
public enum ReferenceValidationPolicy {
IGNORE, CHECK_TYPE_IF_EXISTS, CHECK_EXISTS, CHECK_EXISTS_AND_TYPE, CHECK_VALID;
public boolean checkExists() {
return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID;
}
public boolean checkType() {
return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
}
public boolean checkValid() {
return this == CHECK_VALID;
}
}
public interface IValidatorResourceFetcher {
Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException;
ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url);
boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException;
}
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}
enum IdStatus {
OPTIONAL, REQUIRED, PROHIBITED
}
/**
* how much to check displays for coded elements
* @return
*/
CheckDisplayOption getCheckDisplay();
void setCheckDisplay(CheckDisplayOption checkDisplay);
/**
* whether the resource must have an id or not (depends on context)
*
* @return
*/
IdStatus getResourceIdRule();
void setResourceIdRule(IdStatus resourceIdRule);
/**
* whether the validator should enforce best practice guidelines
* as defined by various HL7 committees
*
*/
BestPracticeWarningLevel getBestPracticeWarningLevel();
IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value);
@ -124,27 +75,29 @@ public interface IResourceValidator {
IValidatorResourceFetcher getFetcher();
IResourceValidator setFetcher(IValidatorResourceFetcher value);
IValidationPolicyAdvisor getPolicyAdvisor();
IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor);
boolean isNoBindingMsgSuppressed();
IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
public boolean isNoInvariantChecks();
public IResourceValidator setNoInvariantChecks(boolean value) ;
boolean isNoInvariantChecks();
IResourceValidator setNoInvariantChecks(boolean value) ;
public boolean isNoTerminologyChecks();
public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
boolean isNoTerminologyChecks();
IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
public boolean isNoExtensibleWarnings();
public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings);
boolean isNoExtensibleWarnings();
IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings);
/**
* Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning
* @return
*/
public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
boolean isErrorForUnknownProfiles();
void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
public String getValidationLanguage();
public void setValidationLanguage(String value);
String getValidationLanguage();
void setValidationLanguage(String value);
/**
* Validate suite
@ -194,5 +147,4 @@ public interface IResourceValidator {
org.hl7.fhir.r4.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, String profile) throws FHIRException;
org.hl7.fhir.r4.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, StructureDefinition profile) throws FHIRException;
}

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.r4.utils.validation;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.utils.validation.constants.ReferenceValidationPolicy;
public interface IValidationPolicyAdvisor {
ReferenceValidationPolicy policyForReference(IResourceValidator validator,
Object appContext,
String path,
String url);
ReferenceValidationPolicy policyForContained(IResourceValidator validator,
Object appContext,
String containerType,
String containerId,
Element.SpecialElement containingResourceType,
String path,
String url);
}

View File

@ -0,0 +1,11 @@
package org.hl7.fhir.r4.utils.validation;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.elementmodel.Element;
import java.io.IOException;
public interface IValidatorResourceFetcher {
Element fetch(Object appContext, String url) throws FHIRException, IOException;
boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException;
}

View File

@ -0,0 +1,8 @@
package org.hl7.fhir.r4.utils.validation.constants;
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}

View File

@ -0,0 +1,9 @@
package org.hl7.fhir.r4.utils.validation.constants;
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}

View File

@ -0,0 +1,7 @@
package org.hl7.fhir.r4.utils.validation.constants;
public enum IdStatus {
OPTIONAL,
REQUIRED,
PROHIBITED
}

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.r4.utils.validation.constants;
public enum ReferenceValidationPolicy {
IGNORE,
CHECK_TYPE_IF_EXISTS,
CHECK_EXISTS,
CHECK_EXISTS_AND_TYPE,
CHECK_VALID;
public boolean checkExists() {
return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID;
}
public boolean checkType() {
return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
}
public boolean checkValid() {
return this == CHECK_VALID;
}
}

View File

@ -21,7 +21,7 @@ import java.io.*;
public class ResourceRoundTripTests {
@BeforeAll
public void setUp() throws Exception {
public static void setUp() throws Exception {
}
@Test

View File

@ -22,7 +22,7 @@ import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.test.utils.TestingUtilities;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.validation.IResourceValidator;
import org.hl7.fhir.r4.utils.NarrativeGenerator;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.5.12-SNAPSHOT</version>
<version>5.6.16-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -691,8 +691,8 @@ public class ProfileUtilities extends TranslatingUtilities {
}
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath());
if (e.hasId()) {
ce++;
if (e.hasId()) {
String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
}
@ -2492,7 +2492,8 @@ public class ProfileUtilities extends TranslatingUtilities {
i = i + 1;
} else {
b.append("](");
b.append(webUrl);
// disabled 7-Dec 2021 GDG - we don't want to fool with relative URLs at all?
// b.append(webUrl);
i = i + 1;
}
} else
@ -4773,7 +4774,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath, boolean skipnoValue) {
String ref = pkp.getLinkFor(corePath, value.fhirType());
if (ref != null) {
if (ref != null && ref.contains(".html")) {
ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#";
} else {
ref = "?gen-fv?";

View File

@ -0,0 +1,48 @@
package org.hl7.fhir.r5.conformance;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.VersionUtilities;
public class StructureDefinitionHacker {
private String version;
public StructureDefinitionHacker(String version) {
super();
this.version = version;
}
public Resource fixSD(StructureDefinition sd) {
if (VersionUtilities.isR4BVer(version) && sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type")) {
// the definition of this one is wrong in R4B
return fixR4BFhirType(sd);
}
return sd;
}
private Resource fixR4BFhirType(StructureDefinition sd) {
for (ElementDefinition ed : sd.getDifferential().getElement()) {
if (ed.getPath().equals("Extension.value[x]")) {
fixEDType(ed, "url", "uri");
}
}
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals("Extension.value[x]")) {
fixEDType(ed, "url", "uri");
}
}
return sd;
}
private void fixEDType(ElementDefinition ed, String orig, String repl) {
for (TypeRefComponent t : ed.getType()) {
if (orig.equals(t.getCode())) {
t.setCode(repl);
}
}
}
}

View File

@ -52,6 +52,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.NoTerminologyServiceException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.context.BaseWorkerContext.ResourceProxy;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion;
import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory;
@ -110,6 +111,7 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorCla
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.ToolingClientLogger;
@ -129,6 +131,35 @@ import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext{
public class ResourceProxy {
private Resource resource;
private CanonicalResourceProxy proxy;
public ResourceProxy(Resource resource) {
super();
this.resource = resource;
}
public ResourceProxy(CanonicalResourceProxy proxy) {
super();
this.proxy = proxy;
}
public Resource getResource() {
return resource != null ? resource : proxy.getResource();
}
public String getUrl() {
if (resource == null) {
return proxy.getUrl();
} else if (resource instanceof CanonicalResource) {
return ((CanonicalResource) resource).getUrl();
} else {
return null;
}
}
}
public class MetadataResourceVersionComparator<T extends CanonicalResource> implements Comparator<T> {
private List<T> list;
@ -165,7 +196,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private boolean isTxCaching;
private Set<String> cached = new HashSet<>();
private Map<String, Map<String, Resource>> allResourcesById = new HashMap<String, Map<String, Resource>>();
private Map<String, Map<String, ResourceProxy>> allResourcesById = new HashMap<String, Map<String, ResourceProxy>>();
// all maps are to the full URI
private CanonicalResourceManager<CodeSystem> codeSystems = new CanonicalResourceManager<CodeSystem>(false);
private Set<String> supportedCodeSystems = new HashSet<String>();
@ -276,6 +307,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
public void registerResourceFromPackage(CanonicalResourceProxy r, PackageVersion packageInfo) throws FHIRException {
synchronized (lock) {
Map<String, ResourceProxy> map = allResourcesById.get(r.getType());
if (map == null) {
map = new HashMap<String, ResourceProxy>();
allResourcesById.put(r.getType(), map);
}
map.put(r.getId(), new ResourceProxy(r));
String url = r.getUrl();
if (!allowLoadingDuplicates && hasResource(r.getType(), url)) {
@ -338,12 +375,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
public void cacheResourceFromPackage(Resource r, PackageVersion packageInfo) throws FHIRException {
synchronized (lock) {
Map<String, Resource> map = allResourcesById.get(r.fhirType());
Map<String, ResourceProxy> map = allResourcesById.get(r.fhirType());
if (map == null) {
map = new HashMap<String, Resource>();
map = new HashMap<String, ResourceProxy>();
allResourcesById.put(r.fhirType(), map);
}
map.put(r.getId(), r);
if ((packageInfo == null || !packageInfo.isExamplesPackage()) || !map.containsKey(r.getId())) {
map.put(r.getId(), new ResourceProxy(r));
}
if (r instanceof CodeSystem || r instanceof NamingSystem) {
oidCache.clear();
@ -498,6 +537,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
@Override
public CodeSystem fetchCodeSystem(String system) {
if (system == null) {
return null;
}
if (system.contains("|")) {
String s = system.substring(0, system.indexOf("|"));
String v = system.substring(system.indexOf("|")+1);
return fetchCodeSystem(s, v);
}
CodeSystem cs;
synchronized (lock) {
cs = codeSystems.get(system);
@ -511,6 +558,23 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return cs;
}
public CodeSystem fetchCodeSystem(String system, String version) {
if (version == null) {
return fetchCodeSystem(system);
}
CodeSystem cs;
synchronized (lock) {
cs = codeSystems.get(system, version);
}
if (cs == null && locator != null) {
locator.findResource(this, system);
synchronized (lock) {
cs = codeSystems.get(system);
}
}
return cs;
}
@Override
public boolean supportsSystem(String system) throws TerminologyServiceException {
synchronized (lock) {
@ -643,10 +707,18 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (expParameters == null)
throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED));
Parameters p = expParameters.copy();
return expandVS(vs, cacheOk, heirarchical, p);
return expandVS(vs, cacheOk, heirarchical, false, p);
}
public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, Parameters p) {
@Override
public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, boolean incompleteOk) {
if (expParameters == null)
throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED));
Parameters p = expParameters.copy();
return expandVS(vs, cacheOk, heirarchical, incompleteOk, p);
}
public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, boolean incompleteOk, Parameters p) {
if (p == null) {
throw new Error(formatMessage(I18nConstants.NO_PARAMETERS_PROVIDED_TO_EXPANDVS));
}
@ -673,6 +745,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
p.setParameter("includeDefinition", false);
p.setParameter("excludeNested", !heirarchical);
if (incompleteOk) {
p.setParameter("incomplete-ok", true);
}
List<String> allErrors = new ArrayList<>();
@ -853,6 +928,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
@Override
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) {
ValidationContextCarrier ctxt = new ValidationContextCarrier();
return validateCode(options, code, vs, ctxt);
}
@Override
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt) {
if (options == null) {
options = ValidationOptions.defaults();
}
@ -872,7 +953,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (options.isUseClient()) {
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this, ctxt);
if (!vsc.isServerSide(code.getSystem())) {
res = vsc.validateCode(code);
if (txCache != null) {
@ -992,14 +1073,16 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (vs != null) {
if (isTxCaching && cacheId != null && cached.contains(vs.getUrl()+"|"+vs.getVersion())) {
if (isTxCaching && cacheId != null && vs.getUrl() != null && cached.contains(vs.getUrl()+"|"+vs.getVersion())) {
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()+(vs.hasVersion() ? "|"+vs.getVersion() : "")));
} else if (options.getVsAsUrl()){
pin.addParameter().setName("url").setValue(new StringType(vs.getUrl()));
} else {
pin.addParameter().setName("valueSet").setResource(vs);
if (vs.getUrl() != null) {
cached.add(vs.getUrl()+"|"+vs.getVersion());
}
}
cache = true;
addDependentResources(pin, vs);
}
@ -1205,8 +1288,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return fetchResourceWithException(cls, uri, null);
}
@SuppressWarnings("unchecked")
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, CanonicalResource source) throws FHIRException {
return fetchResourceWithException(class_, uri, null, source);
}
@SuppressWarnings("unchecked")
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, String version, CanonicalResource source) throws FHIRException {
if (uri == null) {
return null;
}
@ -1216,7 +1303,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
synchronized (lock) {
String version = null;
if (uri.contains("|")) {
version = uri.substring(uri.lastIndexOf("|")+1);
uri = uri.substring(0, uri.lastIndexOf("|"));
@ -1264,12 +1350,22 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (questionnaires.has(uri)) {
return (T) questionnaires.get(uri, version);
}
for (Map<String, Resource> rt : allResourcesById.values()) {
for (Resource r : rt.values()) {
if (r instanceof CanonicalResource) {
CanonicalResource mr = (CanonicalResource) r;
if (uri.equals(mr.getUrl())) {
return (T) mr;
if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) {
return null;
}
// it might be a special URL.
if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
Resource res = null; // findTxValueSet(uri);
if (res != null) {
return (T) res;
}
}
for (Map<String, ResourceProxy> rt : allResourcesById.values()) {
for (ResourceProxy r : rt.values()) {
if (uri.equals(r.getUrl())) {
if (version == null || version == r.getResource().getMeta().getVersionId()) {
return (T) r.getResource();
}
}
}
@ -1314,20 +1410,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (class_ == Questionnaire.class) {
return (T) questionnaires.get(uri, version);
}
if (class_ == null) {
if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) {
return null;
}
// it might be a special URL.
if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
Resource res = null; // findTxValueSet(uri);
if (res != null) {
return (T) res;
}
}
return null;
}
if (supportedCodeSystems.contains(uri)) {
return null;
}
@ -1453,13 +1535,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (questionnaires.has(uri)) {
return (T) questionnaires.get(uri, version);
}
for (Map<String, Resource> rt : allResourcesById.values()) {
for (Resource r : rt.values()) {
if (r instanceof CanonicalResource) {
CanonicalResource mr = (CanonicalResource) r;
if (uri.equals(mr.getUrl())) {
return (T) mr;
}
for (Map<String, ResourceProxy> rt : allResourcesById.values()) {
for (ResourceProxy r : rt.values()) {
if (uri.equals(r.getUrl())) {
return (T) r.getResource();
}
}
}
@ -1533,7 +1612,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
String[] parts = uri.split("\\/");
if (!Utilities.noString(type) && parts.length == 1) {
if (allResourcesById.containsKey(type)) {
return allResourcesById.get(type).get(parts[0]);
return allResourcesById.get(type).get(parts[0]).getResource();
} else {
return null;
}
@ -1544,7 +1623,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
throw new Error(formatMessage(I18nConstants.RESOURCE_TYPE_MISMATCH_FOR___, type, uri));
}
}
return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]);
return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]).getResource();
} else {
throw new Error(formatMessage(I18nConstants.UNABLE_TO_PROCESS_REQUEST_FOR_RESOURCE_FOR___, type, uri));
}
@ -1567,6 +1646,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version) {
try {
return fetchResourceWithException(class_, uri, version, null);
} catch (FHIRException e) {
throw new Error(e);
}
}
@Override
public <T extends Resource> boolean hasResource(Class<T> class_, String uri) {
try {
@ -1656,13 +1743,13 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
public void dropResource(String fhirType, String id) {
synchronized (lock) {
Map<String, Resource> map = allResourcesById.get(fhirType);
Map<String, ResourceProxy> map = allResourcesById.get(fhirType);
if (map == null) {
map = new HashMap<String, Resource>();
map = new HashMap<String, ResourceProxy>();
allResourcesById.put(fhirType, map);
}
if (map.containsKey(id)) {
map.remove(id);
map.remove(id); // this is a challenge because we might have more than one resource with this id (different versions)
}
if (fhirType.equals("StructureDefinition")) {

View File

@ -236,6 +236,9 @@ public class CanonicalResourceManager<T extends CanonicalResource> {
}
}
CachedCanonicalResource<T> existing = cr.hasVersion() ? map.get(cr.getUrl()+"|"+cr.getVersion()) : map.get(cr.getUrl()+"|#0");
if (existing != null && (cr.getPackageInfo() != null && cr.getPackageInfo().isExamplesPackage())) {
return;
}
if (existing != null) {
list.remove(existing);
}

View File

@ -89,12 +89,13 @@ public class HTMLClientLogger extends BaseLogger implements ToolingClientLogger
if (DEBUG) {
System.out.println(" txlog resp: " +outcome+" "+present(body));
}
req = false;
if (file == null)
return;
if (!req) {
System.out.println("Record Response without request");
}
req = false;
file.println("<pre>");
file.println(outcome);
for (String s : headers)

View File

@ -44,8 +44,8 @@ import org.fhir.ucum.UcumService;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest;
import org.hl7.fhir.r5.context.TerminologyCache.CacheToken;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.ParserType;
import org.hl7.fhir.r5.model.Bundle;
@ -64,12 +64,14 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import com.google.gson.JsonSyntaxException;
@ -168,6 +170,9 @@ public interface IWorkerContext {
public String getVersion() {
return version;
}
public boolean isExamplesPackage() {
return !(id.startsWith("hl7.fhir.") && id.endsWith(".example"));
}
}
public class PackageDetails extends PackageVersion {
@ -191,13 +196,14 @@ public interface IWorkerContext {
}
}
public interface ICanonicalResourceLocator {
void findResource(Object caller, String url); // if it can be found, put it in the context
}
public interface IContextResourceLoader {
/**
* @return List of the resource types that shoud be loaded
* @return List of the resource types that should be loaded
*/
String[] getTypes();
@ -242,7 +248,6 @@ public interface IWorkerContext {
IContextResourceLoader getNewLoader(NpmPackage npm) throws JsonSyntaxException, IOException;
}
/**
* Get the versions of the definitions loaded in context
* @return
@ -335,6 +340,7 @@ public interface IWorkerContext {
*/
public <T extends Resource> T fetchResource(Class<T> class_, String uri);
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException;
public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version);
/** has the same functionality as fetchResource, but passes in information about the source of the
* reference (this may affect resolution of version)
@ -476,6 +482,7 @@ public interface IWorkerContext {
* @return
*/
public CodeSystem fetchCodeSystem(String system);
public CodeSystem fetchCodeSystem(String system, String version);
/**
* True if the underlying terminology service provider will do
@ -508,6 +515,14 @@ public interface IWorkerContext {
*/
public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical);
/**
* ValueSet Expansion - see $expand
*
* @param source
* @return
*/
public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical, boolean incompleteOk);
/**
* ValueSet Expansion - see $expand, but resolves the binding first
*
@ -734,6 +749,8 @@ public interface IWorkerContext {
*/
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs);
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt);
public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs);
/**

View File

@ -54,9 +54,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion;
import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory;
import org.hl7.fhir.r5.context.SimpleWorkerContext.PackageResourceLoader;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.ParserType;
@ -76,7 +74,7 @@ import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode;
import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.utilities.CSFileInputStream;
import org.hl7.fhir.utilities.TextFile;
@ -85,15 +83,12 @@ import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import com.google.gson.JsonObject;
import ca.uhn.fhir.parser.DataFormatException;
/*

View File

@ -118,12 +118,14 @@ public abstract class ParserBase {
//FIXME: i18n should be done here
public void logError(int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError {
if (errors != null) {
if (policy == ValidationPolicy.EVERYTHING) {
ValidationMessage msg = new ValidationMessage(Source.InstanceValidator, type, line, col, path, message, level);
errors.add(msg);
} else if (level == IssueSeverity.FATAL || (level == IssueSeverity.ERROR && policy == ValidationPolicy.QUICK))
throw new FHIRFormatError(message+String.format(" at line %d col %d", line, col));
}
}
protected StructureDefinition getDefinition(int line, int col, String ns, String name) throws FHIRFormatError {

View File

@ -237,6 +237,7 @@ public class SHCParser extends ParserBase {
private static final int BUFFER_SIZE = 1024;
public static final String CURRENT_PACKAGE = "hl7.fhir.uv.shc-vaccination#0.6.2";
private static final int MAX_ALLOWED_SHC_LENGTH = 1195;
// todo: deal with chunking
public static String decodeQRCode(String src) {
@ -253,10 +254,14 @@ public class SHCParser extends ParserBase {
return b.toString();
}
public static JWT decodeJWT(String jwt) throws IOException, DataFormatException {
public JWT decodeJWT(String jwt) throws IOException, DataFormatException {
if (jwt.startsWith("shc:/")) {
jwt = decodeQRCode(jwt);
}
if (jwt.length() > MAX_ALLOWED_SHC_LENGTH) {
logError(-1, -1, "jwt", IssueType.TOOLONG, "JWT Payload limit length is "+MAX_ALLOWED_SHC_LENGTH+" bytes for a single image - this has "+jwt.length()+" bytes", IssueSeverity.ERROR);
}
String[] parts = splitToken(jwt);
byte[] headerJson;
byte[] payloadJson;

View File

@ -554,6 +554,10 @@ public abstract class CanonicalResource extends DomainResource {
return true;
}
public String getVersionedUrl() {
return hasVersion() ? getUrl()+"|"+getVersion() : getUrl();
}
// end addition
}

View File

@ -6716,6 +6716,10 @@ The MedicationUsage resource was previously called MedicationStatement.
* R5 Preview #1.
*/
_4_2_0,
/**
* R4B
*/
_4_3_0_CIBUILD,
/**
* R5 Preview #2.
*/
@ -6733,8 +6737,6 @@ The MedicationUsage resource was previously called MedicationStatement.
*/
NULL;
public static final FHIRVersion R4B = FHIRVersion._4_1_0;
public static FHIRVersion fromCode(String codeString) throws FHIRException {
if (codeString == null || "".equals(codeString))
return null;
@ -6788,6 +6790,8 @@ The MedicationUsage resource was previously called MedicationStatement.
return _4_1_0;
if ("4.2.0".equals(codeString))
return _4_2_0;
if ("4.3.0-CIBUILD".equals(codeString))
return _4_3_0_CIBUILD;
if ("4.4.0".equals(codeString))
return _4_4_0;
if ("4.5.0".equals(codeString))
@ -6823,6 +6827,7 @@ The MedicationUsage resource was previously called MedicationStatement.
case _4_0_1: return "4.0.1";
case _4_1_0: return "4.1.0";
case _4_2_0: return "4.2.0";
case _4_3_0_CIBUILD: return "4.3.0-CIBUILD";
case _4_4_0: return "4.4.0";
case _4_5_0: return "4.5.0";
case _4_6_0: return "4.6.0";
@ -6857,6 +6862,7 @@ The MedicationUsage resource was previously called MedicationStatement.
case _4_0_1: return "http://hl7.org/fhir/FHIR-version";
case _4_1_0: return "http://hl7.org/fhir/FHIR-version";
case _4_2_0: return "http://hl7.org/fhir/FHIR-version";
case _4_3_0_CIBUILD: return "http://hl7.org/fhir/FHIR-version";
case _4_4_0: return "http://hl7.org/fhir/FHIR-version";
case _4_5_0: return "http://hl7.org/fhir/FHIR-version";
case _4_6_0: return "http://hl7.org/fhir/FHIR-version";
@ -6891,6 +6897,7 @@ The MedicationUsage resource was previously called MedicationStatement.
case _4_0_1: return "FHIR Release 4 (Normative + STU) with 1 technical errata.";
case _4_1_0: return "Interim Version.";
case _4_2_0: return "R5 Preview #1.";
case _4_3_0_CIBUILD: return "R4B CIBuild";
case _4_4_0: return "R5 Preview #2.";
case _4_5_0: return "R5 Preview #3.";
case _4_6_0: return "R5 Draft Ballot.";
@ -6925,6 +6932,7 @@ The MedicationUsage resource was previously called MedicationStatement.
case _4_0_1: return "4.0.1";
case _4_1_0: return "4.1.0";
case _4_2_0: return "4.2.0";
case _4_3_0_CIBUILD: return "4.3.0-CIBUILD";
case _4_4_0: return "4.4.0";
case _4_5_0: return "4.5.0";
case _4_6_0: return "4.6.0";
@ -6988,6 +6996,8 @@ public String toCode(int len) {
return true;
if ("4.2.0".equals(codeString))
return true;
if ("4.3.0-CIBUILD".equals(codeString))
return true;
return false;
}
@ -6998,7 +7008,7 @@ public String toCode(int len) {
public boolean isR4B() {
return toCode().startsWith("4.1");
return toCode().startsWith("4.1") || toCode().startsWith("4.3");
}
// end addition
@ -7059,6 +7069,8 @@ public String toCode(int len) {
return FHIRVersion._4_1_0;
if ("4.2.0".equals(codeString))
return FHIRVersion._4_2_0;
if ("4.3.0-CIBUILD".equals(codeString))
return FHIRVersion._4_3_0_CIBUILD;
if ("4.4.0".equals(codeString))
return FHIRVersion._4_4_0;
if ("4.5.0".equals(codeString))
@ -7125,6 +7137,8 @@ public String toCode(int len) {
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_1_0);
if ("4.2.0".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_2_0);
if ("4.3.0-CIBUILD".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_3_0_CIBUILD);
if ("4.4.0".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_4_0);
if ("4.5.0".equals(codeString))
@ -7184,6 +7198,8 @@ public String toCode(int len) {
return "4.1.0";
if (code == FHIRVersion._4_2_0)
return "4.2.0";
if (code == FHIRVersion._4_3_0_CIBUILD)
return "4.3.0-CIBUILD";
if (code == FHIRVersion._4_4_0)
return "4.4.0";
if (code == FHIRVersion._4_5_0)

View File

@ -14,6 +14,7 @@ import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent;
import org.hl7.fhir.r5.model.CapabilityStatement.SystemInteractionComponent;
import org.hl7.fhir.r5.model.CapabilityStatement.SystemRestfulInteraction;
import org.hl7.fhir.r5.model.CapabilityStatement.TypeRestfulInteraction;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
@ -79,12 +80,41 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
if (hasHistory)
tr.th().b().attribute("title", "GET changes for all resources of the type (history interaction on type)").tx("History");
XhtmlNode profCell = null;
boolean hasProf = false;
boolean hasSupProf = false;
for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
tr = t.tr();
tr.td().addText(r.getType());
if (r.hasProfile()) {
tr.td().ah(r.getProfile()).addText(r.getProfile());
//Show profiles
profCell = tr.td();
hasProf = r.hasProfile();
hasSupProf = r.hasSupportedProfile();
if ((!hasProf) && (!hasSupProf)) {
profCell.nbsp();
}
else if (hasProf) {
profCell.ah(r.getProfile()).addText(r.getProfile());
if (hasSupProf) {
profCell.br();
profCell.addText("Additional supported profiles:");
for (CanonicalType sp: r.getSupportedProfile()) {
profCell.br();
profCell.nbsp().nbsp();
profCell.ah(sp.getValue()).addText(sp.getValue());
}
}
}
else { //Case of only supported profiles
profCell.addText("Supported profiles:");
for (CanonicalType sp: r.getSupportedProfile()) {
profCell.br();
profCell.nbsp().nbsp();
profCell.ah(sp.getValue()).addText(sp.getValue());
}
}
//Show capabilities
tr.td().addText(showOp(r, TypeRestfulInteraction.READ));
if (hasVRead)
tr.td().addText(showOp(r, TypeRestfulInteraction.VREAD));

View File

@ -94,8 +94,10 @@ public class CodeSystemRenderer extends TerminologyRenderer {
private void generateProperties(XhtmlNode x, CodeSystem cs) {
if (cs.hasProperty()) {
boolean hasRendered = false;
boolean hasURI = false;
for (PropertyComponent p : cs.getProperty()) {
hasRendered = hasRendered || !p.getCode().equals(ToolingExtensions.getPresentation(p, p.getCodeElement()));
hasURI = hasURI || p.hasUri();
}
x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Properties", getContext().getLang()));
@ -105,18 +107,22 @@ public class CodeSystemRenderer extends TerminologyRenderer {
tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Name", getContext().getLang()));
}
tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Code", getContext().getLang()));
if (hasURI) {
tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "URL", getContext().getLang()));
tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang()));
}
tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Type", getContext().getLang()));
tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang()));
for (PropertyComponent p : cs.getProperty()) {
tr = tbl.tr();
if (hasRendered) {
tr.td().tx(ToolingExtensions.getPresentation(p, p.getCodeElement()));
}
tr.td().tx(p.getCode());
if (hasURI) {
tr.td().tx(p.getUri());
tr.td().tx(p.getDescription());
}
tr.td().tx(p.hasType() ? p.getType().toCode() : "");
tr.td().tx(p.getDescription());
}
}
}
@ -126,7 +132,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
if (cs.getContent() == CodeSystemContentMode.COMPLETE)
p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines the following codes", cs.getUrl())+":");
else if (cs.getContent() == CodeSystemContentMode.EXAMPLE)
p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, of which the following are some examples", cs.getUrl())+":");
p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines some example codes", cs.getUrl())+":");
else if (cs.getContent() == CodeSystemContentMode.FRAGMENT )
p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, of which the following are a subset", cs.getUrl())+":");
else if (cs.getContent() == CodeSystemContentMode.NOTPRESENT ) {
@ -141,6 +147,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
boolean hierarchy = false;
boolean version = false;
boolean ignoreStatus = false;
boolean isSupplement = cs.getContent() == CodeSystemContentMode.SUPPLEMENT;
List<PropertyComponent> properties = new ArrayList<>();
for (PropertyComponent cp : cs.getProperty()) {
if (showPropertyInTable(cp)) {
@ -170,7 +177,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
List<String> langs = new ArrayList<>();
addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, definitions, commentS, version, deprecated, properties, null, false), maps);
for (ConceptDefinitionComponent c : csNav.getConcepts(null)) {
hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, definitions, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs) || hasExtensions;
hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, definitions, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs, isSupplement) || hasExtensions;
}
if (langs.size() > 0) {
Collections.sort(langs);
@ -223,10 +230,10 @@ public class CodeSystemRenderer extends TerminologyRenderer {
return true;
}
String uri = cp.getUri();
String code = null;
if (Utilities.noString(uri)){
return false;
return true; // do we always want to render properties in this case? Not sure...
}
String code = null;
if (uri.contains("#")) {
code = uri.substring(uri.indexOf("#")+1);
uri = uri.substring(0, uri.indexOf("#"));
@ -290,7 +297,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean hasDefinitions, boolean comment, boolean version, boolean deprecated, List<UsedConceptMap> maps, String system, CodeSystem cs, List<PropertyComponent> properties, CodeSystemNavigator csNav, List<String> langs) throws FHIRFormatError, DefinitionException, IOException {
private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean hasDefinitions, boolean comment, boolean version, boolean deprecated, List<UsedConceptMap> maps, String system, CodeSystem cs, List<PropertyComponent> properties, CodeSystemNavigator csNav, List<String> langs, boolean isSupplement) throws FHIRFormatError, DefinitionException, IOException {
boolean hasExtensions = false;
XhtmlNode tr = t.tr();
XhtmlNode td = tr.td();
@ -300,7 +307,12 @@ public class CodeSystemRenderer extends TerminologyRenderer {
String s = Utilities.padLeft("", '\u00A0', level*2);
td.addText(s);
}
String link = isSupplement ? getLinkForCode(cs.getSupplements(), null, c.getCode()) : null;
if (link != null) {
td.ah(link).attribute("style", "white-space:nowrap").addText(c.getCode());
} else {
td.attribute("style", "white-space:nowrap").addText(c.getCode());
}
XhtmlNode a;
if (c.hasCodeElement()) {
td.an(cs.getId()+"-" + Utilities.nmtokenize(c.getCode()));
@ -450,7 +462,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
}
List<ConceptDefinitionComponent> ocl = csNav.getOtherChildren(c);
for (ConceptDefinitionComponent cc : csNav.getConcepts(c)) {
hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, hasDefinitions, comment, version, deprecated, maps, system, cs, properties, csNav, langs) || hasExtensions;
hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, hasDefinitions, comment, version, deprecated, maps, system, cs, properties, csNav, langs, isSupplement) || hasExtensions;
}
for (ConceptDefinitionComponent cc : ocl) {
tr = t.tr();

View File

@ -2,7 +2,18 @@ package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Currency;
import java.util.List;
import java.util.TimeZone;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
@ -29,11 +40,14 @@ import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Expression;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.HumanName;
import org.hl7.fhir.r5.model.HumanName.NameUse;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.InstantType;
import org.hl7.fhir.r5.model.MarkdownType;
import org.hl7.fhir.r5.model.Money;
import org.hl7.fhir.r5.model.Period;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Quantity;
@ -63,6 +77,9 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
@ -372,6 +389,8 @@ public class DataRenderer extends Renderer {
return displayTiming((Timing) type);
} else if (type instanceof SampledData) {
return displaySampledData((SampledData) type);
} else if (type.isDateTime()) {
return displayDateTime((BaseDateTimeType) type);
} else if (type.isPrimitive()) {
return type.primitiveValue();
} else {
@ -379,11 +398,58 @@ public class DataRenderer extends Renderer {
}
}
private String displayDateTime(BaseDateTimeType type) {
if (!type.hasPrimitiveValue()) {
return "";
}
// relevant inputs in rendering context:
// timeZone, dateTimeFormat, locale, mode
// timezone - application specified timezone to use.
// null = default to the time of the date/time itself
// dateTimeFormat - application specified format for date times
// null = default to ... depends on mode
// mode - if rendering mode is technical, format defaults to XML format
// locale - otherwise, format defaults to SHORT for the Locale (which defaults to default Locale)
if (isOnlyDate(type.getPrecision())) {
DateTimeFormatter fmt = context.getDateFormat();
if (fmt == null) {
if (context.isTechnicalMode()) {
fmt = DateTimeFormatter.ISO_DATE;
} else {
fmt = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(context.getLocale());
}
}
LocalDate date = LocalDate.of(type.getYear(), type.getMonth()+1, type.getDay());
return fmt.format(date);
}
DateTimeFormatter fmt = context.getDateTimeFormat();
if (fmt == null) {
if (context.isTechnicalMode()) {
fmt = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
} else {
fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withLocale(context.getLocale());
}
}
ZonedDateTime zdt = ZonedDateTime.parse(type.primitiveValue());
ZoneId zone = context.getTimeZoneId();
if (zone != null) {
zdt = zdt.withZoneSameInstant(zone);
}
return fmt.format(zdt);
}
private boolean isOnlyDate(TemporalPrecisionEnum temporalPrecisionEnum) {
return temporalPrecisionEnum == TemporalPrecisionEnum.YEAR || temporalPrecisionEnum == TemporalPrecisionEnum.MONTH || temporalPrecisionEnum == TemporalPrecisionEnum.DAY;
}
public String display(BaseWrapper type) {
return "to do";
}
public void render(XhtmlNode x, BaseWrapper type) {
public void render(XhtmlNode x, BaseWrapper type) throws FHIRFormatError, DefinitionException, IOException {
Base base = null;
try {
base = type.getBase();
@ -398,7 +464,7 @@ public class DataRenderer extends Renderer {
}
}
public void renderBase(XhtmlNode x, Base b) {
public void renderBase(XhtmlNode x, Base b) throws FHIRFormatError, DefinitionException, IOException {
if (b instanceof DataType) {
render(x, (DataType) b);
} else {
@ -406,9 +472,9 @@ public class DataRenderer extends Renderer {
}
}
public void render(XhtmlNode x, DataType type) {
if (type instanceof DateTimeType) {
renderDateTime(x, (DateTimeType) type);
public void render(XhtmlNode x, DataType type) throws FHIRFormatError, DefinitionException, IOException {
if (type instanceof BaseDateTimeType) {
x.tx(displayDateTime((BaseDateTimeType) type));
} else if (type instanceof UriType) {
renderUri(x, (UriType) type);
} else if (type instanceof Annotation) {
@ -423,6 +489,10 @@ public class DataRenderer extends Renderer {
renderHumanName(x, (HumanName) type);
} else if (type instanceof Address) {
renderAddress(x, (Address) type);
} else if (type instanceof Expression) {
renderExpression(x, (Expression) type);
} else if (type instanceof Money) {
renderMoney(x, (Money) type);
} else if (type instanceof ContactPoint) {
renderContactPoint(x, (ContactPoint) type);
} else if (type instanceof Quantity) {
@ -437,10 +507,8 @@ public class DataRenderer extends Renderer {
renderSampledData(x, (SampledData) type);
} else if (type instanceof Reference) {
renderReference(x, (Reference) type);
} else if (type instanceof InstantType) {
x.tx(((InstantType) type).toHumanDisplay());
} else if (type instanceof BaseDateTimeType) {
x.tx(((BaseDateTimeType) type).toHumanDisplay());
} else if (type instanceof MarkdownType) {
addMarkdown(x, ((MarkdownType) type).asStringValue());
} else if (type.isPrimitive()) {
x.tx(type.primitiveValue());
} else {
@ -460,22 +528,24 @@ public class DataRenderer extends Renderer {
public void renderDateTime(XhtmlNode x, Base e) {
if (e.hasPrimitiveValue()) {
x.addText(((DateTimeType) e).toHumanDisplay());
x.addText(displayDateTime((DateTimeType) e));
}
}
public void renderDateTime(XhtmlNode x, String s) {
if (s != null) {
DateTimeType dt = new DateTimeType(s);
x.addText(dt.toHumanDisplay());
x.addText(displayDateTime(dt));
}
}
protected void renderUri(XhtmlNode x, UriType uri) {
if (uri.getValue().startsWith("mailto:")) {
x.ah(uri.getValue()).addText(uri.getValue().substring(7));
} else {
} else if (Utilities.isAbsoluteUrlLinkable(uri.getValue()) && !(uri instanceof IdType)) {
x.ah(uri.getValue()).addText(uri.getValue());
} else {
x.addText(uri.getValue());
}
}
@ -641,6 +711,38 @@ public class DataRenderer extends Renderer {
}
}
protected String getLinkForCode(String system, String version, String code) {
if ("http://snomed.info/sct".equals(system)) {
if (!Utilities.noString(code)) {
return "http://snomed.info/id/"+code;
} else {
return "https://browser.ihtsdotools.org/";
}
} else if ("http://loinc.org".equals(system)) {
if (!Utilities.noString(code)) {
return "https://loinc.org/"+code;
} else {
return "https://loinc.org/";
}
} else if ("http://www.nlm.nih.gov/research/umls/rxnorm".equals(system)) {
if (!Utilities.noString(code)) {
return "https://mor.nlm.nih.gov/RxNav/search?searchBy=RXCUI&searchTerm="+code;
} else {
return "https://www.nlm.nih.gov/research/umls/rxnorm/index.html";
}
} else {
CodeSystem cs = context.getWorker().fetchCodeSystem(system, version);
if (cs != null && cs.hasUserData("path")) {
if (!Utilities.noString(code)) {
return cs.getUserString("path")+"#"+Utilities.nmtokenize(code);
} else {
return cs.getUserString("path");
}
}
}
return null;
}
protected void renderCodingWithDetails(XhtmlNode x, Coding c) {
String s = "";
if (c.hasDisplayElement())
@ -650,22 +752,13 @@ public class DataRenderer extends Renderer {
String sn = describeSystem(c.getSystem());
if ("http://snomed.info/sct".equals(c.getSystem())) {
if (c.hasCode()) {
x.ah("http://snomed.info/id/"+c.getCode()).tx(sn);
} else {
x.ah("https://browser.ihtsdotools.org/").tx(sn);
}
} else if ("http://loinc.org".equals(c.getSystem())) {
x.ah("https://loinc.org/").tx(sn);
} else {
CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem());
if (cs != null && cs.hasUserData("path")) {
x.ah(cs.getUserString("path")).tx(sn);
String link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode());
if (link != null) {
x.ah(link).tx(sn);
} else {
x.tx(sn);
}
}
x.tx(" ");
x.tx(c.getCode());
if (!Utilities.noString(s)) {
@ -903,6 +996,57 @@ public class DataRenderer extends Renderer {
return s.toString();
}
protected String getLocalizedBigDecimalValue(BigDecimal input, Currency c) {
NumberFormat numberFormat = NumberFormat.getNumberInstance(context.getLocale());
numberFormat.setGroupingUsed(true);
numberFormat.setMaximumFractionDigits(c.getDefaultFractionDigits());
numberFormat.setMinimumFractionDigits(c.getDefaultFractionDigits());
return numberFormat.format(input);
}
protected void renderMoney(XhtmlNode x, Money money) {
Currency c = Currency.getInstance(money.getCurrency());
if (c != null) {
XhtmlNode s = x.span(null, c.getDisplayName());
s.tx(c.getSymbol(context.getLocale()));
s.tx(getLocalizedBigDecimalValue(money.getValue(), c));
x.tx(" ("+c.getCurrencyCode()+")");
} else {
x.tx(money.getCurrency());
x.tx(money.getValue().toPlainString());
}
}
protected void renderExpression(XhtmlNode x, Expression expr) {
// there's two parts: what the expression is, and how it's described.
// we start with what it is, and then how it's desceibed
if (expr.hasExpression()) {
XhtmlNode c = x;
if (expr.hasReference()) {
c = x.ah(expr.getReference());
}
if (expr.hasLanguage()) {
c = c.span(null, expr.getLanguage());
}
c.code().tx(expr.getExpression());
} else if (expr.hasReference()) {
x.ah(expr.getReference()).tx("source");
}
if (expr.hasName() || expr.hasDescription()) {
x.tx("(");
if (expr.hasName()) {
x.b().tx(expr.getName());
}
if (expr.hasDescription()) {
x.tx("\"");
x.tx(expr.getDescription());
x.tx("\"");
}
x.tx(")");
}
}
protected void renderContactPoint(XhtmlNode x, ContactPoint contact) {
if (contact != null) {
if (!contact.hasSystem()) {
@ -1046,19 +1190,19 @@ public class DataRenderer extends Renderer {
x.tx(" "+q.getLow().getUnit());
}
public static String displayPeriod(Period p) {
String s = !p.hasStart() ? "(?)" : p.getStartElement().toHumanDisplay();
public String displayPeriod(Period p) {
String s = !p.hasStart() ? "(?)" : displayDateTime(p.getStartElement());
s = s + " --> ";
return s + (!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay());
return s + (!p.hasEnd() ? "(ongoing)" : displayDateTime(p.getEndElement()));
}
public void renderPeriod(XhtmlNode x, Period p) {
x.addText(!p.hasStart() ? "??" : p.getStartElement().toHumanDisplay());
x.addText(!p.hasStart() ? "??" : displayDateTime(p.getStartElement()));
x.tx(" --> ");
x.addText(!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay());
x.addText(!p.hasEnd() ? "(ongoing)" : displayDateTime(p.getEndElement()));
}
public void renderDataRequirement(XhtmlNode x, DataRequirement dr) {
public void renderDataRequirement(XhtmlNode x, DataRequirement dr) throws FHIRFormatError, DefinitionException, IOException {
XhtmlNode tbl = x.table("grid");
XhtmlNode tr = tbl.tr();
XhtmlNode td = tr.td().colspan("2");
@ -1163,7 +1307,7 @@ public class DataRenderer extends Renderer {
CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder();
for (DateTimeType p : s.getEvent()) {
if (p.hasValue()) {
c.append(p.toHumanDisplay());
c.append(displayDateTime(p));
} else if (!renderExpression(c, p)) {
c.append("??");
}
@ -1174,7 +1318,7 @@ public class DataRenderer extends Renderer {
if (s.hasRepeat()) {
TimingRepeatComponent rep = s.getRepeat();
if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasStart())
b.append("Starting "+rep.getBoundsPeriod().getStartElement().toHumanDisplay());
b.append("Starting "+displayDateTime(rep.getBoundsPeriod().getStartElement()));
if (rep.hasCount())
b.append("Count "+Integer.toString(rep.getCount())+" times");
if (rep.hasDuration())
@ -1206,7 +1350,7 @@ public class DataRenderer extends Renderer {
b.append("Do "+st);
}
if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasEnd())
b.append("Until "+rep.getBoundsPeriod().getEndElement().toHumanDisplay());
b.append("Until "+displayDateTime(rep.getBoundsPeriod().getEndElement()));
}
return b.toString();
}

View File

@ -59,7 +59,8 @@ public class ListRenderer extends ResourceRenderer {
shortForRef(td, list.get("subject"));
}
if (list.has("encounter")) {
shortForRef(td.tx("Encounter: "), list.get("encounter"));
td.tx("Encounter: ");
shortForRef(td, list.get("encounter"));
}
if (list.has("source")) {
td.tx("Source: ");

View File

@ -33,12 +33,14 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Dosage;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Expression;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.HumanName;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.InstantType;
import org.hl7.fhir.r5.model.Meta;
import org.hl7.fhir.r5.model.Money;
import org.hl7.fhir.r5.model.Narrative;
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
@ -118,6 +120,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), context.isTechnicalMode(), 0);
}
} catch (Exception e) {
System.out.println("Error Generating Narrative for "+r.fhirType()+"/"+r.getId()+": "+e.getMessage());
e.printStackTrace();
x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
}
@ -291,7 +294,6 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (ew == null)
return;
Base e = ew.getBase();
if (e instanceof StringType)
@ -308,9 +310,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
renderDateTime(x, e);
} else if (e instanceof Base64BinaryType)
x.addText(new Base64().encodeAsString(((Base64BinaryType) e).getValue()));
else if (e instanceof org.hl7.fhir.r5.model.DateType)
else if (e instanceof org.hl7.fhir.r5.model.DateType) {
org.hl7.fhir.r5.model.DateType dt = ((org.hl7.fhir.r5.model.DateType) e);
if (((org.hl7.fhir.r5.model.DateType) e).hasValue()) {
x.addText(((org.hl7.fhir.r5.model.DateType) e).toHumanDisplay());
else if (e instanceof Enumeration) {
}
} else if (e instanceof Enumeration) {
Object ev = ((Enumeration<?>) e).getValue();
x.addText(ev == null ? "" : ev.toString()); // todo: look up a display name if there is one
} else if (e instanceof BooleanType) {
@ -347,6 +352,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
renderAddress(x, (Address) e);
} else if (e instanceof ContactPoint) {
renderContactPoint(x, (ContactPoint) e);
} else if (e instanceof Expression) {
renderExpression(x, (Expression) e);
} else if (e instanceof Money) {
renderMoney(x, (Money) e);
} else if (e instanceof ContactDetail) {
ContactDetail cd = (ContactDetail) e;
if (cd.hasName()) {

View File

@ -703,7 +703,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
}
if (i.hasAnswerValueSet()) {
XhtmlNode ans = item(ul, "Answers");
if (Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
if (!Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
ValueSet vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
if (vs == null) {
ans.tx(i.getAnswerValueSet());
@ -792,7 +792,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
private void listOptions(Questionnaire q, QuestionnaireItemComponent i, XhtmlNode select) {
if (i.hasAnswerValueSet()) {
ValueSet vs = null;
if (Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
if (!Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
if (vs != null && !vs.hasUrl()) {
vs = vs.copy();

View File

@ -214,6 +214,11 @@ public abstract class ResourceRenderer extends DataRenderer {
if ((tr == null || !tr.getReference().startsWith("#")) && name != null) {
x.addText(" \""+name+"\"");
}
if (r.hasExtension(ToolingExtensions.EXT_TARGET_ID)) {
x.addText("(#"+r.getExtensionString(ToolingExtensions.EXT_TARGET_ID)+")");
} else if (r.hasExtension(ToolingExtensions.EXT_TARGET_PATH)) {
x.addText("(#/"+r.getExtensionString(ToolingExtensions.EXT_TARGET_PATH)+")");
}
} else {
if (display != null) {
c.addText(display);
@ -272,6 +277,11 @@ public abstract class ResourceRenderer extends DataRenderer {
}
return null;
}
String version = null;
if (url.contains("/_history/")) {
version = url.substring(url.indexOf("/_history/")+10);
url = url.substring(0, url.indexOf("/_history/"));
}
if (rcontext != null) {
BundleEntryComponent bundleResource = rcontext.resolve(url);
@ -279,7 +289,7 @@ public abstract class ResourceRenderer extends DataRenderer {
String bundleUrl = "#" + bundleResource.getResource().getResourceType().name() + "_" + bundleResource.getResource().getId();
return new ResourceWithReference(bundleUrl, new ResourceWrapperDirect(this.context, bundleResource.getResource()));
}
org.hl7.fhir.r5.elementmodel.Element bundleElement = rcontext.resolveElement(url);
org.hl7.fhir.r5.elementmodel.Element bundleElement = rcontext.resolveElement(url, version);
if (bundleElement != null) {
String bundleUrl = null;
Element br = bundleElement.getNamedChild("resource");
@ -292,7 +302,7 @@ public abstract class ResourceRenderer extends DataRenderer {
}
}
Resource ae = getContext().getWorker().fetchResource(null, url);
Resource ae = getContext().getWorker().fetchResource(null, url, version);
if (ae != null)
return new ResourceWithReference(url, new ResourceWrapperDirect(this.context, ae));
else if (context.getResolver() != null) {
@ -473,10 +483,10 @@ public abstract class ResourceRenderer extends DataRenderer {
}
private String getPrimitiveValue(BaseWrapper b, String name) throws UnsupportedEncodingException, FHIRException, IOException {
return b != null && b.getChildByName(name).hasValues() ? b.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
return b != null && b.has(name) && b.getChildByName(name).hasValues() ? b.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
}
private String getPrimitiveValue(ResourceWrapper r, String name) throws UnsupportedEncodingException, FHIRException, IOException {
return r.getChildByName(name).hasValues() ? r.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
return r.has(name) && r.getChildByName(name).hasValues() ? r.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
}
}

View File

@ -176,23 +176,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
else
x.para().tx("This value set contains "+count.toString()+" concepts");
}
if (ToolingExtensions.hasExtension(vs.getExpansion(), ToolingExtensions.EXT_EXP_FRAGMENT)) {
XhtmlNode div = x.div().style("border: maroon 1px solid; background-color: #FFCCCC; padding: 8px");
List<Extension> exl = vs.getExpansion().getExtensionsByUrl(ToolingExtensions.EXT_EXP_FRAGMENT);
if (exl.size() > 1) {
div.para().addText("Warning: this expansion is generated from fragments of the following code systems, and may be missing codes, or include codes that are not valid:");
XhtmlNode ul = div.ul();
for (Extension ex : exl) {
addCSRef(ul.li(), ex.getValue().primitiveValue());
}
} else {
XhtmlNode p = div.para();
p.addText("Warning: this expansion is generated from a fragment of the code system ");
addCSRef(p, exl.get(0).getValue().primitiveValue());
p.addText(" and may be missing codes, or include codes that are not valid");
}
}
generateContentModeNotices(x, vs.getExpansion());
generateVersionNotice(x, vs.getExpansion());
CodeSystem allCS = null;
@ -273,6 +258,46 @@ public class ValueSetRenderer extends TerminologyRenderer {
return hasExtensions;
}
private void generateContentModeNotices(XhtmlNode x, ValueSetExpansionComponent expansion) {
generateContentModeNotice(x, expansion, "example", "Expansion based on example code system");
generateContentModeNotice(x, expansion, "fragment", "Expansion based on code system fragment");
}
private void generateContentModeNotice(XhtmlNode x, ValueSetExpansionComponent expansion, String mode, String text) {
Multimap<String, String> versions = HashMultimap.create();
for (ValueSetExpansionParameterComponent p : expansion.getParameter()) {
if (p.getName().equals(mode)) {
String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|");
if (parts.length == 2)
versions.put(parts[0], parts[1]);
}
}
if (versions.size() > 0) {
XhtmlNode div = null;
XhtmlNode ul = null;
boolean first = true;
for (String s : versions.keySet()) {
if (versions.size() == 1 && versions.get(s).size() == 1) {
for (String v : versions.get(s)) { // though there'll only be one
XhtmlNode p = x.para().style("border: black 1px dotted; background-color: #ffcccc; padding: 8px; margin-bottom: 8px");
p.tx(text+" ");
expRef(p, s, v);
}
} else {
for (String v : versions.get(s)) {
if (first) {
div = x.div().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
div.para().tx(text+"s: ");
ul = div.ul();
first = false;
}
expRef(ul.li(), s, v);
}
}
}
}
}
private boolean checkDoSystem(ValueSet vs, ValueSet src) {
if (src != null)
vs = src;

View File

@ -49,7 +49,7 @@ public class DOMWrappers {
@Override
public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException {
if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
if (Utilities.noString(type) || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
return null;
String xml;

View File

@ -1,8 +1,14 @@
package org.hl7.fhir.r5.renderers.utils;
import java.io.IOException;
import java.text.DateFormat;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
@ -117,6 +123,11 @@ public class RenderingContext {
private boolean addGeneratedNarrativeHeader = true;
private FhirPublication targetVersion;
private Locale locale;
private ZoneId timeZoneId;
private DateTimeFormatter dateTimeFormat;
private DateTimeFormatter dateFormat;
/**
*
* @param context - access to all related resources that might be needed
@ -136,6 +147,8 @@ public class RenderingContext {
if (terminologyServiceOptions != null) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
// default to US locale - discussion here: https://github.com/hapifhir/org.hl7.fhir.core/issues/666
this.locale = new Locale.Builder().setLanguageTag("en-US").build();
profileUtilities = new ProfileUtilities(worker, null, null);
}
@ -427,4 +440,79 @@ public class RenderingContext {
return mode == ResourceRendererMode.TECHNICAL;
}
public boolean hasLocale() {
return locale != null;
}
public Locale getLocale() {
if (locale == null) {
return Locale.getDefault();
} else {
return locale;
}
}
public void setLocale(Locale locale) {
this.locale = locale;
}
/**
* if the timezone is null, the rendering will default to the source timezone
* in the resource
*
* Note that if you're working server side, the FHIR project recommends the use
* of the Date header so that clients know what timezone the server defaults to,
*
* There is no standard way for the server to know what the client timezone is.
* In the case where the client timezone is unknown, the timezone should be null
*
* @return the specified timezone to render in
*/
public ZoneId getTimeZoneId() {
return timeZoneId;
}
public void setTimeZoneId(ZoneId timeZoneId) {
this.timeZoneId = timeZoneId;
}
/**
* In the absence of a specified format, the renderers will default to
* the FormatStyle.MEDIUM for the current locale.
*
* @return the format to use
*/
public DateTimeFormatter getDateTimeFormat() {
return this.dateTimeFormat;
}
public void setDateTimeFormat(DateTimeFormatter dateTimeFormat) {
this.dateTimeFormat = dateTimeFormat;
}
/**
* In the absence of a specified format, the renderers will default to
* the FormatStyle.MEDIUM for the current locale.
*
* @return the format to use
*/
public DateTimeFormatter getDateFormat() {
return this.dateFormat;
}
public void setDateFormat(DateTimeFormatter dateFormat) {
this.dateFormat = dateFormat;
}
public ResourceRendererMode getMode() {
return mode;
}
public void setMode(ResourceRendererMode mode) {
this.mode = mode;
}
}

View File

@ -101,7 +101,7 @@ public class Resolver {
return null;
}
public org.hl7.fhir.r5.elementmodel.Element resolveElement(String value) {
public org.hl7.fhir.r5.elementmodel.Element resolveElement(String value, String version) {
if (value.startsWith("#")) {
if (resourceElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element r : resourceElement.getChildrenByName("contained")) {
@ -115,24 +115,46 @@ public class Resolver {
if (containerElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element be : containerElement.getChildren("entry")) {
org.hl7.fhir.r5.elementmodel.Element res = be.getNamedChild("resource");
if (value.equals(be.getChildValue("fullUrl")))
if (res != null) {
if (value.equals(be.getChildValue("fullUrl"))) {
if (checkVersion(version, res)) {
return be;
if (value.equals(res.fhirType()+"/"+res.getChildValue("id")))
}
}
if (value.equals(res.fhirType()+"/"+res.getChildValue("id"))) {
if (checkVersion(version, res)) {
return be;
}
}
}
}
}
}
if (type == ResourceContextType.PARAMETERS) {
if (containerElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element p : containerElement.getChildren("parameter")) {
org.hl7.fhir.r5.elementmodel.Element res = p.getNamedChild("resource");
if (res != null && value.equals(res.fhirType()+"/"+res.getChildValue("id")))
if (res != null && value.equals(res.fhirType()+"/"+res.getChildValue("id"))) {
if (checkVersion(version, res)) {
return p;
}
}
}
}
}
return null;
}
private boolean checkVersion(String version, org.hl7.fhir.r5.elementmodel.Element res) {
if (version == null) {
return true;
} else if (!res.hasChild("meta")) {
return false;
} else {
org.hl7.fhir.r5.elementmodel.Element meta = res.getNamedChild("meta");
return version.equals(meta.getChildValue("version"));
}
}
}
public static class ResourceWithReference {

View File

@ -33,6 +33,8 @@ package org.hl7.fhir.r5.terminologies;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -51,6 +53,7 @@ import org.hl7.fhir.r5.model.CodeType;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.Meta;
import org.hl7.fhir.r5.model.UriType;
@ -60,6 +63,15 @@ import org.hl7.fhir.utilities.Utilities;
public class CodeSystemUtilities {
public static class ConceptDefinitionComponentSorter implements Comparator<ConceptDefinitionComponent> {
@Override
public int compare(ConceptDefinitionComponent o1, ConceptDefinitionComponent o2) {
return o1.getCode().compareTo(o2.getCode());
}
}
public static final String USER_DATA_CROSS_LINK = "cs.utils.cross.link";
public static class CodeSystemNavigator {
@ -128,7 +140,7 @@ public class CodeSystemUtilities {
private List<String> getSubsumedBy(ConceptDefinitionComponent cd) {
List<String> codes = new ArrayList<>();
for (ConceptPropertyComponent cp : cd.getProperty()) {
if (cp.getCode().equals("subsumedBy")) {
if ("subsumedBy".equals(cp.getCode())) {
codes.add(cp.getValue().primitiveValue());
}
}
@ -149,7 +161,7 @@ public class CodeSystemUtilities {
public static boolean isNotSelectable(CodeSystem cs, ConceptDefinitionComponent def) {
for (ConceptPropertyComponent p : def.getProperty()) {
if (p.getCode().equals("notSelectable") && p.hasValue() && p.getValue() instanceof BooleanType)
if ("notSelectable".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType)
return ((BooleanType) p.getValue()).getValue();
}
return false;
@ -212,14 +224,14 @@ public class CodeSystemUtilities {
try {
for (ConceptPropertyComponent p : def.getProperty()) {
if (!ignoreStatus) {
if (p.getCode().equals("status") && p.hasValue() && p.hasValueCodeType() && p.getValueCodeType().getCode().equals("deprecated"))
if ("status".equals(p.getCode()) && p.hasValue() && p.hasValueCodeType() && "deprecated".equals(p.getValueCodeType().getCode()))
return true;
}
// this, though status should also be set
if (p.getCode().equals("deprecationDate") && p.hasValue() && p.getValue() instanceof DateTimeType)
if ("deprecationDate".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof DateTimeType)
return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance()));
// legacy
if (p.getCode().equals("deprecated") && p.hasValue() && p.getValue() instanceof BooleanType)
if ("deprecated".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType)
return ((BooleanType) p.getValue()).getValue();
}
return false;
@ -236,7 +248,7 @@ public class CodeSystemUtilities {
public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def) throws FHIRException {
for (ConceptPropertyComponent p : def.getProperty()) {
if (p.getCode().equals("status") && p.hasValueStringType())
if ("status".equals(p.getCode()) && p.hasValueStringType())
return "inactive".equals(p.getValueStringType());
}
return false;
@ -273,10 +285,14 @@ public class CodeSystemUtilities {
}
public static CodeSystem makeShareable(CodeSystem cs) {
if (!cs.hasExperimental()) {
cs.setExperimental(false);
}
if (!cs.hasMeta())
cs.setMeta(new Meta());
for (UriType t : cs.getMeta().getProfile())
if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"))
if ("http://hl7.org/fhir/StructureDefinition/shareablecodesystem".equals(t.getValue()))
return cs;
cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"));
return cs;
@ -286,7 +302,7 @@ public class CodeSystemUtilities {
if (!cs.hasMeta())
cs.setMeta(new Meta());
for (UriType t : cs.getMeta().getProfile())
if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"))
if ("http://hl7.org/fhir/StructureDefinition/shareablecodesystem".equals(t.getValue()))
return false;
cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"));
return true;
@ -350,9 +366,13 @@ public class CodeSystemUtilities {
}
if (fmm != null) {
String sfmm = ToolingExtensions.readStringExtension(cs, ToolingExtensions.EXT_FMM_LEVEL);
if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm))
if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) {
ToolingExtensions.setIntegerExtension(cs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm));
}
if (Integer.parseInt(fmm) <= 1) {
cs.setExperimental(true);
}
}
}
@ -491,4 +511,26 @@ public class CodeSystemUtilities {
}
public static boolean hasHierarchy(CodeSystem cs) {
for (ConceptDefinitionComponent c : cs.getConcept()) {
if (c.hasConcept()) {
return true;
}
}
return false;
}
public static void sortAllCodes(CodeSystem cs) {
sortAllCodes(cs.getConcept());
}
private static void sortAllCodes(List<ConceptDefinitionComponent> list) {
Collections.sort(list, new ConceptDefinitionComponentSorter());
for (ConceptDefinitionComponent cd : list) {
if (cd.hasConcept()) {
sortAllCodes(cd.getConcept());
}
}
}
}

View File

@ -17,6 +17,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@ -75,7 +76,8 @@ public class TerminologyCacheManager {
}
IniFile ini = new IniFile(Utilities.path(cacheFolder, "cache.ini"));
ini.setStringProperty("version", "version", version, null);
ini.setStringProperty("cache", "version", version, null);
ini.setDateProperty("cache", "last-use", new Date(), null);
ini.save();
}
@ -116,7 +118,7 @@ public class TerminologyCacheManager {
private String getCacheVersion() throws IOException {
IniFile ini = new IniFile(Utilities.path(cacheFolder, "cache.ini"));
return ini.getStringProperty("version", "version");
return ini.getStringProperty("cache", "version");
}
public String getFolder() {

View File

@ -56,6 +56,9 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.ValidationContextResourceProxy;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
@ -69,6 +72,8 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
private IWorkerContext context;
private Map<String, ValueSetCheckerSimple> inner = new HashMap<>();
private ValidationOptions options;
private ValidationContextCarrier localContext;
private List<CodeSystem> localSystems = new ArrayList<>();
public ValueSetCheckerSimple(ValidationOptions options, ValueSet source, IWorkerContext context) {
this.valueset = source;
@ -76,6 +81,44 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
this.options = options;
}
public ValueSetCheckerSimple(ValidationOptions options, ValueSet source, IWorkerContext context, ValidationContextCarrier ctxt) {
this.valueset = source;
this.context = context;
this.options = options;
this.localContext = ctxt;
analyseValueSet();
}
private void analyseValueSet() {
if (localContext != null) {
if (valueset != null) {
for (ConceptSetComponent i : valueset.getCompose().getInclude()) {
analyseComponent(i);
}
for (ConceptSetComponent i : valueset.getCompose().getExclude()) {
analyseComponent(i);
}
}
}
}
private void analyseComponent(ConceptSetComponent i) {
if (i.getSystemElement().hasExtension(ToolingExtensions.EXT_VALUESET_SYSTEM)) {
String ref = i.getSystemElement().getExtensionString(ToolingExtensions.EXT_VALUESET_SYSTEM);
if (ref.startsWith("#")) {
String id = ref.substring(1);
for (ValidationContextResourceProxy t : localContext.getResources()) {
CodeSystem cs = (CodeSystem) t.loadContainedResource(id, CodeSystem.class);
if (cs != null) {
localSystems.add(cs);
}
}
} else {
throw new Error("Not done yet #2: "+ref);
}
}
}
public ValidationResult validateCode(CodeableConcept code) throws FHIRException {
// first, we validate the codings themselves
List<String> errors = new ArrayList<String>();
@ -85,7 +128,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (!c.hasSystem()) {
warnings.add(context.formatMessage(I18nConstants.CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE));
}
CodeSystem cs = context.fetchCodeSystem(c.getSystem());
CodeSystem cs = resolveCodeSystem(c.getSystem());
ValidationResult res = null;
if (cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) {
res = context.validateCode(options.noClient(), c, null);
@ -124,6 +167,19 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
}
public CodeSystem resolveCodeSystem(String system) {
for (CodeSystem t : localSystems) {
if (t.getUrl().equals(system)) {
return t;
}
}
CodeSystem cs = context.fetchCodeSystem(system);
if (cs == null) {
cs = findSpecialCodeSystem(system);
}
return cs;
}
public ValidationResult validateCode(Coding code) throws FHIRException {
String warningMessage = null;
// first, we validate the concept itself
@ -144,10 +200,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
inExpansion = checkExpansion(code);
inInclude = checkInclude(code);
CodeSystem cs = context.fetchCodeSystem(system);
if (cs == null) {
cs = findSpecialCodeSystem(system);
}
CodeSystem cs = resolveCodeSystem(system);
if (cs == null) {
warningMessage = "Unable to resolve system "+system;
if (!inExpansion) {
@ -498,7 +551,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (vsi.hasFilter()) {
return null;
}
CodeSystem cs = context.fetchCodeSystem(vsi.getSystem());
CodeSystem cs = resolveCodeSystem(vsi.getSystem());
if (cs == null) {
return null;
}
@ -604,7 +657,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (!system.equals(vsi.getSystem()))
return false;
// ok, we need the code system
CodeSystem cs = context.fetchCodeSystem(system);
CodeSystem cs = resolveCodeSystem(system);
if (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT)) {
// make up a transient value set with
ValueSet vs = new ValueSet();
@ -709,7 +762,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return inner.get(url);
}
ValueSet vs = context.fetchResource(ValueSet.class, url);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context, localContext);
inner.put(url, vsc);
return vsc;
}

View File

@ -634,6 +634,9 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs);
}
if (cs.getContent() == CodeSystemContentMode.EXAMPLE) {
addExampleWarning(exp, cs);
}
}
if (!inc.getConcept().isEmpty()) {
@ -645,6 +648,8 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
if (def == null) {
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs);
} else if (cs.getContent() == CodeSystemContentMode.EXAMPLE) {
addExampleWarning(exp, cs);
} else {
if (checkCodesWhenExpanding) {
throw failTSE("Unable to find code '" + c.getCode() + "' in code system " + cs.getUrl());
@ -733,12 +738,23 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
private void addFragmentWarning(ValueSetExpansionComponent exp, CodeSystem cs) {
for (Extension ex : cs.getExtensionsByUrl(ToolingExtensions.EXT_EXP_FRAGMENT)) {
if (ex.getValue().primitiveValue().equals(cs.getUrl())) {
String url = cs.getVersionedUrl();
for (ValueSetExpansionParameterComponent p : exp.getParameter()) {
if ("fragment".equals(p.getName()) && p.hasValueUriType() && url.equals(p.getValue().primitiveValue())) {
return;
}
}
exp.addExtension(new Extension(ToolingExtensions.EXT_EXP_FRAGMENT).setValue(new UriType(cs.getUrl())));
exp.addParameter().setName("fragment").setValue(new UriType(url));
}
private void addExampleWarning(ValueSetExpansionComponent exp, CodeSystem cs) {
String url = cs.getVersionedUrl();
for (ValueSetExpansionParameterComponent p : exp.getParameter()) {
if ("example".equals(p.getName()) && p.hasValueUriType() && url.equals(p.getValue().primitiveValue())) {
return;
}
}
exp.addParameter().setName("example").setValue(new UriType(url));
}
private List<ConceptDefinitionDesignationComponent> convertDesignations(List<ConceptReferenceDesignationComponent> list) {

View File

@ -48,6 +48,9 @@ import org.hl7.fhir.utilities.Utilities;
public class ValueSetUtilities {
public static ValueSet makeShareable(ValueSet vs) {
if (!vs.hasExperimental()) {
vs.setExperimental(false);
}
if (!vs.hasMeta())
vs.setMeta(new Meta());
for (UriType t : vs.getMeta().getProfile())
@ -129,9 +132,13 @@ public class ValueSetUtilities {
}
if (fmm != null) {
String sfmm = ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_FMM_LEVEL);
if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm))
if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) {
ToolingExtensions.setIntegerExtension(vs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm));
}
if (Integer.parseInt(fmm) <= 1) {
vs.setExperimental(true);
}
}
if (vs.hasUserData("cs"))
CodeSystemUtilities.markStatus((CodeSystem) vs.getUserData("cs"), wg, status, pckage, fmm, normativeVersion);
else if (status == StandardsStatus.NORMATIVE && context != null) {

View File

@ -151,13 +151,14 @@ public class TestingUtilities extends BaseTestingUtilities {
String result = compareXml(f1, f2);
if (result != null && SHOW_DIFF) {
String diff = Utilities.path(System.getenv("ProgramFiles"), "WinMerge", "WinMergeU.exe");
if (new File(diff).exists()) {
List<String> command = new ArrayList<String>();
command.add("\"" + diff + "\" \"" + f1 + "\" \"" + f2 + "\"");
ProcessBuilder builder = new ProcessBuilder(command);
builder.directory(new CSFile("c:\\temp"));
builder.start();
}
}
return result;
}
@ -171,7 +172,7 @@ public class TestingUtilities extends BaseTestingUtilities {
}
private static String compareElements(String path, Element e1, Element e2) {
if (!e1.getNamespaceURI().equals(e2.getNamespaceURI()))
if (!namespacesMatch(e1.getNamespaceURI(), e2.getNamespaceURI()))
return "Namespaces differ at " + path + ": " + e1.getNamespaceURI() + "/" + e2.getNamespaceURI();
if (!e1.getLocalName().equals(e2.getLocalName()))
return "Names differ at " + path + ": " + e1.getLocalName() + "/" + e2.getLocalName();
@ -209,6 +210,10 @@ public class TestingUtilities extends BaseTestingUtilities {
return null;
}
private static boolean namespacesMatch(String ns1, String ns2) {
return ns1 == null ? ns2 == null : ns1.equals(ns2);
}
private static Object normalise(String text) {
String result = text.trim().replace('\r', ' ').replace('\n', ' ').replace('\t', ' ');
while (result.contains(" "))

View File

@ -5539,7 +5539,7 @@ public class FHIRPathEngine {
throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_NAME, expr.getName());
}
} else if (expr.getKind() == Kind.Group) {
throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP);
throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP, expr.toString());
} else if (expr.getKind() == Kind.Constant) {
throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST);
}

View File

@ -40,6 +40,7 @@ public class IGHelper {
public static final String EXT_MAPPING_CSV = ToolingExtensions.EXT_IGP_MAPPING_CSV;
public static final String EXT_BUNDLE = ToolingExtensions.EXT_IGP_BUNDLE;
public static final String EXT_RESOURCE_INFO = ToolingExtensions.EXT_IGP_RESOURCE_INFO;
public static final String EXT_CONTAINED_RESOURCE_INFO = ToolingExtensions.EXT_IGP_CONTAINED_RESOURCE_INFO;
public static final String EXT_PRIVATE_BASE = ToolingExtensions.EXT_PRIVATE_BASE;
public static String readStringParameter(ImplementationGuideDefinitionComponent ig, String name) {

View File

@ -276,7 +276,7 @@ public class NPMPackageGenerator {
return "hl7.fhir.r3.core";
if (v.startsWith("4.0"))
return "hl7.fhir.r4.core";
if (v.startsWith("4.1"))
if (v.startsWith("4.1") || v.startsWith("4.3"))
return "hl7.fhir.r4b.core";
return null;
}

View File

@ -230,6 +230,7 @@ public class QuestionnaireBuilder {
questionnaire.setPublisher(profile.getPublisher());
Questionnaire.QuestionnaireItemComponent item = new Questionnaire.QuestionnaireItemComponent();
questionnaire.addItem(item);
item.setLinkId("meta");
item.getCode().addAll(profile.getKeyword());
questionnaire.setId(nextId("qs"));
}
@ -241,6 +242,7 @@ public class QuestionnaireBuilder {
response.setStatus(QuestionnaireResponseStatus.INPROGRESS);
QuestionnaireResponse.QuestionnaireResponseItemComponent item = new QuestionnaireResponse.QuestionnaireResponseItemComponent();
response.addItem(item);
item.setLinkId("meta");
item.setUserData("object", resource);
}
@ -260,9 +262,10 @@ public class QuestionnaireBuilder {
display.setType(QuestionnaireItemType.DISPLAY);
display.setText(element.getComment());
group.addItem(display);
display.setLinkId(element.getId()+"-display");
}
group.setType(QuestionnaireItemType.GROUP);
ToolingExtensions.addFlyOver(group, element.getDefinition());
ToolingExtensions.addFlyOver(group, element.getDefinition(), element.getId()+"-flyover");
group.setRequired(element.getMin() > 0);
if (element.getMin() > 0)
ToolingExtensions.addMin(group, element.getMin());
@ -284,10 +287,11 @@ public class QuestionnaireBuilder {
nparents.addAll(parents);
nparents.add(child);
QuestionnaireItemComponent childGroup = group.addItem();
childGroup.setLinkId(child.getId()+"-grp");
childGroup.setType(QuestionnaireItemType.GROUP);
List<QuestionnaireResponse.QuestionnaireResponseItemComponent> nResponse = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>();
processExisting(child.getPath(), answerGroups, nResponse);
processExisting(child.getPath(), answerGroups, childGroup, nResponse);
// if the element has a type, we add a question. else we add a group on the basis that
// it will have children of its own
if (child.getType().isEmpty() || isAbstractType(child.getType()))
@ -334,13 +338,14 @@ public class QuestionnaireBuilder {
return path.substring(path.lastIndexOf('.')+1);
}
private void processExisting(String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> nResponse) throws FHIRException {
private void processExisting(String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, QuestionnaireItemComponent item, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> nResponse) throws FHIRException {
// processing existing data
for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) {
List<Base> children = ((Element) ag.getUserData("object")).listChildrenByName(tail(path));
for (Base child : children) {
if (child != null) {
QuestionnaireResponse.QuestionnaireResponseItemComponent ans = ag.addItem();
ag.setLinkId(item.getLinkId());
ans.setUserData("object", child);
nResponse.add(ans);
}
@ -366,9 +371,9 @@ public class QuestionnaireBuilder {
}
if (!Utilities.noString(element.getComment()))
ToolingExtensions.addFlyOver(group, element.getDefinition()+" "+element.getComment());
ToolingExtensions.addFlyOver(group, element.getDefinition()+" "+element.getComment(), group.getLinkId()+"-flyover");
else
ToolingExtensions.addFlyOver(group, element.getDefinition());
ToolingExtensions.addFlyOver(group, element.getDefinition(), group.getLinkId()+"-flyover");
if (element.getType().size() > 1 || element.getType().get(0).getWorkingCode().equals("*")) {
List<TypeRefComponent> types = expandTypeList(element.getType());

View File

@ -69,7 +69,9 @@ import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.fhir.ucum.Utilities;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeType;
@ -89,6 +91,7 @@ import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.MarkdownType;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Property;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.r5.model.StringType;
@ -166,6 +169,7 @@ public class ToolingExtensions {
public static final String EXT_IGP_MAPPING_CSV = "http://hl7.org/fhir/StructureDefinition/igpublisher-mapping-csv";
public static final String EXT_IGP_BUNDLE = "http://hl7.org/fhir/StructureDefinition/igpublisher-bundle";
public static final String EXT_IGP_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/resource-information";
public static final String EXT_IGP_CONTAINED_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/contained-resource-information";
public static final String EXT_IGP_LOADVERSION = "http://hl7.org/fhir/StructureDefinition/igpublisher-loadversion";
public static final String EXT_MAX_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet";
public static final String EXT_MIN_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-minValueSet";
@ -182,7 +186,6 @@ public class ToolingExtensions {
public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type";
public static final String EXT_RENDERED_VALUE = "http://hl7.org/fhir/StructureDefinition/rendered-value";
public static final String EXT_OLD_CONCEPTMAP_EQUIVALENCE = "http://hl7.org/fhir/1.0/StructureDefinition/extension-ConceptMap.element.target.equivalence";
public static final String EXT_EXP_FRAGMENT = "http://hl7.org/fhir/tools/StructureDefinition/expansion-codesystem-fragment";
public static final String EXT_EXP_TOOCOSTLY = "http://hl7.org/fhir/StructureDefinition/valueset-toocostly";
public static final String EXT_MUST_SUPPORT = "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support";
public static final String EXT_TRANSLATABLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-translatable";
@ -192,6 +195,9 @@ public class ToolingExtensions {
public static final String EXT_XML_NAME = "http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name";
public static final String EXT_BINDING_STYLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-binding-style";
public static final String EXT_BINARY_FORMAT = "http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format";
public static final String EXT_TARGET_ID = "http://hl7.org/fhir/StructureDefinition/targetElement";
public static final String EXT_TARGET_PATH = "http://hl7.org/fhir/StructureDefinition/targetPath";
public static final String EXT_VALUESET_SYSTEM = "http://hl7.org/fhir/StructureDefinition/valueset-system";
// specific extension helpers
@ -462,11 +468,12 @@ public class ToolingExtensions {
// return findBooleanExtension(c, EXT_DEPRECATED);
// }
public static void addFlyOver(QuestionnaireItemComponent item, String text){
public static void addFlyOver(QuestionnaireItemComponent item, String text, String linkId){
if (!StringUtils.isBlank(text)) {
QuestionnaireItemComponent display = item.addItem();
display.setType(QuestionnaireItemType.DISPLAY);
display.setText(text);
display.setLinkId(linkId);
display.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept("flyover", "http://hl7.org/fhir/questionnaire-item-control", "Fly-over"), true));
}
}
@ -900,5 +907,25 @@ public class ToolingExtensions {
dr.getExtension().add(Factory.newExtension(url, new UriType(value), true));
}
public static boolean usesExtension(String url, Base base) {
if ("Extension".equals(base.fhirType())) {
Property p = base.getNamedProperty("url");
for (Base b : p.getValues()) {
if (url.equals(b.primitiveValue())) {
return true;
}
}
}
for (Property p : base.children() ) {
for (Base v : p.getValues()) {
if (usesExtension(url, v)) {
return true;
}
}
}
return false;
}
}

View File

@ -35,6 +35,8 @@ import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo;
public class TypesUtilities {
@ -111,7 +113,7 @@ public class TypesUtilities {
res.add(new WildcardInformation("id", TypeClassification.PRIMITIVE));
res.add(new WildcardInformation("instant", TypeClassification.PRIMITIVE));
res.add(new WildcardInformation("integer", TypeClassification.PRIMITIVE));
if (!version.startsWith("4.1")) {
if (!VersionUtilities.isR4BVer(version)) {
res.add(new WildcardInformation("integer64", TypeClassification.PRIMITIVE));
}
res.add(new WildcardInformation("markdown", TypeClassification.PRIMITIVE));
@ -130,6 +132,7 @@ public class TypesUtilities {
res.add(new WildcardInformation("Annotation", TypeClassification.DATATYPE));
res.add(new WildcardInformation("Attachment", TypeClassification.DATATYPE));
res.add(new WildcardInformation("CodeableConcept", TypeClassification.DATATYPE));
res.add(new WildcardInformation("CodeableReference", TypeClassification.DATATYPE));
res.add(new WildcardInformation("Coding", TypeClassification.DATATYPE));
res.add(new WildcardInformation("ContactPoint", TypeClassification.DATATYPE));
res.add(new WildcardInformation("Count", TypeClassification.DATATYPE));
@ -142,6 +145,7 @@ public class TypesUtilities {
res.add(new WildcardInformation("Quantity", TypeClassification.DATATYPE));
res.add(new WildcardInformation("Range", TypeClassification.DATATYPE));
res.add(new WildcardInformation("Ratio", TypeClassification.DATATYPE));
res.add(new WildcardInformation("RatioRange", TypeClassification.DATATYPE));
res.add(new WildcardInformation("Reference", " - a reference to another resource", TypeClassification.DATATYPE));
res.add(new WildcardInformation("SampledData", TypeClassification.DATATYPE));
res.add(new WildcardInformation("Signature", TypeClassification.DATATYPE));

View File

@ -40,7 +40,15 @@ public class XVerExtensionManager {
this.context = context;
}
public boolean isR5(String url) {
String v = url.substring(20, 23);
return "5.0".equals(v);
}
public XVerExtensionStatus status(String url) throws FHIRException {
if (url.length() < 24) {
return XVerExtensionStatus.Invalid;
}
String v = url.substring(20, 23);
if ("5.0".equals(v)) {
v = "4.6"; // for now
@ -125,6 +133,11 @@ public class XVerExtensionManager {
} else {
throw new FHIRException("Internal error - attempt to define extension for "+url+" when it is invalid");
}
if (path.has("modifier") && path.get("modifier").getAsBoolean()) {
ElementDefinition baseDef = new ElementDefinition("Extension");
sd.getDifferential().getElement().add(0, baseDef);
baseDef.setIsModifier(true);
}
return sd;
}

View File

@ -4,7 +4,6 @@ import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.logging.HttpLoggingInterceptor;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.utils.client.EFhirClientException;

View File

@ -2,8 +2,8 @@ package org.hl7.fhir.r5.utils.client.network;
import okhttp3.*;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -22,9 +22,8 @@ public class FhirLoggingInterceptor implements Interceptor {
return this;
}
@NotNull
@Override
public Response intercept(@NotNull Interceptor.Chain chain) throws IOException {
public Response intercept(@Nonnull Interceptor.Chain chain) throws IOException {
// Log Request
Request request = chain.request();
logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()),

View File

@ -35,8 +35,8 @@ public class RetryInterceptor implements Interceptor {
try {
// If we are retrying a failed request that failed due to a bad response from the server, we must close it first
if (response != null) {
System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
+ "> from url -> " + chain.request().url() + ".");
// System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
// + "> from url -> " + chain.request().url() + ".");
response.close();
}
// System.out.println(chain.request().method() + " attempt <" + (retryCounter + 1) + "> to url -> " + chain.request().url());

View File

@ -9,7 +9,7 @@ import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import java.util.ArrayList;

View File

@ -0,0 +1,29 @@
package org.hl7.fhir.r5.utils.validation;
public class BundleValidationRule {
private String rule;
private String profile;
private boolean checked;
public BundleValidationRule(String rule, String profile) {
super();
this.rule = rule;
this.profile = profile;
}
public String getRule() {
return rule;
}
public String getProfile() {
return profile;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.utils;
package org.hl7.fhir.r5.utils.validation;
/*
Copyright (c) 2011+, HL7, Inc.
@ -30,26 +30,18 @@ package org.hl7.fhir.r5.utils;
*/
import com.google.gson.JsonObject;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.CheckDisplayOption;
import org.hl7.fhir.r5.utils.validation.constants.IdStatus;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import com.google.gson.JsonObject;
/**
* Interface to the instance validator. This takes a resource, in one of many forms, and
@ -60,124 +52,21 @@ import com.google.gson.JsonObject;
*/
public interface IResourceValidator {
public class BundleValidationRule {
private String rule;
private String profile;
private boolean checked;
public BundleValidationRule(String rule, String profile) {
super();
this.rule = rule;
this.profile = profile;
}
public String getRule() {
return rule;
}
public String getProfile() {
return profile;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}
public enum ReferenceValidationPolicy {
IGNORE, CHECK_TYPE_IF_EXISTS, CHECK_EXISTS, CHECK_EXISTS_AND_TYPE, CHECK_VALID;
public boolean checkExists() {
return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID || this == CHECK_TYPE_IF_EXISTS;
}
public boolean checkType() {
return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
}
public boolean checkValid() {
return this == CHECK_VALID;
}
}
public interface IValidationProfileUsageTracker {
void recordProfileUsage(StructureDefinition profile, Object appContext, Element element);
}
public interface IValidatorResourceFetcher {
Element fetch(IResourceValidator validator, Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException;
ReferenceValidationPolicy validationPolicy(IResourceValidator validator, Object appContext, String path, String url);
boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException;
byte[] fetchRaw(IResourceValidator validator, String url) throws MalformedURLException, IOException; // for attachment checking
IValidatorResourceFetcher setLocale(Locale locale);
/**
* this is used when the validator encounters a reference to a structure definition, value set or code system at some random URL reference
* while validating.
*
* Added in v5.2.2. return null to leave functionality as it was before then.
*
* @param primitiveValue
* @return an R5 version of the resource
* @throws URISyntaxException
*/
CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException;
/**
* Whether to try calling fetchCanonicalResource for this reference (not whether it will succeed - just throw an exception from fetchCanonicalResource if it doesn't resolve. This is a policy thing.
*
* Added in v5.2.2. return false to leave functionality as it was before then.
*
* @param url
* @return
*/
boolean fetchesCanonicalResource(IResourceValidator validator, String url);
}
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}
enum IdStatus {
OPTIONAL, REQUIRED, PROHIBITED
}
/**
* how much to check displays for coded elements
* @return
*/
CheckDisplayOption getCheckDisplay();
void setCheckDisplay(CheckDisplayOption checkDisplay);
/**
* whether the resource must have an id or not (depends on context)
*
* @return
*/
IdStatus getResourceIdRule();
void setResourceIdRule(IdStatus resourceIdRule);
/**
* whether the validator should enforce best practice guidelines
* as defined by various HL7 committees
*
*/
BestPracticeWarningLevel getBestPracticeWarningLevel();
IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value);
@ -185,48 +74,46 @@ public interface IResourceValidator {
IValidatorResourceFetcher getFetcher();
IResourceValidator setFetcher(IValidatorResourceFetcher value);
IValidationPolicyAdvisor getPolicyAdvisor();
IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor);
IValidationProfileUsageTracker getTracker();
IResourceValidator setTracker(IValidationProfileUsageTracker value);
boolean isNoBindingMsgSuppressed();
IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
public boolean isNoInvariantChecks();
public IResourceValidator setNoInvariantChecks(boolean value) ;
boolean isNoInvariantChecks();
IResourceValidator setNoInvariantChecks(boolean value) ;
public boolean isWantInvariantInMessage();
public IResourceValidator setWantInvariantInMessage(boolean wantInvariantInMessage);
boolean isWantInvariantInMessage();
IResourceValidator setWantInvariantInMessage(boolean wantInvariantInMessage);
public boolean isNoTerminologyChecks();
public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
boolean isNoTerminologyChecks();
IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
public boolean isNoExtensibleWarnings();
public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings);
boolean isNoExtensibleWarnings();
IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings);
public boolean isNoUnicodeBiDiControlChars();
public void setNoUnicodeBiDiControlChars(boolean noUnicodeBiDiControlChars);
boolean isNoUnicodeBiDiControlChars();
void setNoUnicodeBiDiControlChars(boolean noUnicodeBiDiControlChars);
/**
* Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning
* @return
*/
public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
boolean isErrorForUnknownProfiles();
void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
public boolean isShowMessagesFromReferences();
public void setShowMessagesFromReferences(boolean value);
boolean isShowMessagesFromReferences();
void setShowMessagesFromReferences(boolean value);
/**
* this is used internally in the publishing stack to ensure that everything is water tight, but
* this check is not necessary or appropriate at run time when the validator is hosted in HAPI
* @return
*/
public boolean isWantCheckSnapshotUnchanged();
public void setWantCheckSnapshotUnchanged(boolean wantCheckSnapshotUnchanged);
//FIXME: don't need that, gets never used?
// public String getValidationLanguage();
// public void setValidationLanguage(String value);
boolean isWantCheckSnapshotUnchanged();
void setWantCheckSnapshotUnchanged(boolean wantCheckSnapshotUnchanged);
/**
* It's common to see references such as Patient/234234 - these usually mean a reference to a Patient resource.
@ -235,27 +122,28 @@ public interface IResourceValidator {
*
* @return
*/
public boolean isAssumeValidRestReferences();
public void setAssumeValidRestReferences(boolean value);
boolean isAssumeValidRestReferences();
void setAssumeValidRestReferences(boolean value);
/**
* if this is true, the validator will accept extensions and references to example.org and acme.com as
* valid, on the basis that they are understood to be references to content that could exist in priniple but can't in practice
*/
public boolean isAllowExamples();
public void setAllowExamples(boolean value) ;
boolean isAllowExamples();
void setAllowExamples(boolean value) ;
boolean isNoCheckAggregation();
void setNoCheckAggregation(boolean value);
public boolean isNoCheckAggregation();
public void setNoCheckAggregation(boolean value);
/**
* CrumbTrail - whether the validator creates hints to
* @return
*/
public boolean isCrumbTrails();
public void setCrumbTrails(boolean crumbTrails);
boolean isCrumbTrails();
void setCrumbTrails(boolean crumbTrails);
public boolean isValidateValueSetCodesOnTxServer();
public void setValidateValueSetCodesOnTxServer(boolean value);
boolean isValidateValueSetCodesOnTxServer();
void setValidateValueSetCodesOnTxServer(boolean value);
/**
* Bundle validation rules allow for requesting particular entries in a bundle get validated against particular profiles
@ -264,7 +152,7 @@ public interface IResourceValidator {
*
* @return
*/
public List<BundleValidationRule> getBundleValidationRules();
List<BundleValidationRule> getBundleValidationRules();
/**
* Validate suite

View File

@ -0,0 +1,82 @@
package org.hl7.fhir.r5.utils.validation;
import java.util.List;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
public interface IValidationPolicyAdvisor {
/**
*
* @param validator
* @param appContext What was originally provided from the app for it's context
* @param path Path that led us to this resource.
* @param url Url of the profile the container resource is being validated against.
* @return {@link ReferenceValidationPolicy}
*/
ReferenceValidationPolicy policyForReference(IResourceValidator validator,
Object appContext,
String path,
String url);
/**
* //TODO pass through the actual containing Element as opposed to the type, id
* @param validator
* @param appContext What was originally provided from the app for it's context
* @param containerType Type of the resources that contains the resource being validated
* @param containerId Id of the resources that contains the resource being validated
* @param containingResourceType Type of the resource that will be validated (BUNDLE_ENTRY, BUNDLE_OUTCOME, CONTAINED_RESOURCE, PARAMETER)
* @param path Path that led us to this resource.
* @param url Url of the profile the container resource is being validated against.
* @return {@link ReferenceValidationPolicy}
*/
ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator,
Object appContext,
String containerType,
String containerId,
Element.SpecialElement containingResourceType,
String path,
String url);
/**
* Called before validating a concept in an instance against the terminology sub-system
*
* There's two reasons to use this policy advisor feature:
* - save time by not calling the terminology server for validation that don't bring value to the context calling the validation
* - suppressing known issues from being listed as a problem
*
* Note that the terminology subsystem has two parts: a mini-terminology server running inside the
* validator, and then calling out to an external terminology service (usually tx.fhir.org, though you
* run your own local copy of this - see https://confluence.hl7.org/display/FHIR/Running+your+own+copy+of+tx.fhir.org).
* You can't tell which subsystem will handle the terminology validation directly from the content provided here which
* subsystem will be called - you'll haev to investigate based on your set up. (matters, since it makes a huge performance
* difference, though it also depends on caching, and the impact of caching is also not known at this point)
*
* @param validator
* @param appContext What was originally provided from the app for it's context
* @param stackPath The current path for the stack. Note that the because of cross-references and FHIRPath conformsTo() statements, the stack can wind through the content unpredictably.
* @param definition the definition being validated against (might be useful: ElementDefinition.base.path, ElementDefinition.type, ElementDefinition.binding
* @param structure The structure definition that contains the element definition being validated against (may be from the base spec, may be from a profile)
* @param kind The part of the binding being validated
* @param valueSet The value set for the binding part that's being validated
* @param systems A list of canonical URls (including versions if known) of the systems in the instance that's being validated. Note that if a plain code is being validated, then there'll be no known system when this is called (systems will be empty, not null)
* @return {@link CodedContentValidationPolicy}
*/
CodedContentValidationPolicy policyForCodedContent(IResourceValidator validator,
Object appContext,
String stackPath,
ElementDefinition definition,
StructureDefinition structure,
BindingKind kind,
ValueSet valueSet,
List<String> systems);
}

View File

@ -0,0 +1,8 @@
package org.hl7.fhir.r5.utils.validation;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.StructureDefinition;
public interface IValidationProfileUsageTracker {
void recordProfileUsage(StructureDefinition profile, Object appContext, Element element);
}

View File

@ -0,0 +1,41 @@
package org.hl7.fhir.r5.utils.validation;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.CanonicalResource;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Locale;
public interface IValidatorResourceFetcher {
Element fetch(IResourceValidator validator, Object appContext, String url) throws FHIRException, IOException;
boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException;
byte[] fetchRaw(IResourceValidator validator, String url) throws IOException; // for attachment checking
IValidatorResourceFetcher setLocale(Locale locale);
/**
* this is used when the validator encounters a reference to a structure definition, value set or code system at some random URL reference
* while validating.
* <p>
* Added in v5.2.2. return null to leave functionality as it was before then.
*
* @return an R5 version of the resource
* @throws URISyntaxException
*/
CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException;
/**
* Whether to try calling fetchCanonicalResource for this reference (not whether it will succeed - just throw an exception from fetchCanonicalResource if it doesn't resolve. This is a policy thing.
* <p>
* Added in v5.2.2. return false to leave functionality as it was before then.
*
* @param url
* @return
*/
boolean fetchesCanonicalResource(IResourceValidator validator, String url);
}

View File

@ -0,0 +1,81 @@
package org.hl7.fhir.r5.utils.validation;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Questionnaire;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.utilities.validation.ValidationMessage;
public class ValidationContextCarrier {
/**
*
* When the validator is calling validateCode, it typically has a partially loaded resource that may provide
* additional resources that are relevant to the validation. This is a handle back into the validator context
* to ask for the resource to be fully loaded if it becomes relevant. Note that the resource may fail to load
* (e.g. if it's part of what's being validated) and if it does, the validator will record the validation
* issues before throwing an error
*
* This is a reference back int
*
*/
public interface IValidationContextResourceLoader {
public Resource loadContainedResource(List<ValidationMessage> errors, String path, Element resource, String id, Class<? extends Resource> class1) throws FHIRException;
}
/**
* A list of resources that provide context - typically, a container resource, and a bundle resource.
* iterate these in order looking for contained resources
*
*/
public static class ValidationContextResourceProxy {
// either a resource
private Resource resource;
// or an element and a loader
private Element element;
private IValidationContextResourceLoader loader;
private List<ValidationMessage> errors;
private String path;
public ValidationContextResourceProxy(Resource resource) {
this.resource = resource;
}
public ValidationContextResourceProxy(List<ValidationMessage> errors, String path, Element element, IValidationContextResourceLoader loader) {
this.errors = errors;
this.path = path;
this.element = element;
this.loader = loader;
}
public Resource loadContainedResource(String id, Class<? extends Resource> class1) throws FHIRException {
if (resource == null) {
Resource res = loader.loadContainedResource(errors, path, element, id, class1);
return res;
} else {
if (resource instanceof DomainResource) {
for (Resource r : ((DomainResource) resource).getContained()) {
if (r.getId().equals(id)) {
if (class1.isInstance(r))
return r;
}
}
}
return null;
}
}
}
private List<ValidationContextResourceProxy> resources = new ArrayList<>();
public List<ValidationContextResourceProxy> getResources() {
return resources;
}
}

View File

@ -0,0 +1,8 @@
package org.hl7.fhir.r5.utils.validation.constants;
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}

View File

@ -0,0 +1,14 @@
package org.hl7.fhir.r5.utils.validation.constants;
public enum BindingKind {
/**
* The primary binding e.g. ElementDefinition.binding.valueSet
*/
PRIMARY,
/**
* The max value set
*/
MAX_VS;
}

View File

@ -0,0 +1,9 @@
package org.hl7.fhir.r5.utils.validation.constants;
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.r5.utils.validation.constants;
public enum CodedContentValidationPolicy {
/**
* don't validate the code
*/
IGNORE,
/**
* validate the code against the underlying code systems
*/
CODE,
/**
* validate the code against the value set too.
* Note that this isn't much faster than just validating the code since
* the expensive part is hitting the terminology server (if necessary)
* and that has to be done for the code part too
*/
VALUESET //
}

View File

@ -0,0 +1,19 @@
package org.hl7.fhir.r5.utils.validation.constants;
public enum ContainedReferenceValidationPolicy {
IGNORE,
CHECK_TYPE,
CHECK_VALID;
public boolean ignore() {
return this == IGNORE;
}
public boolean checkType() {
return this == CHECK_TYPE || this == CHECK_VALID;
}
public boolean checkValid() {
return this == CHECK_VALID;
}
}

View File

@ -0,0 +1,7 @@
package org.hl7.fhir.r5.utils.validation.constants;
public enum IdStatus {
OPTIONAL,
REQUIRED,
PROHIBITED
}

View File

@ -0,0 +1,25 @@
package org.hl7.fhir.r5.utils.validation.constants;
public enum ReferenceValidationPolicy {
IGNORE,
CHECK_TYPE_IF_EXISTS,
CHECK_EXISTS,
CHECK_EXISTS_AND_TYPE,
CHECK_VALID;
public boolean ignore() {
return this == IGNORE;
}
public boolean checkExists() {
return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID || this == CHECK_TYPE_IF_EXISTS;
}
public boolean checkType() {
return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
}
public boolean checkValid() {
return this == CHECK_VALID;
}
}

Some files were not shown because too many files have changed in this diff Show More