Merge pull request #691 from hapifhir/gg-202112-sd-derivation

Gg 202112 sd derivation
This commit is contained in:
Grahame Grieve 2021-12-17 17:16:17 +11:00 committed by GitHub
commit a3de3cebf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 655 additions and 42 deletions

View File

@ -0,0 +1,6 @@
Validator:
* check StructureDefinition derivation consistency
* fix bug looking for contained resources inside bundles
Other code changes:
* Add support for new FHIR releases

View File

@ -0,0 +1,188 @@
package org.hl7.fhir.convertors.analytics;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.SimpleHTTPClient;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult;
import org.hl7.fhir.utilities.json.JSONUtil;
import org.hl7.fhir.utilities.json.JsonTrackingParser;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.npm.PackageClient;
import org.hl7.fhir.utilities.npm.PackageInfo;
import org.hl7.fhir.utilities.npm.ToolsVersion;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
public class PackageVisitor {
public interface IPackageVisitorProcessor {
public void processResource(String pid, String version, String type, byte[] content) throws FHIRException;
}
private List<String> resourceTypes = new ArrayList<>();
private List<String> versions = new ArrayList<>();
private boolean corePackages;
private boolean oldVersions;
private IPackageVisitorProcessor processor;
private FilesystemPackageCacheManager pcm;
private PackageClient pc;
public List<String> getResourceTypes() {
return resourceTypes;
}
public void setResourceTypes(List<String> resourceTypes) {
this.resourceTypes = resourceTypes;
}
public List<String> getVersions() {
return versions;
}
public void setVersions(List<String> versions) {
this.versions = versions;
}
public boolean isCorePackages() {
return corePackages;
}
public void setCorePackages(boolean corePackages) {
this.corePackages = corePackages;
}
public boolean isOldVersions() {
return oldVersions;
}
public void setOldVersions(boolean oldVersions) {
this.oldVersions = oldVersions;
}
public IPackageVisitorProcessor getProcessor() {
return processor;
}
public void setProcessor(IPackageVisitorProcessor processor) {
this.processor = processor;
}
public void visitPackages() throws IOException, ParserConfigurationException, SAXException {
System.out.println("Finding packages");
pc = new PackageClient(PackageClient.PRIMARY_SERVER);
pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
Set<String> pidList = getAllPackages();
System.out.println("Go: "+pidList.size()+" packages");
for (String pid : pidList) {
List<String> vList = listVersions(pid);
if (oldVersions) {
for (String v : vList) {
processPackage(pid, v);
}
} else if (vList.isEmpty()) {
System.out.println("No Packages for "+pid);
} else {
processPackage(pid, vList.get(vList.size() - 1));
}
}
}
private List<String> listVersions(String pid) throws IOException {
List<String> list = new ArrayList<>();
if (pid !=null) {
for (PackageInfo i : pc.getVersions(pid)) {
list.add(i.getVersion());
}
}
return list;
}
private Set<String> getAllPackages() throws IOException, ParserConfigurationException, SAXException {
Set<String> list = new HashSet<>();
for (PackageInfo i : pc.search(null, null, null, false)) {
list.add(i.getId());
}
JsonObject json = JsonTrackingParser.fetchJson("https://raw.githubusercontent.com/FHIR/ig-registry/master/fhir-ig-list.json");
for (JsonObject ig : JSONUtil.objects(json, "guides")) {
list.add(JSONUtil.str(ig, "npm-name"));
}
json = JsonTrackingParser.fetchJson("https://raw.githubusercontent.com/FHIR/ig-registry/master/package-feeds.json");
for (JsonObject feed : JSONUtil.objects(json, "feeds")) {
processFeed(list, JSONUtil.str(feed, "url"));
}
return list;
}
private void processFeed(Set<String> list, String str) throws IOException, ParserConfigurationException, SAXException {
System.out.println("Feed "+str);
try {
SimpleHTTPClient fetcher = new SimpleHTTPClient();
HTTPResult res = fetcher.get(str+"?nocache=" + System.currentTimeMillis());
res.checkThrowException();
Document xml = XMLUtil.parseToDom(res.getContent());
for (Element channel : XMLUtil.getNamedChildren(xml.getDocumentElement(), "channel")) {
for (Element item : XMLUtil.getNamedChildren(channel, "item")) {
String pid = XMLUtil.getNamedChildText(item, "title");
if (pid.contains("#")) {
list.add(pid.substring(0, pid.indexOf("#")));
}
}
}
} catch (Exception e) {
System.out.println(" "+e.getMessage());
}
}
private void processPackage(String pid, String v) throws IOException {
NpmPackage npm = null;
String fv = null;
try {
npm = pcm.loadPackage(pid, v);
fv = npm.fhirVersion();
} catch (Throwable e) {
System.out.println("Unable to process: "+pid+"#"+v+": "+e.getMessage());
}
int c = 0;
if (fv != null && (versions.isEmpty() || versions.contains(fv))) {
for (String type : resourceTypes) {
for (String s : npm.listResources(type)) {
c++;
processor.processResource(pid+"#"+v, fv, type, TextFile.streamToBytes(npm.load("package", s)));
}
}
}
System.out.println("Processed: "+pid+"#"+v+": "+c+" resources");
}
}

View File

@ -0,0 +1,211 @@
package org.hl7.fhir.convertors.analytics;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.convertors.analytics.PackageVisitor.IPackageVisitorProcessor;
import org.hl7.fhir.dstu2.model.SearchParameter;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.xml.sax.SAXException;
public class SearchParameterAnalysis implements IPackageVisitorProcessor {
public static class SearchParameterTypeUsage {
private Set<String> coreUsage = new HashSet<>();
private Set<String> igUsage = new HashSet<>();
public String summary() {
return ""+coreUsage.size()+" / "+igUsage.size();
}
}
public static class SearchParameterType {
private Map<String, SearchParameterTypeUsage> usages = new HashMap<>();
public void seeUsage(boolean core, String usage, String url) {
if (!usages.containsKey(usage)) {
usages.put(usage, new SearchParameterTypeUsage());
}
SearchParameterTypeUsage tu = usages.get(usage);
if (core) {
tu.coreUsage.add(url);
} else {
tu.igUsage.add(url);
}
}
}
public static class SearchParameterVersionAnalysis {
private Map<String, SearchParameterType> types = new HashMap<>();
private String version;
public void seeUsage(boolean core, String type, String usage, String url) {
// System.out.println("v"+version+" "+Utilities.padRight(url, ' ', 60)+" "+type+"/"+usage);
if (type == null) {
type = "n/a";
}
if (usage == null) {
usage = "n/a";
}
if (!types.containsKey(type)) {
types.put(type, new SearchParameterType());
}
SearchParameterType tu = types.get(type);
tu.seeUsage(core, usage, url);
}
public void printSummary() {
Set<String> usages = new HashSet<>();
for (SearchParameterType tu : types.values()) {
usages.addAll(tu.usages.keySet());
}
List<String> ul = new ArrayList<String>();
ul.addAll(usages);
Collections.sort(ul);
System.out.print(Utilities.padRight("", ' ', 10));
for (String u : ul) {
System.out.print(Utilities.padRight(u, ' ', 10));
}
System.out.println();
for (String t : types.keySet()) {
System.out.print(Utilities.padRight(t, ' ', 10));
SearchParameterType tu = types.get(t);
for (String u : ul) {
SearchParameterTypeUsage uu = tu.usages.get(u);
if (uu == null) {
System.out.print(Utilities.padRight("0 / 0", ' ', 10));
} else {
System.out.print(Utilities.padRight(uu.summary(), ' ', 10));
}
}
System.out.println();
}
}
}
private Map<String, SearchParameterVersionAnalysis> versions = new HashMap<String, SearchParameterAnalysis.SearchParameterVersionAnalysis>();
@Override
public void processResource(String pid, String version, String type, byte[] content) throws FHIRException {
// System.out.println("v"+version+" "+type+" from "+pid);
boolean core = pid.startsWith("hl7.fhir.r") && (pid.contains(".core") || pid.contains(".examples"));
version = VersionUtilities.getMajMin(version);
if (!versions.containsKey(version)) {
versions.put(version, new SearchParameterVersionAnalysis());
versions.get(version).version = version;
}
try {
if (VersionUtilities.isR5Ver(version)) {
processR5SP(core, versions.get(version), content);
} else if (VersionUtilities.isR4BVer(version)) {
processR4SP(core, versions.get(version), content);
} else if (VersionUtilities.isR4Ver(version)) {
processR4SP(core, versions.get(version), content);
} else if (VersionUtilities.isR3Ver(version)) {
processR3SP(core, versions.get(version), content);
} else if (VersionUtilities.isR2Ver(version)) {
processR2SP(core, versions.get(version), content);
}
} catch (IOException e) {
throw new FHIRException(e);
}
}
private void processR5SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException {
org.hl7.fhir.r5.model.Resource res = new org.hl7.fhir.r5.formats.JsonParser().parse(content);
if (res instanceof org.hl7.fhir.r5.model.Bundle) {
for (org.hl7.fhir.r5.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.r5.model.Bundle) res).getEntry()) {
if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.r5.model.SearchParameter) {
org.hl7.fhir.r5.model.SearchParameter sp = (org.hl7.fhir.r5.model.SearchParameter) bnd.getResource();
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
} else {
org.hl7.fhir.r5.model.SearchParameter sp = (org.hl7.fhir.r5.model.SearchParameter) res;
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
private void processR4SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException {
org.hl7.fhir.r4.model.Resource res = new org.hl7.fhir.r4.formats.JsonParser().parse(content);
if (res instanceof org.hl7.fhir.r4.model.Bundle) {
for (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.r4.model.Bundle) res).getEntry()) {
if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.r4.model.SearchParameter) {
org.hl7.fhir.r4.model.SearchParameter sp = (org.hl7.fhir.r4.model.SearchParameter) bnd.getResource();
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
} else {
org.hl7.fhir.r4.model.SearchParameter sp = (org.hl7.fhir.r4.model.SearchParameter) res;
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
private void processR3SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException {
org.hl7.fhir.dstu3.model.Resource res = new org.hl7.fhir.dstu3.formats.JsonParser().parse(content);
if (res instanceof org.hl7.fhir.dstu3.model.Bundle) {
for (org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.dstu3.model.Bundle) res).getEntry()) {
if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.dstu3.model.SearchParameter) {
org.hl7.fhir.dstu3.model.SearchParameter sp = (org.hl7.fhir.dstu3.model.SearchParameter) bnd.getResource();
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
} else {
org.hl7.fhir.dstu3.model.SearchParameter sp = (org.hl7.fhir.dstu3.model.SearchParameter) res;
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
private void processR2SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException {
org.hl7.fhir.dstu2.model.Resource res = new org.hl7.fhir.dstu2.formats.JsonParser().parse(content);
if (res instanceof org.hl7.fhir.dstu2.model.Bundle) {
for (org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.dstu2.model.Bundle) res).getEntry()) {
if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.dstu2.model.SearchParameter) {
org.hl7.fhir.dstu2.model.SearchParameter sp = (org.hl7.fhir.dstu2.model.SearchParameter) bnd.getResource();
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
} else {
org.hl7.fhir.dstu2.model.SearchParameter sp = (org.hl7.fhir.dstu2.model.SearchParameter) res;
analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl());
}
}
public static void main(String[] args) throws Exception {
new SearchParameterAnalysis().execute();
}
private void execute() throws IOException, ParserConfigurationException, SAXException {
PackageVisitor pv = new PackageVisitor();
pv.getResourceTypes().add("SearchParameter");
pv.getResourceTypes().add("Bundle");
pv.setOldVersions(false);
pv.setCorePackages(true);
pv.setProcessor(this);
pv.visitPackages();
printSummary();
}
private void printSummary() {
for (String v : versions.keySet()) {
System.out.println("-- v"+v+"---------------------");
versions.get(v).printSummary();
System.out.println("");
}
}
}

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_SNAPSHOT1,
_4_3_0_CIBUILD,
NULL;
public static FHIRVersion fromCode(String codeString) throws FHIRException {
@ -10089,7 +10090,9 @@ 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))
if ("4.3.0-snapshot1".equals(codeString))
return _4_3_0_SNAPSHOT1;
if ("4.3.0-cibuild".equals(codeString))
return _4_3_0_CIBUILD;
throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'");
}
@ -10123,7 +10126,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 _4_3_0_SNAPSHOT1: return "4.3.0-snapshot1";
case _4_3_0_CIBUILD: return "4.3.0-cibuild";
case NULL: return null;
default: return "?";
@ -10155,6 +10159,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_SNAPSHOT1: 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 "?";
@ -10186,6 +10191,7 @@ The primary difference between a medication statement and a medication administr
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 Ballot #1";
case _4_3_0_SNAPSHOT1: return "FHIR Release 4B Snapshot #1";
case _4_3_0_CIBUILD: return "FHIR Release 4B CI-Builld";
case NULL: return null;
default: return "?";
@ -10217,7 +10223,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 _4_3_0_SNAPSHOT1: return "4.3.0-snapshot";
case _4_3_0_CIBUILD: return "4.3.0-cibuild";
case NULL: return null;
default: return "?";
}
@ -10283,7 +10290,9 @@ 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))
if ("4.3.0-snapshot1".equals(codeString))
return FHIRVersion._4_3_0_SNAPSHOT1;
if ("4.3.0-cibuild".equals(codeString))
return FHIRVersion._4_3_0_CIBUILD;
throw new IllegalArgumentException("Unknown FHIRVersion code '"+codeString+"'");
}
@ -10343,7 +10352,9 @@ 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))
if ("4.3.0-snapshot1".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_3_0_SNAPSHOT1);
if ("4.3.0-cibuild".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_3_0_CIBUILD);
throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'");
}
@ -10396,8 +10407,10 @@ 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_SNAPSHOT1)
return "4.3.0-snapshot1";
if (code == FHIRVersion._4_3_0_CIBUILD)
return "4.3.0_CIBUILD";
return "4.3.0-cibuild";
return "?";
}
public String toSystem(FHIRVersion code) {

View File

@ -6716,6 +6716,10 @@ The MedicationUsage resource was previously called MedicationStatement.
* R5 Preview #1.
*/
_4_2_0,
/**
* R4B
*/
_4_3_0_SNAPSHOT1,
/**
* R4B
*/
@ -6732,6 +6736,14 @@ The MedicationUsage resource was previously called MedicationStatement.
* R5 Draft Ballot.
*/
_4_6_0,
/**
* R5 CI Build.
*/
_5_0_0_SNAPSHOT1,
/**
* R5 CI Build.
*/
_5_0_0_CIBUILD,
/**
* added to help the parsers
*/
@ -6790,7 +6802,9 @@ 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))
if ("4.3.0-snapshot1".equals(codeString))
return _4_3_0_SNAPSHOT1;
if ("4.3.0-cibuild".equals(codeString))
return _4_3_0_CIBUILD;
if ("4.4.0".equals(codeString))
return _4_4_0;
@ -6798,6 +6812,10 @@ The MedicationUsage resource was previously called MedicationStatement.
return _4_5_0;
if ("4.6.0".equals(codeString))
return _4_6_0;
if ("5.0.0-snapshot1".equals(codeString))
return _5_0_0_SNAPSHOT1;
if ("5.0.0-cibuild".equals(codeString))
return _5_0_0_CIBUILD;
throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'");
}
public String toCode() {
@ -6827,10 +6845,13 @@ 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_3_0_SNAPSHOT1: return "4.3.0-snapshot1";
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";
case _5_0_0_SNAPSHOT1: return "5.0.0-snapshot1";
case _5_0_0_CIBUILD: return "5.0.0-cibuild";
case NULL: return null;
default: return "?";
}
@ -6862,10 +6883,13 @@ 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_SNAPSHOT1: 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";
case _5_0_0_SNAPSHOT1: return "http://hl7.org/fhir/FHIR-version";
case _5_0_0_CIBUILD: return "http://hl7.org/fhir/FHIR-version";
case NULL: return null;
default: return "?";
}
@ -6897,10 +6921,13 @@ 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_3_0_SNAPSHOT1: return "R4B Snapshot #1";
case _4_3_0_CIBUILD: return "R4B Rolling CI-Build";
case _4_4_0: return "R5 Preview #2.";
case _4_5_0: return "R5 Preview #3.";
case _4_6_0: return "R5 Draft Ballot.";
case _4_6_0: return "R5 Draft Ballot";
case _5_0_0_SNAPSHOT1: return "R5 Snapshot #1";
case _5_0_0_CIBUILD: return "R5 Rooling CI-Build";
case NULL: return null;
default: return "?";
}
@ -6932,10 +6959,13 @@ 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_3_0_SNAPSHOT1: return "4.3.0-snapshot1";
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";
case _5_0_0_SNAPSHOT1: return "5.0.0-snapshot1";
case _5_0_0_CIBUILD: return "5.0.0-cibuild";
case NULL: return null;
default: return "?";
}
@ -6996,7 +7026,7 @@ public String toCode(int len) {
return true;
if ("4.2.0".equals(codeString))
return true;
if ("4.3.0-CIBUILD".equals(codeString))
if ("4.3.0-cibuild".equals(codeString))
return true;
return false;
}
@ -7069,7 +7099,9 @@ 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))
if ("4.3.0-snapshot1".equals(codeString))
return FHIRVersion._4_3_0_SNAPSHOT1;
if ("4.3.0-cibuild".equals(codeString))
return FHIRVersion._4_3_0_CIBUILD;
if ("4.4.0".equals(codeString))
return FHIRVersion._4_4_0;
@ -7077,6 +7109,10 @@ public String toCode(int len) {
return FHIRVersion._4_5_0;
if ("4.6.0".equals(codeString))
return FHIRVersion._4_6_0;
if ("5.0.0-snapshot1".equals(codeString))
return FHIRVersion._5_0_0_SNAPSHOT1;
if ("5.0.0-cibuild".equals(codeString))
return FHIRVersion._5_0_0_CIBUILD;
throw new IllegalArgumentException("Unknown FHIRVersion code '"+codeString+"'");
}
public Enumeration<FHIRVersion> fromType(Base code) throws FHIRException {
@ -7137,7 +7173,9 @@ 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))
if ("4.3.0-snapshot1".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_3_0_SNAPSHOT1);
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);
@ -7145,6 +7183,10 @@ public String toCode(int len) {
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_5_0);
if ("4.6.0".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._4_6_0);
if ("5.0.0-snapshot1".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._5_0_0_SNAPSHOT1);
if ("5.0.0-cibuild".equals(codeString))
return new Enumeration<FHIRVersion>(this, FHIRVersion._5_0_0_CIBUILD);
throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'");
}
public String toCode(FHIRVersion code) {
@ -7198,14 +7240,20 @@ 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_SNAPSHOT1)
return "4.3.0-snapshot1";
if (code == FHIRVersion._4_3_0_CIBUILD)
return "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)
return "4.5.0";
if (code == FHIRVersion._4_6_0)
return "4.6.0";
if (code == FHIRVersion._5_0_0_SNAPSHOT1)
return "5.0.0-snapshot1";
if (code == FHIRVersion._5_0_0_CIBUILD)
return "5.0.0-cibuild";
return "?";
}
public String toSystem(FHIRVersion code) {

View File

@ -796,7 +796,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
if (vs != null && !vs.hasUrl()) {
vs = vs.copy();
vs.setUrl("urn:uuid:"+UUID.randomUUID().toString().toLowerCase());
vs.setUrl(q.getUrl()+"--"+q.getContained(i.getAnswerValueSet().substring(1)));
}
} else {
vs = context.getContext().fetchResource(ValueSet.class, i.getAnswerValueSet());

View File

@ -613,8 +613,10 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (valueset.hasExpansion()) {
return checkExpansion(new Coding(system, code, null));
} else if (valueset.hasCompose()) {
int i = 0;
for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
Boolean ok = inComponent(vsi, system, code, valueset.getCompose().getInclude().size() == 1, warnings);
Boolean ok = inComponent(vsi, i, system, code, valueset.getCompose().getInclude().size() == 1, warnings);
i++;
if (ok == null && result == false) {
result = null;
} else if (ok) {
@ -623,7 +625,8 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
}
for (ConceptSetComponent vsi : valueset.getCompose().getExclude()) {
Boolean nok = inComponent(vsi, system, code, valueset.getCompose().getInclude().size() == 1, warnings);
Boolean nok = inComponent(vsi, i, system, code, valueset.getCompose().getInclude().size() == 1, warnings);
i++;
if (nok == null && result == false) {
result = null;
} else if (nok != null && nok) {
@ -635,7 +638,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return result;
}
private Boolean inComponent(ConceptSetComponent vsi, String system, String code, boolean only, List<String> warnings) throws FHIRException {
private Boolean inComponent(ConceptSetComponent vsi, int vsiIndex, String system, String code, boolean only, List<String> warnings) throws FHIRException {
for (UriType uri : vsi.getValueSet()) {
if (inImport(uri.getValue(), system, code)) {
return true;
@ -662,7 +665,8 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
// make up a transient value set with
ValueSet vs = new ValueSet();
vs.setStatus(PublicationStatus.ACTIVE);
vs.setUrl(Utilities.makeUuidUrn());
vs.setUrl(valueset.getUrl()+"--"+vsiIndex);
vs.setVersion(valueset.getVersion());
vs.getCompose().addInclude(vsi);
ValidationResult res = context.validateCode(options.noClient(), new Coding(system, code, null), vs);
if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) {

View File

@ -0,0 +1,111 @@
package org.hl7.fhir.r5.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemHierarchyMeaning;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.Resource;
public class ResourceFixer {
public static void main(String[] args) {
new ResourceFixer().vistAllResources(args[0]);
}
private Set<String> refs = new HashSet<>();
private void vistAllResources(String folder) {
for (File f : new File(folder).listFiles()) {
if (f.isDirectory()) {
vistAllResources(f.getAbsolutePath());
} else if (f.getName().endsWith(".json")) {
Resource r = null;
try {
r = new JsonParser().parse(new FileInputStream(f));
} catch (Throwable e) {
// nothing at all
}
if (r != null) {
try {
if (visitResource(r)) {
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(f), r);
}
} catch (Exception e) {
System.out.println("Error processing "+f.getAbsolutePath()+": "+e.getMessage());
// e.printStackTrace();
}
}
} else if (f.getName().endsWith(".xml")) {
Resource r = null;
try {
r = new XmlParser().parse(new FileInputStream(f));
} catch (Throwable e) {
// nothing at all
}
if (r != null) {
try {
if (visitResource(r)) {
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(f), r);
}
} catch (Exception e) {
System.out.println("Error processing "+f.getAbsolutePath()+": "+e.getMessage());
// e.printStackTrace();
}
}
}
}
}
private boolean visitResource(Resource r) {
if (r.hasId()) {
String ref = r.fhirType()+"/"+r.getId();
if (refs.contains(ref)) {
throw new FHIRException("Duplicate resource "+ref);
}
refs.add(ref);
}
if (r instanceof CodeSystem) {
return visitCodeSystem((CodeSystem) r);
}
return false;
}
private boolean visitCodeSystem(CodeSystem cs) {
if (!cs.hasContent()) {
System.out.println("Setting content = complete for CodeSystem/"+cs.getId());
cs.setContent(CodeSystemContentMode.COMPLETE);
return true;
} else if (!cs.hasHierarchyMeaning() && hasHierarchy(cs)) {
System.out.println("Setting hierarchyMeaning = is-a for CodeSystem/"+cs.getId());
cs.setHierarchyMeaning(CodeSystemHierarchyMeaning.ISA);
return true;
} else {
return false;
}
}
private boolean hasHierarchy(CodeSystem cs) {
for (ConceptDefinitionComponent c : cs.getConcept()) {
if (c.hasConcept()) {
return true;
}
}
return false;
}
}

View File

@ -361,6 +361,7 @@ public class I18nConstants {
public static final String SD_MUST_HAVE_DERIVATION = "SD_MUST_HAVE_DERIVATION";
public static final String SD_NESTED_MUST_SUPPORT_DIFF = "SD_NESTED_MUST_SUPPORT_DIFF";
public static final String SD_NESTED_MUST_SUPPORT_SNAPSHOT = "SD_NESTED_MUST_SUPPORT_SNAPSHOT";
public static final String SD_DERIVATION_KIND_MISMATCH = "SD_DERIVATION_KIND_MISMATCH";
public static final String SD_ED_TYPE_PROFILE_UNKNOWN = "SD_ED_TYPE_PROFILE_UNKNOWN";
public static final String SD_ED_TYPE_PROFILE_NOTYPE = "SD_ED_TYPE_PROFILE_NOTYPE";
public static final String SD_ED_TYPE_PROFILE_WRONG = "SD_ED_TYPE_PROFILE_WRONG";

View File

@ -701,4 +701,4 @@ TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG = The value in the instance ({2}) is
TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG_UCUM = The value in the instance ({0} {1}) is greater than the specified maxValue ({2} {3}) after UCUM conversion
TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_ERROR = Base64 encoded values are not allowed to contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway
TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_WARNING = Base64 encoded values SHOULD not contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway
SD_DERIVATION_KIND_MISMATCH = The structure definition constrains a kind of {0}, but has a different kind ({1})

View File

@ -304,8 +304,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
} else if (item instanceof Element) {
Element e = (Element) item;
if (e.getSpecial() != null) {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
if (e.getSpecial() == SpecialElement.CONTAINED) {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
} else if (e.getSpecial() != null) {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
} else {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
}
@ -1312,7 +1314,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String system = c.getSystem();
String display = c.getDisplay();
String version = c.getVersion();
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE);
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, system == null || isCodeSystemReferenceValid(system), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE);
if (system != null && code != null && !noTerminologyChecks) {
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET2, system);
@ -1568,7 +1570,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private void checkCodedElement(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack,
String theCode, String theSystem, String theVersion, String theDisplay) {
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE);
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, theSystem == null || isCodeSystemReferenceValid(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE);
warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, Utilities.noString(theCode) || !Utilities.noString(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
if (theSystem != null && theCode != null && !noTerminologyChecks) {
@ -2023,7 +2025,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private void checkIdentifier(List<ValidationMessage> errors, String path, Element element, ElementDefinition context) {
String system = element.getNamedChildValue("system");
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_SYSTEM);
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, system == null || isIdentifierSystemReferenceValid(system), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_SYSTEM);
if ("urn:ietf:rfc:3986".equals(system)) {
String value = element.getNamedChildValue("value");
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(value), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_IETF_SYSTEM_VALUE);
@ -2889,7 +2891,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !isSuspiciousReference(ref), I18nConstants.REFERENCE_REF_SUSPICIOUS, ref);
ResolvedReference we = localResolve(ref, stack, errors, path, hostContext.getRootResource(), element);
ResolvedReference we = localResolve(ref, stack, errors, path, hostContext.getRootResource(), hostContext.getGroupingResource(), element);
String refType;
if (ref.startsWith("#")) {
refType = "contained";
@ -3488,13 +3490,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return fmt.length() > 10 && (fmt.substring(10).contains("-") || fmt.substring(10).contains("+") || fmt.substring(10).contains("Z"));
}
private boolean isAbsolute(String uri) {
return Utilities.noString(uri) || uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("urn:uuid:") || uri.startsWith("urn:oid:") || uri.startsWith("urn:ietf:")
|| uri.startsWith("urn:iso:") || uri.startsWith("urn:iso-astm:") || uri.startsWith("mailto:")|| isValidFHIRUrn(uri);
private boolean isAbsolute(String uri, String... protocols) {
return Utilities.noString(uri) || uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("urn:");
}
private boolean isValidFHIRUrn(String uri) {
return (uri.equals("urn:x-fhir:uk:id:nhs-number")) || uri.startsWith("urn:"); // Anyone can invent a URN, so why should we complain?
private boolean isCodeSystemReferenceValid(String uri) {
return isSystemReferenceValid(uri);
}
private boolean isIdentifierSystemReferenceValid(String uri) {
return isSystemReferenceValid(uri) || uri.startsWith("ldap:");
}
private boolean isSystemReferenceValid(String uri) {
return uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("urn:");
}
public boolean isAnyExtensionsAllowed() {
@ -3577,7 +3586,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return true;
}
private ResolvedReference localResolve(String ref, NodeStack stack, List<ValidationMessage> errors, String path, Element rootResource, Element source) {
private ResolvedReference localResolve(String ref, NodeStack stack, List<ValidationMessage> errors, String path, Element rootResource, Element groupingResource, Element source) {
if (ref.startsWith("#")) {
// work back through the parent list.
// really, there should only be one level for this (contained resources cannot contain
@ -3677,11 +3686,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
stack = stack.getParent();
}
// we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity.
if (rootResource != null && BUNDLE.equals(rootResource.fhirType())) {
String type = rootResource.getChildValue(TYPE);
Element entry = getEntryForSource(rootResource, source);
if (groupingResource != null && BUNDLE.equals(groupingResource.fhirType())) { // it could also be a Parameters resource - that case isn't handled yet
String type = groupingResource.getChildValue(TYPE);
Element entry = getEntryForSource(groupingResource, source);
fullUrl = entry.getChildValue(FULL_URL);
IndexedElement res = getFromBundle(rootResource, ref, fullUrl, errors, path, type, "transaction".equals(type));
IndexedElement res = getFromBundle(groupingResource, ref, fullUrl, errors, path, type, "transaction".equals(type));
if (res == null) {
return null;
} else {
@ -3758,7 +3767,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private Element resolve(Object appContext, String ref, NodeStack stack, List<ValidationMessage> errors, String path) throws IOException, FHIRException {
Element local = localResolve(ref, stack, errors, path, null, null).getFocus();
Element local = localResolve(ref, stack, errors, path, null, null, null).getFocus();
if (local != null)
return local;
if (fetcher == null)
@ -4591,7 +4600,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ValidatorHostContext hc = null;
if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.PARAMETER) {
resource = element;
hc = hostContext.forEntry(element);
assert Utilities.existsInList(hostContext.getRootResource().fhirType(), "Bundle", "Parameters");
hc = hostContext.forEntry(element, hostContext.getRootResource()); // root becomes the grouping resource (should be either bundle or parameters)
} else {
hc = hostContext.forContained(element);
}

View File

@ -97,6 +97,8 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
}
rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base.getKindElement().primitiveValue().equals(src.getChildValue("kind")),
I18nConstants.SD_DERIVATION_KIND_MISMATCH, base.getKindElement().primitiveValue(), src.getChildValue("kind"));
}
} catch (FHIRException | IOException e) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());

View File

@ -16,6 +16,7 @@ public class ValidatorHostContext {
private Element resource;
// the resource that is the scope of id resolution - either the same as resource, or the resource the contains that resource. This can only be one level deep.
private Element rootResource;
private Element groupingResource; // either a bundle or a parameters that holds the rootResource (for reference resolution)
private StructureDefinition profile; // the profile that contains the content being validated
private boolean checkSpecials = true;
@ -29,7 +30,7 @@ public class ValidatorHostContext {
this.appContext = appContext;
this.resource = element;
this.rootResource = element;
// no container
// no groupingResource (Bundle or Parameters)
dump("creating");
}
@ -37,7 +38,15 @@ public class ValidatorHostContext {
this.appContext = appContext;
this.resource = element;
this.rootResource = root;
// no container
// no groupingResource (Bundle or Parameters)
dump("creating");
}
public ValidatorHostContext(Object appContext, Element element, Element root, Element groupingResource) {
this.appContext = appContext;
this.resource = element;
this.rootResource = root;
this.groupingResource = groupingResource;
dump("creating");
}
@ -65,6 +74,10 @@ public class ValidatorHostContext {
return this;
}
public Element getGroupingResource() {
return groupingResource;
}
public StructureDefinition getProfile() {
return profile;
}
@ -106,15 +119,17 @@ public class ValidatorHostContext {
res.rootResource = resource;
res.resource = element;
res.profile = profile;
res.groupingResource = groupingResource;
res.dump("forContained");
return res;
}
public ValidatorHostContext forEntry(Element element) {
public ValidatorHostContext forEntry(Element element, Element groupingResource) {
ValidatorHostContext res = new ValidatorHostContext(appContext);
res.rootResource = element;
res.resource = element;
res.profile = profile;
res.groupingResource = groupingResource;
res.dump("forEntry");
return res;
}
@ -124,6 +139,7 @@ public class ValidatorHostContext {
res.resource = resource;
res.rootResource = rootResource;
res.profile = profile;
res.groupingResource = groupingResource;
res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap<String, List<ValidationMessage>>();
res.dump("forProfile "+profile.getUrl());
return res;
@ -134,6 +150,7 @@ public class ValidatorHostContext {
res.resource = resource;
res.rootResource = resource;
res.profile = profile;
res.groupingResource = groupingResource;
res.checkSpecials = false;
res.dump("forLocalReference "+profile.getUrl());
return res;
@ -151,6 +168,7 @@ public class ValidatorHostContext {
res.resource = resource;
res.rootResource = resource;
res.profile = profile;
res.groupingResource = null;
res.checkSpecials = false;
res.dump("forRemoteReference "+profile.getUrl());
return res;
@ -160,6 +178,7 @@ public class ValidatorHostContext {
ValidatorHostContext res = new ValidatorHostContext(appContext);
res.resource = resource;
res.rootResource = resource;
res.groupingResource = groupingResource;
res.profile = profile;
res.checkSpecials = false;
res.sliceRecords = new HashMap<String, List<ValidationMessage>>();