Merge branch 'master' into do-20240830-maven-compiler-update

This commit is contained in:
dotasek 2024-10-01 16:08:18 -04:00
commit 4ab12ab39d
107 changed files with 23857 additions and 3700 deletions

View File

@ -1,18 +1,7 @@
## Validator Changes
* Fix expression for con-3 properly (fix validation problem on some condition resources)
* Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types
* FHIRPath: Allow _ in constant names (per FHIRPath spec)
* Fix value set rendering creating wrong references
* Fix bug processing value set includes / excludes that are just value sets (no system value)
* Alter processing of unknown code systems per discussion at ,https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Don't.20error.20when.20you.20can't.20find.20code.20system and implement unknown-codesystems-cause-errors
* Improve message for when elements are out of order in profile differentials
* no changes
## Other code changes
* fix problem where profile rendering had spurious 'slices for' nodes everywhere
* Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository
* Fix problem generating value set spreadsheets
* fix concurrent modification error processing language translations
* Check for null fetcher processing ConceptMaps (#1728)
* no changes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,5 +1,5 @@
Locale,Coverage #,Coverage %
de,831,41%
de,831,40%
es,714,35%
ja,902,44%
nl,1989,98%
nl,1988,97%

1 Locale Coverage # Coverage %
2 de 831 41% 40%
3 es 714 35%
4 ja 902 44%
5 nl 1989 1988 98% 97%

View File

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

View File

@ -47,7 +47,7 @@ public class Consent30_40 {
tgt.setPolicyRule(new CodeableConcept(c));
}
if (src.hasSecurityLabel() || src.hasPeriod() || src.hasActor() || src.hasAction() || src.hasPurpose() || src.hasDataPeriod() || src.hasData() || src.hasExcept()) {
org.hl7.fhir.r4.model.Consent.provisionComponent pc = new org.hl7.fhir.r4.model.Consent.provisionComponent();
org.hl7.fhir.r4.model.Consent.ProvisionComponent pc = new org.hl7.fhir.r4.model.Consent.ProvisionComponent();
if (src.hasPeriod())
pc.setPeriod(Period30_40.convertPeriod(src.getPeriod()));
for (org.hl7.fhir.dstu3.model.Consent.ConsentActorComponent t : src.getActor())
@ -104,7 +104,7 @@ public class Consent30_40 {
}
}
if (src.hasProvision()) {
org.hl7.fhir.r4.model.Consent.provisionComponent p = src.getProvision();
org.hl7.fhir.r4.model.Consent.ProvisionComponent p = src.getProvision();
if (p.hasPeriod())
tgt.setPeriod(Period30_40.convertPeriod(p.getPeriod()));
for (org.hl7.fhir.r4.model.Consent.provisionActorComponent t : p.getActor())
@ -117,7 +117,7 @@ public class Consent30_40 {
tgt.setDataPeriod(Period30_40.convertPeriod(p.getDataPeriod()));
for (org.hl7.fhir.r4.model.Consent.provisionDataComponent t : p.getData())
tgt.addData(convertConsentDataComponent(t));
for (org.hl7.fhir.r4.model.Consent.provisionComponent t : p.getProvision())
for (org.hl7.fhir.r4.model.Consent.ProvisionComponent t : p.getProvision())
tgt.addExcept(convertExceptComponent(t));
}
return tgt;
@ -369,7 +369,7 @@ public class Consent30_40 {
return tgt;
}
static public org.hl7.fhir.dstu3.model.Consent.ExceptComponent convertExceptComponent(org.hl7.fhir.r4.model.Consent.provisionComponent src) throws FHIRException {
static public org.hl7.fhir.dstu3.model.Consent.ExceptComponent convertExceptComponent(org.hl7.fhir.r4.model.Consent.ProvisionComponent src) throws FHIRException {
if (src == null)
return null;
org.hl7.fhir.dstu3.model.Consent.ExceptComponent tgt = new org.hl7.fhir.dstu3.model.Consent.ExceptComponent();
@ -393,10 +393,10 @@ public class Consent30_40 {
return tgt;
}
static public org.hl7.fhir.r4.model.Consent.provisionComponent convertExceptComponent(org.hl7.fhir.dstu3.model.Consent.ExceptComponent src) throws FHIRException {
static public org.hl7.fhir.r4.model.Consent.ProvisionComponent convertExceptComponent(org.hl7.fhir.dstu3.model.Consent.ExceptComponent src) throws FHIRException {
if (src == null)
return null;
org.hl7.fhir.r4.model.Consent.provisionComponent tgt = new org.hl7.fhir.r4.model.Consent.provisionComponent();
org.hl7.fhir.r4.model.Consent.ProvisionComponent tgt = new org.hl7.fhir.r4.model.Consent.ProvisionComponent();
ConversionContext30_40.INSTANCE.getVersionConvertor_30_40().copyBackboneElement(src,tgt);
if (src.hasType())
tgt.setTypeElement(convertConsentExceptType(src.getTypeElement()));

View File

@ -25,8 +25,10 @@ import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r5.model.CodeType;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent;
import org.hl7.fhir.r5.model.UsageContext;
import org.hl7.fhir.r5.utils.ToolingExtensions;
public class ElementDefinition40_50 {
public static org.hl7.fhir.r5.model.ElementDefinition convertElementDefinition(org.hl7.fhir.r4.model.ElementDefinition src) throws FHIRException {
if (src == null) return null;
org.hl7.fhir.r5.model.ElementDefinition tgt = new org.hl7.fhir.r5.model.ElementDefinition();
@ -616,7 +618,7 @@ public class ElementDefinition40_50 {
if (src == null) return null;
org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent tgt = new org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent();
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt,
"http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional");
"http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional", ToolingExtensions.EXT_BINDING_ADDITIONAL);
if (src.hasStrength()) tgt.setStrengthElement(Enumerations40_50.convertBindingStrength(src.getStrengthElement()));
if (src.hasDescription()) tgt.setDescriptionElement(String40_50.convertStringToMarkdown(src.getDescriptionElement()));
if (src.hasValueSet()) tgt.setValueSetElement(Canonical40_50.convertCanonical(src.getValueSetElement()));
@ -624,6 +626,9 @@ public class ElementDefinition40_50 {
for (org.hl7.fhir.r4.model.Extension ext : src.getExtensionsByUrl("http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional")) {
tgt.addAdditional(convertAdditional(ext));
}
for (org.hl7.fhir.r4.model.Extension ext : src.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
tgt.addAdditional(convertAdditional(ext));
}
return tgt;
}
@ -652,12 +657,12 @@ public class ElementDefinition40_50 {
return tgt;
}
private static Extension convertAdditional(ElementDefinitionBindingAdditionalComponent src) {
private static org.hl7.fhir.r4.model.Extension convertAdditional(ElementDefinitionBindingAdditionalComponent src) {
if (src == null) return null;
Extension tgt = new Extension();
org.hl7.fhir.r4.model.Extension tgt = new Extension(ToolingExtensions.EXT_BINDING_ADDITIONAL);
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt);
if (src.hasPurpose()) {
tgt.addExtension(new Extension("purpose", new CodeType(src.getPurposeElement().primitiveValue())));
tgt.addExtension(new Extension("purpose", new org.hl7.fhir.r4.model.CodeType(src.getPurposeElement().primitiveValue())));
}
if (src.hasValueSet()) {
tgt.addExtension(new Extension("valueSet", Canonical40_50.convertCanonical(src.getValueSetElement())));

View File

@ -220,7 +220,7 @@ public class Consent40_50 {
return tgt;
}
public static org.hl7.fhir.r5.model.Consent.ProvisionComponent convertprovisionComponent(org.hl7.fhir.r4.model.Consent.provisionComponent src) throws FHIRException {
public static org.hl7.fhir.r5.model.Consent.ProvisionComponent convertprovisionComponent(org.hl7.fhir.r4.model.Consent.ProvisionComponent src) throws FHIRException {
if (src == null)
return null;
org.hl7.fhir.r5.model.Consent.ProvisionComponent tgt = new org.hl7.fhir.r5.model.Consent.ProvisionComponent();
@ -242,15 +242,15 @@ public class Consent40_50 {
tgt.setDataPeriod(Period40_50.convertPeriod(src.getDataPeriod()));
for (org.hl7.fhir.r4.model.Consent.provisionDataComponent t : src.getData())
tgt.addData(convertprovisionDataComponent(t));
for (org.hl7.fhir.r4.model.Consent.provisionComponent t : src.getProvision())
for (org.hl7.fhir.r4.model.Consent.ProvisionComponent t : src.getProvision())
tgt.addProvision(convertprovisionComponent(t));
return tgt;
}
public static org.hl7.fhir.r4.model.Consent.provisionComponent convertprovisionComponent(org.hl7.fhir.r5.model.Consent.ProvisionComponent src) throws FHIRException {
public static org.hl7.fhir.r4.model.Consent.ProvisionComponent convertprovisionComponent(org.hl7.fhir.r5.model.Consent.ProvisionComponent src) throws FHIRException {
if (src == null)
return null;
org.hl7.fhir.r4.model.Consent.provisionComponent tgt = new org.hl7.fhir.r4.model.Consent.provisionComponent();
org.hl7.fhir.r4.model.Consent.ProvisionComponent tgt = new org.hl7.fhir.r4.model.Consent.ProvisionComponent();
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyBackboneElement(src, tgt);
// if (src.hasType())
// tgt.setTypeElement(convertConsentProvisionType(src.getTypeElement()));

View File

@ -230,12 +230,6 @@ public class Enumerations40_50 {
case _4_3_0:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._4_3_0);
break;
case _4_3_0_CIBUILD:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._4_3_0CIBUILD);
break;
case _4_3_0_SNAPSHOT1:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._4_3_0SNAPSHOT1);
break;
case _5_0_0:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._5_0_0);
break;
@ -254,6 +248,15 @@ public class Enumerations40_50 {
case _5_0_0DRAFTFINAL:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._5_0_0DRAFTFINAL);
break;
case _6_0_0:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._6_0_0);
break;
case _6_0_0_BALLOT1:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._6_0_0_BALLOT1);
break;
case _6_0_0_BALLOT2:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion._6_0_0_BALLOT2);
break;
default:
tgt.setValue(org.hl7.fhir.r5.model.Enumerations.FHIRVersion.NULL);
break;
@ -342,12 +345,6 @@ public class Enumerations40_50 {
case _4_3_0:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._4_3_0);
break;
case _4_3_0CIBUILD:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._4_3_0_CIBUILD);
break;
case _4_3_0SNAPSHOT1:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._4_3_0_SNAPSHOT1);
break;
case _5_0_0:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._5_0_0);
break;
@ -366,6 +363,15 @@ public class Enumerations40_50 {
case _5_0_0DRAFTFINAL:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._5_0_0DRAFTFINAL);
break;
case _6_0_0:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._6_0_0);
break;
case _6_0_0_BALLOT1:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._6_0_0_BALLOT1);
break;
case _6_0_0_BALLOT2:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion._6_0_0_BALLOT2);
break;
default:
tgt.setValue(org.hl7.fhir.r4.model.Enumerations.FHIRVersion.NULL);

View File

@ -33,9 +33,11 @@ import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Enumerations.FHIRVersion;
import org.hl7.fhir.r5.model.Enumerations.FHIRVersionEnumFactory;
import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
@ -237,7 +239,7 @@ public class NpmPackageVersionConverter {
return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_30.convertResource(VersionConvertorFactory_10_30.convertResource(res)));
} else if (VersionUtilities.isR3Ver(version)) {
return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_10_30.convertResource(res));
} else if (VersionUtilities.isR4Ver(version)) {
} else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_10_40.convertResource(res));
} else if (VersionUtilities.isR5Plus(version)) {
return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_10_50.convertResource(res));
@ -251,7 +253,7 @@ public class NpmPackageVersionConverter {
return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(res);
} else if (VersionUtilities.isR3Ver(version)) {
return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_14_30.convertResource(res));
} else if (VersionUtilities.isR4Ver(version)) {
} else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_14_40.convertResource(res));
} else if (VersionUtilities.isR5Plus(version)) {
return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_14_50.convertResource(res));
@ -265,7 +267,7 @@ public class NpmPackageVersionConverter {
return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_30.convertResource(res));
} else if (VersionUtilities.isR3Ver(version)) {
return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(res);
} else if (VersionUtilities.isR4Ver(version)) {
} else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_30_40.convertResource(res));
} else if (VersionUtilities.isR5Plus(version)) {
return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_30_50.convertResource(res));
@ -279,7 +281,21 @@ public class NpmPackageVersionConverter {
return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_40.convertResource(res));
} else if (VersionUtilities.isR3Ver(version)) {
return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_30_40.convertResource(res, new BaseAdvisor_30_40(false)));
} else if (VersionUtilities.isR4Ver(version)) {
} else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(res);
} else if (VersionUtilities.isR5Plus(version)) {
return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_40_50.convertResource(res));
}
} else if (VersionUtilities.isR4BVer(currentVersion)) {
org.hl7.fhir.r4.model.Resource res = new org.hl7.fhir.r4.formats.JsonParser().parse(cnt);
convertResourceR4B(res);
if (VersionUtilities.isR2Ver(version)) {
return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(VersionConvertorFactory_10_40.convertResource(res, new PR2Handler()));
} else if (VersionUtilities.isR2BVer(version)) {
return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_40.convertResource(res));
} else if (VersionUtilities.isR3Ver(version)) {
return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_30_40.convertResource(res, new BaseAdvisor_30_40(false)));
} else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(res);
} else if (VersionUtilities.isR5Plus(version)) {
return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_40_50.convertResource(res));
@ -293,7 +309,7 @@ public class NpmPackageVersionConverter {
return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_50.convertResource(res));
} else if (VersionUtilities.isR3Ver(version)) {
return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_30_50.convertResource(res, new BaseAdvisor_30_50(false)));
} else if (VersionUtilities.isR4Ver(version)) {
} else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_40_50.convertResource(res));
} else if (VersionUtilities.isR5Plus(version)) {
return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(res);
@ -311,6 +327,10 @@ public class NpmPackageVersionConverter {
org.hl7.fhir.dstu2.model.ImplementationGuide ig = (org.hl7.fhir.dstu2.model.ImplementationGuide) res;
ig.setFhirVersion(version);
}
if (res instanceof org.hl7.fhir.dstu2.model.StructureDefinition) {
org.hl7.fhir.dstu2.model.StructureDefinition sd = (org.hl7.fhir.dstu2.model.StructureDefinition) res;
sd.setFhirVersion(version);
}
}
private void convertResourceR2B(org.hl7.fhir.dstu2016may.model.Resource res) {
@ -318,6 +338,10 @@ public class NpmPackageVersionConverter {
org.hl7.fhir.dstu2016may.model.ImplementationGuide ig = (org.hl7.fhir.dstu2016may.model.ImplementationGuide) res;
ig.setFhirVersion(version);
}
if (res instanceof org.hl7.fhir.dstu2016may.model.StructureDefinition) {
org.hl7.fhir.dstu2016may.model.StructureDefinition sd = (org.hl7.fhir.dstu2016may.model.StructureDefinition) res;
sd.setFhirVersion(version);
}
}
private void convertResourceR3(org.hl7.fhir.dstu3.model.Resource res) {
@ -325,6 +349,10 @@ public class NpmPackageVersionConverter {
org.hl7.fhir.dstu3.model.ImplementationGuide ig = (org.hl7.fhir.dstu3.model.ImplementationGuide) res;
ig.setFhirVersion(version);
}
if (res instanceof org.hl7.fhir.dstu3.model.StructureDefinition) {
org.hl7.fhir.dstu3.model.StructureDefinition sd = (org.hl7.fhir.dstu3.model.StructureDefinition) res;
sd.setFhirVersion(version);
}
}
private void convertResourceR4(org.hl7.fhir.r4.model.Resource res) {
@ -334,6 +362,23 @@ public class NpmPackageVersionConverter {
ig.getFhirVersion().add(new org.hl7.fhir.r4.model.Enumeration<>(new org.hl7.fhir.r4.model.Enumerations.FHIRVersionEnumFactory(), version));
ig.setPackageId(packageId);
}
if (res instanceof org.hl7.fhir.r4.model.StructureDefinition) {
org.hl7.fhir.r4.model.StructureDefinition sd = (org.hl7.fhir.r4.model.StructureDefinition) res;
sd.setFhirVersion(org.hl7.fhir.r4.model.Enumerations.FHIRVersion.fromCode(version));
}
}
private void convertResourceR4B(org.hl7.fhir.r4.model.Resource res) {
if (res instanceof org.hl7.fhir.r4.model.ImplementationGuide) {
org.hl7.fhir.r4.model.ImplementationGuide ig = (org.hl7.fhir.r4.model.ImplementationGuide) res;
ig.getFhirVersion().clear();
ig.getFhirVersion().add(new org.hl7.fhir.r4.model.Enumeration<>(new org.hl7.fhir.r4.model.Enumerations.FHIRVersionEnumFactory(), version));
ig.setPackageId(packageId);
}
if (res instanceof org.hl7.fhir.r4.model.StructureDefinition) {
org.hl7.fhir.r4.model.StructureDefinition sd = (org.hl7.fhir.r4.model.StructureDefinition) res;
sd.setFhirVersion(org.hl7.fhir.r4.model.Enumerations.FHIRVersion.fromCode(version));
}
}
@ -344,6 +389,10 @@ public class NpmPackageVersionConverter {
ig.getFhirVersion().add(new Enumeration<>(new FHIRVersionEnumFactory(), version));
ig.setPackageId(packageId);
}
if (res instanceof StructureDefinition) {
StructureDefinition sd = (StructureDefinition) res;
sd.setFhirVersion(FHIRVersion.fromCode(version));
}
}
}

View File

@ -45,10 +45,10 @@ public class VSACImporter extends OIDBasedValueSetImporter {
public static void main(String[] args) throws FHIRException, IOException, ParseException, URISyntaxException {
VSACImporter self = new VSACImporter();
self.process(args[0], args[1], args[2], "true".equals(args[3]));
self.process(args[0], args[1], args[2], "true".equals(args[3]), "true".equals(args[4]));
}
private void process(String source, String dest, String apiKey, boolean onlyNew) throws FHIRException, IOException, URISyntaxException {
private void process(String source, String dest, String apiKey, boolean onlyNew, boolean onlyActive) throws FHIRException, IOException, URISyntaxException {
CSVReader csv = new CSVReader(ManagedFileAccess.inStream(source));
csv.readHeaders();
Map<String, String> errs = new HashMap<>();
@ -75,13 +75,20 @@ public class VSACImporter extends OIDBasedValueSetImporter {
System.out.println("Loading");
List<String> oids = new ArrayList<>();
List<String> allOids = new ArrayList<>();
while (csv.line()) {
String oid = csv.cell("OID");
if (!onlyNew || !(ManagedFileAccess.file(Utilities.path(dest, "ValueSet-" + oid + ".json")).exists())) {
oids.add(oid);
String status = csv.cell("Expansion Status");
if (!onlyActive || "Active".equals(status)) {
String oid = csv.cell("OID");
allOids.add(oid);
if (!onlyNew || !(ManagedFileAccess.file(Utilities.path(dest, "ValueSet-" + oid + ".json")).exists())) {
oids.add(oid);
}
}
}
Collections.sort(oids);
System.out.println("Cleaning");
cleanValueSets(allOids, dest);
System.out.println("Go: "+oids.size()+" oids");
int i = 0;
int j = 0;
@ -108,6 +115,7 @@ public class VSACImporter extends OIDBasedValueSetImporter {
errs.put(oid, e.getMessage());
}
}
OperationOutcome oo = new OperationOutcome();
for (String oid : errs.keySet()) {
oo.addIssue().setSeverity(IssueSeverity.ERROR).setCode(IssueType.EXCEPTION).setDiagnostics(errs.get(oid)).addLocation(oid);
@ -116,6 +124,22 @@ public class VSACImporter extends OIDBasedValueSetImporter {
System.out.println("Done. " + i + " ValueSets in "+Utilities.describeDuration(System.currentTimeMillis() - tt));
}
private void cleanValueSets(List<String> allOids, String dest) throws IOException {
cleanValueSets(allOids, new File(Utilities.path(dest)));
}
private void cleanValueSets(List<String> allOids, File file) {
for (File f : file.listFiles()) {
if (f.getName().startsWith("ValueSet-")) {
String oid = f.getName().substring(9).replace(".json", "");
if (!allOids.contains(oid)) {
f.delete();
}
}
}
}
private long estimate(int i, int size, long tt) {
long elapsed = System.currentTimeMillis() - tt;
long average = elapsed / i;
@ -201,7 +225,7 @@ public class VSACImporter extends OIDBasedValueSetImporter {
}
vs.setName(makeValidName(vs.getName()));
JurisdictionUtilities.setJurisdictionCountry(vs.getJurisdiction(), "US");
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(dest, "ValueSet-" + oid + ".json")), vs);
new JsonParser().setOutputStyle(OutputStyle.NORMAL).compose(ManagedFileAccess.outStream(Utilities.path(dest, "ValueSet-" + oid + ".json")), vs);
return true;
}
}

View File

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

View File

@ -683,7 +683,7 @@ public class ClientUtils {
for (Header h : response.getAllHeaders()) {
headers.add(h.toString());
}
logger.logResponse(response.getStatusLine().toString(), headers, cnt);
logger.logResponse(response.getStatusLine().toString(), headers, cnt, 0);
}
return cnt;
}

View File

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

View File

@ -625,7 +625,7 @@ public class ClientUtils {
for (Header h : response.getAllHeaders()) {
headers.add(h.toString());
}
logger.logResponse(response.getStatusLine().toString(), headers, cnt);
logger.logResponse(response.getStatusLine().toString(), headers, cnt, 0);
}
return cnt;
}

View File

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

View File

@ -1,6 +1,7 @@
package org.hl7.fhir.dstu3.model;
import java.math.BigDecimal;
import java.util.ArrayList;
/*
Copyright (c) 2011+, HL7, Inc.
@ -668,12 +669,16 @@ public class Quantity extends Type implements ICompositeType {
public Quantity copy() {
Quantity dst = new Quantity();
copyValues(dst);
return dst;
}
public void copyValues(Quantity dst) {
super.copyValues(dst);
dst.value = value == null ? null : value.copy();
dst.comparator = comparator == null ? null : comparator.copy();
dst.unit = unit == null ? null : unit.copy();
dst.system = system == null ? null : system.copy();
dst.code = code == null ? null : code.copy();
return dst;
}
protected Quantity typedCopy() {

View File

@ -371,7 +371,7 @@ public class FhirRequestBuilder {
try {
if (logger != null) {
logger.logResponse(Integer.toString(responseCode), headerList, responseBody);
logger.logResponse(Integer.toString(responseCode), headerList, responseBody, 0);
}
} catch (Exception e) {
System.out.println("Error parsing response body passed in to logger ->\n" + e.getLocalizedMessage());

View File

@ -0,0 +1,40 @@
package org.hl7.fhir.dstu3.model;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import org.hl7.fhir.dstu3.formats.JsonParser;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class MedicationAdministrationCopyTest {
@DisplayName("Test MedicationAdministration copy")
@Test
public void test() throws IOException {
MedicationAdministration beforeCopy = createMedAdminWithDosageDurationExtension();
MedicationAdministration afterCopy = beforeCopy.copy();
System.out.println("---- BEFORE COPY (BEGIN)");
System.out.println(new JsonParser().composeString(beforeCopy));
System.out.println("---- BEFORE COPY (END)");
System.out.println();
System.out.println("---- AFTER COPY (BEGIN)");
System.out.println(new JsonParser().composeString(afterCopy));
System.out.println("---- AFTER COPY (END)");
assertTrue(beforeCopy.equalsDeep(afterCopy));
}
private static MedicationAdministration createMedAdminWithDosageDurationExtension() {
MedicationAdministration resource = new MedicationAdministration();
resource.setId("12345");
var dosage = new MedicationAdministration.MedicationAdministrationDosageComponent();
dosage.setDose((SimpleQuantity) new SimpleQuantity().setValue(40))
.addExtension(new Extension()
.setUrl("http://duration")
.setValue(new Duration().setValue(5340000)));
resource.setDosage(dosage);
return resource;
}
}

View File

@ -95,13 +95,13 @@ public class FhirRequestBuilderTests {
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()));
Mockito.verify(logger).logResponse(ArgumentMatchers.eq("200"), ArgumentMatchers.anyList(), AdditionalMatchers.aryEq(RESPONSE_BODY_STRING.getBytes()), ArgumentMatchers.anyLong());
}
@Test
public void testUnmarshallFeedLogging() {
fhirRequestBuilder.unmarshalFeed(response, "application/json");
Mockito.verify(logger).logResponse(ArgumentMatchers.eq("200"), ArgumentMatchers.anyList(), AdditionalMatchers.aryEq(RESPONSE_BODY_STRING.getBytes()));
Mockito.verify(logger).logResponse(ArgumentMatchers.eq("200"), ArgumentMatchers.anyList(), AdditionalMatchers.aryEq(RESPONSE_BODY_STRING.getBytes()), ArgumentMatchers.anyLong());
}
}

View File

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

View File

@ -77,7 +77,7 @@ public class HTMLClientLogger implements ToolingClientLogger {
}
@Override
public void logResponse(String outcome, List<String> headers, byte[] body) {
public void logResponse(String outcome, List<String> headers, byte[] body, long start) {
if (file == null)
return;
file.println("<pre>");

View File

@ -8400,15 +8400,15 @@ public class JsonParser extends JsonParserBase {
parseElementProperties(getJObject(json, "_verificationDate"), res.getVerificationDateElement());
}
protected Consent.provisionComponent parseConsentprovisionComponent(JsonObject json, Consent owner)
protected Consent.ProvisionComponent parseConsentprovisionComponent(JsonObject json, Consent owner)
throws IOException, FHIRFormatError {
Consent.provisionComponent res = new Consent.provisionComponent();
Consent.ProvisionComponent res = new Consent.ProvisionComponent();
parseConsentprovisionComponentProperties(json, owner, res);
return res;
}
protected void parseConsentprovisionComponentProperties(JsonObject json, Consent owner,
Consent.provisionComponent res) throws IOException, FHIRFormatError {
Consent.ProvisionComponent res) throws IOException, FHIRFormatError {
parseBackboneElementProperties(json, res);
if (json.has("type"))
res.setTypeElement(parseEnumeration(json.get("type").getAsString(), Consent.ConsentProvisionType.NULL,
@ -41383,7 +41383,7 @@ public class JsonParser extends JsonParserBase {
}
}
protected void composeConsentprovisionComponent(String name, Consent.provisionComponent element) throws IOException {
protected void composeConsentprovisionComponent(String name, Consent.ProvisionComponent element) throws IOException {
if (element != null) {
open(name);
composeConsentprovisionComponentInner(element);
@ -41391,7 +41391,7 @@ public class JsonParser extends JsonParserBase {
}
}
protected void composeConsentprovisionComponentInner(Consent.provisionComponent element) throws IOException {
protected void composeConsentprovisionComponentInner(Consent.ProvisionComponent element) throws IOException {
composeBackbone(element);
if (element.hasTypeElement()) {
composeEnumerationCore("type", element.getTypeElement(), new Consent.ConsentProvisionTypeEnumFactory(), false);
@ -41454,7 +41454,7 @@ public class JsonParser extends JsonParserBase {
;
if (element.hasProvision()) {
openArray("provision");
for (Consent.provisionComponent e : element.getProvision())
for (Consent.ProvisionComponent e : element.getProvision())
composeConsentprovisionComponent(null, e);
closeArray();
}

View File

@ -4917,7 +4917,7 @@ public class RdfParser extends RdfParserBase {
}
protected void composeConsentprovisionComponent(Complex parent, String parentType, String name,
Consent.provisionComponent element, int index) {
Consent.ProvisionComponent element, int index) {
if (element == null)
return;
Complex t;

View File

@ -6924,9 +6924,9 @@ public class XmlParser extends XmlParserBase {
return true;
}
protected Consent.provisionComponent parseConsentprovisionComponent(XmlPullParser xpp, Consent owner)
protected Consent.ProvisionComponent parseConsentprovisionComponent(XmlPullParser xpp, Consent owner)
throws XmlPullParserException, IOException, FHIRFormatError {
Consent.provisionComponent res = new Consent.provisionComponent();
Consent.ProvisionComponent res = new Consent.ProvisionComponent();
parseBackboneAttributes(xpp, res);
next(xpp);
int eventType = nextNoWhitespace(xpp);
@ -6941,7 +6941,7 @@ public class XmlParser extends XmlParserBase {
}
protected boolean parseConsentprovisionComponentContent(int eventType, XmlPullParser xpp, Consent owner,
Consent.provisionComponent res) throws XmlPullParserException, IOException, FHIRFormatError {
Consent.ProvisionComponent res) throws XmlPullParserException, IOException, FHIRFormatError {
if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("type")) {
res.setTypeElement(
parseEnumeration(xpp, Consent.ConsentProvisionType.NULL, new Consent.ConsentProvisionTypeEnumFactory()));
@ -34008,7 +34008,7 @@ public class XmlParser extends XmlParserBase {
}
}
protected void composeConsentprovisionComponent(String name, Consent.provisionComponent element) throws IOException {
protected void composeConsentprovisionComponent(String name, Consent.ProvisionComponent element) throws IOException {
if (element != null) {
composeElementAttributes(element);
xml.enter(FHIR_NS, name);
@ -34018,7 +34018,7 @@ public class XmlParser extends XmlParserBase {
}
}
protected void composeConsentprovisionComponentElements(Consent.provisionComponent element) throws IOException {
protected void composeConsentprovisionComponentElements(Consent.ProvisionComponent element) throws IOException {
composeBackboneElementElements(element);
if (element.hasTypeElement())
composeEnumeration("type", element.getTypeElement(), new Consent.ConsentProvisionTypeEnumFactory());
@ -34057,7 +34057,7 @@ public class XmlParser extends XmlParserBase {
composeConsentprovisionDataComponent("data", e);
}
if (element.hasProvision()) {
for (Consent.provisionComponent e : element.getProvision())
for (Consent.ProvisionComponent e : element.getProvision())
composeConsentprovisionComponent("provision", e);
}
}

View File

@ -1221,7 +1221,7 @@ public class Consent extends DomainResource {
}
@Block()
public static class provisionComponent extends BackboneElement implements IBaseBackboneElement {
public static class ProvisionComponent extends BackboneElement implements IBaseBackboneElement {
/**
* Action to take - permit or deny - when the rule conditions are met. Not
* permitted in root rule, required in all nested rules.
@ -1314,16 +1314,16 @@ public class Consent extends DomainResource {
* Rules which provide exceptions to the base rule or subrules.
*/
@Child(name = "provision", type = {
provisionComponent.class }, order = 11, min = 0, max = Child.MAX_UNLIMITED, modifier = false, summary = false)
ProvisionComponent.class }, order = 11, min = 0, max = Child.MAX_UNLIMITED, modifier = false, summary = false)
@Description(shortDefinition = "Nested Exception Rules", formalDefinition = "Rules which provide exceptions to the base rule or subrules.")
protected List<provisionComponent> provision;
protected List<ProvisionComponent> provision;
private static final long serialVersionUID = -1280172451L;
/**
* Constructor
*/
public provisionComponent() {
public ProvisionComponent() {
super();
}
@ -1357,7 +1357,7 @@ public class Consent extends DomainResource {
* extensions. The accessor "getType" gives direct access to the
* value
*/
public provisionComponent setTypeElement(Enumeration<ConsentProvisionType> value) {
public ProvisionComponent setTypeElement(Enumeration<ConsentProvisionType> value) {
this.type = value;
return this;
}
@ -1374,7 +1374,7 @@ public class Consent extends DomainResource {
* @param value Action to take - permit or deny - when the rule conditions are
* met. Not permitted in root rule, required in all nested rules.
*/
public provisionComponent setType(ConsentProvisionType value) {
public ProvisionComponent setType(ConsentProvisionType value) {
if (value == null)
this.type = null;
else {
@ -1404,7 +1404,7 @@ public class Consent extends DomainResource {
/**
* @param value {@link #period} (The timeframe in this rule is valid.)
*/
public provisionComponent setPeriod(Period value) {
public ProvisionComponent setPeriod(Period value) {
this.period = value;
return this;
}
@ -1423,7 +1423,7 @@ public class Consent extends DomainResource {
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setActor(List<provisionActorComponent> theActor) {
public ProvisionComponent setActor(List<provisionActorComponent> theActor) {
this.actor = theActor;
return this;
}
@ -1445,7 +1445,7 @@ public class Consent extends DomainResource {
return t;
}
public provisionComponent addActor(provisionActorComponent t) { // 3
public ProvisionComponent addActor(provisionActorComponent t) { // 3
if (t == null)
return this;
if (this.actor == null)
@ -1477,7 +1477,7 @@ public class Consent extends DomainResource {
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setAction(List<CodeableConcept> theAction) {
public ProvisionComponent setAction(List<CodeableConcept> theAction) {
this.action = theAction;
return this;
}
@ -1499,7 +1499,7 @@ public class Consent extends DomainResource {
return t;
}
public provisionComponent addAction(CodeableConcept t) { // 3
public ProvisionComponent addAction(CodeableConcept t) { // 3
if (t == null)
return this;
if (this.action == null)
@ -1533,7 +1533,7 @@ public class Consent extends DomainResource {
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setSecurityLabel(List<Coding> theSecurityLabel) {
public ProvisionComponent setSecurityLabel(List<Coding> theSecurityLabel) {
this.securityLabel = theSecurityLabel;
return this;
}
@ -1555,7 +1555,7 @@ public class Consent extends DomainResource {
return t;
}
public provisionComponent addSecurityLabel(Coding t) { // 3
public ProvisionComponent addSecurityLabel(Coding t) { // 3
if (t == null)
return this;
if (this.securityLabel == null)
@ -1589,7 +1589,7 @@ public class Consent extends DomainResource {
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setPurpose(List<Coding> thePurpose) {
public ProvisionComponent setPurpose(List<Coding> thePurpose) {
this.purpose = thePurpose;
return this;
}
@ -1611,7 +1611,7 @@ public class Consent extends DomainResource {
return t;
}
public provisionComponent addPurpose(Coding t) { // 3
public ProvisionComponent addPurpose(Coding t) { // 3
if (t == null)
return this;
if (this.purpose == null)
@ -1646,7 +1646,7 @@ public class Consent extends DomainResource {
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setClass_(List<Coding> theClass_) {
public ProvisionComponent setClass_(List<Coding> theClass_) {
this.class_ = theClass_;
return this;
}
@ -1668,7 +1668,7 @@ public class Consent extends DomainResource {
return t;
}
public provisionComponent addClass_(Coding t) { // 3
public ProvisionComponent addClass_(Coding t) { // 3
if (t == null)
return this;
if (this.class_ == null)
@ -1701,7 +1701,7 @@ public class Consent extends DomainResource {
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setCode(List<CodeableConcept> theCode) {
public ProvisionComponent setCode(List<CodeableConcept> theCode) {
this.code = theCode;
return this;
}
@ -1723,7 +1723,7 @@ public class Consent extends DomainResource {
return t;
}
public provisionComponent addCode(CodeableConcept t) { // 3
public ProvisionComponent addCode(CodeableConcept t) { // 3
if (t == null)
return this;
if (this.code == null)
@ -1764,7 +1764,7 @@ public class Consent extends DomainResource {
* @param value {@link #dataPeriod} (Clinical or Operational Relevant period of
* time that bounds the data controlled by this rule.)
*/
public provisionComponent setDataPeriod(Period value) {
public ProvisionComponent setDataPeriod(Period value) {
this.dataPeriod = value;
return this;
}
@ -1782,7 +1782,7 @@ public class Consent extends DomainResource {
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setData(List<provisionDataComponent> theData) {
public ProvisionComponent setData(List<provisionDataComponent> theData) {
this.data = theData;
return this;
}
@ -1804,7 +1804,7 @@ public class Consent extends DomainResource {
return t;
}
public provisionComponent addData(provisionDataComponent t) { // 3
public ProvisionComponent addData(provisionDataComponent t) { // 3
if (t == null)
return this;
if (this.data == null)
@ -1828,16 +1828,16 @@ public class Consent extends DomainResource {
* @return {@link #provision} (Rules which provide exceptions to the base rule
* or subrules.)
*/
public List<provisionComponent> getProvision() {
public List<ProvisionComponent> getProvision() {
if (this.provision == null)
this.provision = new ArrayList<provisionComponent>();
this.provision = new ArrayList<ProvisionComponent>();
return this.provision;
}
/**
* @return Returns a reference to <code>this</code> for easy method chaining
*/
public provisionComponent setProvision(List<provisionComponent> theProvision) {
public ProvisionComponent setProvision(List<ProvisionComponent> theProvision) {
this.provision = theProvision;
return this;
}
@ -1845,25 +1845,25 @@ public class Consent extends DomainResource {
public boolean hasProvision() {
if (this.provision == null)
return false;
for (provisionComponent item : this.provision)
for (ProvisionComponent item : this.provision)
if (!item.isEmpty())
return true;
return false;
}
public provisionComponent addProvision() { // 3
provisionComponent t = new provisionComponent();
public ProvisionComponent addProvision() { // 3
ProvisionComponent t = new ProvisionComponent();
if (this.provision == null)
this.provision = new ArrayList<provisionComponent>();
this.provision = new ArrayList<ProvisionComponent>();
this.provision.add(t);
return t;
}
public provisionComponent addProvision(provisionComponent t) { // 3
public ProvisionComponent addProvision(ProvisionComponent t) { // 3
if (t == null)
return this;
if (this.provision == null)
this.provision = new ArrayList<provisionComponent>();
this.provision = new ArrayList<ProvisionComponent>();
this.provision.add(t);
return this;
}
@ -1872,7 +1872,7 @@ public class Consent extends DomainResource {
* @return The first repetition of repeating field {@link #provision}, creating
* it if it does not already exist
*/
public provisionComponent getProvisionFirstRep() {
public ProvisionComponent getProvisionFirstRep() {
if (getProvision().isEmpty()) {
addProvision();
}
@ -2027,7 +2027,7 @@ public class Consent extends DomainResource {
this.getData().add((provisionDataComponent) value); // provisionDataComponent
return value;
case -547120939: // provision
this.getProvision().add((provisionComponent) value); // provisionComponent
this.getProvision().add((ProvisionComponent) value); // provisionComponent
return value;
default:
return super.setProperty(hash, name, value);
@ -2059,7 +2059,7 @@ public class Consent extends DomainResource {
} else if (name.equals("data")) {
this.getData().add((provisionDataComponent) value);
} else if (name.equals("provision")) {
this.getProvision().add((provisionComponent) value);
this.getProvision().add((ProvisionComponent) value);
} else
return super.setProperty(name, value);
return value;
@ -2088,7 +2088,7 @@ public class Consent extends DomainResource {
} else if (name.equals("data")) {
this.getData().remove((provisionDataComponent) value);
} else if (name.equals("provision")) {
this.getProvision().remove((provisionComponent) value);
this.getProvision().remove((ProvisionComponent) value);
} else
super.removeChild(name, value);
@ -2186,13 +2186,13 @@ public class Consent extends DomainResource {
return super.addChild(name);
}
public provisionComponent copy() {
provisionComponent dst = new provisionComponent();
public ProvisionComponent copy() {
ProvisionComponent dst = new ProvisionComponent();
copyValues(dst);
return dst;
}
public void copyValues(provisionComponent dst) {
public void copyValues(ProvisionComponent dst) {
super.copyValues(dst);
dst.type = type == null ? null : type.copy();
dst.period = period == null ? null : period.copy();
@ -2240,8 +2240,8 @@ public class Consent extends DomainResource {
}
;
if (provision != null) {
dst.provision = new ArrayList<provisionComponent>();
for (provisionComponent i : provision)
dst.provision = new ArrayList<ProvisionComponent>();
for (ProvisionComponent i : provision)
dst.provision.add(i.copy());
}
;
@ -2251,9 +2251,9 @@ public class Consent extends DomainResource {
public boolean equalsDeep(Base other_) {
if (!super.equalsDeep(other_))
return false;
if (!(other_ instanceof provisionComponent))
if (!(other_ instanceof ProvisionComponent))
return false;
provisionComponent o = (provisionComponent) other_;
ProvisionComponent o = (ProvisionComponent) other_;
return compareDeep(type, o.type, true) && compareDeep(period, o.period, true) && compareDeep(actor, o.actor, true)
&& compareDeep(action, o.action, true) && compareDeep(securityLabel, o.securityLabel, true)
&& compareDeep(purpose, o.purpose, true) && compareDeep(class_, o.class_, true)
@ -2265,9 +2265,9 @@ public class Consent extends DomainResource {
public boolean equalsShallow(Base other_) {
if (!super.equalsShallow(other_))
return false;
if (!(other_ instanceof provisionComponent))
if (!(other_ instanceof ProvisionComponent))
return false;
provisionComponent o = (provisionComponent) other_;
ProvisionComponent o = (ProvisionComponent) other_;
return compareValues(type, o.type, true);
}
@ -3002,7 +3002,7 @@ public class Consent extends DomainResource {
*/
@Child(name = "provision", type = {}, order = 12, min = 0, max = 1, modifier = false, summary = true)
@Description(shortDefinition = "Constraints to the base Consent.policyRule", formalDefinition = "An exception to the base policy of this consent. An exception can be an addition or removal of access permissions.")
protected provisionComponent provision;
protected ProvisionComponent provision;
private static final long serialVersionUID = 206528051L;
@ -3669,12 +3669,12 @@ public class Consent extends DomainResource {
* @return {@link #provision} (An exception to the base policy of this consent.
* An exception can be an addition or removal of access permissions.)
*/
public provisionComponent getProvision() {
public ProvisionComponent getProvision() {
if (this.provision == null)
if (Configuration.errorOnAutoCreate())
throw new Error("Attempt to auto-create Consent.provision");
else if (Configuration.doAutoCreate())
this.provision = new provisionComponent(); // cc
this.provision = new ProvisionComponent(); // cc
return this.provision;
}
@ -3687,7 +3687,7 @@ public class Consent extends DomainResource {
* consent. An exception can be an addition or removal of access
* permissions.)
*/
public Consent setProvision(provisionComponent value) {
public Consent setProvision(ProvisionComponent value) {
this.provision = value;
return this;
}
@ -3883,7 +3883,7 @@ public class Consent extends DomainResource {
this.getVerification().add((ConsentVerificationComponent) value); // ConsentVerificationComponent
return value;
case -547120939: // provision
this.provision = (provisionComponent) value; // provisionComponent
this.provision = (ProvisionComponent) value; // provisionComponent
return value;
default:
return super.setProperty(hash, name, value);
@ -3919,7 +3919,7 @@ public class Consent extends DomainResource {
} else if (name.equals("verification")) {
this.getVerification().add((ConsentVerificationComponent) value);
} else if (name.equals("provision")) {
this.provision = (provisionComponent) value; // provisionComponent
this.provision = (ProvisionComponent) value; // provisionComponent
} else
return super.setProperty(name, value);
return value;
@ -3952,7 +3952,7 @@ public class Consent extends DomainResource {
} else if (name.equals("verification")) {
this.getVerification().remove((ConsentVerificationComponent) value);
} else if (name.equals("provision")) {
this.provision = (provisionComponent) value; // provisionComponent
this.provision = (ProvisionComponent) value; // provisionComponent
} else
super.removeChild(name, value);
@ -4064,7 +4064,7 @@ public class Consent extends DomainResource {
} else if (name.equals("verification")) {
return addVerification();
} else if (name.equals("provision")) {
this.provision = new provisionComponent();
this.provision = new ProvisionComponent();
return this.provision;
} else
return super.addChild(name);

View File

@ -64,7 +64,7 @@ public class FhirLoggingInterceptor implements Interceptor {
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
if (logger != null) {
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes);
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes, 0);
}
// Reading byte[] clears body. Need to recreate.

View File

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

View File

@ -85,7 +85,7 @@ public class HTMLClientLogger extends BaseLogger implements ToolingClientLogger
}
@Override
public void logResponse(String outcome, List<String> headers, byte[] body) {
public void logResponse(String outcome, List<String> headers, byte[] body, long start) {
if (DEBUG) {
System.out.println(" txlog resp: " + outcome + " " + present(body));
}

View File

@ -72,7 +72,7 @@ public class TextClientLogger extends BaseLogger implements ToolingClientLogger
}
@Override
public void logResponse(String outcome, List<String> headers, byte[] body) {
public void logResponse(String outcome, List<String> headers, byte[] body, long start) {
if (file == null)
return;
file.println("\r\n\r\nResponse: \r\n");

View File

@ -59,7 +59,7 @@ public class FhirLoggingInterceptor implements Interceptor {
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
if (logger != null) {
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes);
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes, 0);
}
// Reading byte[] clears body. Need to recreate.

View File

@ -134,6 +134,6 @@ class ClientTest {
server.takeRequest();
Mockito.verify(mockLogger, Mockito.times(1)).logRequest(Mockito.anyString(), Mockito.anyString(), Mockito.anyList(),
Mockito.any());
Mockito.verify(mockLogger, Mockito.times(1)).logResponse(Mockito.anyString(), Mockito.anyList(), Mockito.any());
Mockito.verify(mockLogger, Mockito.times(1)).logResponse(Mockito.anyString(), Mockito.anyList(), Mockito.any(), Mockito.anyLong());
}
}

View File

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

View File

@ -413,11 +413,10 @@ public class ProfileUtilities {
// note that ProfileUtilities are used re-entrantly internally, so nothing with process state can be here
private final IWorkerContext context;
private FHIRPathEngine fpe;
private List<ValidationMessage> messages;
private List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
private List<String> snapshotStack = new ArrayList<String>();
private ProfileKnowledgeProvider pkp;
// private boolean igmode;
private boolean exception;
private ValidationOptions terminologyServiceOptions = new ValidationOptions(FhirPublication.R5);
private boolean newSlicingProcessing;
private String defWebRoot;
@ -431,11 +430,16 @@ public class ProfileUtilities {
private MappingMergeModeOption mappingMergeMode = MappingMergeModeOption.APPEND;
private boolean forPublication;
private List<StructureDefinition> obligationProfiles = new ArrayList<>();
private boolean wantThrowExceptions;
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) {
super();
this.context = context;
this.messages = messages;
if (messages != null) {
this.messages = messages;
} else {
wantThrowExceptions = true;
}
this.pkp = pkp;
this.fpe = fpe;
@ -447,7 +451,11 @@ public class ProfileUtilities {
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp) {
super();
this.context = context;
this.messages = messages;
if (messages != null) {
this.messages = messages;
} else {
wantThrowExceptions = true;
}
this.pkp = pkp;
if (context != null) {
this.fpe = new FHIRPathEngine(context, this);
@ -789,7 +797,7 @@ public class ProfileUtilities {
ce++;
if (e.hasId()) {
String msg = "No match found for "+e.getId()+" 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, "StructureDefinition.differential.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, "StructureDefinition.differential.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR));
}
}
i++;
@ -862,19 +870,19 @@ public class ProfileUtilities {
slice.getFocus().setMin(count);
} else {
String msg = "The slice definition for "+slice.getFocus().getId()+" has a minimum of "+slice.getFocus().getMin()+" but the slices add up to a minimum of "+count;
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+slice.getIndex()+"]", msg, forPublication ? ValidationMessage.IssueSeverity.ERROR : ValidationMessage.IssueSeverity.INFORMATION).setIgnorableError(true));
}
}
count = slice.checkMax();
if (count > -1 && repeats) {
String msg = "The slice definition for "+slice.getFocus().getId()+" has a maximum of "+slice.getFocus().getMax()+" but the slices add up to a maximum of "+count+". Check that this is what is intended";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+slice.getIndex()+"]", msg, ValidationMessage.IssueSeverity.INFORMATION));
}
if (!slice.checkMinMax()) {
String msg = "The slice definition for "+slice.getFocus().getId()+" has a maximum of "+slice.getFocus().getMax()+" which is less than the minimum of "+slice.getFocus().getMin();
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+slice.getIndex()+"]", msg, ValidationMessage.IssueSeverity.WARNING));
}
slices.remove(s);
@ -885,13 +893,13 @@ public class ProfileUtilities {
}
if (ed.hasSliceName() && !slices.containsKey(ed.getPath())) {
String msg = "The element "+ed.getId()+" launches straight into slicing without the slicing being set up properly first";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR).setIgnorableError(true));
}
if (ed.hasSliceName() && slices.containsKey(ed.getPath())) {
if (!slices.get(ed.getPath()).count(ed, ed.getSliceName())) {
String msg = "Duplicate slice name "+ed.getSliceName()+" on "+ed.getId()+" (["+i+"])";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR).setIgnorableError(true));
}
}
@ -910,10 +918,8 @@ public class ProfileUtilities {
}
}
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+i+"]", "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING));
}
} else {
String wt = t.getWorkingCode();
if (ed.getPath().equals("Bundle.entry.response.outcome")) {
@ -1012,13 +1018,15 @@ public class ProfileUtilities {
}
private void handleError(String url, String msg) {
if (exception)
throw new DefinitionException(msg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR));
}
private void addMessage(ValidationMessage msg) {
messages.add(msg);
if (msg.getLevel() == IssueSeverity.ERROR && wantThrowExceptions) {
throw new DefinitionException(msg.getMessage());
}
}
private void copyInheritedExtensions(StructureDefinition base, StructureDefinition derived, String webUrl) {
@ -2249,7 +2257,7 @@ public class ProfileUtilities {
* Not sure we have enough information here to do the check properly. Might be better done when we're sorting the profile?
if (i != start && result.isEmpty() && !path.startsWith(context.getElement().get(start).getPath()))
messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.VALUE, "StructureDefinition.differential.element["+Integer.toString(start)+"]", "Error: unknown element '"+context.getElement().get(start).getPath()+"' (or it is out of order) in profile '"+url+"' (looking for '"+path+"')", IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, IssueType.VALUE, "StructureDefinition.differential.element["+Integer.toString(start)+"]", "Error: unknown element '"+context.getElement().get(start).getPath()+"' (or it is out of order) in profile '"+url+"' (looking for '"+path+"')", IssueSeverity.WARNING));
*/
result.add(context.getElement().get(i));
@ -2529,7 +2537,7 @@ public class ProfileUtilities {
if (derived.hasMinElement()) {
if (!Base.compareDeep(derived.getMinElement(), base.getMinElement(), false)) {
if (derived.getMin() < base.getMin() && !derived.hasSliceName()) // in a slice, minimum cardinality rules do not apply
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived min ("+Integer.toString(derived.getMin())+") cannot be less than the base min ("+Integer.toString(base.getMin())+") in "+srcSD.getVersionedUrl(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived min ("+Integer.toString(derived.getMin())+") cannot be less than the base min ("+Integer.toString(base.getMin())+") in "+srcSD.getVersionedUrl(), ValidationMessage.IssueSeverity.ERROR));
base.setMinElement(derived.getMinElement().copy());
} else if (trimDifferential)
derived.setMinElement(null);
@ -2540,7 +2548,7 @@ public class ProfileUtilities {
if (derived.hasMaxElement()) {
if (!Base.compareDeep(derived.getMaxElement(), base.getMaxElement(), false)) {
if (isLargerMax(derived.getMax(), base.getMax()))
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived max ("+derived.getMax()+") cannot be greater than the base max ("+base.getMax()+")", ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived max ("+derived.getMax()+") cannot be greater than the base max ("+base.getMax()+")", ValidationMessage.IssueSeverity.ERROR));
base.setMaxElement(derived.getMaxElement().copy());
} else if (trimDifferential)
derived.setMaxElement(null);
@ -2642,7 +2650,7 @@ public class ProfileUtilities {
}
if (!(base.hasMustSupportElement() && Base.compareDeep(base.getMustSupportElement(), mse, false))) {
if (base.hasMustSupport() && base.getMustSupport() && !derived.getMustSupport()) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-support = false] when [must-support = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-support = false] when [must-support = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
}
base.setMustSupportElement(mse);
} else if (trimDifferential)
@ -2654,7 +2662,7 @@ public class ProfileUtilities {
if (derived.hasMustHaveValueElement()) {
if (!(base.hasMustHaveValueElement() && Base.compareDeep(derived.getMustHaveValueElement(), base.getMustHaveValueElement(), false))) {
if (base.hasMustHaveValue() && base.getMustHaveValue() && !derived.getMustHaveValue()) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-have-value = false] when [must-have-value = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-have-value = false] when [must-have-value = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
}
base.setMustHaveValueElement(derived.getMustHaveValueElement().copy());
} else if (trimDifferential)
@ -2721,25 +2729,25 @@ public class ProfileUtilities {
if (!base.hasBinding() || !Base.compareDeep(derived.getBinding(), base.getBinding(), false)) {
if (base.hasBinding() && base.getBinding().getStrength() == BindingStrength.REQUIRED && derived.getBinding().getStrength() != BindingStrength.REQUIRED)
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "illegal attempt to change the binding on "+derived.getPath()+" from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "illegal attempt to change the binding on "+derived.getPath()+" from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode(), ValidationMessage.IssueSeverity.ERROR));
// throw new DefinitionException("StructureDefinition "+pn+" at "+derived.getPath()+": illegal attempt to change a binding from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode());
else if (base.hasBinding() && derived.hasBinding() && base.getBinding().getStrength() == BindingStrength.REQUIRED && base.getBinding().hasValueSet() && derived.getBinding().hasValueSet()) {
ValueSet baseVs = context.findTxResource(ValueSet.class, base.getBinding().getValueSet(), srcSD);
ValueSet contextVs = context.findTxResource(ValueSet.class, derived.getBinding().getValueSet(), derivedSrc);
if (baseVs == null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
} else if (contextVs == null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
} else {
ValueSetExpansionOutcome expBase = context.expandVS(baseVs, true, false);
ValueSetExpansionOutcome expDerived = context.expandVS(contextVs, true, false);
if (expBase.getValueset() == null)
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
else if (expDerived.getValueset() == null)
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
else if (ToolingExtensions.hasExtension(expBase.getValueset().getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY)) {
if (ToolingExtensions.hasExtension(expDerived.getValueset().getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY) || expDerived.getValueset().getExpansion().getContains().size() > 100) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Unable to check if "+derived.getBinding().getValueSet()+" is a proper subset of " +base.getBinding().getValueSet()+" - base value set is too large to check", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Unable to check if "+derived.getBinding().getValueSet()+" is a proper subset of " +base.getBinding().getValueSet()+" - base value set is too large to check", ValidationMessage.IssueSeverity.WARNING));
} else {
boolean ok = true;
for (ValueSetExpansionContainsComponent cc : expDerived.getValueset().getExpansion().getContains()) {
@ -2750,11 +2758,11 @@ public class ProfileUtilities {
}
}
if (!ok) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
}
}
} else if (!isSubset(expBase.getValueset(), expDerived.getValueset()))
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
}
}
ElementDefinitionBindingComponent d = derived.getBinding();
@ -2915,7 +2923,7 @@ public class ProfileUtilities {
private ElementDefinitionBindingAdditionalComponent getMatchingAdditionalBinding(ElementDefinitionBindingComponent nb,ElementDefinitionBindingAdditionalComponent ab) {
for (ElementDefinitionBindingAdditionalComponent t : nb.getAdditional()) {
if (t.getValueSet() != null && t.getValueSet().equals(ab.getValueSet()) && t.getPurpose() == ab.getPurpose()) {
if (t.getValueSet() != null && t.getValueSet().equals(ab.getValueSet()) && t.getPurpose() == ab.getPurpose() && !ab.hasUsage()) {
return t;
}
}
@ -2965,11 +2973,7 @@ public class ProfileUtilities {
if (tgtOk) {
ok = true;
} else {
if (messages == null) {
throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT__THE_TARGET_PROFILE__IS_NOT__VALID_CONSTRAINT_ON_THE_BASE_, purl, derived.getPath(), url, td.getTargetProfile()));
} else {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, derived.getPath(), "The target profile " + u.getValue() + " is not a valid constraint on the base (" + td.getTargetProfile() + ") at " + derived.getPath(), IssueSeverity.ERROR));
}
addMessage(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, derived.getPath(), context.formatMessage(I18nConstants.ERROR_AT__THE_TARGET_PROFILE__IS_NOT__VALID_CONSTRAINT_ON_THE_BASE_, purl, derived.getPath(), url, td.getTargetProfile()), IssueSeverity.ERROR));
}
}
} else {
@ -2989,9 +2993,7 @@ public class ProfileUtilities {
}
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, path, "Cannot check whether the target profile " + url + " on "+dPath+" is valid constraint on the base because it is not known", IssueSeverity.WARNING));
}
addMessage(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, path, "Cannot check whether the target profile " + url + " on "+dPath+" is valid constraint on the base because it is not known", IssueSeverity.WARNING));
return true;
} else {
if (sd.hasBaseDefinition() && sdConformsToTargets(path, dPath, sd.getBaseDefinition(), td)) {
@ -3022,7 +3024,7 @@ public class ProfileUtilities {
}
if (!ok) {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.CONFLICT, dest.getId(), "The "+fieldName+" value has type '"+ft+"' which is not valid (valid "+Utilities.pluralize("type", dest.getType().size())+": "+types.toString()+")", IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.InstanceValidator, IssueType.CONFLICT, dest.getId(), "The "+fieldName+" value has type '"+ft+"' which is not valid (valid "+Utilities.pluralize("type", dest.getType().size())+": "+types.toString()+")", IssueSeverity.ERROR));
}
}
@ -3936,10 +3938,7 @@ public class ProfileUtilities {
}
ed.setId(bs);
if (idList.containsKey(bs)) {
if (exception || messages == null) {
throw new DefinitionException(context.formatMessage(I18nConstants.SAME_ID_ON_MULTIPLE_ELEMENTS__IN_, bs, idList.get(bs), ed.getPath(), name));
} else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, name+"."+bs, "Duplicate Element id "+bs, ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, name+"."+bs, context.formatMessage(I18nConstants.SAME_ID_ON_MULTIPLE_ELEMENTS__IN_, bs, idList.get(bs), ed.getPath(), name), ValidationMessage.IssueSeverity.ERROR));
}
idList.put(bs, ed.getPath());
if (ed.hasContentReference() && ed.getContentReference().startsWith("#")) {
@ -4315,12 +4314,12 @@ public class ProfileUtilities {
public boolean isThrowException() {
return exception;
return wantThrowExceptions;
}
public void setThrowException(boolean exception) {
this.exception = exception;
this.wantThrowExceptions = exception;
}
@ -4578,7 +4577,10 @@ public class ProfileUtilities {
}
public void setMessages(List<ValidationMessage> messages) {
this.messages = messages;
if (messages != null) {
this.messages = messages;
wantThrowExceptions = false;
}
}
private Map<String, List<Property>> propertyCache = new HashMap<>();

View File

@ -1276,9 +1276,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
public ValidationResult validateCode(final ValidationOptions optionsArg, String path, final Coding code, final ValueSet vs, final ValidationContextCarrier ctxt) {
ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults();
if (code.hasSystem()) {
codeSystemsUsed.add(code.getSystem());
}
@ -1303,6 +1303,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
// ok, first we try to validate locally
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs, ctxt);
if (vsc.getOpContext() != null) {
vsc.getOpContext().note("Validate "+code.toString()+" @ "+path+" against "+(vs == null ? "null" : vs.getVersionedUrl()));
}
vsc.setUnknownSystems(unknownSystems);
vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient());
if (!ValueSetUtilities.isServerSide(code.getSystem())) {
@ -1471,15 +1474,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
protected ValueSetExpander constructValueSetExpanderSimple(ValidationOptions options) {
return new ValueSetExpander(this, new TerminologyOperationContext(this, options)).setDebug(logger.isDebugLogging());
return new ValueSetExpander(this, new TerminologyOperationContext(this, options, "expansion")).setDebug(logger.isDebugLogging());
}
protected ValueSetValidator constructValueSetCheckerSimple(ValidationOptions options, ValueSet vs, ValidationContextCarrier ctxt) {
return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, ctxt, expParameters, terminologyClientManager);
return new ValueSetValidator(this, new TerminologyOperationContext(this, options, "validation"), options, vs, ctxt, expParameters, terminologyClientManager);
}
protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs) {
return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, expParameters, terminologyClientManager);
return new ValueSetValidator(this, new TerminologyOperationContext(this, options, "validation"), options, vs, expParameters, terminologyClientManager);
}
protected Parameters constructParameters(TerminologyClientContext tcd, ValueSet vs, boolean hierarchical) {
@ -1643,7 +1646,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Parameters pIn = constructParameters(options, code);
res = validateOnServer(tc, vs, pIn, options);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId());
issues.clear();
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXCEPTION);
iss.getDetails().setText(e.getMessage());
issues.add(iss);
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), issues).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR);
}
if (cachingAllowed) {
txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT);
@ -1779,6 +1786,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (options.isDisplayWarningMode()) {
pin.addParameter("mode","lenient-display-validation");
}
pin.addParameter("diagnostics", true);
}
private boolean addDependentResources(TerminologyClientContext tc, Parameters pin, ValueSet vs) {

View File

@ -87,7 +87,7 @@ public class HTMLClientLogger extends BaseLogger implements ToolingClientLogger
}
@Override
public void logResponse(String outcome, List<String> headers, byte[] body) {
public void logResponse(String outcome, List<String> headers, byte[] body, long start) {
if (DEBUG) {
System.out.println(" txlog resp: " +outcome+" "+present(body));
}

View File

@ -76,10 +76,10 @@ public class TextClientLogger extends BaseLogger implements ToolingClientLogger
}
@Override
public void logResponse(String outcome, List<String> headers, byte[] body) {
public void logResponse(String outcome, List<String> headers, byte[] body, long length) {
if (file == null)
return;
file.println("\r\n\r\nResponse: \r\n");
file.println("\r\n\r\nResponse ("+Utilities.describeDuration(length)+"): \r\n");
file.println(outcome);
for (String s : headers)
file.println(s);

View File

@ -162,6 +162,7 @@ public class Element extends Base implements NamedItem {
private FhirFormat format;
private Object nativeObject;
private List<SliceDefinition> sliceDefinitions;
private boolean elided;
public Element(String name) {
super();
@ -1429,6 +1430,8 @@ public class Element extends Base implements NamedItem {
public Base copy() {
Element element = new Element(this);
this.copyValues(element);
if (this.isElided())
element.setElided(true);
return element;
}
@ -1638,4 +1641,11 @@ public class Element extends Base implements NamedItem {
return FhirPublication.fromCode(property.getStructure().getVersion());
}
public void setElided(boolean elided) {
this.elided = elided;
}
public boolean isElided() {
return this.elided;
}
}

View File

@ -62,6 +62,7 @@ import org.hl7.fhir.r5.formats.JsonCreatorDirect;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.StringPair;
import org.hl7.fhir.utilities.TextFile;
@ -85,6 +86,8 @@ public class JsonParser extends ParserBase {
private JsonCreator json;
private boolean allowComments;
private boolean elideElements;
// private boolean suppressResourceType;
private Element baseElement;
private boolean markedXhtml;
@ -782,7 +785,8 @@ public class JsonParser extends ParserBase {
}
checkComposeComments(e);
json.beginObject();
prop("resourceType", e.getType(), null);
// if (!isSuppressResourceType())
prop("resourceType", e.getType(), null);
Set<String> done = new HashSet<String>();
for (Element child : e.getChildren()) {
compose(e.getName(), e, done, child);
@ -807,7 +811,8 @@ public class JsonParser extends ParserBase {
checkComposeComments(e);
json.beginObject();
prop("resourceType", e.getType(), linkResolver == null ? null : linkResolver.resolveProperty(e.getProperty()));
// if (!isSuppressResourceType())
prop("resourceType", e.getType(), linkResolver == null ? null : linkResolver.resolveProperty(e.getProperty()));
Set<String> done = new HashSet<String>();
for (Element child : e.getChildren()) {
compose(e.getName(), e, done, child);
@ -821,15 +826,50 @@ public class JsonParser extends ParserBase {
if (wantCompose(path, child)) {
boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList();
if (!isList) {// for specials, ignore the cardinality of the stated type
compose(path, child);
if (child.isElided() && isElideElements() && json.canElide())
json.elide();
else
compose(path, child);
} else if (!done.contains(child.getName())) {
done.add(child.getName());
List<Element> list = e.getChildrenByName(child.getName());
composeList(path, list);
if (child.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY))
composeKeyList(path, list);
else
composeList(path, list);
}
}
}
private void composeKeyList(String path, List<Element> list) throws IOException {
String keyName = list.get(0).getProperty().getDefinition().getExtensionString(ToolingExtensions.EXT_JSON_PROP_KEY);
json.name(list.get(0).getName());
json.beginObject();
for (Element e: list) {
Element key = null;
Element value = null;
for (Element child: e.getChildren()) {
if (child.getName().equals(keyName))
key = child;
else
value = child;
}
if (value.isPrimitive())
primitiveValue(key.getValue(), value);
else {
json.name(key.getValue());
checkComposeComments(e);
json.beginObject();
Set<String> done = new HashSet<String>();
for (Element child : value.getChildren()) {
compose(value.getName(), value, done, child);
}
json.endObject();
compose(path + "." + key.getValue(), value);
}
}
json.endObject();
}
private void composeList(String path, List<Element> list) throws IOException {
// there will be at least one element
@ -847,7 +887,9 @@ public class JsonParser extends ParserBase {
if (prim) {
openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty()));
for (Element item : list) {
if (item.hasValue()) {
if (item.isElided() && json.canElide())
json.elide();
else if (item.hasValue()) {
if (linkResolver != null && item.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(item));
if (ref != null) {
@ -866,7 +908,9 @@ public class JsonParser extends ParserBase {
openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty()));
int i = 0;
for (Element item : list) {
if (item.hasChildren()) {
if (item.isElided() && json.canElide())
json.elide();
else if (item.hasChildren()) {
open(null,null);
if (item.getProperty().isResource()) {
prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType()));
@ -933,9 +977,10 @@ public class JsonParser extends ParserBase {
json.externalLink(ref);
}
}
Set<String> done = new HashSet<String>();
for (Element child : element.getChildren()) {
compose(path+"."+element.getName(), element, done, child);
compose(path + "." + element.getName(), element, done, child);
}
close();
}
@ -951,5 +996,23 @@ public class JsonParser extends ParserBase {
return this;
}
public boolean isElideElements() {
return elideElements;
}
public JsonParser setElideElements(boolean elideElements) {
this.elideElements = elideElements;
return this;
}
/*
public boolean isSuppressResourceType() {
return suppressResourceType;
}
public JsonParser setSuppressResourceType(boolean suppressResourceType) {
this.suppressResourceType = suppressResourceType;
return this;
}
*/
}

View File

@ -94,6 +94,7 @@ import org.xml.sax.XMLReader;
public class XmlParser extends ParserBase {
private boolean allowXsiLocation;
private String version;
private boolean elideElements;
public XmlParser(IWorkerContext context) {
super(context);
@ -805,92 +806,125 @@ public class XmlParser extends ParserBase {
}
private void composeElement(IXMLWriter xml, Element element, String elementName, boolean root) throws IOException, FHIRException {
if (showDecorations) {
@SuppressWarnings("unchecked")
List<ElementDecoration> decorations = (List<ElementDecoration>) element.getUserData("fhir.decorations");
if (decorations != null)
for (ElementDecoration d : decorations)
xml.decorate(d);
}
for (String s : element.getComments()) {
xml.comment(s, true);
if (!(isElideElements() && element.isElided())) {
if (showDecorations) {
@SuppressWarnings("unchecked")
List<ElementDecoration> decorations = (List<ElementDecoration>) element.getUserData("fhir.decorations");
if (decorations != null)
for (ElementDecoration d : decorations)
xml.decorate(d);
}
for (String s : element.getComments()) {
xml.comment(s, true);
}
}
if (isText(element.getProperty())) {
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.enter(element.getProperty().getXmlNamespace(),elementName);
if (linkResolver != null && element.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(element));
if (ref != null) {
xml.externalLink(ref);
if (isElideElements() && element.isElided() && xml.canElide())
xml.elide();
else {
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.enter(element.getProperty().getXmlNamespace(),elementName);
if (linkResolver != null && element.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(element));
if (ref != null) {
xml.externalLink(ref);
}
}
xml.text(element.getValue());
xml.exit(element.getProperty().getXmlNamespace(),elementName);
}
xml.text(element.getValue());
xml.exit(element.getProperty().getXmlNamespace(),elementName);
} else if (!element.hasChildren() && !element.hasValue()) {
if (element.getExplicitType() != null)
xml.attribute("xsi:type", element.getExplicitType());
xml.element(elementName);
if (isElideElements() && element.isElided() && xml.canElide())
xml.elide();
else {
if (element.getExplicitType() != null)
xml.attribute("xsi:type", element.getExplicitType());
xml.element(elementName);
}
} else if (element.isPrimitive() || (element.hasType() && isPrimitive(element.getType()))) {
if (element.getType().equals("xhtml")) {
String rawXhtml = element.getValue();
if (isCdaText(element.getProperty())) {
new CDANarrativeFormat().convert(xml, new XhtmlParser().parseFragment(rawXhtml));
} else {
xml.escapedText(rawXhtml);
if (!markedXhtml) {
xml.anchor("end-xhtml");
markedXhtml = true;
if (isElideElements() && element.isElided() && xml.canElide())
xml.elide();
else {
String rawXhtml = element.getValue();
if (isCdaText(element.getProperty())) {
new CDANarrativeFormat().convert(xml, new XhtmlParser().parseFragment(rawXhtml));
} else {
xml.escapedText(rawXhtml);
if (!markedXhtml) {
xml.anchor("end-xhtml");
markedXhtml = true;
}
}
}
} else if (isText(element.getProperty())) {
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.text(element.getValue());
} else {
setXsiTypeIfIsTypeAttr(xml, element);
if (element.hasValue()) {
if (isElideElements() && element.isElided() && xml.canElide())
xml.elide();
else {
if (linkResolver != null)
xml.link(linkResolver.resolveType(element.getType()));
xml.attribute("value", element.getValue());
xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.text(element.getValue());
}
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
if (element.hasChildren()) {
xml.enter(element.getProperty().getXmlNamespace(), elementName);
if (linkResolver != null && element.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(element));
if (ref != null) {
xml.externalLink(ref);
}
} else {
if (isElideElements() && element.isElided())
xml.attributeElide();
else {
setXsiTypeIfIsTypeAttr(xml, element);
if (element.hasValue()) {
if (linkResolver != null)
xml.link(linkResolver.resolveType(element.getType()));
xml.attribute("value", element.getValue());
}
for (Element child : element.getChildren())
composeElement(xml, child, child.getName(), false);
xml.exit(element.getProperty().getXmlNamespace(),elementName);
} else
xml.element(elementName);
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
if (element.hasChildren()) {
xml.enter(element.getProperty().getXmlNamespace(), elementName);
if (linkResolver != null && element.getProperty().isReference()) {
String ref = linkResolver.resolveReference(getReferenceForElement(element));
if (ref != null) {
xml.externalLink(ref);
}
}
for (Element child : element.getChildren())
composeElement(xml, child, child.getName(), false);
xml.exit(element.getProperty().getXmlNamespace(),elementName);
} else
xml.element(elementName);
}
}
} else {
setXsiTypeIfIsTypeAttr(xml, element);
Set<String> handled = new HashSet<>();
if (isElideElements() && element.isElided() && xml.canElide())
xml.elide();
else {
setXsiTypeIfIsTypeAttr(xml, element);
Set<String> handled = new HashSet<>();
for (Element child : element.getChildren()) {
if (!handled.contains(child.getName()) && isAttr(child.getProperty()) && wantCompose(element.getPath(), child)) {
handled.add(child.getName());
String av = child.getValue();
if (child.getProperty().isList()) {
for (Element c2 : element.getChildren()) {
if (c2 != child && c2.getName().equals(child.getName())) {
av = av + " "+c2.getValue();
if (isElideElements() && child.isElided())
xml.attributeElide();
else {
String av = child.getValue();
if (child.getProperty().isList()) {
for (Element c2 : element.getChildren()) {
if (c2 != child && c2.getName().equals(child.getName())) {
if (c2.isElided())
av = av + " ...";
else
av = av + " " + c2.getValue();
}
}
}
}
if (linkResolver != null)
xml.link(linkResolver.resolveType(child.getType()));
if (ToolingExtensions.hasExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT))
av = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av);
xml.attribute(child.getProperty().getXmlNamespace(), child.getProperty().getXmlName(), av);
}
if (linkResolver != null)
xml.link(linkResolver.resolveType(child.getType()));
if (ToolingExtensions.hasExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT))
av = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(child.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), av);
xml.attribute(child.getProperty().getXmlNamespace(),child.getProperty().getXmlName(), av);
}
}
}
if (!element.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) {
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
@ -914,12 +948,16 @@ public class XmlParser extends ParserBase {
}
for (Element child : element.getChildren()) {
if (wantCompose(element.getPath(), child)) {
if (isText(child.getProperty())) {
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.text(child.getValue());
} else if (!isAttr(child.getProperty())) {
composeElement(xml, child, child.getName(), false);
if (isElideElements() && child.isElided() && xml.canElide())
xml.elide();
else {
if (isText(child.getProperty())) {
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.text(child.getValue());
} else if (!isAttr(child.getProperty())) {
composeElement(xml, child, child.getName(), false);
}
}
}
}
@ -1034,4 +1072,13 @@ public class XmlParser extends ParserBase {
// do nothing
}
}
public boolean isElideElements() {
return elideElements;
}
public void setElideElements(boolean elideElements) {
this.elideElements = elideElements;
}
}

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats;
package org.hl7.fhir.r5.formats;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -28,47 +28,49 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.IOException;
import java.math.BigDecimal;
/**
* Facade to GSON writer, or something that imposes property ordering first
*
* @author Grahame
*
*/
public interface JsonCreator {
void comment(String comment);
void beginObject() throws IOException;
void endObject() throws IOException;
void nullValue() throws IOException;
void name(String name) throws IOException;
void value(String value) throws IOException;
void value(Boolean value) throws IOException;
void value(BigDecimal value) throws IOException;
void valueNum(String value) throws IOException; // allow full control of representation
void value(Integer value) throws IOException;
void beginArray() throws IOException;
void endArray() throws IOException;
void finish() throws IOException;
// only used by an creator that actually produces xhtml
void link(String href);
void anchor(String string);
void externalLink(String string);
import java.io.IOException;
import java.math.BigDecimal;
/**
* Facade to GSON writer, or something that imposes property ordering first
*
* @author Grahame
*
*/
public interface JsonCreator {
void comment(String comment);
void beginObject() throws IOException;
void endObject() throws IOException;
void nullValue() throws IOException;
void name(String name) throws IOException;
void value(String value) throws IOException;
void value(Boolean value) throws IOException;
void value(BigDecimal value) throws IOException;
void valueNum(String value) throws IOException; // allow full control of representation
void value(Integer value) throws IOException;
void beginArray() throws IOException;
void endArray() throws IOException;
void finish() throws IOException;
// only used by an creator that actually produces xhtml
void link(String href);
void anchor(String string);
void externalLink(String string);
void elide();
boolean canElide();
}

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats;
package org.hl7.fhir.r5.formats;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -28,257 +28,264 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
public class JsonCreatorCanonical implements JsonCreator {
public class JsonCanValue {
String name;
private JsonCanValue(String name) {
this.name = name;
}
}
private class JsonCanNumberValue extends JsonCanValue {
private BigDecimal value;
private JsonCanNumberValue(String name, BigDecimal value) {
super(name);
this.value = value;
}
}
private class JsonCanPresentedNumberValue extends JsonCanValue {
private String value;
private JsonCanPresentedNumberValue(String name, String value) {
super(name);
this.value = value;
}
}
private class JsonCanIntegerValue extends JsonCanValue {
private Integer value;
private JsonCanIntegerValue(String name, Integer value) {
super(name);
this.value = value;
}
}
private class JsonCanBooleanValue extends JsonCanValue {
private Boolean value;
private JsonCanBooleanValue(String name, Boolean value) {
super(name);
this.value = value;
}
}
private class JsonCanStringValue extends JsonCanValue {
private String value;
private JsonCanStringValue(String name, String value) {
super(name);
this.value = value;
}
}
private class JsonCanNullValue extends JsonCanValue {
private JsonCanNullValue(String name) {
super(name);
}
}
public class JsonCanObject extends JsonCanValue {
boolean array;
List<JsonCanValue> children = new ArrayList<JsonCanValue>();
public JsonCanObject(String name, boolean array) {
super(name);
this.array = array;
}
public void addProp(JsonCanValue obj) {
children.add(obj);
}
}
Stack<JsonCanObject> stack;
JsonCanObject root;
JsonCreatorDirect jj;
String name;
public JsonCreatorCanonical(OutputStreamWriter osw) {
stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
jj = new JsonCreatorDirect(osw, false, false);
name = null;
}
private String takeName() {
String res = name;
name = null;
return res;
}
@Override
public void beginObject() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), false);
if (stack.isEmpty())
root = obj;
else
stack.peek().addProp(obj);
stack.push(obj);
}
@Override
public void endObject() throws IOException {
stack.pop();
}
@Override
public void nullValue() throws IOException {
stack.peek().addProp(new JsonCanNullValue(takeName()));
}
@Override
public void name(String name) throws IOException {
this.name = name;
}
@Override
public void value(String value) throws IOException {
stack.peek().addProp(new JsonCanStringValue(takeName(), value));
}
@Override
public void value(Boolean value) throws IOException {
stack.peek().addProp(new JsonCanBooleanValue(takeName(), value));
}
@Override
public void value(BigDecimal value) throws IOException {
stack.peek().addProp(new JsonCanNumberValue(takeName(), value));
}
@Override
public void valueNum(String value) throws IOException {
stack.peek().addProp(new JsonCanPresentedNumberValue(takeName(), value));
}
@Override
public void value(Integer value) throws IOException {
stack.peek().addProp(new JsonCanIntegerValue(takeName(), value));
}
@Override
public void beginArray() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), true);
if (!stack.isEmpty())
stack.peek().addProp(obj);
stack.push(obj);
}
@Override
public void endArray() throws IOException {
stack.pop();
}
@Override
public void finish() throws IOException {
writeObject(root);
}
private void writeObject(JsonCanObject obj) throws IOException {
jj.beginObject();
List<String> names = new ArrayList<String>();
for (JsonCanValue v : obj.children)
names.add(v.name);
Collections.sort(names);
for (String n : names) {
jj.name(n);
JsonCanValue v = getPropForName(n, obj.children);
if (v instanceof JsonCanNumberValue)
jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanPresentedNumberValue)
jj.valueNum(((JsonCanPresentedNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
jj.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
writeArray(o);
else
writeObject(o);
} else
throw new Error("not possible");
}
jj.endObject();
}
private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
for (JsonCanValue child : children)
if (child.name.equals(name))
return child;
return null;
}
private void writeArray(JsonCanObject arr) throws IOException {
jj.beginArray();
for (JsonCanValue v : arr.children) {
if (v instanceof JsonCanNumberValue)
jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
jj.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
writeArray(o);
else
writeObject(o);
} else
throw new Error("not possible");
}
jj.endArray();
}
@Override
public void comment(String content) {
// canonical JSON ignores comments
}
@Override
public void link(String href) {
// not used
}
@Override
public void anchor(String name) {
// not used
}
@Override
public void externalLink(String string) {
// not used
}
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
public class JsonCreatorCanonical implements JsonCreator {
public class JsonCanValue {
String name;
private JsonCanValue(String name) {
this.name = name;
}
}
private class JsonCanNumberValue extends JsonCanValue {
private BigDecimal value;
private JsonCanNumberValue(String name, BigDecimal value) {
super(name);
this.value = value;
}
}
private class JsonCanPresentedNumberValue extends JsonCanValue {
private String value;
private JsonCanPresentedNumberValue(String name, String value) {
super(name);
this.value = value;
}
}
private class JsonCanIntegerValue extends JsonCanValue {
private Integer value;
private JsonCanIntegerValue(String name, Integer value) {
super(name);
this.value = value;
}
}
private class JsonCanBooleanValue extends JsonCanValue {
private Boolean value;
private JsonCanBooleanValue(String name, Boolean value) {
super(name);
this.value = value;
}
}
private class JsonCanStringValue extends JsonCanValue {
private String value;
private JsonCanStringValue(String name, String value) {
super(name);
this.value = value;
}
}
private class JsonCanNullValue extends JsonCanValue {
private JsonCanNullValue(String name) {
super(name);
}
}
public class JsonCanObject extends JsonCanValue {
boolean array;
List<JsonCanValue> children = new ArrayList<JsonCanValue>();
public JsonCanObject(String name, boolean array) {
super(name);
this.array = array;
}
public void addProp(JsonCanValue obj) {
children.add(obj);
}
}
Stack<JsonCanObject> stack;
JsonCanObject root;
JsonCreatorDirect jj;
String name;
public JsonCreatorCanonical(OutputStreamWriter osw) {
stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
jj = new JsonCreatorDirect(osw, false, false);
name = null;
}
private String takeName() {
String res = name;
name = null;
return res;
}
@Override
public void beginObject() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), false);
if (stack.isEmpty())
root = obj;
else
stack.peek().addProp(obj);
stack.push(obj);
}
@Override
public void endObject() throws IOException {
stack.pop();
}
@Override
public void nullValue() throws IOException {
stack.peek().addProp(new JsonCanNullValue(takeName()));
}
@Override
public void name(String name) throws IOException {
this.name = name;
}
@Override
public void value(String value) throws IOException {
stack.peek().addProp(new JsonCanStringValue(takeName(), value));
}
@Override
public void value(Boolean value) throws IOException {
stack.peek().addProp(new JsonCanBooleanValue(takeName(), value));
}
@Override
public void value(BigDecimal value) throws IOException {
stack.peek().addProp(new JsonCanNumberValue(takeName(), value));
}
@Override
public void valueNum(String value) throws IOException {
stack.peek().addProp(new JsonCanPresentedNumberValue(takeName(), value));
}
@Override
public void value(Integer value) throws IOException {
stack.peek().addProp(new JsonCanIntegerValue(takeName(), value));
}
@Override
public void beginArray() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), true);
if (!stack.isEmpty())
stack.peek().addProp(obj);
stack.push(obj);
}
@Override
public void endArray() throws IOException {
stack.pop();
}
@Override
public void finish() throws IOException {
writeObject(root);
}
private void writeObject(JsonCanObject obj) throws IOException {
jj.beginObject();
List<String> names = new ArrayList<String>();
for (JsonCanValue v : obj.children)
names.add(v.name);
Collections.sort(names);
for (String n : names) {
jj.name(n);
JsonCanValue v = getPropForName(n, obj.children);
if (v instanceof JsonCanNumberValue)
jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanPresentedNumberValue)
jj.valueNum(((JsonCanPresentedNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
jj.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
writeArray(o);
else
writeObject(o);
} else
throw new Error("not possible");
}
jj.endObject();
}
private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
for (JsonCanValue child : children)
if (child.name.equals(name))
return child;
return null;
}
private void writeArray(JsonCanObject arr) throws IOException {
jj.beginArray();
for (JsonCanValue v : arr.children) {
if (v instanceof JsonCanNumberValue)
jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
jj.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
writeArray(o);
else
writeObject(o);
} else
throw new Error("not possible");
}
jj.endArray();
}
@Override
public void comment(String content) {
// canonical JSON ignores comments
}
@Override
public void link(String href) {
// not used
}
@Override
public void anchor(String name) {
// not used
}
@Override
public void externalLink(String string) {
// not used
}
@Override
public boolean canElide() { return false; }
@Override
public void elide() {
// not used
}
}

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats;
package org.hl7.fhir.r5.formats;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -28,219 +28,227 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.utilities.Utilities;
/**
* A little implementation of a json write to replace Gson .... because Gson screws up decimal values, and *we care*
*
* @author Grahame Grieve
*
*/
public class JsonCreatorDirect implements JsonCreator {
private Writer writer;
private boolean pretty;
private boolean comments;
private boolean named;
private List<Boolean> valued = new ArrayList<Boolean>();
private int indent;
private List<String> commentList = new ArrayList<>();
public JsonCreatorDirect(Writer writer, boolean pretty, boolean comments) {
super();
this.writer = writer;
this.pretty = pretty;
this.comments = pretty && comments;
}
@Override
public void comment(String content) {
if (comments) {
commentList.add(content);
}
}
@Override
public void beginObject() throws IOException {
checkState();
writer.write("{");
stepIn();
if (!valued.isEmpty()) {
valued.set(0, true);
}
valued.add(0, false);
}
private void commitComments() throws IOException {
if (comments) {
for (String s : commentList) {
writer.write("// ");
writer.write(s);
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
commentList.clear();
}
}
public void stepIn() throws IOException {
if (pretty) {
indent++;
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
}
public void stepOut() throws IOException {
if (pretty) {
indent--;
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
}
private void checkState() throws IOException {
commitComments();
if (named) {
if (pretty)
writer.write(" : ");
else
writer.write(":");
named = false;
}
if (!valued.isEmpty() && valued.get(0)) {
writer.write(",");
if (pretty) {
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
valued.set(0, false);
}
}
@Override
public void endObject() throws IOException {
stepOut();
writer.write("}");
valued.remove(0);
}
@Override
public void nullValue() throws IOException {
checkState();
writer.write("null");
valued.set(0, true);
}
@Override
public void name(String name) throws IOException {
checkState();
writer.write("\""+name+"\"");
named = true;
}
@Override
public void value(String value) throws IOException {
checkState();
writer.write("\""+Utilities.escapeJson(value)+"\"");
valued.set(0, true);
}
@Override
public void value(Boolean value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else if (value.booleanValue())
writer.write("true");
else
writer.write("false");
valued.set(0, true);
}
@Override
public void value(BigDecimal value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value.toString());
valued.set(0, true);
}
@Override
public void valueNum(String value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value);
valued.set(0, true);
}
@Override
public void value(Integer value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value.toString());
valued.set(0, true);
}
@Override
public void beginArray() throws IOException {
checkState();
writer.write("[");
if (!valued.isEmpty()) {
valued.set(0, true);
}
valued.add(0, false);
}
@Override
public void endArray() throws IOException {
writer.write("]");
valued.remove(0);
}
@Override
public void finish() throws IOException {
writer.flush();
}
@Override
public void link(String href) {
// not used
}
@Override
public void anchor(String name) {
// not used
}
@Override
public void externalLink(String string) {
// not used
}
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.utilities.Utilities;
/**
* A little implementation of a json write to replace Gson .... because Gson screws up decimal values, and *we care*
*
* @author Grahame Grieve
*
*/
public class JsonCreatorDirect implements JsonCreator {
private Writer writer;
private boolean pretty;
private boolean comments;
private boolean named;
private List<Boolean> valued = new ArrayList<Boolean>();
private int indent;
private List<String> commentList = new ArrayList<>();
public JsonCreatorDirect(Writer writer, boolean pretty, boolean comments) {
super();
this.writer = writer;
this.pretty = pretty;
this.comments = pretty && comments;
}
@Override
public void comment(String content) {
if (comments) {
commentList.add(content);
}
}
@Override
public void beginObject() throws IOException {
checkState();
writer.write("{");
stepIn();
if (!valued.isEmpty()) {
valued.set(0, true);
}
valued.add(0, false);
}
private void commitComments() throws IOException {
if (comments) {
for (String s : commentList) {
writer.write("// ");
writer.write(s);
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
commentList.clear();
}
}
public void stepIn() throws IOException {
if (pretty) {
indent++;
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
}
public void stepOut() throws IOException {
if (pretty) {
indent--;
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
}
private void checkState() throws IOException {
commitComments();
if (named) {
if (pretty)
writer.write(" : ");
else
writer.write(":");
named = false;
}
if (!valued.isEmpty() && valued.get(0)) {
writer.write(",");
if (pretty) {
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
valued.set(0, false);
}
}
@Override
public void endObject() throws IOException {
stepOut();
writer.write("}");
valued.remove(0);
}
@Override
public void nullValue() throws IOException {
checkState();
writer.write("null");
valued.set(0, true);
}
@Override
public void name(String name) throws IOException {
checkState();
writer.write("\""+name+"\"");
named = true;
}
@Override
public void value(String value) throws IOException {
checkState();
writer.write("\""+Utilities.escapeJson(value)+"\"");
valued.set(0, true);
}
@Override
public void value(Boolean value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else if (value.booleanValue())
writer.write("true");
else
writer.write("false");
valued.set(0, true);
}
@Override
public void value(BigDecimal value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value.toString());
valued.set(0, true);
}
@Override
public void valueNum(String value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value);
valued.set(0, true);
}
@Override
public void value(Integer value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value.toString());
valued.set(0, true);
}
@Override
public void beginArray() throws IOException {
checkState();
writer.write("[");
if (!valued.isEmpty()) {
valued.set(0, true);
}
valued.add(0, false);
}
@Override
public void endArray() throws IOException {
writer.write("]");
valued.remove(0);
}
@Override
public void finish() throws IOException {
writer.flush();
}
@Override
public void link(String href) {
// not used
}
@Override
public void anchor(String name) {
// not used
}
@Override
public void externalLink(String string) {
// not used
}
@Override
public boolean canElide() { return false; }
@Override
public void elide() {
// not used
}
}

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.r5.formats;
package org.hl7.fhir.r5.formats;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -28,103 +28,111 @@ package org.hl7.fhir.r5.formats;
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import com.google.gson.stream.JsonWriter;
public class JsonCreatorGson implements JsonCreator {
JsonWriter gson;
public JsonCreatorGson(OutputStreamWriter osw) {
gson = new JsonWriter(osw);
}
@Override
public void beginObject() throws IOException {
gson.beginObject();
}
@Override
public void endObject() throws IOException {
gson.endObject();
}
@Override
public void nullValue() throws IOException {
gson.nullValue();
}
@Override
public void name(String name) throws IOException {
gson.name(name);
}
@Override
public void value(String value) throws IOException {
gson.value(value);
}
@Override
public void value(Boolean value) throws IOException {
gson.value(value);
}
@Override
public void value(BigDecimal value) throws IOException {
gson.value(value);
}
@Override
public void value(Integer value) throws IOException {
gson.value(value);
}
@Override
public void beginArray() throws IOException {
gson.beginArray();
}
@Override
public void endArray() throws IOException {
gson.endArray();
}
@Override
public void finish() {
// nothing to do here
}
@Override
public void link(String href) {
// not used
}
@Override
public void valueNum(String value) throws IOException {
value(new BigDecimal(value));
}
@Override
public void anchor(String name) {
// not used
}
@Override
public void comment(String content) {
// gson (dense json) ignores comments
}
@Override
public void externalLink(String string) {
// not used
}
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import com.google.gson.stream.JsonWriter;
public class JsonCreatorGson implements JsonCreator {
JsonWriter gson;
public JsonCreatorGson(OutputStreamWriter osw) {
gson = new JsonWriter(osw);
}
@Override
public void beginObject() throws IOException {
gson.beginObject();
}
@Override
public void endObject() throws IOException {
gson.endObject();
}
@Override
public void nullValue() throws IOException {
gson.nullValue();
}
@Override
public void name(String name) throws IOException {
gson.name(name);
}
@Override
public void value(String value) throws IOException {
gson.value(value);
}
@Override
public void value(Boolean value) throws IOException {
gson.value(value);
}
@Override
public void value(BigDecimal value) throws IOException {
gson.value(value);
}
@Override
public void value(Integer value) throws IOException {
gson.value(value);
}
@Override
public void beginArray() throws IOException {
gson.beginArray();
}
@Override
public void endArray() throws IOException {
gson.endArray();
}
@Override
public void finish() {
// nothing to do here
}
@Override
public void link(String href) {
// not used
}
@Override
public void valueNum(String value) throws IOException {
value(new BigDecimal(value));
}
@Override
public void anchor(String name) {
// not used
}
@Override
public void comment(String content) {
// gson (dense json) ignores comments
}
@Override
public void externalLink(String string) {
// not used
}
@Override
public void elide() {
// not used
}
@Override
public boolean canElide() { return false;}
}

View File

@ -36,7 +36,7 @@ public class AdditionalBindingsRenderer {
private String valueSet;
private String doco;
private String docoShort;
private UsageContext usage;
private List<UsageContext> usages = new ArrayList<UsageContext>();
private boolean any = false;
private boolean isUnchanged = false;
private boolean matched = false;
@ -72,7 +72,7 @@ public class AdditionalBindingsRenderer {
isUnchanged = isUnchanged && ((valueSet==null && compare.valueSet==null) || valueSet.equals(compare.valueSet));
isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco));
isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort));
isUnchanged = isUnchanged && ((usage==null && compare.usage==null) || usage.equals(compare.usage));
isUnchanged = isUnchanged && ((usages==null && compare.usages==null) || usages.equals(compare.usages));
return isUnchanged;
}
}
@ -174,8 +174,12 @@ public class AdditionalBindingsRenderer {
abr.purpose = ext.getExtensionString("purpose");
abr.valueSet = ext.getExtensionString("valueSet");
abr.doco = ext.getExtensionString("documentation");
abr.docoShort = ext.getExtensionString("shortDoco");
abr.usage = (ext.hasExtension("usage")) && ext.getExtensionByUrl("usage").hasValueUsageContext() ? ext.getExtensionByUrl("usage").getValueUsageContext() : null;
abr.docoShort = ext.getExtensionString("shortDoco");
for (Extension x : ext.getExtensionsByUrl("usage")) {
if (x.hasValueUsageContext()) {
abr.usages.add(x.getValueUsageContext());
}
}
abr.any = "any".equals(ext.getExtensionString("scope"));
abr.isUnchanged = ext.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS);
return abr;
@ -187,7 +191,7 @@ public class AdditionalBindingsRenderer {
abr.valueSet = ab.getValueSet();
abr.doco = ab.getDocumentation();
abr.docoShort = ab.getShortDoco();
abr.usage = ab.hasUsage() ? ab.getUsageFirstRep() : null;
abr.usages.addAll(ab.getUsage());
abr.any = ab.getAny();
abr.isUnchanged = ab.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS);
return abr;
@ -220,7 +224,7 @@ public class AdditionalBindingsRenderer {
boolean any = false;
for (AdditionalBindingDetail binding : bindings) {
doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null);
usage = usage || binding.usage != null || (binding.compare!=null && binding.compare.usage!=null);
usage = usage || !binding.usages.isEmpty() || (binding.compare!=null && !binding.compare.usages.isEmpty());
any = any || binding.any || (binding.compare!=null && binding.compare.any);
}
@ -283,9 +287,12 @@ public class AdditionalBindingsRenderer {
renderPurpose(purpose, binding.compare.purpose);
}
if (usage) {
if (binding.usage != null) {
// TODO: This isn't rendered at all yet. Ideally, we want it to render with comparison...
new DataRenderer(context).renderBase(new RenderingStatus(), tr.td(), binding.usage);
if (!binding.usages.isEmpty()) {
XhtmlNode td = tr.td();
for (UsageContext uc : binding.usages) {
td.sep(", ");
new DataRenderer(context).renderBase(new RenderingStatus(), td, uc);
}
} else {
tr.td();
}

View File

@ -46,6 +46,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.r5.terminologies.utilities.SnomedUtilities;
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
@ -1177,8 +1178,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
private String getLinkForSystem(String system, String version) {
if ("http://snomed.info/sct".equals(system)) {
return "https://browser.ihtsdotools.org/";
if ("http://snomed.info/sct".equals(system)) {
return "https://browser.ihtsdotools.org/";
} else if ("http://loinc.org".equals(system)) {
return "https://loinc.org/";
} else if ("http://unitsofmeasure.org".equals(system)) {
@ -1198,11 +1199,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
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/";
}
return SnomedUtilities.getSctLink(version, code, context.getContext().getExpansionParameters());
} else if ("http://loinc.org".equals(system)) {
if (!Utilities.noString(code)) {
return "https://loinc.org/"+code;
@ -1301,7 +1298,11 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
}
protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceWrapper c) {
protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceWrapper c) {
renderCoding(status, x, c, true);
}
protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceWrapper c, boolean details) {
String s = "";
if (c.has("display"))
s = context.getTranslated(c.child("display"));
@ -1311,10 +1312,13 @@ public class DataRenderer extends Renderer implements CodeResolver {
if (Utilities.noString(s))
s = c.primitiveValue("code");
if (context.isTechnicalMode()) {
x.addText(s+" "+context.formatPhrase(RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.primitiveValue("system")), c.primitiveValue("code"), " = '", lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")), c.primitiveValue("display"), "')"));
} else
x.span(null, "{"+c.primitiveValue("system")+" "+c.primitiveValue("code")+"}").addText(s);
if (context.isTechnicalMode() && details) {
String d = c.primitiveValue("display") == null ? lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")): c.primitiveValue("display");
d = context.formatPhrase(d == null || d.equals(c.primitiveValue("code")) ? RenderingContext.DATA_REND_DETAILS_STATED_ND : RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.primitiveValue("system")), c.primitiveValue("code"), d);
x.addText(s+" "+d);
} else {
x.span(null, "{"+c.primitiveValue("system")+" "+c.primitiveValue("code")+"}").addText(s);
}
}
public String displayCodeableConcept(ResourceWrapper cc) {
@ -1860,8 +1864,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
}
public void renderUsageContext(RenderingStatus status, XhtmlNode x, ResourceWrapper u) throws FHIRFormatError, DefinitionException, IOException {
renderCoding(status, x, u.child("code"));
x.tx(": ");
renderCoding(status, x, u.child("code"), false);
x.tx(" = ");
renderDataType(status, x, u.child("value"));
}

View File

@ -51,6 +51,7 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest;
import org.hl7.fhir.r5.terminologies.utilities.SnomedUtilities;
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -750,9 +751,9 @@ public class ValueSetRenderer extends TerminologyRenderer {
if (cs == null) {
return "?cs-n?";
}
String ref = (String) cs.getUserData("filename");
String ref = cs.getWebPath();
if (ref == null) {
ref = (String) cs.getWebPath();
ref = cs.getUserString("filename");
}
return ref == null ? null : ref.replace("\\", "/");
}
@ -825,7 +826,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
}
String s = Utilities.padLeft("", '\u00A0', i*2);
td.attribute("style", "white-space:nowrap").addText(s);
addCodeToTable(c.getAbstract(), c.getSystem(), c.getCode(), c.getDisplay(), td);
addCodeToTable(c.getAbstract(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay(), td);
td = tr.td();
td.addText(c.getSystem());
td = tr.td();
@ -863,7 +864,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
first = false;
XhtmlNode span = td.span(null, mapping.comp.getRelationship().toString());
span.addText(getCharForRelationship(mapping.comp));
addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode());
addRefToCode(td, mapping.group.getTarget(), null, m.getLink(), mapping.comp.getCode());
if (!Utilities.noString(mapping.comp.getComment()))
td.i().tx("("+mapping.comp.getComment()+")");
}
@ -900,13 +901,13 @@ public class ValueSetRenderer extends TerminologyRenderer {
return true;
}
private void addCodeToTable(boolean isAbstract, String system, String code, String display, XhtmlNode td) {
private void addCodeToTable(boolean isAbstract, String system, String version, String code, String display, XhtmlNode td) {
CodeSystem e = getContext().getWorker().fetchCodeSystem(system);
if (e == null || (e.getContent() != org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode.COMPLETE && e.getContent() != org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode.FRAGMENT)) {
if (isAbstract)
td.i().setAttribute("title", context.formatPhrase(RenderingContext.VS_ABSTRACT_CODE_HINT)).addText(code);
else if ("http://snomed.info/sct".equals(system)) {
td.ah(context.prefixLocalHref(sctLink(code))).addText(code);
td.ah(context.prefixLocalHref(SnomedUtilities.getSctLink(version, code, context.getContext().getExpansionParameters()))).addText(code);
} else if ("http://loinc.org".equals(system)) {
td.ah(context.prefixLocalHref(LoincLinker.getLinkForCode(code))).addText(code);
} else
@ -928,15 +929,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
}
}
public String sctLink(String code) {
// if (snomedEdition != null)
// http://browser.ihtsdotools.org/?perspective=full&conceptId1=428041000124106&edition=us-edition&release=v20180301&server=https://prod-browser-exten.ihtsdotools.org/api/snomed&langRefset=900000000000509007
return "http://snomed.info/id/"+code;
}
private void addRefToCode(XhtmlNode td, String target, String vslink, String code) {
addCodeToTable(false, target, code, null, td);
private void addRefToCode(XhtmlNode td, String target, String vslink, String code, String version) {
addCodeToTable(false, target, version, code, null, td);
// CodeSystem cs = getContext().getWorker().fetchCodeSystem(target);
// String cslink = getCsRef(cs);
// String link = cslink != null ? cslink+"#"+cs.getId()+"-"+code : vslink+"#"+code;
@ -1212,10 +1206,10 @@ public class ValueSetRenderer extends TerminologyRenderer {
}
addMapHeaders(addTableHeaderRowStandard(t, false, true, hasDefinition, hasComments, false, false, null, langs, designations, doDesignations), maps);
for (ConceptReferenceComponent c : inc.getConcept()) {
renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, c);
renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, c, inc.getVersion());
}
for (Base b : VersionComparisonAnnotation.getDeleted(inc, "concept" )) {
renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, (ConceptReferenceComponent) b);
renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, (ConceptReferenceComponent) b, inc.getVersion());
}
}
if (inc.getFilter().size() > 0) {
@ -1312,11 +1306,11 @@ public class ValueSetRenderer extends TerminologyRenderer {
private void renderConcept(ConceptSetComponent inc, List<String> langs, boolean doDesignations,
List<UsedConceptMap> maps, Map<String, String> designations, Map<String, ConceptDefinitionComponent> definitions,
XhtmlNode t, boolean hasComments, boolean hasDefinition, ConceptReferenceComponent c) {
XhtmlNode t, boolean hasComments, boolean hasDefinition, ConceptReferenceComponent c, String version) {
XhtmlNode tr = t.tr();
XhtmlNode td = renderStatusRow(c, t, tr);
ConceptDefinitionComponent cc = definitions == null ? null : definitions.get(c.getCode());
addCodeToTable(false, inc.getSystem(), c.getCode(), c.hasDisplay()? c.getDisplay() : cc != null ? cc.getDisplay() : "", td);
addCodeToTable(false, inc.getSystem(), version, c.getCode(), c.hasDisplay()? c.getDisplay() : cc != null ? cc.getDisplay() : "", td);
td = tr.td();
if (!Utilities.noString(c.getDisplay()))
@ -1355,7 +1349,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
first = false;
XhtmlNode span = td.span(null, mapping.comp.getRelationship().toString());
span.addText(getCharForRelationship(mapping.comp));
addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode());
addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode(), version);
if (!Utilities.noString(mapping.comp.getComment()))
td.i().tx("("+mapping.comp.getComment()+")");
}

View File

@ -51,6 +51,8 @@ public class TerminologyClientContext {
}
private static boolean canUseCacheId;
private ITerminologyClient client;
private boolean initialised = false;
private CapabilityStatement capabilitiesStatementQuick;
@ -180,7 +182,7 @@ public class TerminologyClientContext {
txCache.cacheTerminologyCapabilities(getAddress(), txcaps);
}
}
if (txcaps != null) {
if (txcaps != null && TerminologyClientContext.canUseCacheId) {
for (TerminologyCapabilitiesExpansionParameterComponent t : txcaps.getExpansion().getParameter()) {
if ("cache-id".equals(t.getName())) {
setTxCaching(true);
@ -206,6 +208,13 @@ public class TerminologyClientContext {
public String toString() {
return client.getAddress();
}
public static boolean isCanUseCacheId() {
return canUseCacheId;
}
public static void setCanUseCacheId(boolean canUseCacheId) {
TerminologyClientContext.canUseCacheId = canUseCacheId;
}
}

View File

@ -188,7 +188,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
private ValueSetExpansionContainsComponent addCode(WorkingContext wc, String system, String code, String display, String dispLang, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, Parameters expParams,
boolean isAbstract, boolean inactive, List<ValueSet> filters, boolean noInactive, boolean deprecated, List<ValueSetExpansionPropertyComponent> vsProp,
List<ConceptPropertyComponent> csProps, CodeSystem cs, List<org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent> expProps, List<Extension> csExtList, List<Extension> vsExtList, ValueSetExpansionComponent exp) throws ETooCostly {
opContext.deadCheck();
opContext.deadCheck("addCode"+code);
if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code, exp))
return null;
@ -447,7 +447,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private void addCodeAndDescendents(WorkingContext wc, ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
opContext.deadCheck();
opContext.deadCheck("addCodeAndDescendents");
focus.checkNoModifiers("Expansion.contains", "expanding");
ValueSetExpansionContainsComponent np = null;
for (String code : getCodesForConcept(focus, expParams)) {
@ -497,7 +497,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
private void addCodeAndDescendents(WorkingContext wc, CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters,
ConceptDefinitionComponent exclusion, ConceptFilter filterFunc, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, List<WorkingContext> otherFilters, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
opContext.deadCheck();
opContext.deadCheck("addCodeAndDescendents");
def.checkNoModifiers("Code in Code System", "expanding");
if (exclusion != null) {
if (exclusion.getCode().equals(def.getCode()))
@ -528,7 +528,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
private void excludeCodeAndDescendents(WorkingContext wc, CodeSystem cs, String system, ConceptDefinitionComponent def, Parameters expParams, List<ValueSet> filters,
ConceptDefinitionComponent exclusion, ConceptFilter filterFunc, List<WorkingContext> otherFilters, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
opContext.deadCheck();
opContext.deadCheck("excludeCodeAndDescendents");
def.checkNoModifiers("Code in Code System", "expanding");
if (exclusion != null) {
if (exclusion.getCode().equals(def.getCode()))
@ -596,7 +596,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private void excludeCodes(WorkingContext wc, ConceptSetComponent exc, Parameters expParams, ValueSetExpansionComponent exp, ValueSet vs) throws FHIRException, FileNotFoundException, ETooCostly, IOException {
opContext.deadCheck();
opContext.deadCheck("excludeCodes");
exc.checkNoModifiers("Compose.exclude", "expanding");
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
wc.getExcludeSystems().add(exc.getSystem());
@ -639,7 +639,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private void excludeCodes(WorkingContext wc, ValueSetExpansionComponent expand) {
opContext.deadCheck();
opContext.deadCheck("excludeCodes");
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
excludeCode(wc, c.getSystem(), c.getCode());
}
@ -959,7 +959,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
public void copyExpansion(WorkingContext wc,List<ValueSetExpansionContainsComponent> list) {
opContext.deadCheck();
opContext.deadCheck("copyExpansion");
for (ValueSetExpansionContainsComponent cc : list) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(cc.getSystem());
@ -988,7 +988,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
private int copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filter, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
int count = 0;
opContext.deadCheck();
opContext.deadCheck("copyImportContains");
for (ValueSetExpansionContainsComponent c : list) {
c.checkNoModifiers("Imported Expansion in Code System", "expanding");
ValueSetExpansionContainsComponent np = addCode(dwc, c.getSystem(), c.getCode(), c.getDisplay(), vsSrc.getLanguage(), parent, null, expParams, c.getAbstract(), c.getInactive(),
@ -1002,7 +1002,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private void includeCodes(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, boolean heirarchical, boolean noInactive, List<Extension> extensions, ValueSet valueSet) throws ETooCostly, FileNotFoundException, IOException, FHIRException, CodeSystemProviderExtension {
opContext.deadCheck();
opContext.deadCheck("includeCodes");
inc.checkNoModifiers("Compose.include", "expanding");
List<ValueSet> imports = new ArrayList<ValueSet>();
for (CanonicalType imp : inc.getValueSet()) {
@ -1033,7 +1033,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private void doServerIncludeCodes(ConceptSetComponent inc, boolean heirarchical, ValueSetExpansionComponent exp, List<ValueSet> imports, Parameters expParams, List<Extension> extensions, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws FHIRException, CodeSystemProviderExtension, ETooCostly {
opContext.deadCheck();
opContext.deadCheck("doServerIncludeCodes");
CodeSystemProvider csp = CodeSystemProvider.factory(inc.getSystem());
if (csp != null) {
csp.includeCodes(inc, heirarchical, exp, imports, expParams, extensions, noInactive, vsProps);
@ -1075,7 +1075,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
public void doInternalIncludeCodes(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, List<ValueSet> imports, CodeSystem cs, boolean noInactive, Resource vsSrc) throws NoTerminologyServiceException, TerminologyServiceException, FHIRException, ETooCostly {
opContext.deadCheck();
opContext.deadCheck("doInternalIncludeCodes");
if (cs == null) {
if (context.isNoTerminologyServer())
throw failTSE("Unable to find code system " + inc.getSystem().toString());
@ -1160,7 +1160,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
private void processFilter(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, List<ValueSet> imports, CodeSystem cs, boolean noInactive,
ConceptSetFilterComponent fc, WorkingContext wc, List<WorkingContext> filters, boolean exclude)
throws ETooCostly {
opContext.deadCheck();
opContext.deadCheck("processFilter");
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
@ -1213,7 +1213,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue()) && passesOtherFilters(filters, cs, def.getCode())) {
for (String code : getCodesForConcept(def, expParams)) {
opContext.deadCheck();
opContext.deadCheck("processFilter2");
if (exclude) {
excludeCode(wc, inc.getSystem(), code);
} else {

View File

@ -0,0 +1,74 @@
package org.hl7.fhir.r5.terminologies.utilities;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.utilities.Utilities;
//URL: http://snomed.info/sct/[module]/version/[e.g. 20150131]'
//International: 900000000000207008
//US: 731000124108
//Australia: 32506021000036107
//Belgium: 11000172109
//Canada: 20611000087101
//Spain: 449081005
//Denmark: 554471000005108
//Netherlands: 11000146104
//Sweden: 45991000052106
//Switzerland: 2011000195101
//UK: 83821000000107
//IPS: 827022005
public class SnomedUtilities {
public static String getVersionFromParameters(Parameters p, String version) {
for (ParametersParameterComponent pp : p.getParameter()) {
switch (pp.getName()) {
case "system-version" :
if (version == null) {
return pp.getValue().primitiveValue();
}
case "force-system-version":
return pp.getValue().primitiveValue();
}
}
return version;
}
public static String getEditionFromVersion(String version) {
if (version == null) {
return null;
}
if (version.startsWith("http://snomed.info/sct/")) {
version = version.substring(23);
}
if (version.contains("/")) {
version = version.substring(0, version.indexOf("/"));
}
if (Utilities.existsInList(version, "900000000000207008", "731000124108", "32506021000036107", "11000172109", "20611000087101",
"449081005", "554471000005108", "11000146104", "45991000052106", "2011000195101", "83821000000107", "827022005")) {
return version;
} else {
return null;
}
}
public static String getSctLink(String version, String code, Parameters p) {
if (!Utilities.noString(code)) {
version = SnomedUtilities.getVersionFromParameters(p, version);
String edId = SnomedUtilities.getEditionFromVersion(version);
if (edId != null) {
// if there's a version that's an edition, then:
// http://snomed.info/sct/11000172109/id//371305003
return "http://snomed.info/sct/"+edId+"/id/"+code;
} else {
// no, version:
return "http://snomed.info/id/"+code;
}
} else {
return "https://browser.ihtsdotools.org/";
}
}
}

View File

@ -302,22 +302,26 @@ public class TerminologyCache {
this.lock = lock;
if (folder == null) {
folder = Utilities.path("[tmp]", "default-tx-cache");
} else if ("n/a".equals(folder)) {
// this is a weird way to do things but it maintains the legacy interface
folder = null;
}
this.folder = folder;
requestCount = 0;
hitCount = 0;
networkCount = 0;
File f = ManagedFileAccess.file(folder);
if (!f.exists()) {
Utilities.createDirectory(folder);
if (folder != null) {
File f = ManagedFileAccess.file(folder);
if (!f.exists()) {
Utilities.createDirectory(folder);
}
if (!f.exists()) {
throw new IOException("Unable to create terminology cache at "+folder);
}
checkVersion();
load();
}
if (!f.exists()) {
throw new IOException("Unable to create terminology cache at "+folder);
}
checkVersion();
load();
}
private void checkVersion() throws IOException {
@ -361,7 +365,9 @@ public class TerminologyCache {
}
private void clear() throws IOException {
Utilities.clearDirectory(folder);
if (folder != null) {
Utilities.clearDirectory(folder);
}
caches.clear();
vsCache.clear();
csCache.clear();
@ -1039,7 +1045,7 @@ public class TerminologyCache {
public SourcedValueSet getValueSet(String canonical) {
SourcedValueSetEntry sp = vsCache.get(canonical);
if (sp == null) {
if (sp == null || folder == null) {
return null;
} else {
try {
@ -1052,7 +1058,7 @@ public class TerminologyCache {
public SourcedCodeSystem getCodeSystem(String canonical) {
SourcedCodeSystemEntry sp = csCache.get(canonical);
if (sp == null) {
if (sp == null || folder == null) {
return null;
} else {
try {
@ -1073,7 +1079,9 @@ public class TerminologyCache {
} else {
String uuid = Utilities.makeUuidLC();
String fn = "vs-"+uuid+".json";
new JsonParser().compose(ManagedFileAccess.outStream(Utilities.path(folder, fn)), svs.getVs());
if (folder != null) {
new JsonParser().compose(ManagedFileAccess.outStream(Utilities.path(folder, fn)), svs.getVs());
}
vsCache.put(canonical, new SourcedValueSetEntry(svs.getServer(), fn));
}
org.hl7.fhir.utilities.json.model.JsonObject j = new org.hl7.fhir.utilities.json.model.JsonObject();
@ -1090,7 +1098,9 @@ public class TerminologyCache {
j.add(k, e);
}
}
org.hl7.fhir.utilities.json.parser.JsonParser.compose(j, ManagedFileAccess.file(Utilities.path(folder, "vs-externals.json")), true);
if (folder != null) {
org.hl7.fhir.utilities.json.parser.JsonParser.compose(j, ManagedFileAccess.file(Utilities.path(folder, "vs-externals.json")), true);
}
} catch (Exception e) {
e.printStackTrace();
}
@ -1106,7 +1116,9 @@ public class TerminologyCache {
} else {
String uuid = Utilities.makeUuidLC();
String fn = "cs-"+uuid+".json";
new JsonParser().compose(ManagedFileAccess.outStream(Utilities.path(folder, fn)), scs.getCs());
if (folder != null) {
new JsonParser().compose(ManagedFileAccess.outStream(Utilities.path(folder, fn)), scs.getCs());
}
csCache.put(canonical, new SourcedCodeSystemEntry(scs.getServer(), fn));
}
org.hl7.fhir.utilities.json.model.JsonObject j = new org.hl7.fhir.utilities.json.model.JsonObject();
@ -1123,7 +1135,9 @@ public class TerminologyCache {
j.add(k, e);
}
}
org.hl7.fhir.utilities.json.parser.JsonParser.compose(j, ManagedFileAccess.file(Utilities.path(folder, "cs-externals.json")), true);
if (folder != null) {
org.hl7.fhir.utilities.json.parser.JsonParser.compose(j, ManagedFileAccess.file(Utilities.path(folder, "cs-externals.json")), true);
}
} catch (Exception e) {
e.printStackTrace();
}

View File

@ -7,6 +7,7 @@ import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationOptions;
@ -38,16 +39,22 @@ public class TerminologyOperationContext {
public static boolean debugging = java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0;
private static final int EXPANSION_DEAD_TIME_SECS = 60;
private long deadTime;
private int nestCount = 0;
private long startTime;
private List<String> contexts = new ArrayList<>();
private IWorkerContext worker;
private boolean original;
private ValidationOptions options;
private String name;
private List<String> notes = new ArrayList<>();
public TerminologyOperationContext(IWorkerContext worker, ValidationOptions options) {
public TerminologyOperationContext(IWorkerContext worker, ValidationOptions options, String name) {
super();
this.worker = worker;
this.original = true;
this.options = options;
this.name = name;
this.startTime = System.currentTimeMillis();
if (EXPANSION_DEAD_TIME_SECS == 0 || debugging) {
deadTime = 0;
@ -56,22 +63,33 @@ public class TerminologyOperationContext {
}
}
private TerminologyOperationContext(ValidationOptions options) {
private TerminologyOperationContext(ValidationOptions options, String name) {
super();
this.options = options;
this.name = name;
this.startTime = System.currentTimeMillis();
}
public TerminologyOperationContext copy() {
TerminologyOperationContext ret = new TerminologyOperationContext(this.options);
TerminologyOperationContext ret = new TerminologyOperationContext(this.options, name);
ret.worker = worker;
ret.contexts.addAll(contexts);
ret.deadTime = deadTime;
ret.notes = notes;
ret.startTime = startTime;
ret.nestCount = nestCount + 1;
return ret;
}
public void deadCheck() {
public void deadCheck(String note) {
note(note);
if (deadTime != 0 && System.currentTimeMillis() > deadTime) {
throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_TIME, contexts.get(0), EXPANSION_DEAD_TIME_SECS), TerminologyServiceErrorClass.TOO_COSTLY, IssueType.TOOCOSTLY);
System.out.println();
System.out.println("Operation took too long - longer than "+(deadTime - startTime)+"ms");
for (String s : notes) {
System.out.println(s);
}
throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_TIME, contexts.get(0), EXPANSION_DEAD_TIME_SECS, name+" (local)"), TerminologyServiceErrorClass.TOO_COSTLY, IssueType.TOOCOSTLY);
}
}
@ -90,5 +108,8 @@ public class TerminologyOperationContext {
return options;
}
public void note(String s) {
s = Utilities.padLeft("", ' ', nestCount)+" "+(System.currentTimeMillis() - startTime)+" "+s;
notes.add(s);
}
}

View File

@ -49,7 +49,7 @@ public class ValueSetProcessBase {
protected IWorkerContext context;
protected TerminologyOperationContext opContext;
protected List<String> requiredSupplements = new ArrayList<>();
protected ValueSetProcessBase(IWorkerContext context, TerminologyOperationContext opContext) {
super();
this.context = context;
@ -229,7 +229,11 @@ public class ValueSetProcessBase {
}
}
}
public TerminologyOperationContext getOpContext() {
return opContext;
}
protected AlternateCodesProcessingRules altCodeParams = new AlternateCodesProcessingRules(false);
protected AlternateCodesProcessingRules allAltCodes = new AlternateCodesProcessingRules(true);

View File

@ -175,11 +175,15 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
private void analyseValueSet() {
opContext.note("analyse");
if (valueset != null) {
opContext.note("vs = "+valueset.getVersionedUrl());
opContext.seeContext(valueset.getVersionedUrl());
for (Extension s : valueset.getExtensionsByUrl(ExtensionConstants.EXT_VSSUPPLEMENT)) {
requiredSupplements.add(s.getValue().primitiveValue());
}
} else {
opContext.note("vs = null");
}
altCodeParams.seeParameters(expansionProfile);
@ -187,17 +191,18 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (localContext != null) {
if (valueset != null) {
for (ConceptSetComponent i : valueset.getCompose().getInclude()) {
analyseComponent(i);
analyseComponent(i, "inc"+i);
}
for (ConceptSetComponent i : valueset.getCompose().getExclude()) {
analyseComponent(i);
analyseComponent(i, "exc"+i);
}
}
}
opContext.note("analysed");
}
private void analyseComponent(ConceptSetComponent i) {
opContext.deadCheck();
private void analyseComponent(ConceptSetComponent i, String name) {
opContext.deadCheck("analyse Component "+name);
if (i.getSystemElement().hasExtension(ToolingExtensions.EXT_VALUESET_SYSTEM)) {
String ref = i.getSystemElement().getExtensionString(ToolingExtensions.EXT_VALUESET_SYSTEM);
if (ref.startsWith("#")) {
@ -219,11 +224,15 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
public ValidationResult validateCode(String path, CodeableConcept code) throws FHIRException {
opContext.deadCheck();
opContext.deadCheck("validate "+code.toString());
checkValueSetOptions();
// first, we validate the codings themselves
ValidationProcessInfo info = new ValidationProcessInfo();
if (throwToServer) {
checkValueSetLoad(info);
}
CodeableConcept vcc = new CodeableConcept();
List<ValidationResult> resList = new ArrayList<>();
@ -395,6 +404,45 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
}
private void checkValueSetLoad(ValidationProcessInfo info) {
int serverCount = getServerLoad(info);
// There's a trade off here: if we're going to hit the server inside the components, then
// the amount of value set collateral we send is limited, but we pay the price of hitting
// the server multiple times. If, on the other hand, we give up on that, and hit the server
// directly, we have to send value set collateral (though we cache at the higher level)
//
// the cutoff value is chosen experimentally
if (serverCount > 2) {
throw new VSCheckerException("This value set is better processed on the server for performance reasons", null, true);
}
}
private int getServerLoad(ValidationProcessInfo info) {
int serverCount = 0;
if (valueset != null) {
for (ConceptSetComponent inc : valueset.getCompose().getInclude()) {
serverCount = serverCount + checkValueSetLoad(inc, info);
}
for (ConceptSetComponent inc : valueset.getCompose().getExclude()) {
serverCount = serverCount + checkValueSetLoad(inc, info);
}
}
return serverCount;
}
private int checkValueSetLoad(ConceptSetComponent inc, ValidationProcessInfo info) {
int serverCount = 0;
for (UriType uri : inc.getValueSet()) {
ValueSetValidator vsv = getVs(uri.getValue(), info);
serverCount += vsv.getServerLoad(info);
}
CodeSystem cs = resolveCodeSystem(inc.getSystem(), inc.getVersion());
if (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT)) {
serverCount++;
}
return serverCount;
}
private boolean checkRequiredSupplements(ValidationProcessInfo info) {
if (!requiredSupplements.isEmpty()) {
String msg= context.formatMessagePlural(requiredSupplements.size(), I18nConstants.VALUESET_SUPPLEMENT_MISSING, CommaSeparatedStringBuilder.build(requiredSupplements));
@ -466,7 +514,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
public ValidationResult validateCode(String path, Coding code) throws FHIRException {
opContext.deadCheck();
opContext.deadCheck("validate "+code.toString());
checkValueSetOptions();
String warningMessage = null;
@ -798,7 +846,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
private ValidationResult findCodeInExpansion(Coding code, List<ValueSetExpansionContainsComponent> contains) {
for (ValueSetExpansionContainsComponent containsComponent: contains) {
opContext.deadCheck();
opContext.deadCheck("findCodeInExpansion");
if (containsComponent.getSystem().equals(code.getSystem()) && containsComponent.getCode().equals(code.getCode())) {
ConceptDefinitionComponent ccd = new ConceptDefinitionComponent();
ccd.setCode(containsComponent.getCode());
@ -825,7 +873,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
private boolean checkExpansion(Coding code, List<ValueSetExpansionContainsComponent> contains, VersionInfo vi) {
for (ValueSetExpansionContainsComponent containsComponent: contains) {
opContext.deadCheck();
opContext.deadCheck("checkExpansion: "+code.toString());
if (containsComponent.hasSystem() && containsComponent.hasCode() && containsComponent.getSystem().equals(code.getSystem()) && containsComponent.getCode().equals(code.getCode())) {
vi.setExpansionVersion(containsComponent.getVersion());
return true;
@ -882,7 +930,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
for (ConceptDefinitionDesignationComponent ds : cc.getDesignation()) {
opContext.deadCheck();
opContext.deadCheck("validateCode1 "+ds.toString());
if (isOkLanguage(ds.getLanguage())) {
b.append("'"+ds.getValue()+"' ("+ds.getLanguage()+")");
if (code.getDisplay().equalsIgnoreCase(ds.getValue())) {
@ -904,7 +952,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
}
for (ConceptReferenceDesignationComponent ds : vs.getCc().getDesignation()) {
opContext.deadCheck();
opContext.deadCheck("validateCode2 "+ds.toString());
if (isOkLanguage(ds.getLanguage())) {
b.append("'"+ds.getValue()+"'");
if (code.getDisplay().equalsIgnoreCase(ds.getValue())) {
@ -1009,7 +1057,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
return null;
// if it has an expansion
for (ValueSetExpansionContainsComponent exp : valueset.getExpansion().getContains()) {
opContext.deadCheck();
opContext.deadCheck("findValueSetRef "+exp.toString());
if (system.equals(exp.getSystem()) && code.equals(exp.getCode())) {
ConceptReferenceComponent cc = new ConceptReferenceComponent();
cc.setDisplay(exp.getDisplay());
@ -1048,7 +1096,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
private ConceptDefinitionComponent findCodeInConcept(ConceptDefinitionComponent concept, String code, boolean caseSensitive, AlternateCodesProcessingRules altCodeRules) {
opContext.deadCheck();
opContext.deadCheck("findCodeInConcept: "+code.toString()+", "+concept.toString());
if (code.equals(concept.getCode())) {
return concept;
}
@ -1121,7 +1169,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
int i = 0;
for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
opContext.deadCheck();
opContext.deadCheck("scanForCodeInValueSet: "+code.toString());
if (scanForCodeInValueSetInclude(code, sys, problems, i, vsi)) {
return true;
}
@ -1241,7 +1289,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
*/
private boolean checkSystems(List<ValueSetExpansionContainsComponent> contains, String code, Set<String> systems, List<StringWithCode> problems) {
for (ValueSetExpansionContainsComponent c: contains) {
opContext.deadCheck();
opContext.deadCheck("checkSystems "+code.toString());
if (c.getCode().equals(code)) {
systems.add(c.getSystem());
}
@ -1255,7 +1303,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (valueset == null) {
return null;
}
opContext.deadCheck();
opContext.deadCheck("codeInValueSet: "+system+"#"+code);
checkCanonical(info.getIssues(), path, valueset, valueset);
Boolean result = false;
VersionInfo vi = new VersionInfo(this);
@ -1290,7 +1338,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
private Boolean inComponent(String path, ConceptSetComponent vsi, int vsiIndex, String system, String version, String code, boolean only, ValidationProcessInfo info) throws FHIRException {
opContext.deadCheck();
opContext.deadCheck("inComponent "+vsiIndex);
boolean ok = true;
if (vsi.hasValueSet()) {
@ -1339,6 +1387,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
vs.setUrl(valueset.getUrl()+"--"+vsiIndex);
vs.setVersion(valueset.getVersion());
vs.getCompose().addInclude(vsi);
opContext.deadCheck("hit server "+vs.getVersionedUrl());
ValidationResult res = context.validateCode(options.withNoClient(), new Coding(system, code, null), vs);
if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) {
if (info != null && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
@ -1348,16 +1397,20 @@ public class ValueSetValidator extends ValueSetProcessBase {
info.addIssue(makeIssue(IssueSeverity.WARNING, IssueType.UNKNOWN, path, msg, OpIssueCode.NotFound, null));
for (ConceptReferenceComponent cc : vsi.getConcept()) {
if (cc.getCode().equals(code)) {
opContext.deadCheck("server true");
return true;
}
}
}
info.setErr(TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED);
opContext.deadCheck("server codesystem unsupported");
return null;
}
opContext.deadCheck("server not found");
return false;
}
if (res.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) {
opContext.deadCheck("server no server");
throw new NoTerminologyServiceException();
}
return res.isOk();
@ -1538,7 +1591,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
public boolean validateCodeInConceptList(String code, CodeSystem def, List<ConceptDefinitionComponent> list, AlternateCodesProcessingRules altCodeRules) {
opContext.deadCheck();
opContext.deadCheck("validateCodeInConceptList");
if (def.hasUserData("tx.cs.special")) {
return ((SpecialCodeSystem) def.getUserData("tx.cs.special")).findConcept(new Coding().setCode(code)) != null;
} else if (def.getCaseSensitive()) {
@ -1580,7 +1633,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
inner.put(url, vsc);
return vsc;
}
private Boolean inImport(String path, String uri, String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
ValueSetValidator vs = getVs(uri, info);
if (vs == null) {

View File

@ -36,6 +36,10 @@ public class PackageHackerR5 {
r.hack("http://terminology.hl7.org/CodeSystem/v2-0360-2.3.1", "2.3.1");
}
if ("http://hl7.org/fhir/ValueSet/languages".equals(r.getUrl())) {
r.getResource().setExperimental(false);
}
if ("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor".equals(r.getUrl()) && "4.0.1".equals(r.getVersion())) {
StructureDefinition sd = (StructureDefinition) r.getResource();
for (ElementDefinition ed : sd.getSnapshot().getElement()) {

View File

@ -59,7 +59,8 @@ public class FhirLoggingInterceptor implements Interceptor {
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
if (logger != null) {
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes);
long responseTimeInMillis = response.receivedResponseAtMillis() - response.sentRequestAtMillis();
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes, responseTimeInMillis);
}
// Reading byte[] clears body. Need to recreate.

View File

@ -44,6 +44,8 @@ import org.w3c.dom.events.MutationEvent;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.LocatorImpl;
import org.xml.sax.helpers.XMLFilterImpl;
@ -71,6 +73,22 @@ public class XmlLocationAnnotator extends XMLFilterImpl {
((EventTarget) dom).addEventListener("DOMNodeInserted", modListener, true);
}
@Override
public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
if (!name.equals("http://javax.xml.XMLConstants/property/accessExternalDTD")) {
super.setProperty(name, value);
}
}
@Override
public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
if (!name.equals("http://javax.xml.XMLConstants/property/accessExternalDTD")) {
return null;
} else {
return super.getProperty(name);
}
}
@Override
public void setDocumentLocator(Locator locator) {
super.setDocumentLocator(locator);

View File

@ -13,6 +13,7 @@ import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -341,6 +342,7 @@ public class BaseWorkerContextTests {
public void testValidateCodingWithValueSetChecker() throws IOException {
ValidationOptions validationOptions = new ValidationOptions(FhirPublication.R5).withGuessSystem().withVersionFlexible(false);
ValueSet valueSet = new ValueSet();
valueSet.setUrl(Utilities.makeUuidUrn());
Coding coding = new Coding();
Mockito.doReturn(cacheToken).when(terminologyCache).generateValidationToken(validationOptions, coding, valueSet, expParameters);

View File

@ -172,6 +172,6 @@ class ClientTest {
Mockito.verify(mockLogger, Mockito.times(1))
.logRequest(Mockito.anyString(), Mockito.anyString(), Mockito.anyList(), Mockito.any());
Mockito.verify(mockLogger, Mockito.times(1))
.logResponse(Mockito.anyString(), Mockito.anyList(), Mockito.any());
.logResponse(Mockito.anyString(), Mockito.anyList(), Mockito.any(), Mockito.anyLong());
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.30-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>6.3.26-SNAPSHOT</version>
<version>6.3.30-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -36,7 +36,7 @@ import java.util.List;
public interface ToolingClientLogger {
void logRequest(String method, String url, List<String> headers, byte[] body);
void logResponse(String outcome, List<String> headers, byte[] body);
void logResponse(String outcome, List<String> headers, byte[] body, long length);
String getLastId();
void clearLastId();

View File

@ -64,9 +64,9 @@ public class ManagedWebAccess {
}
public enum WebAccessPolicy {
DIRECT, // open access to the local file system, though access can be restricted only to files under the paths in AllowedPaths
MANAGED, // no access except by the FileSystemProxyProvider
PROHIBITED, // no access at all to File() services
DIRECT, // open access to the web, though access can be restricted only to domains in AllowedDomains
MANAGED, // no access except by the IWebAccessor
PROHIBITED, // no access at all to the web
}
private static WebAccessPolicy accessPolicy = WebAccessPolicy.DIRECT; // for legacy reasons

View File

@ -125,7 +125,7 @@ public abstract class I18nBase {
if (Objects.nonNull(theMessageArguments) && theMessageArguments.length > 0) {
message = MessageFormat.format(messages.getString(theMessage).trim(), theMessageArguments);
} else {
message = MessageFormat.format(messages.getString(theMessage).trim(), null);
message = MessageFormat.format(messages.getString(theMessage).trim(), (Object) null);
}
}
return message;

View File

@ -18,7 +18,9 @@ public class I18nConstants {
public static final String BAD_FILE_PATH_ERROR = "Bad_file_path_error";
public static final String BASE_PROFILE__HAS_NO_TYPE = "Base_profile__has_no_type";
public static final String BASE__DERIVED_PROFILES_HAVE_DIFFERENT_TYPES____VS___ = "Base__Derived_profiles_have_different_types____vs___";
public static final String BINDING_ADDITIONAL = "BINDING_ADDITIONAL";
public static final String BINDING_ADDITIONAL_D = "BINDING_ADDITIONAL_D";
public static final String BINDING_ADDITIONAL_UC = "BINDING_ADDITIONAL_UC";
public static final String BINDING_ADDITIONAL_USAGE = "BINDING_ADDITIONAL_USAGE";
public static final String BINDING_MAX = "BINDING_MAX";
public static final String BUNDLE_BUNDLE_ENTRY_CANONICAL = "Bundle_BUNDLE_Entry_Canonical";
public static final String BUNDLE_BUNDLE_ENTRY_DOCUMENT = "Bundle_BUNDLE_Entry_Document";
@ -1108,4 +1110,10 @@ public class I18nConstants {
public static final String SM_TARGET_TYPE_UNKNOWN = "SM_TARGET_TYPE_UNKNOWN";
public static final String XHTML_XHTML_ATTRIBUTE_XML_SPACE = "XHTML_XHTML_ATTRIBUTE_XML_SPACE";
public static final String VALIDATION_HL7_PUBLISHER_MULTIPLE_WGS = "VALIDATION_HL7_PUBLISHER_MULTIPLE_WGS";
public static final String SD_BASE_EXPERIMENTAL = "SD_BASE_EXPERIMENTAL";
public static final String SD_ED_EXPERIMENTAL_BINDING = "SD_ED_EXPERIMENTAL_BINDING";
public static final String VALIDATION_NO_EXPERIMENTAL_CONTENT = "VALIDATION_NO_EXPERIMENTAL_CONTENT";
public static final String SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN = "SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN";
public static final String SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT = "SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT";
public static final String SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE = "SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE";
}

View File

@ -214,6 +214,7 @@ public class RenderingI18nContext extends I18nBase {
public static final String DATA_REND_DATA = "DATA_REND_DATA";
public static final String DATA_REND_DETAILS = "DATA_REND_DETAILS";
public static final String DATA_REND_DETAILS_STATED = "DATA_REND_DETAILS_STATED";
public static final String DATA_REND_DETAILS_STATED_ND = "DATA_REND_DETAILS_STATED_ND";
public static final String DATA_REND_DICOM = "DATA_REND_DICOM";
public static final String DATA_REND_DIM = "DATA_REND_DIM";
public static final String DATA_REND_DURATION = "DATA_REND_DURATION";

View File

@ -8,6 +8,7 @@ import java.text.SimpleDateFormat;
import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
@ -77,12 +78,13 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
private final FilesystemPackageCacheManagerLocks locks;
private final FilesystemPackageCacheManagerLocks.LockParameters lockParameters;
// When running in testing mode, some packages are provided from the test case repository rather than by the normal means
// the PackageProvider is responsible for this. if no package provider is defined, or it declines to handle the package,
// then the normal means will be used
public interface IPackageProvider {
boolean handlesPackage(String id, String version);
InputStreamWithSrc provide(String id, String version) throws IOException;
}
@ -92,6 +94,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
public static final String PACKAGE_VERSION_REGEX_OPT = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+(\\#[A-Za-z0-9\\-\\_]+(\\.[A-Za-z0-9\\-\\_]+)*)?$";
private static final Logger ourLog = LoggerFactory.getLogger(FilesystemPackageCacheManager.class);
private static final String CACHE_VERSION = "3"; // second version - see wiki page
@Nonnull
private final File cacheFolder;
@ -100,6 +103,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
private final Map<String, String> ciList = new HashMap<>();
private JsonArray buildInfo;
private boolean suppressErrors;
@Setter
@Getter
private boolean minimalMemory;
@ -113,9 +117,20 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
@Getter
private final List<PackageServer> packageServers;
@With
@Getter
private final FilesystemPackageCacheManagerLocks.LockParameters lockParameters;
public Builder() throws IOException {
this.cacheFolder = getUserCacheFolder();
this.packageServers = getPackageServersFromFHIRSettings();
this.lockParameters = null;
}
private Builder(File cacheFolder, List<PackageServer> packageServers, FilesystemPackageCacheManagerLocks.LockParameters lockParameters) {
this.cacheFolder = cacheFolder;
this.packageServers = packageServers;
this.lockParameters = lockParameters;
}
private File getUserCacheFolder() throws IOException {
@ -143,17 +158,12 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return PackageServer.getConfiguredServers();
}
private Builder(File cacheFolder, List<PackageServer> packageServers) {
this.cacheFolder = cacheFolder;
this.packageServers = packageServers;
}
public Builder withCacheFolder(String cacheFolderPath) throws IOException {
File cacheFolder = ManagedFileAccess.file(cacheFolderPath);
if (!cacheFolder.exists()) {
throw new FHIRException("The folder '" + cacheFolder + "' could not be found");
}
return new Builder(cacheFolder, this.packageServers);
return new Builder(cacheFolder, this.packageServers, this.lockParameters);
}
public Builder withSystemCacheFolder() throws IOException {
@ -163,32 +173,33 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
} else {
systemCacheFolder = ManagedFileAccess.file(Utilities.path("/var", "lib", ".fhir", "packages"));
}
return new Builder(systemCacheFolder, this.packageServers);
return new Builder(systemCacheFolder, this.packageServers, this.lockParameters);
}
public Builder withTestingCacheFolder() throws IOException {
return new Builder(ManagedFileAccess.file(Utilities.path("[tmp]", ".fhir", "packages")), this.packageServers);
return new Builder(ManagedFileAccess.file(Utilities.path("[tmp]", ".fhir", "packages")), this.packageServers, this.lockParameters);
}
public FilesystemPackageCacheManager build() throws IOException {
return new FilesystemPackageCacheManager(cacheFolder, packageServers);
final FilesystemPackageCacheManagerLocks locks;
try {
locks = FilesystemPackageCacheManagerLocks.getFilesystemPackageCacheManagerLocks(cacheFolder);
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw e;
}
}
return new FilesystemPackageCacheManager(cacheFolder, packageServers, locks, lockParameters);
}
}
private FilesystemPackageCacheManager(@Nonnull File cacheFolder, @Nonnull List<PackageServer> packageServers) throws IOException {
private FilesystemPackageCacheManager(@Nonnull File cacheFolder, @Nonnull List<PackageServer> packageServers, @Nonnull FilesystemPackageCacheManagerLocks locks, @Nullable FilesystemPackageCacheManagerLocks.LockParameters lockParameters) throws IOException {
super(packageServers);
this.cacheFolder = cacheFolder;
try {
this.locks = FilesystemPackageCacheManagerLocks.getFilesystemPackageCacheManagerLocks(cacheFolder);
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw e;
}
}
this.locks = locks;
this.lockParameters = lockParameters;
prepareCacheFolder();
}
@ -218,11 +229,35 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
createIniFile();
}
deleteOldTempDirectories();
cleanUpCorruptPackages();
}
return null;
});
}
/*
Look for .lock files that are not actively held by a process. If found, delete the lock file, and the package
referenced.
*/
protected void cleanUpCorruptPackages() throws IOException {
for (File file : Objects.requireNonNull(cacheFolder.listFiles())) {
if (file.getName().endsWith(".lock")) {
if (locks.getCacheLock().canLockFileBeHeldByThisProcess(file)) {
String packageDirectoryName = file.getName().substring(0, file.getName().length() - 5);
log("Detected potential incomplete package installed in cache: " + packageDirectoryName + ". Attempting to delete");
File packageDirectory = ManagedFileAccess.file(Utilities.path(cacheFolder, packageDirectoryName));
if (packageDirectory.exists()) {
Utilities.clearDirectory(packageDirectory.getAbsolutePath());
packageDirectory.delete();
}
file.delete();
log("Deleted potential incomplete package: " + packageDirectoryName);
}
}
}
}
private boolean iniFileExists() throws IOException {
String iniPath = getPackagesIniPath();
File iniFile = ManagedFileAccess.file(iniPath);
@ -421,7 +456,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
}
return null;
});
}, lockParameters);
}
/**
@ -465,7 +500,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return null;
}
return loadPackageInfo(path);
});
}, lockParameters);
if (foundPackage != null) {
if (foundPackage.isIndexed()){
return foundPackage;
@ -488,7 +523,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
String path = Utilities.path(cacheFolder, foundPackageFolder);
output.checkIndexed(path);
return output;
});
}, lockParameters);
}
}
}
@ -589,7 +624,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
throw e;
}
return npmPackage;
});
}, lockParameters);
}
private void log(String s) {

View File

@ -1,14 +1,19 @@
package org.hl7.fhir.utilities.npm;
import lombok.Getter;
import org.hl7.fhir.utilities.TextFile;
import lombok.With;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@ -27,9 +32,23 @@ public class FilesystemPackageCacheManagerLocks {
private final File cacheFolder;
private final Long lockTimeoutTime;
private static final LockParameters lockParameters = new LockParameters();
private final TimeUnit lockTimeoutTimeUnit;
public static class LockParameters {
@Getter @With
private final long lockTimeoutTime;
@Getter @With
private final TimeUnit lockTimeoutTimeUnit;
public LockParameters() {
this(60L, TimeUnit.SECONDS);
}
public LockParameters(long lockTimeoutTime, TimeUnit lockTimeoutTimeUnit) {
this.lockTimeoutTime = lockTimeoutTime;
this.lockTimeoutTimeUnit = lockTimeoutTimeUnit;
}
}
/**
* This method is intended to be used only for testing purposes.
@ -43,21 +62,9 @@ public class FilesystemPackageCacheManagerLocks {
* @throws IOException
*/
public FilesystemPackageCacheManagerLocks(File cacheFolder) throws IOException {
this(cacheFolder, 60L, TimeUnit.SECONDS);
}
private FilesystemPackageCacheManagerLocks(File cacheFolder, Long lockTimeoutTime, TimeUnit lockTimeoutTimeUnit) throws IOException {
this.cacheFolder = cacheFolder;
this.lockTimeoutTime = lockTimeoutTime;
this.lockTimeoutTimeUnit = lockTimeoutTimeUnit;
}
/**
* This method is intended to be used only for testing purposes.
*/
protected FilesystemPackageCacheManagerLocks withLockTimeout(Long lockTimeoutTime, TimeUnit lockTimeoutTimeUnit) throws IOException {
return new FilesystemPackageCacheManagerLocks(cacheFolder, lockTimeoutTime, lockTimeoutTimeUnit);
}
/**
* Returns a single FilesystemPackageCacheManagerLocks instance for the given cacheFolder.
@ -102,6 +109,19 @@ public class FilesystemPackageCacheManagerLocks {
}
return result;
}
public boolean canLockFileBeHeldByThisProcess(File lockFile) throws IOException {
return doWriteWithLock(() -> {
try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
FileLock fileLock = channel.tryLock(0, Long.MAX_VALUE, false);
if (fileLock != null) {
fileLock.release();
channel.close();
return true;
}
}
return false;});
}
}
public class PackageLock {
@ -114,15 +134,43 @@ public class FilesystemPackageCacheManagerLocks {
this.lock = lock;
}
private void checkForLockFileWaitForDeleteIfExists(File lockFile) throws IOException {
private void checkForLockFileWaitForDeleteIfExists(File lockFile, @Nonnull LockParameters lockParameters) throws IOException {
if (!lockFile.exists()) {
return;
}
// Check if the file is locked by a process. If it is not, it is likely an incomplete package cache install, and
// we should throw an exception.
if (lockFile.isFile()) {
try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
FileLock fileLock = channel.tryLock(0, Long.MAX_VALUE, false);
if (fileLock != null) {
fileLock.release();
channel.close();
throw new IOException("Lock file exists, but is not locked by a process: " + lockFile.getName());
}
System.out.println("File is locked.");
}
}
try {
waitForLockFileDeletion(lockFile, lockParameters);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Thread interrupted while waiting for lock", e);
}
}
/*
Wait for the lock file to be deleted. If the lock file is not deleted within the timeout or if the thread is
interrupted, an IOException is thrown.
*/
private void waitForLockFileDeletion(File lockFile, @Nonnull LockParameters lockParameters) throws IOException, InterruptedException {
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
Path dir = lockFile.getParentFile().toPath();
dir.register(watchService, StandardWatchEventKinds.ENTRY_DELETE);
WatchKey key = watchService.poll(lockTimeoutTime, lockTimeoutTimeUnit);
WatchKey key = watchService.poll(lockParameters.lockTimeoutTime, lockParameters.lockTimeoutTimeUnit);
if (key == null) {
// It is possible that the lock file is deleted before the watch service is registered, so if we timeout at
// this point, we should check if the lock file still exists.
@ -141,24 +189,33 @@ public class FilesystemPackageCacheManagerLocks {
key.reset();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Error reading package.", e);
} catch (TimeoutException e) {
throw new IOException("Error reading package.", e);
throw new IOException("Package cache timed out waiting for lock.", e);
}
}
public <T> T doReadWithLock(FilesystemPackageCacheManager.CacheLockFunction<T> f) throws IOException {
/**
* Wraps the execution of a package read function with the appropriate cache, package, and .lock file locking and
* checks.
*
* @param function The function to execute
* @param lockParameters The parameters for the lock
* @return The return of the function
* @param <T> The return type of the function
* @throws IOException If an error occurs while managing locking.
*/
public <T> T doReadWithLock(FilesystemPackageCacheManager.CacheLockFunction<T> function, @Nullable LockParameters lockParameters) throws IOException {
final LockParameters resolvedLockParameters = lockParameters != null ? lockParameters : FilesystemPackageCacheManagerLocks.lockParameters;
cacheLock.getLock().readLock().lock();
lock.readLock().lock();
checkForLockFileWaitForDeleteIfExists(lockFile);
checkForLockFileWaitForDeleteIfExists(lockFile, resolvedLockParameters);
T result = null;
try {
result = f.get();
result = function.get();
} finally {
lock.readLock().unlock();
cacheLock.getLock().readLock().unlock();
@ -166,35 +223,55 @@ public class FilesystemPackageCacheManagerLocks {
return result;
}
public <T> T doWriteWithLock(FilesystemPackageCacheManager.CacheLockFunction<T> f) throws IOException {
/**
* Wraps the execution of a package write function with the appropriate cache, package, and .lock file locking and
* checks.
*
* @param function The function to execute
* @param lockParameters The parameters for the lock
* @return The return of the function
* @param <T> The return type of the function
* @throws IOException If an error occurs while managing locking.
*/
public <T> T doWriteWithLock(FilesystemPackageCacheManager.CacheLockFunction<T> function, @Nullable LockParameters lockParameters) throws IOException {
final LockParameters resolvedLockParameters = lockParameters != null ? lockParameters : FilesystemPackageCacheManagerLocks.lockParameters;
cacheLock.getLock().writeLock().lock();
lock.writeLock().lock();
if (!lockFile.isFile()) {
try {
TextFile.stringToFile("", lockFile);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/*TODO Eventually, this logic should exist in a Lockfile class so that it isn't duplicated between the main code and
the test code.
*/
try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
FileLock fileLock = null;
while (fileLock == null) {
fileLock = channel.tryLock(0, Long.MAX_VALUE, true);
if (fileLock == null) {
Thread.sleep(100); // Wait and retry
}
FileLock fileLock = channel.tryLock(0, Long.MAX_VALUE, false);
if (fileLock == null) {
waitForLockFileDeletion(lockFile, resolvedLockParameters);
fileLock = channel.tryLock(0, Long.MAX_VALUE, false);
}
if (fileLock == null) {
throw new IOException("Failed to acquire lock on file: " + lockFile.getName());
}
if (!lockFile.isFile()) {
final ByteBuffer buff = ByteBuffer.wrap(String.valueOf(ProcessHandle.current().pid()).getBytes(StandardCharsets.UTF_8));
channel.write(buff);
}
T result = null;
try {
result = f.get();
result = function.get();
} finally {
lockFile.renameTo(ManagedFileAccess.file(File.createTempFile(lockFile.getName(), ".lock-renamed").getAbsolutePath()));
fileLock.release();
channel.close();
if (!lockFile.delete()) {
lockFile.deleteOnExit();
}
lock.writeLock().unlock();
cacheLock.getLock().writeLock().unlock();
}

View File

@ -12,6 +12,7 @@ import java.util.Map;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
import org.hl7.fhir.utilities.json.model.JsonArray;
import org.hl7.fhir.utilities.json.model.JsonObject;
@ -43,7 +44,44 @@ public class PackageHacker {
// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.search.tgz");
// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/core/v311/package.tgz", "http://hl7.org/fhir/us/core/STU3.1.1");
new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/core/v700/package.tgz", "http://hl7.org/fhir/us/core/STU7");
// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/core/v700/package.tgz", "http://hl7.org/fhir/us/core/STU7");
PackageHacker self = new PackageHacker();
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/1.0.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/1.0.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/2.0.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/2.0.0/hl7.terminology.r4b.tgz");
self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/2.1.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/2.1.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/3.0.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/3.0.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/3.1.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/3.1.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/4.0.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/4.0.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/5.0.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/5.0.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/5.1.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/5.1.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/5.2.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/5.2.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/5.3.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/5.3.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/5.4.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/5.4.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/5.5.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/5.5.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/6.0.0/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/6.0.0/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/6.0.1/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/6.0.1/hl7.terminology.r4b.tgz");
// self.cloneToR4B("/Users/grahamegrieve/web/terminology.hl7.org/6.0.2/hl7.terminology.r4.tgz", "/Users/grahamegrieve/web/terminology.hl7.org/6.0.2/hl7.terminology.r4b.tgz");
}
private void cloneToR4B(String src, String dst) throws IOException {
FileInputStream fs = ManagedFileAccess.inStream(src);
NpmPackage pck = NpmPackage.fromPackage(fs);
System.out.println(nice(pck.getNpm()));
JsonObject json = pck.getNpm();
String name = json.asString("name");
json.remove("name");
json.add("name", name.replace(".r4", ".r4b"));
json.remove("fhirVersions");
json.remove("dependencies");
JsonArray fv = new JsonArray();
json.add("fhirVersions", fv);
fv.add("4.3.0");
JsonObject dep = new JsonObject();
json.add("dependencies", dep);
dep.add(VersionUtilities.packageForVersion("4.3.0"), "4.3.0");
pck.save(new FileOutputStream(dst));
}
// private void massEdit(File dir) throws IOException {

View File

@ -33,7 +33,7 @@ public class CacheVerificationLogger implements ToolingClientLogger {
}
@Override
public void logResponse(String outcome, List<String> headers, byte[] body) {
public void logResponse(String outcome, List<String> headers, byte[] body, long start) {
}

View File

@ -1,5 +1,5 @@
package org.hl7.fhir.utilities.xml;
package org.hl7.fhir.utilities.xml;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -28,80 +28,85 @@ package org.hl7.fhir.utilities.xml;
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.IOException;
import org.hl7.fhir.utilities.ElementDecoration;
/**
* Generalize
* @author dennisn
*/
public interface IXMLWriter {
public abstract void start() throws IOException;
public abstract void end() throws IOException;
public abstract void attribute(String namespace, String name, String value, boolean onlyIfNotEmpty) throws IOException;
public abstract void attribute(String namespace, String name, String value) throws IOException;
public abstract void attribute(String name, String value, boolean onlyIfNotEmpty) throws IOException;
public abstract void attribute(String name, String value) throws IOException;
public abstract void attributeNoLines(String name, String value) throws IOException;
public abstract boolean namespaceDefined(String namespace);
public abstract boolean abbreviationDefined(String abbreviation);
public abstract String getDefaultNamespace();
public abstract void namespace(String namespace) throws IOException;
public abstract void setDefaultNamespace(String namespace) throws IOException;
public abstract void namespace(String namespace, String abbreviation) throws IOException;
public abstract void comment(String comment, boolean doPretty) throws IOException;
public abstract void decorate(ElementDecoration decoration) throws IOException;
public abstract void setSchemaLocation(String ns, String loc) throws IOException;
public abstract void enter(String name) throws IOException;
public abstract void enter(String namespace, String name) throws IOException;
public abstract void enter(String namespace, String name, String comment) throws IOException;
public abstract void exit() throws IOException;
public abstract void exit(String name) throws IOException;
public abstract void exit(String namespace, String name) throws IOException;
public abstract void exitToLevel(int count) throws IOException;
public abstract void element(String namespace, String name, String content, boolean onlyIfNotEmpty) throws IOException;
public abstract void element(String namespace, String name, String content, String comment) throws IOException;
public abstract void element(String namespace, String name, String content) throws IOException;
public abstract void element(String name, String content, boolean onlyIfNotEmpty) throws IOException;
public abstract void element(String name, String content) throws IOException;
public abstract void element(String name) throws IOException;
public abstract void text(String content) throws IOException;
public abstract void text(String content, boolean dontEscape) throws IOException;
public abstract void cData(String text) throws IOException;
public abstract void writeBytes(byte[] bytes) throws IOException;
public abstract boolean isPretty() throws IOException;
public abstract void setPretty(boolean pretty) throws IOException;
/**
* Start comment inserts a <!-- in the stream, but allows the user to
* go on creating xml content as usual, with proper formatting applied etc.
* Any comments inserted inside a comment will be terminated with -- > instead of -->
* so the comment doesn't close prematurely.
* @throws IOException
*/
public abstract void startCommentBlock() throws IOException;
public abstract void endCommentBlock() throws IOException;
public abstract void escapedText(String content) throws IOException;
// this is only implemented by an implementation that is producing an xhtml representation, and is able to render elements as hyperlinks
public abstract void link(String href);
public abstract void anchor(String name);
public abstract void externalLink(String ref) throws IOException;
import java.io.IOException;
import org.hl7.fhir.utilities.ElementDecoration;
/**
* Generalize
* @author dennisn
*/
public interface IXMLWriter {
public abstract void start() throws IOException;
public abstract void end() throws IOException;
public abstract void attribute(String namespace, String name, String value, boolean onlyIfNotEmpty) throws IOException;
public abstract void attribute(String namespace, String name, String value) throws IOException;
public abstract void attribute(String name, String value, boolean onlyIfNotEmpty) throws IOException;
public abstract void attribute(String name, String value) throws IOException;
public abstract void attributeNoLines(String name, String value) throws IOException;
public abstract boolean namespaceDefined(String namespace);
public abstract boolean abbreviationDefined(String abbreviation);
public abstract String getDefaultNamespace();
public abstract void namespace(String namespace) throws IOException;
public abstract void setDefaultNamespace(String namespace) throws IOException;
public abstract void namespace(String namespace, String abbreviation) throws IOException;
public abstract void comment(String comment, boolean doPretty) throws IOException;
public abstract void decorate(ElementDecoration decoration) throws IOException;
public abstract void setSchemaLocation(String ns, String loc) throws IOException;
public abstract void enter(String name) throws IOException;
public abstract void enter(String namespace, String name) throws IOException;
public abstract void enter(String namespace, String name, String comment) throws IOException;
public abstract void exit() throws IOException;
public abstract void exit(String name) throws IOException;
public abstract void exit(String namespace, String name) throws IOException;
public abstract void exitToLevel(int count) throws IOException;
public abstract void element(String namespace, String name, String content, boolean onlyIfNotEmpty) throws IOException;
public abstract void element(String namespace, String name, String content, String comment) throws IOException;
public abstract void element(String namespace, String name, String content) throws IOException;
public abstract void element(String name, String content, boolean onlyIfNotEmpty) throws IOException;
public abstract void element(String name, String content) throws IOException;
public abstract void element(String name) throws IOException;
public abstract void text(String content) throws IOException;
public abstract void text(String content, boolean dontEscape) throws IOException;
public abstract void cData(String text) throws IOException;
public abstract void writeBytes(byte[] bytes) throws IOException;
public abstract boolean isPretty() throws IOException;
public abstract void setPretty(boolean pretty) throws IOException;
/**
* Start comment inserts a <!-- in the stream, but allows the user to
* go on creating xml content as usual, with proper formatting applied etc.
* Any comments inserted inside a comment will be terminated with -- > instead of -->
* so the comment doesn't close prematurely.
* @throws IOException
*/
public abstract void startCommentBlock() throws IOException;
public abstract void endCommentBlock() throws IOException;
public abstract void escapedText(String content) throws IOException;
// this is only implemented by an implementation that is producing an xhtml representation, and is able to render elements as hyperlinks
public abstract void link(String href);
public abstract void anchor(String name);
public abstract void externalLink(String ref) throws IOException;
// this is only implemented by an implementation that is producing an xhtml representation and handles ellipsing elements
public abstract boolean canElide();
public abstract void elide() throws IOException;
public abstract void attributeElide();
}

View File

@ -13,7 +13,9 @@ Attempt_to_a_slice_an_element_that_does_not_repeat__from__in_ = Attempt to a sli
Attempt_to_replace_element_name_for_a_nonchoice_type=Attempt to replace element name for a non-choice type
Attempt_to_use_Terminology_server_when_no_Terminology_server_is_available = Attempt to use Terminology server when no Terminology server is available
Attempt_to_use_a_snapshot_on_profile__as__before_it_is_generated = Attempt to use a snapshot on profile ''{0}'' as {1} before it is generated
BINDING_ADDITIONAL = {0} specified in an additional binding
BINDING_ADDITIONAL_D = {0} specified in an additional binding
BINDING_ADDITIONAL_UC = {0} specified in an additional binding which applies because {1}
BINDING_ADDITIONAL_USAGE = {0} = {1}
BINDING_MAX = {0} specified in the max binding
BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE = Found {0} matches for ''{1}'' in the bundle ({2})
BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE_FRAGMENT = Found {0} matches for fragment {2} in resource ''{1}'' in the bundle ({3})
@ -1029,7 +1031,7 @@ VALUESET_SUPPLEMENT_MISSING_one = Required supplement not found: {1}
VALUESET_SUPPLEMENT_MISSING_other = Required supplements not found: {1}
VALUESET_TOO_COSTLY = The value set ''{0}'' expansion has too many codes to display ({1})
VALUESET_TOO_COSTLY_COUNT = The value set ''{0}'' expansion has {2} codes, which is too many to display ({1})
VALUESET_TOO_COSTLY_TIME = The value set ''{0}'' expansion took too long to process (>{1}sec)
VALUESET_TOO_COSTLY_TIME = The value set ''{0}'' {2} took too long to process (>{1}sec)
VALUESET_UNC_SYSTEM_WARNING = Unknown System ''{0}'' specified, so Concepts and Filters can''t be checked (Details: {1})
VALUESET_UNC_SYSTEM_WARNING_VER = Unknown System/Version ''{0}'' specified, so Concepts and Filters can''t be checked (Details: {1})
VALUESET_UNKNOWN_FILTER_PROPERTY = The property ''{0}'' is not known for the system ''{1}'', so may not be understood by the terminology ecosystem. Known properties for this system: {2}
@ -1138,6 +1140,12 @@ TYPE_SPECIFIC_CHECKS_DT_XHTML_LITERAL_HREF = Hyperlink scheme ''{3}'' in ''{0}''
SM_TARGET_TYPE_UNKNOWN = The type of the target variable is not known: {0}
XHTML_XHTML_ATTRIBUTE_XML_SPACE = The attribute 'xml:space' is legal but has a fixed value of 'preserve'. It''s use is discouraged
VALIDATION_HL7_PUBLISHER_MULTIPLE_WGS = This resource has more than workgroup extension (http://hl7.org/fhir/StructureDefinition/structuredefinition-wg)
NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG = Wrong Display Name ''{0}'' for {1}#{2}. There are no valid display names found for language(s) ''{3}''. Default display is ''{4}''
NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_OK = There are no valid display names found for the code {1}#{2} for language(s) ''{3}''. The display is ''{4}'' the default language display
NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR = Wrong Display Name ''{0}'' for {1}#{2}. There are no valid display names found for language(s) ''{3}''. Default display is ''{4}''
NO_VALID_DISPLAY_AT_ALL = Cannot validate display Name ''{0}'' for {1}#{2}: No displays are known
SD_BASE_EXPERIMENTAL = The definition builds on ''{0}'' which is experimental, but this definition is not labeled as experimental
SD_ED_EXPERIMENTAL_BINDING = The definition for the element ''{0}'' binds to the value set ''{1}'' which is experimental, but this structure is not labeled as experimental
VALIDATION_NO_EXPERIMENTAL_CONTENT = Experimental content is not allowed in this context
SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN = The Usage Context {0}#{1} is not recognised and may not be correct
SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT = The Usage Context {0}#{1} is a reference to an element that does not exist
SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE = The Usage Context value must be of type {1} not {0}

View File

@ -207,7 +207,8 @@ DATA_REND_COND = Condition
DATA_REND_COUNT = Count {0}
DATA_REND_DATA = Data: {0}
DATA_REND_DETAILS = (Details: {0} code
DATA_REND_DETAILS_STATED = (Details: {0} code {1} {2} {3} '', stated as '' {4} {5}
DATA_REND_DETAILS_STATED = (Details: {0} code {1} = ''{2}'')
DATA_REND_DETAILS_STATED_ND = (Details: {0} code {1})
DATA_REND_DICOM = DICOM
DATA_REND_DIM = Dimensions: {0}
DATA_REND_DURATION = Duration {0}

View File

@ -1,7 +1,7 @@
package org.hl7.fhir.utilities.npm;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -12,10 +12,12 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class FilesystemPackageManagerLockTests {
@ -49,13 +51,13 @@ public class FilesystemPackageManagerLockTests {
packageLock.doWriteWithLock(() -> {
assertThat(packageLock.getLockFile()).exists();
return null;
});
}, null);
assertThat(packageLock.getLockFile()).doesNotExist();
packageLock.doReadWithLock(() -> {
assertThat(packageLock.getLockFile()).doesNotExist();
return null;
});
}, null);
}
@Test void testNoPackageWriteOrReadWhileWholeCacheIsLocked() throws IOException, InterruptedException {
@ -87,7 +89,7 @@ public class FilesystemPackageManagerLockTests {
packageLock.doWriteWithLock(() -> {
assertThat(cacheLockFinished.get()).isTrue();
return null;
});
}, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -97,7 +99,7 @@ public class FilesystemPackageManagerLockTests {
packageLock.doReadWithLock(() -> {
assertThat(cacheLockFinished.get()).isTrue();
return null;
});
}, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -116,6 +118,23 @@ public class FilesystemPackageManagerLockTests {
}
}
@Test void testWhenLockIsntHeld_canLockFileBeHeldByThisProcessIsTrue() throws IOException {
File lockFile = getPackageLockFile();
lockFile.createNewFile();
Assertions.assertTrue(filesystemPackageCacheLockManager.getCacheLock().canLockFileBeHeldByThisProcess(lockFile));
}
@Test void testWhenLockIsHelp_canLockFileBeHeldByThisProcessIsFalse() throws InterruptedException, TimeoutException, IOException {
File lockFile = getPackageLockFile();
Thread lockThread = LockfileTestProcessUtility.lockWaitAndDeleteInNewProcess(cachePath, DUMMY_PACKAGE + ".lock", 2);
LockfileTestUtility.waitForLockfileCreation(cacheDirectory.getAbsolutePath(), DUMMY_PACKAGE + ".lock");
Assertions.assertFalse(filesystemPackageCacheLockManager.getCacheLock().canLockFileBeHeldByThisProcess(lockFile));
lockThread.join();
}
@Test void testSinglePackageWriteMultiPackageRead() throws IOException {
final FilesystemPackageCacheManagerLocks.PackageLock packageLock = filesystemPackageCacheLockManager.getPackageLock(DUMMY_PACKAGE);
AtomicInteger writeCounter = new AtomicInteger(0);
@ -133,7 +152,7 @@ public class FilesystemPackageManagerLockTests {
assertThat(writeCount).isEqualTo(1);
writeCounter.decrementAndGet();
return null;
});
}, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -156,7 +175,7 @@ public class FilesystemPackageManagerLockTests {
}
readCounter.decrementAndGet();
return null;
});
}, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -179,49 +198,47 @@ public class FilesystemPackageManagerLockTests {
}
@Test
public void testReadWhenLockedByFileTimesOut() throws IOException {
FilesystemPackageCacheManagerLocks shorterTimeoutManager = filesystemPackageCacheLockManager.withLockTimeout(3L, TimeUnit.SECONDS);
public void testReadWhenLockedByFileTimesOut() throws InterruptedException, TimeoutException, IOException {
FilesystemPackageCacheManagerLocks shorterTimeoutManager = filesystemPackageCacheLockManager;
final FilesystemPackageCacheManagerLocks.PackageLock packageLock = shorterTimeoutManager.getPackageLock(DUMMY_PACKAGE);
File lockFile = createPackageLockFile();
File lockFile = getPackageLockFile();
Thread lockThread = LockfileTestProcessUtility.lockWaitAndDeleteInNewProcess(cachePath, lockFile.getName(), 5);
LockfileTestUtility.waitForLockfileCreation(cachePath,lockFile.getName());
Exception exception = assertThrows(IOException.class, () -> {
packageLock.doReadWithLock(() -> {
assertThat(lockFile).exists();
return null;
});
}, new FilesystemPackageCacheManagerLocks.LockParameters(3L, TimeUnit.SECONDS));
});
assertThat(exception.getMessage()).contains("Error reading package");
assertThat(exception.getMessage()).contains("Package cache timed out waiting for lock");
assertThat(exception.getCause().getMessage()).contains("Timeout waiting for lock file deletion: " + lockFile.getName());
lockThread.join();
}
@Test
public void testReadWhenLockFileIsDeleted() throws IOException {
FilesystemPackageCacheManagerLocks shorterTimeoutManager = filesystemPackageCacheLockManager.withLockTimeout(5L, TimeUnit.SECONDS);
final FilesystemPackageCacheManagerLocks.PackageLock packageLock = shorterTimeoutManager.getPackageLock(DUMMY_PACKAGE);
File lockFile = createPackageLockFile();
Thread t = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
lockFile.delete();
});
t.start();
@Test
public void testReadWhenLockFileIsDeleted() throws InterruptedException, TimeoutException, IOException {
final FilesystemPackageCacheManagerLocks.PackageLock packageLock = filesystemPackageCacheLockManager.getPackageLock(DUMMY_PACKAGE);
final File lockFile = getPackageLockFile();
Thread lockThread = LockfileTestProcessUtility.lockWaitAndDeleteInNewProcess(cachePath, lockFile.getName(), 5);
LockfileTestUtility.waitForLockfileCreation(cachePath,lockFile.getName());
packageLock.doReadWithLock(() -> {
assertThat(lockFile).doesNotExist();
return null;
});
}, new FilesystemPackageCacheManagerLocks.LockParameters(10L, TimeUnit.SECONDS));
lockThread.join();
}
private File createPackageLockFile() throws IOException {
File lockFile = Path.of(cachePath, DUMMY_PACKAGE + ".lock").toFile();
TextFile.stringToFile("", lockFile);
return lockFile;
private File getPackageLockFile() {
return Path.of(cachePath, DUMMY_PACKAGE + ".lock").toFile();
}
}

View File

@ -2,6 +2,7 @@ package org.hl7.fhir.utilities.npm;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.File;
@ -13,6 +14,8 @@ import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
@ -20,6 +23,7 @@ import javax.annotation.Nonnull;
import org.hl7.fhir.utilities.IniFile;
import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledOnOs;
@ -113,6 +117,97 @@ public class FilesystemPackageManagerTests {
assertEquals( System.getenv("ProgramData") + "\\.fhir\\packages", folder.getAbsolutePath());
}
@Test
public void testCorruptPackageCleanup() throws IOException {
File cacheDirectory = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest"));
File dummyPackage = createDummyPackage(cacheDirectory, "example.fhir.uv.myig", "1.2.3");
File dummyLockFile = createDummyLockFile(cacheDirectory, "example.fhir.uv.myig" , "1.2.3");
assertThat(dummyPackage).isDirectory();
assertThat(dummyPackage).exists();
assertThat(dummyLockFile).exists();
FilesystemPackageCacheManager filesystemPackageCacheManager = new FilesystemPackageCacheManager.Builder().withCacheFolder(cacheDirectory.getAbsolutePath()).build();
assertThat(dummyPackage).doesNotExist();
assertThat(dummyLockFile).doesNotExist();
}
@Test
public void testLockedPackageIsntCleanedUp() throws IOException, InterruptedException, TimeoutException {
File cacheDirectory = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest"));
File dummyPackage = createDummyPackage(cacheDirectory, "example.fhir.uv.myig", "1.2.3");
Thread lockThread = LockfileTestProcessUtility.lockWaitAndDeleteInNewProcess(cacheDirectory.getAbsolutePath(), "example.fhir.uv.myig#1.2.3.lock", 2);
LockfileTestUtility.waitForLockfileCreation(cacheDirectory.getAbsolutePath(), "example.fhir.uv.myig#1.2.3.lock");
File dummyLockFile = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), "example.fhir.uv.myig#1.2.3.lock");
assertThat(dummyPackage).isDirectory();
assertThat(dummyPackage).exists();
assertThat(dummyLockFile).exists();
FilesystemPackageCacheManager filesystemPackageCacheManager = new FilesystemPackageCacheManager.Builder().withCacheFolder(cacheDirectory.getAbsolutePath()).build();
assertThat(dummyPackage).exists();
assertThat(dummyLockFile).exists();
lockThread.join();
}
@Test
public void testTimeoutForLockedPackageRead() throws IOException, InterruptedException, TimeoutException {
String pcmPath = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest")).getAbsolutePath();
final FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder()
.withCacheFolder(pcmPath)
.withLockParameters(new FilesystemPackageCacheManagerLocks.LockParameters(5,TimeUnit.SECONDS))
.build();
Assertions.assertTrue(pcm.listPackages().isEmpty());
Thread lockThread = LockfileTestProcessUtility.lockWaitAndDeleteInNewProcess(pcmPath, "example.fhir.uv.myig#1.2.3.lock", 10);
File directory = ManagedFileAccess.file(pcmPath, "example.fhir.uv.myig#1.2.3" );
directory.mkdir();
LockfileTestUtility.waitForLockfileCreation(pcmPath, "example.fhir.uv.myig#1.2.3.lock");
IOException exception = assertThrows(IOException.class, () -> pcm.loadPackageFromCacheOnly("example.fhir.uv.myig", "1.2.3"));
assertThat(exception.getMessage()).contains("Package cache timed out waiting for lock");
assertThat(exception.getCause().getMessage()).contains("Timeout waiting for lock file deletion");
lockThread.join();
}
@Test
public void testReadFromCacheOnlyWaitsForLockDelete() throws IOException, InterruptedException, TimeoutException {
String pcmPath = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest")).getAbsolutePath();
final FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder().withCacheFolder(pcmPath).build();
Assertions.assertTrue(pcm.listPackages().isEmpty());
pcm.addPackageToCache("example.fhir.uv.myig", "1.2.3", this.getClass().getResourceAsStream("/npm/dummy-package.tgz"), "https://packages.fhir.org/example.fhir.uv.myig/1.2.3");
String packageAndVersion = "example.fhir.uv.myig#1.2.3";
//Now sneak in a new lock file and directory:
File directory = ManagedFileAccess.file(pcmPath, packageAndVersion);
directory.mkdir();
Thread lockThread = LockfileTestProcessUtility.lockWaitAndDeleteInNewProcess(pcmPath, "example.fhir.uv.myig#1.2.3.lock", 5);
LockfileTestUtility.waitForLockfileCreation(pcmPath, "example.fhir.uv.myig#1.2.3.lock");
NpmPackage npmPackage = pcm.loadPackageFromCacheOnly("example.fhir.uv.myig", "1.2.3");
assertThat(npmPackage.id()).isEqualTo("example.fhir.uv.myig");
lockThread.join();
}
/**
We repeat the same tests multiple times here, in order to catch very rare edge cases.
*/
@ -126,16 +221,16 @@ public class FilesystemPackageManagerTests {
return params.stream();
}
private void createDummyTemp(File cacheDirectory, String lowerCase) throws IOException {
createDummyPackage(cacheDirectory, lowerCase);
private File createDummyTemp(File cacheDirectory, String lowerCase) throws IOException {
return createDummyPackage(cacheDirectory, lowerCase);
}
private void createDummyPackage(File cacheDirectory, String packageName, String packageVersion) throws IOException {
private File createDummyPackage(File cacheDirectory, String packageName, String packageVersion) throws IOException {
String directoryName = packageName + "#" + packageVersion;
createDummyPackage(cacheDirectory, directoryName);
return createDummyPackage(cacheDirectory, directoryName);
}
private static void createDummyPackage(File cacheDirectory, String directoryName) throws IOException {
private static File createDummyPackage(File cacheDirectory, String directoryName) throws IOException {
File packageDirectory = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), directoryName);
packageDirectory.mkdirs();
@ -144,6 +239,16 @@ public class FilesystemPackageManagerTests {
wr.write("Ain't nobody here but us chickens");
wr.flush();
wr.close();
return packageDirectory;
}
private File createDummyLockFile(File cacheDirectory, String packageName, String packageVersion) throws IOException {
final File dummyLockFile = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), packageName + "#" + packageVersion + ".lock");
final FileWriter wr = new FileWriter(dummyLockFile);
wr.write("Ain't nobody here but us chickens");
wr.flush();
wr.close();
return dummyLockFile;
}
private void assertThatDummyTempExists(File cacheDirectory, String dummyTempPackage) throws IOException {
@ -241,13 +346,13 @@ public class FilesystemPackageManagerTests {
@MethodSource("packageCacheMultiThreadTestParams")
@ParameterizedTest
public void packageCacheMultiThreadTest(final int threadTotal, final int packageCacheManagerTotal) throws IOException {
String pcmPath = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest")).getAbsolutePath();
System.out.println("Using temp pcm path: " + pcmPath);
FilesystemPackageCacheManager[] packageCacheManagers = new FilesystemPackageCacheManager[packageCacheManagerTotal];
Random rand = new Random();
final AtomicInteger totalSuccessful = new AtomicInteger();
final ConcurrentHashMap successfulThreads = new ConcurrentHashMap();
final ConcurrentHashMap<Long, Integer> successfulThreads = new ConcurrentHashMap<>();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadTotal; i++) {
final int index = i;
@ -256,22 +361,27 @@ public class FilesystemPackageManagerTests {
System.out.println("Thread #" + index + ": " + Thread.currentThread().getId() + " started");
final int randomPCM = rand.nextInt(packageCacheManagerTotal);
final int randomOperation = rand.nextInt(4);
final String operationName;
if (packageCacheManagers[randomPCM] == null) {
packageCacheManagers[randomPCM] = new FilesystemPackageCacheManager.Builder().withCacheFolder(pcmPath).build();
}
FilesystemPackageCacheManager pcm = packageCacheManagers[randomPCM];
if (randomOperation == 0) {
operationName = "addPackageToCache";
pcm.addPackageToCache("example.fhir.uv.myig", "1.2.3", this.getClass().getResourceAsStream("/npm/dummy-package.tgz"), "https://packages.fhir.org/example.fhir.uv.myig/1.2.3");
} else if (randomOperation == 1) {
operationName = "clear";
pcm.clear();
} else if (randomOperation == 2) {
operationName = "loadPackageFromCacheOnly";
pcm.loadPackageFromCacheOnly("example.fhir.uv.myig", "1.2.3");
} else {
operationName = "removePackage";
pcm.removePackage("example.fhir.uv.myig", "1.2.3");
}
totalSuccessful.incrementAndGet();
successfulThreads.put(Thread.currentThread().getId(), index);
System.out.println("Thread #" + index + ": " + Thread.currentThread().getId() + " completed");
System.out.println("Thread #" + index + ": " + Thread.currentThread().getId() + " completed. Ran: " + operationName);
} catch (Exception e) {
e.printStackTrace();
System.err.println("Thread #" + index + ": " + Thread.currentThread().getId() + " failed");

View File

@ -0,0 +1,122 @@
package org.hl7.fhir.utilities.npm;
import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
/**
* FilesystemPackageCacheManagerLocks relies on the existence of .lock files to prevent access to packages being written
* by processes outside the current JVM. Testing this functionality means creating a process outside the JUnit test JVM,
* which is achieved by running a separate Java process.
* <p/>
* Intended usage:
* <p/>
* The helper method {@link #lockWaitAndDeleteInNewProcess(String, String, int)} is the intended starting point for
* using this class.
* <p/>
*
*
* This class deliberately avoids using any dependencies outside java.*, which avoids having to construct a classpath
* for the separate process.
*/
public class LockfileTestProcessUtility {
/**
* Main method to allow running this class.
* <p/
* It is not recommended to call this method directly. Instead, use the provided {@link #lockWaitAndDeleteInNewProcess(String, String, int)} method.
*
* @param args The arguments to the main method. The first argument is the path to create the lockfile in, the second
* argument is the name of the lockfile, and the third argument is the number of seconds to wait before
* deleting the lockfile.
*/
public static void main(String[] args) {
String path = args[0];
String lockFileName = args[1];
int seconds = Integer.parseInt(args[2]);
try {
lockWaitAndDelete(path, lockFileName, seconds);
} catch (InterruptedException | IOException e) {
throw new RuntimeException(e);
}
}
/**
* Static helper method that starts a new process, creates a lock file in the path and waits for a specified number of
* seconds before deleting it.
* <p/>
* This method calls the {@link #main(String[])} method in a new process.
*
* @param path The path to create the lockfile in
* @param lockFileName The name of the lockfile
* @param seconds The number of seconds to wait before deleting the lockfile
* @return The thread wrapping the process execution. This can be used to wait for the process to complete, so that
* System.out and System.err can be processed before tests return results.
*/
public static Thread lockWaitAndDeleteInNewProcess(String path, String lockFileName, int seconds) {
Thread t = new Thread(() -> {
ProcessBuilder processBuilder = new ProcessBuilder("java", "-cp", "target/test-classes", LockfileTestProcessUtility.class.getName(), path, lockFileName, Integer.toString(seconds));
try {
Process process = processBuilder.start();
process.getErrorStream().transferTo(System.err);
process.getInputStream().transferTo(System.out);
process.waitFor();
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
});
t.start();
return t;
}
/**
* The actual logic to create a .lock file.
* <p/>
* This should match the logic in FilesystemPackageCacheManagerLocks
* <p/>
*
* @param path The path to create the lockfile in
* @param lockFileName The name of the lockfile
* @param seconds The number of seconds to wait before deleting the lockfile
* @throws InterruptedException If the thread is interrupted while waiting
* @throws IOException If there is an error accessing the file system
*/
/* TODO Eventually, this logic should exist in a Lockfile class so that it isn't duplicated between the main code and
the test code.
*/
private static void lockWaitAndDelete(String path, String lockFileName, int seconds) throws InterruptedException, IOException {
File lockFile = Paths.get(path,lockFileName).toFile();
try (FileChannel channel = new RandomAccessFile(lockFile.getAbsolutePath(), "rw").getChannel()) {
FileLock fileLock = channel.tryLock(0, Long.MAX_VALUE, false);
if (fileLock != null) {
final ByteBuffer buff = ByteBuffer.wrap("Hello world".getBytes(StandardCharsets.UTF_8));
channel.write(buff);
System.out.println("File "+lockFileName+" is locked. Waiting for " + seconds + " seconds to release. ");
Thread.sleep(seconds * 1000L);
lockFile.renameTo(ManagedFileAccess.file(File.createTempFile(lockFile.getName(), ".lock-renamed").getAbsolutePath()));
fileLock.release();
channel.close();
System.out.println(System.currentTimeMillis());
System.out.println("File "+lockFileName+" is released.");
lockFile.delete();
}}finally {
if (lockFile.exists()) {
lockFile.delete();
}
}
}
}

View File

@ -0,0 +1,78 @@
package org.hl7.fhir.utilities.npm;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import java.io.*;
import java.nio.file.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class LockfileTestUtility {
/**
* Wait for the lock file to be created in the given path.
* <p/>
* Normally, within the same JVM, you could use a CountdownLatch for the same purpose, but since this the lock file is
* being created in a separate process, we need to use a mechanism that doesn't rely on shared threads.
*
* @param path The path containing the lock file
* @param lockFileName The name of the lock file
* @throws InterruptedException If the thread is interrupted while waiting
* @throws TimeoutException If the lock file is not created within 10 seconds
*/
public static void waitForLockfileCreation(String path, String lockFileName) throws InterruptedException, TimeoutException {
CountDownLatch latch = new CountDownLatch(1);
FileAlterationMonitor monitor = new FileAlterationMonitor(100);
FileAlterationObserver observer = new FileAlterationObserver(path);
observer.addListener(new FileAlterationListenerAdaptor(){
@Override
public void onStart(FileAlterationObserver observer) {
if (Files.exists(Paths.get(path, lockFileName))) {
latch.countDown();
}
}
@Override
public void onFileCreate(File file) {
System.out.println("File created: " + file.getName());
latch.countDown();
}
});
monitor.addObserver(observer);
try {
monitor.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
boolean success = latch.await(10, TimeUnit.SECONDS);
try {
monitor.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!success) {
throw new TimeoutException("Timed out waiting for lock file creation: " + lockFileName);
}
// TODO This is a workaround for an edge condition that shows up with testing, where the lock is not reflected in
// the file system immediately. It is unlikely to appear in production environments. Should it occur, it will
// result in a lock file being erroneously reported as not having an owning process, and will cause a package to
// fail to be loaded from that cache until the lock is cleaned up by cache initialization.
Thread.sleep(100);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.30-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>6.3.26-SNAPSHOT</version>
<version>6.3.30-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -1192,6 +1192,34 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
protected int countFragmentMatches(Element element, String fragment, NodeStack stack) {
int count = countFragmentMatches(element, fragment);
if (count == 0 && element.isResource() && element.hasParentForValidator()) {
Element bnd = getElementBundle(element);
if (bnd != null) {
// in this case, we look into the parent - if there is one - and if it's a bundle, we look at the entries (but not in them)
for (Element be : bnd.getChildrenByName("entry")) {
String id = be.getIdBase();
if (fragment.equals(id)) {
count++;
}
}
}
}
return count;
}
private Element getElementBundle(Element element) {
Element p = element.getParentForValidator();
if (p != null) {
Element b = p.getParentForValidator();
if (b != null && b.fhirType().equals("Bundle")) {
return b;
}
}
return null;
}
protected int countFragmentMatches(Element element, String fragment) {
int count = 0;
if (fragment.equals(element.getIdBase())) {

View File

@ -227,6 +227,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
@Getter @Setter private boolean checkIPSCodes;
@Getter @Setter private BestPracticeWarningLevel bestPracticeLevel;
@Getter @Setter private boolean unknownCodeSystemsCauseErrors;
@Getter @Setter private boolean noExperimentalContent;
@Getter @Setter private Locale locale;
@Getter @Setter private List<ImplementationGuide> igs = new ArrayList<>();
@Getter @Setter private List<String> extensionDomains = new ArrayList<>();
@ -909,6 +910,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
validator.setPolicyAdvisor(policyAdvisor);
}
validator.setUnknownCodeSystemsCauseErrors(unknownCodeSystemsCauseErrors);
validator.setNoExperimentalContent(noExperimentalContent);
return validator;
}

View File

@ -130,7 +130,7 @@ public class CliContext {
private boolean showTimes = false;
@JsonProperty("locale")
private String locale = Locale.ENGLISH.getDisplayLanguage();
private String locale = Locale.ENGLISH.toLanguageTag();
@JsonProperty("locations")
private Map<String, String> locations = new HashMap<String, String>();
@ -165,6 +165,9 @@ public class CliContext {
@JsonProperty("unknownCodeSystemsCauseErrors")
private boolean unknownCodeSystemsCauseErrors;
@JsonProperty("noExperimentalContent")
private boolean noExperimentalContent;
@JsonProperty("baseEngine")
public String getBaseEngine() {
@ -836,6 +839,7 @@ public class CliContext {
Objects.equals(bestPracticeLevel, that.bestPracticeLevel) &&
Objects.equals(watchScanDelay, that.watchScanDelay) &&
Objects.equals(unknownCodeSystemsCauseErrors, that.unknownCodeSystemsCauseErrors) &&
Objects.equals(noExperimentalContent, that.noExperimentalContent) &&
Objects.equals(watchSettleTime, that.watchSettleTime) ;
}
@ -844,7 +848,7 @@ public class CliContext {
return Objects.hash(baseEngine, doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching,
noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT,
targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars,
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, noExperimentalContent, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
}
@Override
@ -904,6 +908,7 @@ public class CliContext {
", watchSettleTime=" + watchSettleTime +
", watchScanDelay=" + watchScanDelay +
", unknownCodeSystemsCauseErrors=" + unknownCodeSystemsCauseErrors +
", noExperimentalContent=" + noExperimentalContent +
'}';
}
@ -973,5 +978,16 @@ public class CliContext {
this.unknownCodeSystemsCauseErrors = unknownCodeSystemsCauseErrors;
}
@JsonProperty("noExperimentalContent")
public boolean isNoExperimentalContent() {
return noExperimentalContent;
}
@JsonProperty("noExperimentalContent")
public void setNoExperimentalContent(boolean noExperimentalContent) {
this.noExperimentalContent = noExperimentalContent;
}
}

View File

@ -529,14 +529,18 @@ public class ValidationService {
}
@Nonnull
protected ValidationEngine buildValidationEngine( CliContext cliContext, String definitions, TimeTracker timeTracker) throws IOException, URISyntaxException {
protected ValidationEngine buildValidationEngine(CliContext cliContext, String definitions, TimeTracker timeTracker) throws IOException, URISyntaxException {
System.out.print(" Load FHIR v" + cliContext.getSv() + " from " + definitions);
ValidationEngine validationEngine = getValidationEngineBuilder().withTHO(false).withVersion(cliContext.getSv()).withTimeTracker(timeTracker).withUserAgent(Common.getValidatorUserAgent()).fromSource(definitions);
System.out.println(" - " + validationEngine.getContext().countAllCaches() + " resources (" + timeTracker.milestone() + ")");
loadIgsAndExtensions(validationEngine, cliContext, timeTracker);
if (validationEngine.getContext().getTxCache() == null) {
if (cliContext.getTxCache() != null) {
TerminologyCache cache = new TerminologyCache(new Object(), cliContext.getTxCache());
validationEngine.getContext().initTxCache(cache);
}
if (validationEngine.getContext().getTxCache() == null || validationEngine.getContext().getTxCache().getFolder() == null) {
System.out.println(" No Terminology Cache");
} else {
System.out.println(" Terminology Cache at "+validationEngine.getContext().getTxCache().getFolder());
@ -582,6 +586,7 @@ public class ValidationService {
validationEngine.getBundleValidationRules().addAll(cliContext.getBundleValidationRules());
validationEngine.setJurisdiction(CodeSystemUtilities.readCoding(cliContext.getJurisdiction()));
validationEngine.setUnknownCodeSystemsCauseErrors(cliContext.isUnknownCodeSystemsCauseErrors());
validationEngine.setNoExperimentalContent(cliContext.isNoExperimentalContent());
TerminologyCache.setNoCaching(cliContext.isNoInternalCaching());
validationEngine.prepare(); // generate any missing snapshots
System.out.println(" go (" + timeTracker.milestone() + ")");

View File

@ -94,7 +94,8 @@ public class Params {
public static final String DISABLE_DEFAULT_RESOURCE_FETCHER = "-disable-default-resource-fetcher";
public static final String CHECK_IPS_CODES = "-check-ips-codes";
public static final String BEST_PRACTICE = "-best-practice";
public static final String UNKNOWN_CODESYSTEMS_CAUSE_ERROR = "-unknown-codesystems-cause-errors";
public static final String UNKNOWN_CODESYSTEMS_CAUSE_ERROR = "-unknown-codesystems-cause-errors";
public static final String NO_EXPERIMENTAL_CONTENT = "-no-experimental-content";
public static final String RUN_TESTS = "-run-tests";
@ -321,6 +322,8 @@ public class Params {
cliContext.setForPublication(true);
} else if (args[i].equals(UNKNOWN_CODESYSTEMS_CAUSE_ERROR)) {
cliContext.setUnknownCodeSystemsCauseErrors(true);
} else if (args[i].equals(NO_EXPERIMENTAL_CONTENT)) {
cliContext.setNoExperimentalContent(true);
} else if (args[i].equals(VERBOSE)) {
cliContext.setCrumbTrails(true);
} else if (args[i].equals(ALLOW_EXAMPLE_URLS)) {

View File

@ -217,6 +217,7 @@ import org.hl7.fhir.validation.instance.type.ValueSetValidator;
import org.hl7.fhir.validation.instance.utils.*;
import org.w3c.dom.Document;
/**
* Thinking of using this in a java program? Don't!
* You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine
@ -599,6 +600,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private IDigitalSignatureServices signatureServices;
private ContextUtilities cu;
private boolean unknownCodeSystemsCauseErrors;
private boolean noExperimentalContent;
public InstanceValidator(@Nonnull IWorkerContext theContext, @Nonnull IEvaluationContext hostServices, @Nonnull XVerExtensionManager xverManager) {
super(theContext, xverManager, false);
@ -1355,19 +1357,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength();
Extension maxVS = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, maxVS, true);
checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, maxVS, true, null);
// } else if (binding.hasValueSet()) {
// hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK);
} else if (!noBindingMsgSuppressed) {
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path);
}
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) {
StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, null, false) && checkDisp;
checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, null, false, b.toString()) && checkDisp;
}
}
} catch (CheckCodeOnServerException e) {
@ -1393,25 +1396,186 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return checkDisp;
}
private boolean isInScope(ElementDefinitionBindingAdditionalComponent ab) {
private boolean isInScope(ElementDefinitionBindingAdditionalComponent ab, StructureDefinition profile, Element resource, StringBuilder b) {
if (ab.getUsage().isEmpty()) {
return true;
}
boolean ok = true;
for (UsageContext usage : ab.getUsage()) {
if (isInScope(usage)) {
if (!isInScope(usage, profile, resource, b)) {
ok = false;
}
}
return ok;
}
private boolean isInScope(UsageContext usage, StructureDefinition profile, Element resource, StringBuilder b) {
if (isKnownUsage(usage)) {
return true;
}
if (usage.getCode().hasSystem() && (usage.getCode().getSystem().equals(profile.getUrl()) || usage.getCode().getSystem().equals(profile.getVersionedUrl()))) {
// if it's not a defined usage from external sources, it might match something in the data content
List<Element> items = findDataValue(resource, usage.getCode().getCode());
if (matchesUsage(items, usage.getValue())) {
b.append(context.formatMessage(I18nConstants.BINDING_ADDITIONAL_USAGE, displayCoding(usage.getCode()), display(usage.getValue())));
return true;
}
}
return ab.getUsage().isEmpty();
return false;
}
private boolean isInScope(UsageContext usage) {
if (isKnownUsage(usage)) {
private String displayCoding(Coding value) {
return value.getCode();
}
private String displayCodeableConcept(CodeableConcept value) {
for (Coding c : value.getCoding()) {
String s = displayCoding(c);
if (s != null) {
return s;
}
}
return value.getText();
}
private String display(DataType value) {
switch (value.fhirType()) {
case "Coding" : return displayCoding((Coding) value);
case "CodeableConcept" : return displayCodeableConcept((CodeableConcept) value);
}
return value.fhirType();
}
private boolean matchesUsage(List<Element> items, DataType value) {
for (Element item : items) {
if (matchesUsage(item, value)) {
return true;
}
}
return false;
}
private String display(List<Element> items) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (Element item : items) {
display(b, item);
}
return b.toString();
}
private void display(CommaSeparatedStringBuilder b, Element item) {
if (item.isPrimitive()) {
b.append(item.primitiveValue());
} else if (item.fhirType().equals("CodeableConcept")) {
for (Element c : item.getChildren("coding")) {
b.append(c.getNamedChildValue("code"));
}
} else {
b.append(item.toString());
}
}
private boolean matchesUsage(Element item, DataType value) {
switch (value.fhirType()) {
case "CodeableConcept": return matchesUsageCodeableConcept(item, (CodeableConcept) value);
case "Quantity": return false;
case "Range": return false;
case "Reference": return false;
default: return false;
}
}
private boolean matchesUsageCodeableConcept(Element item, CodeableConcept value) {
switch (item.fhirType()) {
case "CodeableConcept": return matchesUsageCodeableConceptCodeableConcept(item, value);
case "Coding": return matchesUsageCodeableConceptCoding(item, value);
default: return false;
}
}
private boolean matchesUsageCodeableConceptCoding(Element item, CodeableConcept value) {
String system = item.getNamedChildValue("system");
String version = item.getNamedChildValue("version");
String code = item.getNamedChildValue("code");
for (Coding c : value.getCoding()) {
if (system == null || !system.equals(c.getSystem())) {
return false;
}
if (code == null || !code.equals(c.getCode())) {
return false;
}
if (c.hasVersion()) {
if (version == null || !version.equals(c.getVersion())) {
return false;
}
}
return true;
}
return false;
}
private boolean matchesUsageCodeableConceptCodeableConcept(Element item, CodeableConcept value) {
for (Element code : item.getChildren("coding")) {
if (matchesUsageCodeableConceptCoding(code, value)) {
return true;
}
}
return false;
}
private List<Element> findDataValue(Element resource, String code) {
List<Element> items = new ArrayList<Element>();
if (resource != null) {
findDataValues(items, resource, code);
}
return items;
}
private void findDataValues(List<Element> items, Element element, String path) {
if (element.getPath() == null) {
return;
}
if (pathMatches(element.getPath(), path)) {
items.add(element);
} else if (element.hasChildren() && path.startsWith(element.getPath())) {
for (Element child : element.getChildren()) {
findDataValues(items, child, path);
}
}
}
private boolean pathMatches(String actualPath, String pathSpec) {
String[] ap = actualPath.split("\\.");
String[] ps = pathSpec.split("\\.");
if (ap.length != ps.length) {
return false;
}
for (int i = 0; i < ap.length; i++) {
if (!pathSegmentMatches(ap[i], ps[i])) {
return false;
}
}
return true;
}
private boolean pathSegmentMatches(String ap, String ps) {
if (ps.contains("[")) {
return ap.equals(ps);
} else {
if (ap.contains("[")) {
ap = ap.substring(0, ap.indexOf("["));
}
return ap.equals(ps);
}
}
private BindingStrength convertPurposeToStrength(AdditionalBindingPurposeVS purpose) {
switch (purpose) {
case MAXIMUM: return BindingStrength.REQUIRED;
case EXTENSIBLE: return BindingStrength.EXTENSIBLE;
case PREFERRED: return BindingStrength.PREFERRED;
case REQUIRED: return BindingStrength.REQUIRED;
default: return null;
@ -1423,7 +1587,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private boolean validateBindingCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, NodeStack stack, BooleanHolder bh, boolean checkDisp, BooleanHolder checked,
CodeableConcept cc, String vsRef, ValueSet valueset, BindingStrength strength, Extension maxVS, boolean base) throws CheckCodeOnServerException {
CodeableConcept cc, String vsRef, ValueSet valueset, BindingStrength strength, Extension maxVS, boolean base, String usageNote) throws CheckCodeOnServerException {
if (valueset == null) {
CodeSystem cs = context.fetchCodeSystem(vsRef);
if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) {
@ -1435,12 +1599,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingContext bc = base ? BindingContext.BASE : BindingContext.ADDITIONAL;
if (!cc.hasCoding()) {
if (strength == BindingStrength.REQUIRED)
bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(vsRef, valueset, bc)));
bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(vsRef, valueset, bc, usageNote)));
else if (strength == BindingStrength.EXTENSIBLE) {
if (maxVS != null)
bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringFromExtension(maxVS)), valueset.getVersionedUrl()));
else if (!noExtensibleWarnings) {
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(vsRef, valueset, bc));
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(vsRef, valueset, bc, usageNote));
}
}
} else {
@ -1490,17 +1654,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
} else if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
// we've already handled the warnings / errors about this, and set the status correctly. We don't need to do anything more?
} else {
} else if (vr.getErrorClass() != TerminologyServiceErrorClass.SERVER_ERROR) { // (should have?) already handled server error
if (strength == BindingStrength.REQUIRED) {
bh.see(txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(vsRef, valueset, bc), ccSummary(cc)));
bh.see(txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(vsRef, valueset, bc, usageNote), ccSummary(cc)));
} else if (strength == BindingStrength.EXTENSIBLE) {
if (maxVS != null)
bh.see(checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(maxVS), cc, stack));
if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(vsRef, valueset, bc), ccSummary(cc));
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(vsRef, valueset, bc, usageNote), ccSummary(cc));
} else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(vsRef, valueset, bc), ccSummary(cc));
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(vsRef, valueset, bc, usageNote), ccSummary(cc));
}
}
}
@ -1647,7 +1811,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength();
Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, vsMax, true);
validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, vsMax, true, null);
// special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding.
if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) {
@ -1659,11 +1823,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path);
}
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) {
StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, null, false);
validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, null, false, b.toString());
}
}
}
@ -1697,18 +1862,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength();
Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, vsMax, true);
ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, vsMax, true, null);
} else if (binding.hasValueSet()) {
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK);
} else if (!inCodeableConcept && !noBindingMsgSuppressed) {
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path);
}
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) {
StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, null, true) && ok;
ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, null, true, b.toString()) && ok;
}
}
}
@ -1727,7 +1893,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean validateBindingTerminologyCoding(List<ValidationMessage> errors, String path, Element element,
StructureDefinition profile, NodeStack stack, boolean ok, Coding c, String code, String system, String display,
String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base) {
String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base, String usageNote) {
if (valueset == null) {
CodeSystem cs = context.fetchCodeSystem(vsRef);
if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) {
@ -1753,27 +1919,27 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, system+"#"+code);
else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
if (strength == BindingStrength.REQUIRED)
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc), vr.getMessage(), system+"#"+code);
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc, usageNote), vr.getMessage(), system+"#"+code);
else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null)
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack);
else if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc));
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc, usageNote));
} else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc));
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc, usageNote));
}
}
} else if (strength == BindingStrength.REQUIRED)
ok= txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_4, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code) && ok;
ok= txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_4, describeReference(vsRef, valueset, bc, usageNote), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code) && ok;
else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null)
ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok;
else
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_5, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code);
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_5, describeReference(vsRef, valueset, bc, usageNote), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code);
} else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_6, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code);
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_6, describeReference(vsRef, valueset, bc, usageNote), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code);
}
}
} else if (vr != null && vr.getMessage() != null){
@ -1895,9 +2061,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+cc.toString());
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), vr.getMessage());
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), vr.getMessage());
else
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), ccSummary(cc)) && ok;
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), ccSummary(cc)) && ok;
}
} catch (CheckCodeOnServerException e) {
if (STACK_TRACE) e.getCause().printStackTrace();
@ -1935,9 +2101,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), vr.getMessage(), c.getSystem()+"#"+c.getCode());
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), vr.getMessage(), c.getSystem()+"#"+c.getCode());
else
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), c.getSystem(), c.getCode()) && ok;
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), c.getSystem(), c.getCode()) && ok;
}
} catch (Exception e) {
if (STACK_TRACE) e.printStackTrace();
@ -1965,9 +2131,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+value);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.BASE), vr.getMessage(), value);
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.BASE, null), vr.getMessage(), value);
else {
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl, valueset, BindingContext.BASE), vr.getMessage()) && ok;
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl, valueset, BindingContext.BASE, null), vr.getMessage()) && ok;
}
}
} catch (Exception e) {
@ -2028,7 +2194,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength();
Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, vsMax, true);
ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, vsMax, true, null);
// } else if (binding.hasValueSet()) {
// hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK);
@ -2037,12 +2203,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) {
StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, null, false) && ok;
ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, null, false, b.toString()) && ok;
}
}
} catch (Exception e) {
@ -2067,9 +2234,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok;
}
private Element getResource(NodeStack stack) {
if (stack.getElement().isResource()) {
return stack.getElement();
}
if (stack.getParent() == null) {
return null;
}
return getResource(stack.getParent());
}
private boolean validateBindingCodedElement(List<ValidationMessage> errors, String path, Element element,
StructureDefinition profile, NodeStack stack, String theCode, String theSystem, boolean ok, BooleanHolder checked,
Coding c, String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base) {
Coding c, String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base, String usageNote) {
if (valueset == null) {
CodeSystem cs = context.fetchCodeSystem(vsRef);
if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) {
@ -2097,28 +2274,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, theSystem+"#"+theCode);
else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) {
if (strength == BindingStrength.REQUIRED)
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc), vr.getMessage(), theSystem+"#"+theCode) && ok;
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc, usageNote), vr.getMessage(), theSystem+"#"+theCode) && ok;
else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null)
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack);
else if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc), theSystem+"#"+theCode);
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc, usageNote), theSystem+"#"+theCode);
} else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc), theSystem+"#"+theCode);
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc, usageNote), theSystem+"#"+theCode);
}
}
} else if (strength == BindingStrength.REQUIRED)
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_12, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode) && ok;
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_12, describeReference(vsRef, valueset, bc, usageNote), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode) && ok;
else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null)
ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok;
else if (!noExtensibleWarnings) {
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_13, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), c.getSystem()+"#"+c.getCode());
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_13, describeReference(vsRef, valueset, bc, usageNote), getErrorMessage(vr.getMessage()), c.getSystem()+"#"+c.getCode());
}
} else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_14, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode);
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_14, describeReference(vsRef, valueset, bc, usageNote), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode);
}
}
} else if (vr != null && vr.getMessage() != null) {
@ -3016,7 +3193,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// check that no illegal elements and attributes have been used
ok = checkInnerNames(errors, e, path, xhtml.getChildNodes(), false) && ok;
ok = checkUrls(errors, e, path, xhtml.getChildNodes()) && ok;
ok = checkIdRefs(errors, e, path, xhtml, resource) && ok;
ok = checkIdRefs(errors, e, path, xhtml, resource, node) && ok;
if (true) {
ok = checkReferences(valContext, errors, e, path, "div", xhtml, resource) && ok;
}
@ -3465,11 +3642,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok;
}
private boolean checkIdRefs(List<ValidationMessage> errors, Element e, String path, XhtmlNode node, Element resource) {
private boolean checkIdRefs(List<ValidationMessage> errors, Element e, String path, XhtmlNode node, Element resource, NodeStack stack) {
boolean ok = true;
if (node.getNodeType() == NodeType.Element && node.getAttribute("idref") != null) {
String idref = node.getAttribute("idref");
int count = countFragmentMatches(resource, idref);
int count = countFragmentMatches(resource, idref, stack);
if (count == 0) {
ok = warning(errors, "2023-12-01", IssueType.INVALID, e.line(), e.col(), path, idref == null, I18nConstants.XHTML_IDREF_NOT_FOUND, idref) && ok;
} else if (count > 1) {
@ -3478,7 +3655,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
if (node.hasChildren()) {
for (XhtmlNode child : node.getChildNodes()) {
checkIdRefs(errors, e, path, child, resource);
checkIdRefs(errors, e, path, child, resource, stack);
}
}
return ok;
@ -3562,15 +3739,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
} else if (binding.getStrength() == BindingStrength.REQUIRED) {
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage())) && ok;
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE, null), getErrorMessage(vr.getMessage())) && ok;
} else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET))
ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), value, stack) && ok;
else if (!noExtensibleWarnings && !isOkExtension(value, vs))
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage()));
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE, null), getErrorMessage(vr.getMessage()));
} else if (binding.getStrength() == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage()));
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE, null), getErrorMessage(vr.getMessage()));
}
}
} else if (vr != null && vr.getMessage() != null){
@ -4278,7 +4455,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return false;
}
private String describeReference(String reference, CanonicalResource target, BindingContext ctxt) {
private String describeReference(String reference, CanonicalResource target, BindingContext ctxt, String usageNote) {
if (reference == null && target == null)
return "null";
String res = null;
@ -4296,7 +4473,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
switch (ctxt) {
case ADDITIONAL: return context.formatMessage(I18nConstants.BINDING_ADDITIONAL, res);
case ADDITIONAL: return context.formatMessage(Utilities.noString(usageNote) ? I18nConstants.BINDING_ADDITIONAL_D : I18nConstants.BINDING_ADDITIONAL_UC, res, usageNote);
case MAXVS: return context.formatMessage(I18nConstants.BINDING_MAX, res);
default: return res;
}
@ -5718,6 +5895,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
hint(errors, "2023-08-14", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), statusCodesDeeplyConsistent(status, standardsStatus), I18nConstants.VALIDATION_VAL_STATUS_INCONSISTENT_HINT, status, standardsStatus);
}
}
if (noExperimentalContent) {
String exp = element.getNamedChildValue("experimental");
ok = rule(errors, "2024-09-17", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), !"true".equals(exp), I18nConstants.VALIDATION_NO_EXPERIMENTAL_CONTENT) && ok;
}
if (isHL7Core(element) && !isExample()) {
ok = checkPublisherConsistency(valContext, errors, element, stack, contained) && ok;
@ -5886,20 +6067,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
Element div = text.getNamedChild("div", false);
if (lang != null && div != null) {
XhtmlNode xhtml = div.getXhtml();
String l = xhtml.getAttribute("lang");
String xl = xhtml.getAttribute("xml:lang");
if (l == null && xl == null) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING1);
} else {
if (l == null) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING2);
} else if (!l.equals(lang)) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_DIFFERENT1, lang, l);
}
if (xl == null) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING3);
} else if (!xl.equals(lang)) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_DIFFERENT2, lang, xl);
if (xhtml != null) {
String l = xhtml.getAttribute("lang");
String xl = xhtml.getAttribute("xml:lang");
if (l == null && xl == null) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING1);
} else {
if (l == null) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING2);
} else if (!l.equals(lang)) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_DIFFERENT1, lang, l);
}
if (xl == null) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING3);
} else if (!xl.equals(lang)) {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_DIFFERENT2, lang, xl);
}
}
}
}
@ -7434,6 +7617,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
public long timeNoTX() {
return (timeTracker.getOverall() - timeTracker.getTxTime()) / 1000000;
}
public String reportTimes() {
String s = String.format("Times (ms): overall = %d:4, tx = %d, sd = %d, load = %d, fpe = %d, spec = %d", timeTracker.getOverall() / 1000000, timeTracker.getTxTime() / 1000000, timeTracker.getSdTime() / 1000000, timeTracker.getLoadTime() / 1000000, timeTracker.getFpeTime() / 1000000, timeTracker.getSpecTime() / 1000000);
timeTracker.reset();
@ -7839,6 +8025,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public void setUnknownCodeSystemsCauseErrors(boolean unknownCodeSystemsCauseErrors) {
this.unknownCodeSystemsCauseErrors = unknownCodeSystemsCauseErrors;
}
public boolean isNoExperimentalContent() {
return noExperimentalContent;
}
public void setNoExperimentalContent(boolean noExperimentalContent) {
this.noExperimentalContent = noExperimentalContent;
}
public void resetTimes() {
timeTracker.reset();
}
}

View File

@ -85,6 +85,7 @@ public class StructureDefinitionValidator extends BaseValidator {
List<ElementDefinition> snapshot = sd.getSnapshot().getElement();
sd.setSnapshot(null);
typeName = sd.getTypeName();
boolean experimental = "true".equals(src.getNamedChildValue("experimental", false));
StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (warning(errors, NO_RULE_DATE, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) {
if (rule(errors, NO_RULE_DATE, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasDerivation(), I18nConstants.SD_MUST_HAVE_DERIVATION, sd.getUrl())) {
@ -129,6 +130,7 @@ public class StructureDefinitionValidator extends BaseValidator {
ok = rule(errors, "2022-11-02", IssueType.NOTFOUND, stack.getLiteralPath(), base.getKindElement().primitiveValue().equals(src.getChildValue("kind")),
I18nConstants.SD_DERIVATION_KIND_MISMATCH, base.getKindElement().primitiveValue(), src.getChildValue("kind")) && ok;
}
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !base.getExperimental() || experimental, I18nConstants.SD_BASE_EXPERIMENTAL, sd.getBaseDefinition());
}
List<Element> differentials = src.getChildrenByName("differential");
@ -136,10 +138,10 @@ public class StructureDefinitionValidator extends BaseValidator {
boolean logical = "logical".equals(src.getNamedChildValue("kind", false));
boolean constraint = "constraint".equals(src.getNamedChildValue("derivation", false));
for (Element differential : differentials) {
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base) && ok;
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base, experimental) && ok;
}
for (Element snapshotE : snapshots) {
ok = validateElementList(errors, snapshotE, stack.push(snapshotE, -1, null, null), true, true, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base) && ok;
ok = validateElementList(errors, snapshotE, stack.push(snapshotE, -1, null, null), true, true, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base, experimental) && ok;
}
// obligation profile support
@ -189,6 +191,7 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
} catch (Exception e) {
//e.printStackTrace();
rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
ok = false;
}
@ -409,19 +412,19 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, String rootPath, String profileUrl, String profileType, StructureDefinition base) {
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, String rootPath, String profileUrl, String profileType, StructureDefinition base, boolean experimental) {
Map<String, String> invariantMap = new HashMap<>();
boolean ok = true;
List<Element> elements = elementList.getChildrenByName("element");
int cc = 0;
for (Element element : elements) {
ok = validateElementDefinition(errors, elements, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical, constraint, invariantMap, rootPath, profileUrl, profileType, base) && ok;
ok = validateElementDefinition(errors, elements, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical, constraint, invariantMap, rootPath, profileUrl, profileType, base, experimental) && ok;
cc++;
}
return ok;
}
private boolean validateElementDefinition(List<ValidationMessage> errors, List<Element> elements, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, Map<String, String> invariantMap, String rootPath, String profileUrl, String profileType, StructureDefinition base) {
private boolean validateElementDefinition(List<ValidationMessage> errors, List<Element> elements, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, Map<String, String> invariantMap, String rootPath, String profileUrl, String profileType, StructureDefinition base, boolean experimental) {
boolean ok = true;
boolean typeMustSupport = false;
String path = element.getNamedChildValue("path", false);
@ -522,7 +525,7 @@ public class StructureDefinitionValidator extends BaseValidator {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("can-bind") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Binding", typeCodes) && ok;
}
Element binding = element.getNamedChild("binding", false);
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path) && ok;
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path, experimental, sd) && ok;
} else {
// this is a good idea but there's plenty of cases where the rule isn't met; maybe one day it's worth investing the time to exclude these cases and bring this rule back
// String bt = boundType(typeCodes);
@ -986,7 +989,7 @@ public class StructureDefinitionValidator extends BaseValidator {
return null;
}
private boolean validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path) {
private boolean validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path, boolean experimental, StructureDefinition profile) {
boolean ok = true;
if (bindableType(typeCodes) == null) {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot, I18nConstants.SD_ED_BIND_NO_BINDABLE, path, typeCodes.toString()) && ok;
@ -1006,14 +1009,132 @@ public class StructureDefinitionValidator extends BaseValidator {
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
if (vs != null) {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType()) && ok;
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType())) {
ValueSet vsr = (ValueSet) vs;
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref);
} else {
ok = false;
}
}
}
}
}
if (binding.hasChildren("additional")) {
int i = 0;
for (Element ab : binding.getChildren("additional")) {
ok = validateAdditionalBinding(errors, ab, stack.push(ab, i, null, null), snapshot, path, experimental) && ok;
i++;
}
}
if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
int i = 0;
for (Element ab : binding.getChildren("extension")) {
String url = ab.getNamedChildValue("url");
if (ToolingExtensions.EXT_BINDING_ADDITIONAL.equals(url)) {
ok = validateAdditionalBindingExtension(errors, ab, stack.push(ab, i, null, null), snapshot, path, experimental, profile) && ok;
}
i++;
}
}
return ok;
}
private boolean validateAdditionalBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, boolean snapshot, String path, boolean experimental) {
boolean ok = true;
if (binding.hasChild("valueSet", false)) {
Element valueSet = binding.getNamedChild("valueSet", false);
String ref = valueSet.hasPrimitiveValue() ? valueSet.primitiveValue() : valueSet.getNamedChildValue("reference", false);
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || ref != null, I18nConstants.SD_ED_SHOULD_BIND_WITH_VS, path)) {
Resource vs = context.fetchResource(Resource.class, ref);
// just because we can't resolve it directly doesn't mean that terminology server can't. Check with it
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
if (vs != null) {
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType())) {
ValueSet vsr = (ValueSet) vs;
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref);
} else {
ok = false;
}
}
}
}
}
if (binding.hasChildren("usage")) {
for (Element usage : binding.getChildren("usage")) {
warning(errors, "2024-09-20", IssueType.BUSINESSRULE, stack.getLiteralPath(), false, "test");
}
}
return ok;
}
private boolean validateAdditionalBindingExtension(List<ValidationMessage> errors, Element binding, NodeStack stack, boolean snapshot, String path, boolean experimental, StructureDefinition profile) {
boolean ok = true;
if (binding.hasExtension("valueSet")) {
Element valueSet = binding.getExtension("valueSet");
Element vv = valueSet.getNamedChild("value");
String ref = vv.hasPrimitiveValue() ? vv.primitiveValue() : vv.getNamedChildValue("reference", false);
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || ref != null, I18nConstants.SD_ED_SHOULD_BIND_WITH_VS, path)) {
Resource vs = context.fetchResource(Resource.class, ref);
// just because we can't resolve it directly doesn't mean that terminology server can't. Check with it
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
if (vs != null) {
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType())) {
ValueSet vsr = (ValueSet) vs;
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref);
} else {
ok = false;
}
}
}
}
}
if (binding.hasExtension("usage")) {
int i = 0;
for (Element usage : binding.getChildren("extension")) {
String url = usage.getNamedChildValue("url");
if ("usage".equals(url)) {
Element uv = usage.getNamedChild("value");
ok = validateAdditionalBindingUsage(errors, uv, stack.push(uv, -1, null, null), path, profile) && ok;
}
i++;
}
}
return ok;
}
private boolean validateAdditionalBindingUsage(List<ValidationMessage> errors, Element usage, NodeStack stack, String path, StructureDefinition profile) {
boolean ok = true;
Element cc = usage.getNamedChild("code");
if (cc != null) {
String system = cc.getNamedChildValue("system");
String code = cc.getNamedChildValue("code");
if (system != null && system.equals(profile.getUrl())) {
ElementDefinition ed = profile.getDifferential().getElementByPath(code);
if (ed == null) {
ed = profile.getSnapshot().getElementByPath(code);
}
if (ed == null) {
ok = false;
rule(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), false, I18nConstants.SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT, system, code);
} else {
if (usage.hasChild("value")) {
String t = usage.getNamedChild("value").fhirType();
ok = rule(errors, "2024-09-20", IssueType.BUSINESSRULE, stack.getLiteralPath(), "CodeableConcept".equals(t), I18nConstants.SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE, t, "CodeableConcept") && ok;
}
}
} else {
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), false, I18nConstants.SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN, system, code);
}
}
return ok;
}
private Set<String> getListofBindableTypes(Set<String> types) {
Set<String> res = new HashSet<>();
for (String s : types) {

View File

@ -362,6 +362,9 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
if (content.has("security-checks")) {
val.setSecurityChecks(content.get("security-checks").getAsBoolean());
}
if (content.has("no-experimental-content")) {
val.setNoExperimentalContent(content.get("no-experimental-content").getAsBoolean());
}
if (content.has("noHtmlInMarkdown")) {
val.setHtmlInMarkdownCheck(HtmlInMarkdownCheck.ERROR);
}

View File

@ -1,3 +1,4 @@
[servers]
tx-dev.fhir.org.r2 = http://tx-dev.fhir.org/r2
local.fhir.org.r2 = http://local.fhir.org/r2

View File

@ -1,3 +1,4 @@
[servers]
tx-dev.fhir.org.r3 = http://tx-dev.fhir.org/r3
local.fhir.org.r3 = http://local.fhir.org/r3

View File

@ -521,7 +521,6 @@ v: {
"system" : "http://unitsofmeasure.org",
"version" : "2.0.1",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -549,7 +548,6 @@ v: {
"code" : "image/jpg",
"system" : "urn:ietf:bcp:13",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -577,7 +575,6 @@ v: {
"code" : "image/jpg",
"system" : "urn:ietf:bcp:13",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -605,7 +602,6 @@ v: {
"code" : "application/pdf",
"system" : "urn:ietf:bcp:13",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -633,7 +629,6 @@ v: {
"code" : "application/pdf",
"system" : "urn:ietf:bcp:13",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -654,7 +649,6 @@ v: {
"code" : "de-CH",
"system" : "urn:ietf:bcp:47",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -720,3 +714,29 @@ v: {
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "urn:iso:std:iso:3166",
"code" : "US",
"display" : "United States of America"
}]
}, "url": "http://hl7.org/fhir/ValueSet/jurisdiction", "version": "3.0.2", "langs":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "United States of America",
"code" : "US",
"system" : "urn:iso:std:iso:3166",
"version" : "2018",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------

View File

@ -1,3 +1,4 @@
[servers]
tx-dev.fhir.org.r3 = http://tx-dev.fhir.org/r3
local.fhir.org.r3 = http://local.fhir.org/r3

View File

@ -1108,7 +1108,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1132,7 +1131,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1159,7 +1157,6 @@ v: {
"error" : "Wrong Display Name 'Laboratory test finding (finding)' for http://snomed.info/sct#118246004. Valid display is one of 4 choices: 'Laboratory test finding', 'Laboratory test observations', 'Laboratory test result' or 'Laboratory test finding (navigational concept)' (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1203,7 +1200,6 @@ v: {
"error" : "Wrong Display Name 'Chemistry' for http://snomed.info/sct#275711006. Valid display is one of 2 choices: 'Serum chemistry test' or 'Serum chemistry test (procedure)' (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1244,7 +1240,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1268,7 +1263,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1292,7 +1286,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1316,7 +1309,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1340,7 +1332,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1364,7 +1355,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1388,7 +1378,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -1413,7 +1402,6 @@ v: {
"error" : "Unknown code '823681000000100' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1455,7 +1443,6 @@ v: {
"error" : "Unknown code '886921000000105' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1497,7 +1484,6 @@ v: {
"error" : "Unknown code '1077881000000105' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1539,7 +1525,6 @@ v: {
"error" : "Unknown code '887181000000106' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1581,7 +1566,6 @@ v: {
"error" : "Unknown code '887161000000102' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1623,7 +1607,6 @@ v: {
"error" : "Unknown code '1052891000000108' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1665,7 +1648,6 @@ v: {
"error" : "Unknown code '715851000000102' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1707,7 +1689,6 @@ v: {
"error" : "Unknown code '717121000000105' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1749,7 +1730,6 @@ v: {
"error" : "Unknown code '933361000000108' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1791,7 +1771,6 @@ v: {
"error" : "Unknown code '887171000000109' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1833,7 +1812,6 @@ v: {
"error" : "Unknown code '887201000000105' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1875,7 +1853,6 @@ v: {
"error" : "Unknown code '1052951000000105' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1917,7 +1894,6 @@ v: {
"error" : "Unknown code '886731000000109' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -1959,7 +1935,6 @@ v: {
"error" : "Unknown code '887231000000104' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -2001,7 +1976,6 @@ v: {
"error" : "Unknown code '9290701000001101' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -2042,7 +2016,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -2066,7 +2039,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -2089,7 +2061,6 @@ v: {
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -2113,7 +2084,6 @@ v: {
"error" : "Unknown code '11181000146103' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/900000000000207008/version/20240201'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -2137,3 +2107,61 @@ v: {
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://snomed.info/sct",
"code" : "17621005",
"display" : "Normal (qualifier value)"
}]
}, "url": "http://hl7.org/fhir/ValueSet/security-labels", "version": "3.0.2", "langs":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"version" : "http://snomed.info/sct/900000000000207008/version/20240201",
"severity" : "error",
"error" : "No valid coding was found for the value set 'http://hl7.org/fhir/ValueSet/security-labels|3.0.2'; The provided code 'http://snomed.info/sct#17621005 ('Normal (qualifier value)')' was not found in the value set 'http://hl7.org/fhir/ValueSet/security-labels|3.0.2'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r3"
}],
"severity" : "information",
"code" : "code-invalid",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "this-code-not-in-vs"
}],
"text" : "The provided code 'http://snomed.info/sct#17621005 ('Normal (qualifier value)')' was not found in the value set 'http://hl7.org/fhir/ValueSet/security-labels|3.0.2'"
},
"location" : ["CodeableConcept.coding[0].code"],
"expression" : ["CodeableConcept.coding[0].code"]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r3"
}],
"severity" : "error",
"code" : "code-invalid",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "not-in-vs"
}],
"text" : "No valid coding was found for the value set 'http://hl7.org/fhir/ValueSet/security-labels|3.0.2'"
}
}]
}
}
-------------------------------------------------------------------------------------

View File

@ -39,10 +39,68 @@ v: {
"system" : "http://www.ada.org/snodent",
"version" : "2.1.0",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://www.ada.org/snodent",
"code" : "210965D",
"display" : "Anterior part of lower alveolar ridge"
}],
"text" : "Anterior part of lower alveolar ridge"
}, "url": "http://hl7.org/fhir/us/dental-data-exchange/ValueSet/dental-anatomy", "version": "1.0.0", "langs":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"version" : "2.1.0",
"severity" : "error",
"error" : "No valid coding was found for the value set 'http://hl7.org/fhir/us/dental-data-exchange/ValueSet/dental-anatomy|1.0.0'; The provided code 'http://www.ada.org/snodent#210965D ('Anterior part of lower alveolar ridge')' was not found in the value set 'http://hl7.org/fhir/us/dental-data-exchange/ValueSet/dental-anatomy|1.0.0'",
"class" : "UNKNOWN",
"server" : "http://local.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://local.fhir.org/r4"
}],
"severity" : "information",
"code" : "code-invalid",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "this-code-not-in-vs"
}],
"text" : "The provided code 'http://www.ada.org/snodent#210965D ('Anterior part of lower alveolar ridge')' was not found in the value set 'http://hl7.org/fhir/us/dental-data-exchange/ValueSet/dental-anatomy|1.0.0'"
},
"location" : ["CodeableConcept.coding[0].code"],
"expression" : ["CodeableConcept.coding[0].code"]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://local.fhir.org/r4"
}],
"severity" : "error",
"code" : "code-invalid",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "not-in-vs"
}],
"text" : "No valid coding was found for the value set 'http://hl7.org/fhir/us/dental-data-exchange/ValueSet/dental-anatomy|1.0.0'"
}
}]
}
}
-------------------------------------------------------------------------------------

View File

@ -8422,7 +8422,6 @@ v: {
"error" : "Wrong Display Name 'O2 % BldC Oximetry' for http://loinc.org#59408-5. Valid display is 'Oxygen saturation in Arterial blood by Pulse oximetry' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -8463,7 +8462,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8489,7 +8487,6 @@ v: {
"error" : "The provided code 'http://loinc.org#59408-5' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult--0|4.0.1'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -8529,7 +8526,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8553,7 +8549,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8579,7 +8574,6 @@ v: {
"error" : "The provided code 'http://loinc.org#3150-0' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult--0|4.0.1'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -8623,7 +8617,6 @@ v: {
"error" : "Wrong Display Name 'Flow Rate' for http://loinc.org#3151-8. Valid display is 'Inhaled oxygen flow rate' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -8666,7 +8659,6 @@ v: {
"error" : "The provided code 'http://loinc.org#3151-8' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult--0|4.0.1'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -8706,7 +8698,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8732,7 +8723,6 @@ v: {
"error" : "The provided code 'http://loinc.org#2708-6' was not found in the value set 'http://hl7.org/fhir/us/core/ValueSet/us-core-vital-signs--0|4.0.0'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -8772,7 +8762,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8795,7 +8784,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8818,7 +8806,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8844,7 +8831,6 @@ v: {
"error" : "The provided code 'http://loinc.org#100066-0' was not found in the value set 'http://hl7.org/fhir/test/StructureDefinition/additional-bindings-vs1--0|1.0.0'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -8884,7 +8870,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8907,7 +8892,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8930,7 +8914,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8954,7 +8937,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -8978,7 +8960,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9002,7 +8983,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9026,7 +9006,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9049,7 +9028,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9073,7 +9051,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9096,7 +9073,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9120,7 +9096,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9143,7 +9118,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9167,7 +9141,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9190,7 +9163,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9214,7 +9186,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9237,7 +9208,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9261,7 +9231,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9284,7 +9253,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9308,7 +9276,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9332,7 +9299,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9359,7 +9325,6 @@ v: {
"error" : "Wrong Display Name 'Patient Authorization Signature' for http://loinc.org#59284-0. Valid display is 'Consent Document' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -9399,7 +9364,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9423,7 +9387,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9446,7 +9409,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9470,7 +9432,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9494,7 +9455,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9518,7 +9478,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9545,7 +9504,6 @@ v: {
"error" : "Wrong Display Name 'NDC labeler code request' for http://loinc.org#51726-8. Valid display is 'FDA product label NDC labeler code request' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -9586,7 +9544,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9611,7 +9568,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9634,7 +9590,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9662,7 +9617,6 @@ v: {
"error" : "Wrong Display Name 'Allergies and adverse reactions' for http://loinc.org#48765-2. Valid display is 'Allergies and adverse reactions Document' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -9703,7 +9657,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9726,7 +9679,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9749,7 +9701,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9773,7 +9724,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9796,7 +9746,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9821,7 +9770,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9845,7 +9793,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9868,7 +9815,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9892,7 +9838,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9915,7 +9860,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9939,7 +9883,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9962,7 +9905,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -9986,7 +9928,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10010,7 +9951,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10033,7 +9973,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10057,7 +9996,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10080,7 +10018,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10103,7 +10040,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10128,7 +10064,6 @@ v: {
"error" : "Unknown code '<27>g<EFBFBD><67>' in the CodeSystem 'http://loinc.org' version '2.77'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -10169,7 +10104,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10196,7 +10130,6 @@ v: {
"error" : "Wrong Display Name '<27><><EFBFBD><EFBFBD>' for http://loinc.org#18684-1. Valid display is 'First Blood pressure Set' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -10240,7 +10173,6 @@ v: {
"error" : "Wrong Display Name '<27><><EFBFBD>k<EFBFBD><6B><EFBFBD><EFBFBD><EFBFBD><EFBFBD>' for http://loinc.org#8480-6. Valid display is 'Systolic blood pressure' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -10284,7 +10216,6 @@ v: {
"error" : "Wrong Display Name '<27>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>' for http://loinc.org#8462-4. Valid display is 'Diastolic blood pressure' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -10325,7 +10256,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10349,7 +10279,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10373,7 +10302,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10397,7 +10325,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10422,7 +10349,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10447,7 +10373,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10471,7 +10396,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10495,7 +10419,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10518,7 +10441,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10541,7 +10463,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10564,7 +10485,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10587,7 +10507,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10610,7 +10529,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10633,7 +10551,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10656,7 +10573,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10679,7 +10595,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10702,7 +10617,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10737,7 +10651,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10772,7 +10685,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10807,7 +10719,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10831,7 +10742,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10854,7 +10764,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10877,7 +10786,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10901,7 +10809,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10927,7 +10834,6 @@ v: {
"error" : "The provided code 'http://loinc.org#76534-7' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult--0|4.0.1'",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
@ -10967,7 +10873,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -10991,7 +10896,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11015,7 +10919,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11039,7 +10942,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11063,7 +10965,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11087,7 +10988,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11111,7 +11011,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11135,7 +11034,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11158,7 +11056,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11181,7 +11078,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11205,7 +11101,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11229,7 +11124,6 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -11252,6 +11146,82 @@ v: {
"system" : "http://loinc.org",
"version" : "2.77",
"server" : "http://tx-dev.fhir.org/r4",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "59408-5",
"display" : "O2 % BldC Oximetry"
},
{
"system" : "http://loinc.org",
"code" : "2708-6",
"display" : "Oxygen saturation in Arterial blood"
}]
}, "url": "http://hl7.org/fhir/us/core/ValueSet/us-core-vital-signs", "version": "4.0.0", "langs":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Oxygen saturation in Arterial blood",
"code" : "2708-6",
"system" : "http://loinc.org",
"version" : "2.78",
"severity" : "error",
"error" : "Wrong Display Name 'O2 % BldC Oximetry' for http://loinc.org#59408-5. Valid display is one of 3 choices: 'Oxygen saturation in Arterial blood by Pulse oximetry' (en-US), 'Oxygen saturation in Arterial blood by Pulse oximetry' (en-US) or 'SaO2 % BldA PulseOx' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r4"
}],
"severity" : "error",
"code" : "invalid",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "invalid-display"
}],
"text" : "Wrong Display Name 'O2 % BldC Oximetry' for http://loinc.org#59408-5. Valid display is one of 3 choices: 'Oxygen saturation in Arterial blood by Pulse oximetry' (en-US), 'Oxygen saturation in Arterial blood by Pulse oximetry' (en-US) or 'SaO2 % BldA PulseOx' (en-US) (for the language(s) 'en-US')"
},
"location" : ["CodeableConcept.coding[0].display"],
"expression" : ["CodeableConcept.coding[0].display"]
}]
}
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "3150-0",
"display" : "Inhaled Oxygen Concentration"
}]
}, "url": "http://hl7.org/fhir/us/core/ValueSet/us-core-vital-signs", "version": "4.0.0", "langs":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Inhaled oxygen concentration",
"code" : "3150-0",
"system" : "http://loinc.org",
"version" : "2.78",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
@ -11259,3 +11229,49 @@ v: {
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "3151-8",
"display" : "Flow Rate"
}]
}, "url": "http://hl7.org/fhir/us/core/ValueSet/us-core-vital-signs", "version": "4.0.0", "langs":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Inhaled oxygen flow rate",
"code" : "3151-8",
"system" : "http://loinc.org",
"version" : "2.78",
"severity" : "error",
"error" : "Wrong Display Name 'Flow Rate' for http://loinc.org#3151-8. Valid display is one of 4 choices: 'Inhaled oxygen flow rate' (en-US), 'Inhaled oxygen flow rate' (en-US), 'Inhaled O2 flow rate' (en-US) or 'Inhaled oxygen' (en-US) (for the language(s) 'en-US')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r4"
}],
"severity" : "error",
"code" : "invalid",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "invalid-display"
}],
"text" : "Wrong Display Name 'Flow Rate' for http://loinc.org#3151-8. Valid display is one of 4 choices: 'Inhaled oxygen flow rate' (en-US), 'Inhaled oxygen flow rate' (en-US), 'Inhaled O2 flow rate' (en-US) or 'Inhaled oxygen' (en-US) (for the language(s) 'en-US')"
},
"location" : ["CodeableConcept.coding[0].display"],
"expression" : ["CodeableConcept.coding[0].display"]
}]
}
}
-------------------------------------------------------------------------------------

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