Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir.core into main_convertor_files

This commit is contained in:
markiantorno 2020-02-13 10:53:24 -05:00
commit a4a6f852bd
46 changed files with 1841 additions and 885 deletions

View File

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

View File

@ -152,8 +152,32 @@ public class VersionConvertor_30_50 {
return tgt;
}
public static org.hl7.fhir.r5.model.CodeType convertStringToCode(org.hl7.fhir.dstu3.model.StringType src) throws FHIRException {
org.hl7.fhir.r5.model.CodeType tgt = new org.hl7.fhir.r5.model.CodeType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.dstu3.model.StringType convertCodeToString(org.hl7.fhir.r5.model.CodeType src) throws FHIRException {
org.hl7.fhir.dstu3.model.StringType tgt = new org.hl7.fhir.dstu3.model.StringType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.dstu3.model.CodeType convertStringToCode(org.hl7.fhir.r5.model.StringType src) throws FHIRException {
org.hl7.fhir.dstu3.model.CodeType tgt = new org.hl7.fhir.dstu3.model.CodeType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.r5.model.StringType convertCodeToString(org.hl7.fhir.dstu3.model.CodeType src) throws FHIRException {
org.hl7.fhir.r5.model.StringType tgt = new org.hl7.fhir.r5.model.StringType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.r5.model.DateType convertDate(org.hl7.fhir.dstu3.model.DateType src) throws FHIRException {
org.hl7.fhir.r5.model.DateType tgt = new org.hl7.fhir.r5.model.DateType(src.getValueAsString());
org.hl7.fhir.r5.model.DateType tgt = new org.hl7.fhir.r5.model.DateType(src.getValueAsString());
copyElement(src, tgt);
return tgt;
}
@ -290,6 +314,18 @@ public class VersionConvertor_30_50 {
return tgt;
}
public static org.hl7.fhir.r5.model.MarkdownType convertStringToMarkdown(org.hl7.fhir.dstu3.model.StringType src) throws FHIRException {
org.hl7.fhir.r5.model.MarkdownType tgt = new org.hl7.fhir.r5.model.MarkdownType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.dstu3.model.StringType convertMarkdownToString(org.hl7.fhir.r5.model.MarkdownType src) throws FHIRException {
org.hl7.fhir.dstu3.model.StringType tgt = new org.hl7.fhir.dstu3.model.StringType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.r5.model.TimeType convertTime(org.hl7.fhir.dstu3.model.TimeType src) throws FHIRException {
org.hl7.fhir.r5.model.TimeType tgt = new org.hl7.fhir.r5.model.TimeType(src.getValue());
copyElement(src, tgt);
@ -326,6 +362,18 @@ public class VersionConvertor_30_50 {
return tgt;
}
public static org.hl7.fhir.r5.model.UriType convertCodeToUri(org.hl7.fhir.dstu3.model.CodeType src) throws FHIRException {
org.hl7.fhir.r5.model.UriType tgt = new org.hl7.fhir.r5.model.UriType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.dstu3.model.CodeType convertUriToCode(org.hl7.fhir.r5.model.UriType src) throws FHIRException {
org.hl7.fhir.dstu3.model.CodeType tgt = new org.hl7.fhir.dstu3.model.CodeType(src.getValue());
copyElement(src, tgt);
return tgt;
}
public static org.hl7.fhir.r5.model.UuidType convertUuid(org.hl7.fhir.dstu3.model.UuidType src) throws FHIRException {
org.hl7.fhir.r5.model.UuidType tgt = new org.hl7.fhir.r5.model.UuidType(src.getValue());
copyElement(src, tgt);

View File

@ -49,7 +49,7 @@ public class OperationDefinition30_50 {
if (src.hasCodeElement())
tgt.setCodeElement((org.hl7.fhir.r5.model.CodeType) VersionConvertor_30_50.convertType(src.getCodeElement()));
if (src.hasCommentElement())
tgt.setCommentElement((org.hl7.fhir.r5.model.MarkdownType) VersionConvertor_30_50.convertType(src.getCommentElement()));
tgt.setCommentElement(VersionConvertor_30_50.convertStringToMarkdown(src.getCommentElement()));
if (src.hasBase())
tgt.setBaseElement(VersionConvertor_30_50.convertReferenceToCanonical(src.getBase()));
if (src.hasResource()) {
@ -109,7 +109,7 @@ public class OperationDefinition30_50 {
if (src.hasCodeElement())
tgt.setCodeElement((org.hl7.fhir.dstu3.model.CodeType) VersionConvertor_30_50.convertType(src.getCodeElement()));
if (src.hasCommentElement())
tgt.setCommentElement((org.hl7.fhir.dstu3.model.StringType) VersionConvertor_30_50.convertType(src.getCommentElement()));
tgt.setCommentElement(VersionConvertor_30_50.convertMarkdownToString(src.getCommentElement()));
if (src.hasBase())
tgt.setBase(VersionConvertor_30_50.convertCanonicalToReference(src.getBaseElement()));
if (src.hasResource()) {

View File

@ -175,8 +175,9 @@ public class StructureDefinition30_50 {
if (src.hasAbstractElement())
tgt.setAbstractElement((org.hl7.fhir.dstu3.model.BooleanType) VersionConvertor_30_50.convertType(src.getAbstractElement()));
for (org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionContextComponent t : src.getContext()) {
if (!tgt.hasContextType())
tgt.setTypeElement((org.hl7.fhir.dstu3.model.CodeType) VersionConvertor_30_50.convertType(src.getTypeElement()));
if (!tgt.hasContextType()) {
tgt.setTypeElement((org.hl7.fhir.dstu3.model.CodeType) VersionConvertor_30_50.convertUriToCode(src.getTypeElement()));
}
tgt.addContext("Element".equals(t.getExpression()) ? "*" : t.getExpression());
}
if (src.hasContextInvariant()) {

View File

@ -117,7 +117,7 @@ public class ValueSet30_50 {
if (src.hasOp())
tgt.setOp(convertFilterOperator2(src.getOp()));
if (src.hasValueElement())
tgt.setValueElement((org.hl7.fhir.dstu3.model.CodeType) VersionConvertor_30_50.convertType(src.getValueElement()));
tgt.setValueElement((org.hl7.fhir.dstu3.model.CodeType) VersionConvertor_30_50.convertStringToCode(src.getValueElement()));
return tgt;
}
@ -131,7 +131,7 @@ public class ValueSet30_50 {
if (src.hasOp())
tgt.setOp(VersionConvertor_30_50.convertFilterOperator(src.getOp()));
if (src.hasValueElement())
tgt.setValueElement((org.hl7.fhir.r5.model.StringType) VersionConvertor_30_50.convertType(src.getValueElement()));
tgt.setValueElement((org.hl7.fhir.r5.model.StringType) VersionConvertor_30_50.convertCodeToString(src.getValueElement()));
return tgt;
}

View File

@ -29,7 +29,7 @@
* <code>null</code> if none
*/
public Coding getSecurity(String theSystem, String theCode) {
for (Coding next : getTag()) {
for (Coding next : getSecurity()) {
if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) {
return next;
}

View File

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

View File

@ -277,7 +277,7 @@ public class Meta extends Type implements IBaseMetaType {
* <code>null</code> if none
*/
public Coding getSecurity(String theSystem, String theCode) {
for (Coding next : getTag()) {
for (Coding next : getSecurity()) {
if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) {
return next;
}

View File

@ -0,0 +1,24 @@
package org.hl7.fhir.dstu2.test;
import org.hl7.fhir.dstu2.model.Coding;
import org.hl7.fhir.dstu2.model.Meta;
import org.junit.Test;
import static org.junit.Assert.*;
public class MetaTest {
public static String TEST_SYSTEM = "TEST_SYSTEM";
public static String TEST_CODE = "TEST_CODE";
@Test
public void testMetaSecurity() {
Meta meta = new Meta();
Coding coding = meta.addSecurity().setSystem(TEST_SYSTEM).setCode(TEST_CODE);
assertTrue(meta.hasSecurity());
assertNotNull(meta.getSecurity());
assertNotNull(meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(1, meta.getSecurity().size());
assertEquals(meta.getSecurity().get(0), meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(coding, meta.getSecurity(TEST_SYSTEM, TEST_CODE));
}
}

View File

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

View File

@ -421,7 +421,7 @@ public class Meta extends Type implements IBaseMetaType {
* <code>null</code> if none
*/
public Coding getSecurity(String theSystem, String theCode) {
for (Coding next : getTag()) {
for (Coding next : getSecurity()) {
if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) {
return next;
}

View File

@ -0,0 +1,26 @@
package org.hl7.fhir.dstu3.test;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Meta;
import org.junit.Test;
import static org.junit.Assert.*;
public class MetaTest {
public static String TEST_SYSTEM = "TEST_SYSTEM";
public static String TEST_CODE = "TEST_CODE";
@Test
public void testMetaSecurity() {
Meta meta = new Meta();
Coding coding = meta.addSecurity().setSystem(TEST_SYSTEM).setCode(TEST_CODE);
assertTrue(meta.hasSecurity());
assertNotNull(meta.getSecurity());
assertNotNull(meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(1, meta.getSecurity().size());
assertEquals(meta.getSecurity().get(0), meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(meta.getSecurityFirstRep(), meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(coding, meta.getSecurity(TEST_SYSTEM, TEST_CODE));
}
}

View File

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

View File

@ -476,7 +476,7 @@ public class Meta extends Type implements IBaseMetaType {
* <code>null</code> if none
*/
public Coding getSecurity(String theSystem, String theCode) {
for (Coding next : getTag()) {
for (Coding next : getSecurity()) {
if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) {
return next;
}

View File

@ -0,0 +1,26 @@
package org.hl7.fhir.r4.test;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Meta;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
public class MetaTest {
public static String TEST_SYSTEM = "TEST_SYSTEM";
public static String TEST_CODE = "TEST_CODE";
@Test
public void testMetaSecurity() {
Meta meta = new Meta();
Coding coding = meta.addSecurity().setSystem(TEST_SYSTEM).setCode(TEST_CODE);
assertTrue(meta.hasSecurity());
assertNotNull(meta.getSecurity());
assertNotNull(meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(1, meta.getSecurity().size());
assertEquals(meta.getSecurity().get(0), meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(meta.getSecurityFirstRep(), meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(coding, meta.getSecurity(TEST_SYSTEM, TEST_CODE));
}
}

View File

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

View File

@ -463,6 +463,9 @@ public class ProfileUtilities extends TranslatingUtilities {
if (derived == null) {
throw new DefinitionException("no derived structure provided");
}
checkNotGenerating(base, "Base for generating a snapshot for the profile "+derived.getUrl());
checkNotGenerating(derived, "Focus for generating a snapshot");
derived.setUserData("profileutils.snapshot.generating", true);
if (!base.hasType()) {
throw new DefinitionException("Base profile "+base.getUrl()+" has no type");
@ -622,6 +625,7 @@ public class ProfileUtilities extends TranslatingUtilities {
derived.setSnapshot(null);
throw e;
}
derived.clearUserData("profileutils.snapshot.generating");
}
private void checkDifferential(List<ElementDefinition> elements, String type, String url) {
@ -853,10 +857,12 @@ public class ProfileUtilities extends TranslatingUtilities {
CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
if (sd != null) {
checkNotGenerating(sd, "an extension definition");
if (!sd.hasSnapshot()) {
StructureDefinition sdb = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (sdb == null)
throw new DefinitionException("Unable to find base "+sd.getBaseDefinition()+" for "+sd.getUrl());
checkNotGenerating(sdb, "an extension base");
generateSnapshot(sdb, sd, sd.getUrl(), (sdb.hasUserData("path")) ? Utilities.extractBaseUrl(sdb.getUserString("path")) : webUrl, sd.getName());
}
ElementDefinition src;
@ -905,7 +911,7 @@ public class ProfileUtilities extends TranslatingUtilities {
result.getElement().add(outcome);
baseCursor++;
diffCursor = differential.getElement().indexOf(diffMatches.get(0))+1;
if (diffLimit >= diffCursor && outcome.getPath().contains(".") && (isDataType(outcome.getType()) || outcome.hasContentReference())) { // don't want to do this for the root, since that's base, and we're already processing it
if (diffLimit >= diffCursor && outcome.getPath().contains(".") && (isDataType(outcome.getType()) || isBaseResource(outcome.getType()) || outcome.hasContentReference())) { // don't want to do this for the root, since that's base, and we're already processing it
if (pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath()+".") && !baseWalksInto(base.getElement(), baseCursor)) {
if (outcome.getType().size() > 1) {
if (outcome.getPath().endsWith("[x]") && !diffMatches.get(0).getPath().endsWith("[x]")) {
@ -1424,7 +1430,7 @@ public class ProfileUtilities extends TranslatingUtilities {
outcome.setSlicing(null);
if (!outcome.getPath().startsWith(resultPathBase))
throw new DefinitionException("Adding wrong path");
if (diffpos < diffMatches.size() && diffMatches.get(diffpos).getSliceName().equals(outcome.getSliceName())) {
if (diffpos < diffMatches.size() && diffMatches.get(diffpos).hasSliceName() && diffMatches.get(diffpos).getSliceName().equals(outcome.getSliceName())) {
// if there's a diff, we update the outcome with diff
// no? updateFromDefinition(outcome, diffMatches.get(diffpos), profileName, closed, url);
//then process any children
@ -1540,6 +1546,24 @@ public class ProfileUtilities extends TranslatingUtilities {
}
private void checkNotGenerating(StructureDefinition sd, String role) {
if (sd.hasUserData("profileutils.snapshot.generating")) {
throw new FHIRException("Attempt to use a snapshot on profile '"+sd.getUrl()+"' as "+role+" before it is generated");
}
}
private boolean isBaseResource(List<TypeRefComponent> types) {
if (types.isEmpty())
return false;
for (TypeRefComponent type : types) {
String t = type.getWorkingCode();
if ("Resource".equals(t))
return false;
}
return true;
}
public String determineFixedType(List<ElementDefinition> diffMatches, String fixedType, int i) {
if (diffMatches.get(i).getType().size() == 0 && diffMatches.get(i).hasSliceName()) {
String n = tail(diffMatches.get(i).getPath()).replace("[x]", "");
@ -2441,62 +2465,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!Base.compareDeep(derived.getType(), base.getType(), false)) {
if (base.hasType()) {
for (TypeRefComponent ts : derived.getType()) {
boolean ok = false;
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
String t = ts.getWorkingCode();
for (TypeRefComponent td : base.getType()) {;
String tt = td.getWorkingCode();
b.append(tt);
if (td.hasCode() && (tt.equals(t))) {
ok = true;
}
if (!ok) {
StructureDefinition sdt = context.fetchTypeDefinition(tt);
if (sdt != null && sdt.getAbstract()) {
StructureDefinition sdb = context.fetchTypeDefinition(t);
while (sdb != null && !ok) {
ok = sdb.getType().equals(sdt.getUrl());
sdb = context.fetchResource(StructureDefinition.class, sdb.getBaseDefinition());
}
}
}
// work around for old badly generated SDs
if (DONT_DO_THIS && Utilities.existsInList(tt, "Extension", "uri", "string", "Element")) {
ok = true;
}
if (DONT_DO_THIS && Utilities.existsInList(tt, "Resource","DomainResource") && pkp.isResource(t)) {
ok = true;
}
if (ok && ts.hasTargetProfile()) {
// check that any derived target has a reference chain back to one of the base target profiles
for (UriType u : ts.getTargetProfile()) {
String url = u.getValue();
boolean tgtOk = !td.hasTargetProfile() || td.hasTargetProfile(url);
while (url != null && !tgtOk) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, purl+"#"+derived.getPath(), "Connect check whether the target profile "+url+" is valid constraint on the base because it is not known", IssueSeverity.WARNING));
}
url = null;
tgtOk = true; // suppress error message
} else {
url = sd.getBaseDefinition();
tgtOk = td.hasTargetProfile(url);
}
}
if (!tgtOk) {
if (messages == null) {
throw new FHIRException("Error at "+purl+"#"+derived.getPath()+": The target profile "+url+" is not valid constraint on the base ("+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));
}
}
}
}
}
if (!ok)
throw new DefinitionException("StructureDefinition "+purl+" at "+derived.getPath()+": illegal constrained type "+t+" from "+b.toString()+" in "+srcSD.getUrl());
checkTypeDerivation(purl, srcSD, base, derived, ts);
}
}
base.getType().clear();
@ -2576,6 +2545,66 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
public void checkTypeDerivation(String purl, StructureDefinition srcSD, ElementDefinition base, ElementDefinition derived, TypeRefComponent ts) {
boolean ok = false;
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
String t = ts.getWorkingCode();
for (TypeRefComponent td : base.getType()) {;
String tt = td.getWorkingCode();
b.append(tt);
if (td.hasCode() && (tt.equals(t))) {
ok = true;
}
if (!ok) {
StructureDefinition sdt = context.fetchTypeDefinition(tt);
if (sdt != null && sdt.getAbstract()) {
StructureDefinition sdb = context.fetchTypeDefinition(t);
while (sdb != null && !ok) {
ok = sdb.getType().equals(sdt.getType());
sdb = context.fetchResource(StructureDefinition.class, sdb.getBaseDefinition());
}
}
}
// work around for old badly generated SDs
if (DONT_DO_THIS && Utilities.existsInList(tt, "Extension", "uri", "string", "Element")) {
ok = true;
}
if (DONT_DO_THIS && Utilities.existsInList(tt, "Resource","DomainResource") && pkp.isResource(t)) {
ok = true;
}
if (ok && ts.hasTargetProfile()) {
// check that any derived target has a reference chain back to one of the base target profiles
for (UriType u : ts.getTargetProfile()) {
String url = u.getValue();
boolean tgtOk = !td.hasTargetProfile() || td.hasTargetProfile(url);
while (url != null && !tgtOk) {
StructureDefinition sd = context.fetchRawProfile(url);
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, purl+"#"+derived.getPath(), "Connect check whether the target profile "+url+" is valid constraint on the base because it is not known", IssueSeverity.WARNING));
}
url = null;
tgtOk = true; // suppress error message
} else {
url = sd.getBaseDefinition();
tgtOk = td.hasTargetProfile(url);
}
}
if (!tgtOk) {
if (messages == null) {
throw new FHIRException("Error at "+purl+"#"+derived.getPath()+": The target profile "+url+" is not valid constraint on the base ("+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));
}
}
}
}
}
if (!ok) {
throw new DefinitionException("StructureDefinition "+purl+" at "+derived.getPath()+": illegal constrained type "+t+" from "+b.toString()+" in "+srcSD.getUrl());
}
}
public void checkTypeOk(ElementDefinition dest, String ft) {
boolean ok = false;
@ -4354,13 +4383,13 @@ public class ProfileUtilities extends TranslatingUtilities {
@Override
public int compare(ElementDefinitionHolder o1, ElementDefinitionHolder o2) {
if (o1.getBaseIndex() == 0)
o1.setBaseIndex(find(o1.getSelf().getPath()));
o1.setBaseIndex(find(o1.getSelf().getPath(), true));
if (o2.getBaseIndex() == 0)
o2.setBaseIndex(find(o2.getSelf().getPath()));
o2.setBaseIndex(find(o2.getSelf().getPath(), true));
return o1.getBaseIndex() - o2.getBaseIndex();
}
private int find(String path) {
private int find(String path, boolean mandatory) {
String op = path;
int lc = 0;
String actual = base+path.substring(prefixLength);
@ -4392,10 +4421,12 @@ public class ProfileUtilities extends TranslatingUtilities {
throw new Error("Internal recursion detection: find() loop path recursion > "+MAX_RECURSION_LIMIT+" - check paths are valid (for path "+path+"/"+op+")");
}
}
if (prefixLength == 0)
errors.add("Differential contains path "+path+" which is not found in the base");
else
errors.add("Differential contains path "+path+" which is actually "+actual+", which is not found in the base");
if (mandatory) {
if (prefixLength == 0)
errors.add("Differential contains path "+path+" which is not found in the base");
else
errors.add("Differential contains path "+path+" which is actually "+actual+", which is not found in the base");
}
return 0;
}
@ -4508,7 +4539,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private void sortElements(ElementDefinitionHolder edh, ElementDefinitionComparer cmp, List<String> errors) throws FHIRException {
if (edh.getChildren().size() == 1)
// special case - sort needsto allocate base numbers, but there'll be no sort if there's only 1 child. So in that case, we just go ahead and allocated base number directly
edh.getChildren().get(0).baseIndex = cmp.find(edh.getChildren().get(0).getSelf().getPath());
edh.getChildren().get(0).baseIndex = cmp.find(edh.getChildren().get(0).getSelf().getPath(), false);
else
Collections.sort(edh.getChildren(), cmp);
cmp.checkForErrors(errors);
@ -4516,8 +4547,9 @@ public class ProfileUtilities extends TranslatingUtilities {
for (ElementDefinitionHolder child : edh.getChildren()) {
if (child.getChildren().size() > 0) {
ElementDefinitionComparer ccmp = getComparer(cmp, child);
if (ccmp != null)
sortElements(child, ccmp, errors);
if (ccmp != null) {
sortElements(child, ccmp, errors);
}
}
}
}
@ -4528,7 +4560,22 @@ public class ProfileUtilities extends TranslatingUtilities {
ElementDefinition ed = cmp.snapshot.get(child.getBaseIndex());
ElementDefinitionComparer ccmp;
if (ed.getType().isEmpty() || isAbstract(ed.getType().get(0).getWorkingCode()) || ed.getType().get(0).getWorkingCode().equals(ed.getPath())) {
ccmp = new ElementDefinitionComparer(true, cmp.snapshot, cmp.base, cmp.prefixLength, cmp.name);
if (ed.hasType() && "Resource".equals(ed.getType().get(0).getWorkingCode()) && child.getSelf().getType().get(0).hasProfile()) {
if (child.getSelf().getType().get(0).getProfile().size() > 1) {
throw new FHIRException("Unhandled situation: resource is profiled to more than one option - cannot sort profile");
}
StructureDefinition profile = context.fetchResource(StructureDefinition.class, child.getSelf().getType().get(0).getProfile().get(0).getValue());
while (profile != null && profile.getDerivation() == TypeDerivationRule.CONSTRAINT) {
profile = context.fetchResource(StructureDefinition.class, profile.getBaseDefinition());
}
if (profile==null) {
ccmp = null; // this might happen before everything is loaded. And we don't so much care about sot order in this case
} else {
ccmp = new ElementDefinitionComparer(true, profile.getSnapshot().getElement(), profile.getType(), child.getSelf().getPath().length(), cmp.name);
}
} else {
ccmp = new ElementDefinitionComparer(true, cmp.snapshot, cmp.base, cmp.prefixLength, cmp.name);
}
} else if (ed.getType().get(0).getWorkingCode().equals("Extension") && child.getSelf().getType().size() == 1 && child.getSelf().getType().get(0).hasProfile()) {
StructureDefinition profile = context.fetchResource(StructureDefinition.class, child.getSelf().getType().get(0).getProfile().get(0).getValue());
if (profile==null)
@ -5264,14 +5311,18 @@ public class ProfileUtilities extends TranslatingUtilities {
private String getCardinality(ElementDefinition ed, List<ElementDefinition> list) {
int min = ed.getMin();
int max = !ed.hasMax() || ed.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(ed.getMax());
while (ed != null && ed.getPath().contains(".")) {
ed = findParent(ed, list);
if (ed.getMax().equals("0"))
max = 0;
else if (!ed.getMax().equals("1") && !ed.hasSlicing())
max = Integer.MAX_VALUE;
if (ed.getMin() == 0)
min = 0;
ElementDefinition ned = ed;
while (ned != null && ned.getPath().contains(".")) {
ned = findParent(ned, list);
if (ned != null) { // todo: this can happen if we've walked into a resoruce. Not sure what to about that?
if ("0".equals(ned.getMax()))
max = 0;
else if (!ned.getMax().equals("1") && !ned.hasSlicing())
max = Integer.MAX_VALUE;
if (ned.getMin() == 0) {
min = 0;
}
}
}
return Integer.toString(min)+".."+(max == Integer.MAX_VALUE ? "*" : Integer.toString(max));
}

View File

@ -539,6 +539,7 @@ public interface IWorkerContext {
public void setOverrideVersionNs(String value);
public StructureDefinition fetchTypeDefinition(String typeName);
public StructureDefinition fetchRawProfile(String url);
public void setUcumService(UcumService ucumService);

View File

@ -640,6 +640,12 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return r;
}
@Override
public StructureDefinition fetchRawProfile(String uri) {
StructureDefinition r = super.fetchResource(StructureDefinition.class, uri);
return r;
}
@Override
public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException {
generateSnapshot(p, false);

View File

@ -671,7 +671,7 @@ public class Meta extends DataType implements IBaseMetaType {
* <code>null</code> if none
*/
public Coding getSecurity(String theSystem, String theCode) {
for (Coding next : getTag()) {
for (Coding next : getSecurity()) {
if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) {
return next;
}

View File

@ -1620,6 +1620,8 @@ public class NarrativeGenerator implements INarrativeGenerator {
return false;
} else if (e instanceof ElementDefinition) {
return false;
} else if (e instanceof Base64BinaryType) {
return false;
} else if (!(e instanceof Attachment))
throw new NotImplementedException("type "+e.getClass().getName()+" not handled yet");
return false;
@ -2515,6 +2517,8 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (!x.hasAttribute("xmlns"))
x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
if (r.hasLanguage()) {
// use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues
x.setAttribute("lang", r.getLanguage());
x.setAttribute("xml:lang", r.getLanguage());
}
if (!r.hasText() || !r.getText().hasDiv() || r.getText().getDiv().getChildNodes().isEmpty()) {
@ -2539,6 +2543,13 @@ public class NarrativeGenerator implements INarrativeGenerator {
private void inject(Element er, XhtmlNode x, NarrativeStatus status) {
if (!x.hasAttribute("xmlns"))
x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
Element le = XMLUtil.getNamedChild(er, "language");
String l = le == null ? null : le.getAttribute("value");
if (!Utilities.noString(l)) {
// use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues
x.setAttribute("lang", l);
x.setAttribute("xml:lang", l);
}
Element txt = XMLUtil.getNamedChild(er, "text");
if (txt == null) {
txt = er.getOwnerDocument().createElementNS(FormatUtilities.FHIR_NS, "text");
@ -2574,6 +2585,12 @@ public class NarrativeGenerator implements INarrativeGenerator {
private void inject(org.hl7.fhir.r5.elementmodel.Element er, XhtmlNode x, NarrativeStatus status) throws IOException, FHIRException {
if (!x.hasAttribute("xmlns"))
x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
String l = er.getChildValue("language");
if (!Utilities.noString(l)) {
// use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues
x.setAttribute("lang", l);
x.setAttribute("xml:lang", l);
}
org.hl7.fhir.r5.elementmodel.Element txt = er.getNamedChild("text");
if (txt == null) {
txt = new org.hl7.fhir.r5.elementmodel.Element("text", er.getProperty().getChild(null, "text"));
@ -2919,6 +2936,9 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
private ConceptMapRenderInstructions findByTarget(DataType source) {
if (source == null) {
return null;
}
String src = source.primitiveValue();
if (src != null)
for (ConceptMapRenderInstructions t : renderingMaps) {

View File

@ -7,6 +7,8 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
NpmPackageTests.class,
PackageClientTests.class,
SnomedExpressionsTests.class,
GraphQLParserTests.class,
TurtleTests.class,
@ -21,7 +23,8 @@ import org.junit.runners.Suite.SuiteClasses;
BaseDateTimeTypeTest.class,
OpenApiGeneratorTest.class,
MetadataResourceManagerTester.class,
NpmPackageTests.class,
MetaTest.class,
UtilitiesTests.class,
SnapShotGenerationTests.class})
public class AllR5Tests {

View File

@ -99,7 +99,7 @@ public class FHIRPathTests {
@Parameters(name = "{index}: file {0}")
public static Iterable<Object[]> data() throws ParserConfigurationException, SAXException, IOException {
Document dom = XMLUtil.parseToDom(TestingUtilities.loadTestResource("r5", "fhirpath", "tests-fhir-r4.xml"));
Document dom = XMLUtil.parseToDom(TestingUtilities.loadTestResource("r5", "fhirpath", "tests-fhir-r5.xml"));
List<Element> list = new ArrayList<Element>();
List<Element> groups = new ArrayList<Element>();

View File

@ -0,0 +1,26 @@
package org.hl7.fhir.r5.test;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Meta;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
public class MetaTest {
public static String TEST_SYSTEM = "TEST_SYSTEM";
public static String TEST_CODE = "TEST_CODE";
@Test
public void testMetaSecurity() {
Meta meta = new Meta();
Coding coding = meta.addSecurity().setSystem(TEST_SYSTEM).setCode(TEST_CODE);
assertTrue(meta.hasSecurity());
assertNotNull(meta.getSecurity());
assertNotNull(meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(1, meta.getSecurity().size());
assertEquals(meta.getSecurity().get(0), meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(meta.getSecurityFirstRep(), meta.getSecurity(TEST_SYSTEM, TEST_CODE));
assertEquals(coding, meta.getSecurity(TEST_SYSTEM, TEST_CODE));
}
}

View File

@ -0,0 +1,36 @@
package org.hl7.fhir.r5.test;
import java.io.File;
import java.io.IOException;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.cache.NpmPackage;
import org.hl7.fhir.utilities.cache.PackageCacheManager;
import org.hl7.fhir.utilities.cache.ToolsVersion;
import org.junit.Assert;
import org.junit.Test;
public class PackageCacheTests {
@Test
public void testPath() throws IOException {
PackageCacheManager cache = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
cache.clear();
NpmPackage npm = cache.loadPackage("hl7.fhir.pubpack", "0.0.3");
npm.loadAllFiles();
Assert.assertNotNull(npm);
File dir = new File(Utilities.path("[tmp]", "cache"));
if (dir.exists()) {
Utilities.clearDirectory(dir.getAbsolutePath());
} else {
Utilities.createDirectory(dir.getAbsolutePath());
}
npm.save(dir);
NpmPackage npm2 = cache.loadPackage("hl7.fhir.pubpack", "file:"+dir.getAbsolutePath());
Assert.assertNotNull(npm2);
}
}

View File

@ -0,0 +1,110 @@
package org.hl7.fhir.r5.test;
import java.io.IOException;
import java.util.List;
import org.hl7.fhir.utilities.cache.PackageClient;
import org.hl7.fhir.utilities.cache.PackageClient.PackageInfo;
import org.junit.Assert;
import org.junit.Test;
public class PackageClientTests {
@Test
public void testExists() throws IOException {
PackageClient client = new PackageClient("http://packages.fhir.org");
Assert.assertTrue(client.exists("hl7.fhir.r4.core", "4.0.1"));
Assert.assertTrue(!client.exists("hl7.fhir.r4.core", "1.0.2"));
Assert.assertTrue(!client.exists("hl7.fhir.nothing", "1.0.1"));
}
@Test
public void testSearch() throws IOException {
PackageClient client = new PackageClient("http://packages.fhir.org");
List<PackageInfo> matches = client.search("core", null, null, false);
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
Assert.assertTrue(matches.size() > 0);
}
@Test
public void testSearchNoMatches() throws IOException {
PackageClient client = new PackageClient("http://packages.fhir.org");
List<PackageInfo> matches = client.search("corezxxx", null, null, false);
Assert.assertTrue(matches.size() == 0);
}
@Test
public void testVersions() throws IOException {
PackageClient client = new PackageClient("http://packages.fhir.org");
List<PackageInfo> matches = client.getVersions("Simplifier.Core.STU3");
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
Assert.assertTrue(matches.size() > 0);
}
@Test
public void testVersionsNone() throws IOException {
PackageClient client = new PackageClient("http://packages.fhir.org");
List<PackageInfo> matches = client.getVersions("Simplifier.Core.STU3X");
Assert.assertTrue(matches.size() == 0);
}
@Test
public void testExists2() throws IOException {
PackageClient client = new PackageClient("http://test.fhir.org/packages");
Assert.assertTrue(client.exists("hl7.fhir.r4.core", "4.0.1"));
Assert.assertTrue(!client.exists("hl7.fhir.r4.core", "1.0.2"));
Assert.assertTrue(!client.exists("hl7.fhir.nothing", "1.0.1"));
}
@Test
public void testSearch2() throws IOException {
PackageClient client = new PackageClient("http://test.fhir.org/packages");
List<PackageInfo> matches = client.search("core", null, null, false);
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
Assert.assertTrue(matches.size() > 0);
}
@Test
public void testSearchNoMatches2() throws IOException {
PackageClient client = new PackageClient("http://test.fhir.org/packages");
List<PackageInfo> matches = client.search("corezxxx", null, null, false);
Assert.assertTrue(matches.size() == 0);
}
@Test
public void testVersions2() throws IOException {
PackageClient client = new PackageClient("http://test.fhir.org/packages");
List<PackageInfo> matches = client.getVersions("Simplifier.Core.STU3");
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
Assert.assertTrue(matches.size() == 0);
}
@Test
public void testVersions2A() throws IOException {
PackageClient client = new PackageClient("http://test.fhir.org/packages");
List<PackageInfo> matches = client.getVersions("hl7.fhir.us.core");
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
Assert.assertTrue(matches.size() > 0);
}
@Test
public void testVersionsNone2() throws IOException {
PackageClient client = new PackageClient("http://test.fhir.org/packages");
List<PackageInfo> matches = client.getVersions("Simplifier.Core.STU3X");
Assert.assertTrue(matches.size() == 0);
}
}

View File

@ -473,7 +473,9 @@ public class SnapShotGenerationTests {
pu.setNewSlicingProcessing(true);
pu.setIds(test.included, false);
StructureDefinition base = TestingUtilities.context().fetchResource(StructureDefinition.class, test.included.getBaseDefinition());
pu.generateSnapshot(base, test.included, test.included.getUrl(), "http://test.org/profile", test.included.getName());
if (base != null) {
pu.generateSnapshot(base, test.included, test.included.getUrl(), "http://test.org/profile", test.included.getName());
}
if (!TestingUtilities.context().hasResource(StructureDefinition.class, test.included.getUrl()))
TestingUtilities.context().cacheResource(test.included);
int ec = 0;

View File

@ -0,0 +1,19 @@
package org.hl7.fhir.r5.test;
import java.io.IOException;
import org.apache.commons.lang3.SystemUtils;
import org.hl7.fhir.utilities.Utilities;
import org.junit.Test;
import junit.framework.Assert;
public class UtilitiesTests {
@Test
public void testPath() throws IOException {
Assert.assertEquals(Utilities.path("[tmp]", "test.txt"), SystemUtils.IS_OS_WINDOWS ? "c:\\temp\\test.txt" : "/temp/test.txt");
Assert.assertEquals(Utilities.path("[user]", "test.txt"), System.getProperty("user.home")+"\\test.txt");
Assert.assertEquals(Utilities.path("[JAVA_HOME]", "test.txt"), System.getenv("JAVA_HOME")+"\\test.txt");
}
}

View File

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

View File

@ -63,6 +63,7 @@ import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
@ -568,11 +569,20 @@ public class Utilities {
else if (!s.toString().endsWith(File.separator))
s.append(File.separator);
String a = arg;
if ("[tmp]".equals(a)) {
if (hasCTempDir()) {
a = "c:\\temp";
} else {
a = System.getProperty("java.io.tmpdir");
if (s.length() == 0) {
if ("[tmp]".equals(a)) {
if (hasCTempDir()) {
a = "c:\\temp";
} else {
a = System.getProperty("java.io.tmpdir");
}
} else if ("[user]".equals(a)) {
a = System.getProperty("user.home");
} else if (a.startsWith("[") && a.endsWith("]")){
String ev = System.getenv(a.replace("[", "").replace("]", ""));
if (ev != null) {
a = ev;
}
}
}
a = a.replace("\\", File.separator);
@ -581,11 +591,15 @@ public class Utilities {
a = a.substring(File.separator.length());
while (a.startsWith(".."+File.separator)) {
String p = s.toString().substring(0, s.length()-1);
if (!p.contains(File.separator)) {
s = new StringBuilder();
if (s.length() == 0) {
s = new StringBuilder(Paths.get(".").toAbsolutePath().normalize().toString());
} else {
s = new StringBuilder(p.substring(0, p.lastIndexOf(File.separator))+File.separator);
String p = s.toString().substring(0, s.length()-1);
if (!p.contains(File.separator)) {
s = new StringBuilder();
} else {
s = new StringBuilder(p.substring(0, p.lastIndexOf(File.separator))+File.separator);
}
}
a = a.substring(3);
}
@ -599,6 +613,9 @@ public class Utilities {
}
private static boolean hasCTempDir() {
if (!System.getProperty("os.name").toLowerCase().contains("win")) {
return false;
}
File tmp = new File("c:\\temp");
return tmp.exists() && tmp.isDirectory();
}

View File

@ -186,6 +186,7 @@ public class NpmPackage {
public static void loadFiles(NpmPackage res, String path, File source, String... exemptions) throws FileNotFoundException, IOException {
res.npm = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(Utilities.path(path, "package", "package.json")));
res.path = path;
File dir = new File(path);
for (File f : dir.listFiles()) {
@ -622,6 +623,37 @@ public class NpmPackage {
return folders;
}
public void save(File directory) throws IOException {
File dir = new File(Utilities.path(directory.getAbsolutePath(), name()));
if (!dir.exists()) {
Utilities.createDirectory(dir.getAbsolutePath());
} else {
Utilities.clearDirectory(dir.getAbsolutePath());
}
for (NpmPackageFolder folder : folders.values()) {
String n = folder.name;
File pd = new File(Utilities.path(dir.getAbsolutePath(), n));
if (!pd.exists()) {
Utilities.createDirectory(pd.getAbsolutePath());
}
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
for (String s : folder.content.keySet()) {
byte[] b = folder.content.get(s);
indexer.seeFile(s, b);
if (!s.equals(".index.json") && !s.equals("package.json")) {
TextFile.bytesToFile(b, Utilities.path(dir.getAbsolutePath(), n, s));
}
}
byte[] cnt = indexer.build().getBytes(Charset.forName("UTF-8"));
TextFile.bytesToFile(cnt, Utilities.path(dir.getAbsolutePath(), n, ".index.json"));
}
byte[] cnt = TextFile.stringToBytes(new GsonBuilder().setPrettyPrinting().create().toJson(npm), false);
TextFile.bytesToFile(cnt, Utilities.path(dir.getAbsolutePath(), "package", "package.json"));
}
public void save(OutputStream stream) throws IOException {
TarArchiveOutputStream tar;
ByteArrayOutputStream OutputStream;
@ -756,5 +788,35 @@ public class NpmPackage {
Collections.sort(res);
return res ;
}
public void clearFolder(String folderName) {
NpmPackageFolder folder = folders.get(folderName);
folder.content.clear();
folder.types.clear();
}
public void deleteFolder(String folderName) {
folders.remove(folderName);
}
public void addFile(String folderName, String name, byte[] cnt, String type) {
NpmPackageFolder folder = folders.get(folderName);
folder.content.put(name, cnt);
if (!folder.types.containsKey(type))
folder.types.put(type, new ArrayList<>());
folder.types.get(type).add(name);
}
public void loadAllFiles() throws IOException {
for (String folder : folders.keySet()) {
NpmPackageFolder pf = folders.get(folder);
String p = Utilities.path(path, folder);
for (File f : new File(p).listFiles()) {
if (!f.isDirectory()) {
pf.getContent().put(f.getName(), TextFile.fileToBytes(f));
}
}
}
}
}

View File

@ -268,8 +268,10 @@ public class PackageCacheManager {
private void clearCache() throws IOException {
for (File f : new File(cacheFolder).listFiles()) {
if (f.isDirectory())
if (f.isDirectory()) {
Utilities.clearDirectory(f.getAbsolutePath());
FileUtils.deleteDirectory(f);
}
else if (!f.getName().equals("packages.ini"))
FileUtils.forceDelete(f);
}
@ -316,6 +318,10 @@ public class PackageCacheManager {
save = checkIniHasMapping("hl7.fhir.core", "http://hl7.org/fhir", ini) || save;
save = checkIniHasMapping("hl7.fhir.pubpack", "http://fhir.org/packages/hl7.fhir.pubpack", ini) || save;
save = checkIniHasMapping("hl7.fhir.xver-extensions", "http://fhir.org/packages/hl7.fhir.xver-extensions", ini) || save;
save = checkIniHasMapping("fhir.base.template", "http://fhir.org/templates/fhir.base.template", ini) || save;
save = checkIniHasMapping("hl7.base.template", "http://fhir.org/templates/hl7.base.template", ini) || save;
save = checkIniHasMapping("hl7.fhir.template", "http://fhir.org/templates/hl7.fhir.template", ini) || save;
save = checkIniHasMapping("ihe.fhir.template", "http://fhir.org/templates/ihe.fhir.template", ini) || save;
save = checkIniHasMapping("hl7.fhir.r2.core", "http://hl7.org/fhir/DSTU2/hl7.fhir.r2.core.tgz", ini) || save;
save = checkIniHasMapping("hl7.fhir.r2.examples", "http://hl7.org/fhir/DSTU2/hl7.fhir.r2.examples.tgz", ini) || save;
@ -382,6 +388,9 @@ public class PackageCacheManager {
public void recordMap(String url, String id) throws IOException {
if (url == null)
return;
if (url.contains("github.com")) {
return;
}
if (!(new File(Utilities.path(cacheFolder, "packages.ini")).exists()))
throw new Error("File "+Utilities.path(cacheFolder, "packages.ini")+" not found #1");
@ -431,7 +440,7 @@ public class PackageCacheManager {
String u = o.get("url").getAsString();
if (u.contains("/ImplementationGuide/"))
u = u.substring(0, u.indexOf("/ImplementationGuide/"));
builds.add(new BuildRecord(u, o.get("package-id").getAsString(), o.get("repo").getAsString(), readDate(o.get("date").getAsString())));
builds.add(new BuildRecord(u, o.get("package-id").getAsString(), getRepo(o.get("repo").getAsString()), readDate(o.get("date").getAsString())));
}
}
Collections.sort(builds, new BuildRecordSorter());
@ -443,6 +452,11 @@ public class PackageCacheManager {
}
}
private String getRepo(String path) {
String[] p = path.split("\\/");
return p[0]+"/"+p[1];
}
private Date readDate(String s) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM, yyyy HH:mm:ss Z", new Locale("en", "US"));
return sdf.parse(s);
@ -540,6 +554,13 @@ public class PackageCacheManager {
* @throws IOException
*/
public NpmPackage loadPackageFromCacheOnly(String id, String version) throws IOException {
if (Utilities.noString(version)) {
throw new FHIRException("Invalid version - ''");
}
if (version.startsWith("file:")) {
return loadPackageFromFile(id, version.substring(5));
}
for (NpmPackage p : temporaryPackages) {
if (p.name().equals(id) && ("current".equals(version) || "dev".equals(version) || p.version().equals(version)))
return p;
@ -559,6 +580,7 @@ public class PackageCacheManager {
* Add an already fetched package to the cache
*/
public NpmPackage addPackageToCache(String id, String version, InputStream tgz, String sourceDesc) throws IOException {
checkValidVersionString(version, id);
if (progress ) {
System.out.println("Installing "+id+"#"+(version == null ? "?" : version)+" to the package cache");
System.out.print(" Fetching:");
@ -636,11 +658,31 @@ public class PackageCacheManager {
return pck;
}
private void checkValidVersionString(String version, String id) {
if (Utilities.noString(version)) {
throw new FHIRException("Cannot add package "+id+" to the package cache - a version must be provided");
}
if (version.startsWith("file:")) {
throw new FHIRException("Cannot add package "+id+" to the package cache - the version '"+version+"' is illegal in this context");
}
for (char ch : version.toCharArray()) {
if (!Character.isAlphabetic(ch) && !Character.isDigit(ch) && !Utilities.existsInList(ch, '.', '-')) {
throw new FHIRException("Cannot add package "+id+" to the package cache - the version '"+version+"' is illegal (ch '"+ch+"'");
}
}
}
public NpmPackage loadPackage(String id) throws FHIRException, IOException {
throw new Error("Not done yet");
}
public NpmPackage loadPackage(String id, String v) throws FHIRException, IOException {
if (Utilities.noString(v)) {
throw new FHIRException("Invalid version - ''");
}
if (v.startsWith("file:")) {
return loadPackageFromFile(id, v.substring(5));
}
NpmPackage p = loadPackageFromCacheOnly(id, v);
if (p != null) {
if ("current".equals(v)) {
@ -659,33 +701,42 @@ public class PackageCacheManager {
}
String url = getPackageUrl(id);
if (url == null)
if (url == null) {
throw new FHIRException("Unable to resolve the package '"+id+"'");
}
String aurl = null;
try {
if (url.contains(".tgz")) {
aurl = url;
InputStream stream = fetchFromUrlSpecific(url, true);
if (stream != null)
return addPackageToCache(id, v, stream, url);
throw new FHIRException("Unable to find the package source for '"+id+"' at "+url);
}
if (v == null) {
aurl = Utilities.pathURL(url, "package.tgz");
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(url, "package.tgz"), true);
if (stream == null && isBuildLoaded()) {
aurl = Utilities.pathURL(buildPath(url), "package.tgz");
stream = fetchFromUrlSpecific(Utilities.pathURL(buildPath(url), "package.tgz"), true);
}
if (stream != null)
return addPackageToCache(id, null, stream, url);
throw new FHIRException("Unable to find the package source for '"+id+"' at "+url);
} else if ("current".equals(v) && ciList.containsKey(id)){
aurl = Utilities.pathURL(buildPath(url), "package.tgz");
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(ciList.get(id), "package.tgz"), true);
return addPackageToCache(id, v, stream, Utilities.pathURL(ciList.get(id), "package.tgz"));
} else {
String pu = Utilities.pathURL(url, "package-list.json");
aurl = pu;
JsonObject json;
try {
json = fetchJson(pu);
} catch (Exception e) {
String pv = Utilities.pathURL(url, v, "package.tgz");
try {
aurl = pv;
InputStream stream = fetchFromUrlSpecific(pv, true);
return addPackageToCache(id, v, stream, pv);
} catch (Exception e1) {
@ -697,6 +748,7 @@ public class PackageCacheManager {
for (JsonElement e : json.getAsJsonArray("list")) {
JsonObject vo = (JsonObject) e;
if (v.equals(JSONUtil.str(vo, "version"))) {
aurl = Utilities.pathURL(JSONUtil.str(vo, "path"), "package.tgz");
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(JSONUtil.str(vo, "path"), "package.tgz"), true);
if (stream == null)
throw new FHIRException("Unable to find the package source for '"+id+"#"+v+"' at "+Utilities.pathURL(JSONUtil.str(vo, "path"), "package.tgz"));
@ -712,6 +764,9 @@ public class PackageCacheManager {
// }
throw new FHIRException("Unable to resolve version "+v+" for package "+id);
}
} catch (Exception e) {
throw new FHIRException(e.getMessage()+(aurl == null ? "" : " (url = "+aurl+")"), e);
}
}
@ -731,6 +786,16 @@ public class PackageCacheManager {
* @return
*/
public boolean hasPackage(String id, String version) {
if (Utilities.noString(version)) {
throw new FHIRException("Invalid version - ''");
}
if (version.startsWith("file:")) {
try {
return loadPackageFromFile(id, version.substring(5)) != null;
} catch (IOException e) {
return false;
}
}
for (NpmPackage p : temporaryPackages) {
if (p.name().equals(id) && ("current".equals(version) || "dev".equals(version) || p.version().equals(version)))
return true;
@ -747,6 +812,21 @@ public class PackageCacheManager {
}
private NpmPackage loadPackageFromFile(String id, String folder) throws IOException {
File f = new File(Utilities.path(folder, id));
if (!f.exists()) {
throw new FHIRException("Package '"+id+" not found in folder "+folder);
}
if (!f.isDirectory()) {
throw new FHIRException("File for '"+id+" found in folder "+folder+", not a folder");
}
File fp = new File(Utilities.path(folder, id, "package", "package.json"));
if (!fp.exists()) {
throw new FHIRException("Package '"+id+" found in folder "+folder+", but does not contain a package.json file in /package");
}
return NpmPackage.fromFolder(f.getAbsolutePath());
}
/**
* List which versions of a package are available
*

View File

@ -0,0 +1,165 @@
package org.hl7.fhir.utilities.cache;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.cache.PackageClient.PackageInfo;
import org.hl7.fhir.utilities.json.JSONUtil;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class PackageClient {
public class PackageInfo {
private String id;
private String version;
private String fhirVersion;
private String description;
private String url;
public PackageInfo(String id, String version, String fhirVersion, String description, String url) {
super();
this.id = id;
this.version = version;
this.fhirVersion = fhirVersion;
this.description = description;
this.url = url;
}
public String getId() {
return id;
}
public String getVersion() {
return version;
}
public String getFhirVersion() {
return fhirVersion;
}
public String getDescription() {
return description;
}
public String getUrl() {
return url;
}
@Override
public String toString() {
return id+"#"+(version == null ? "??" : version)+(fhirVersion == null ? "": "for FHIR "+fhirVersion)+(url == null ? "" : " @"+url)+(description == null ? "" : " '"+description+"'");
}
}
private String address;
public PackageClient(String address) {
super();
this.address = address;
}
public boolean exists(String id, String ver) throws IOException {
List<PackageInfo> vl = getVersions(id);
for (PackageInfo pi : vl) {
if (ver.equals(pi.getVersion())) {
return true;
}
}
return false;
}
public InputStream fetch(String id, String ver) throws IOException {
return fetchUrl(Utilities.pathURL(address, id, ver));
}
public InputStream fetch(PackageInfo info) throws IOException {
return fetchUrl(Utilities.pathURL(address, info.getId(), info.getVersion()));
}
public InputStream fetchNpm(String id, String ver) throws IOException {
return fetchUrl(Utilities.pathURL(address, id, "-", id+"-"+ver+".tgz"));
}
public List<PackageInfo> getVersions(String id) throws IOException {
List<PackageInfo> res = new ArrayList<>();
JsonObject json;
try {
json = fetchJson(Utilities.pathURL(address, id));
JsonObject versions = json.getAsJsonObject("versions");
if (versions != null) {
for (String v : sorted(versions.keySet())) {
JsonObject obj = versions.getAsJsonObject(v);
res.add(new PackageInfo(JSONUtil.str(obj, "name"), JSONUtil.str(obj, "version"), JSONUtil.str(obj, "FhirVersion"), JSONUtil.str(obj, "description"), JSONUtil.str(obj, "url")));
}
}
} catch (FileNotFoundException e) {
}
return res;
}
private List<String> sorted(Set<String> keys) {
List<String> res = new ArrayList<>();
res.addAll(keys);
Collections.sort(res);
return res;
}
public List<PackageInfo> search(String name, String canonical, String fhirVersion, boolean preRelease) throws IOException {
CommaSeparatedStringBuilder params = new CommaSeparatedStringBuilder("&");
if (!Utilities.noString(name)) {
params.append("name="+name);
}
if (!Utilities.noString(canonical)) {
params.append("canonical="+canonical);
}
if (!Utilities.noString(fhirVersion)) {
params.append("fhirversion="+fhirVersion);
}
if (preRelease) {
params.append("prerelease="+preRelease);
}
List<PackageInfo> res = new ArrayList<>();
try {
JsonArray json = fetchJsonArray(Utilities.pathURL(address, "catalog?")+params.toString());
for (JsonElement e : json) {
JsonObject obj = (JsonObject) e;
res.add(new PackageInfo(JSONUtil.str(obj, "Name"), null, JSONUtil.str(obj, "FhirVersion"), JSONUtil.str(obj, "Description"), null));
}
} catch (FileNotFoundException e1) {
}
return res;
}
public Date getNewPackages(Date lastCalled, List<PackageInfo> updates) {
return null;
}
private InputStream fetchUrl(String source) throws IOException {
URL url = new URL(source);
URLConnection c = url.openConnection();
return c.getInputStream();
}
private JsonObject fetchJson(String source) throws IOException {
String src = TextFile.streamToString(fetchUrl(source));
//System.out.println(src);
return (JsonObject) new com.google.gson.JsonParser().parse(src);
}
private JsonArray fetchJsonArray(String source) throws IOException {
String src = TextFile.streamToString(fetchUrl(source));
//System.out.println(src);
return (JsonArray) new com.google.gson.JsonParser().parse(src);
}
}

View File

@ -28,6 +28,7 @@ import java.util.Map.Entry;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
public class JSONUtil {
@ -73,7 +74,7 @@ public class JSONUtil {
public static String str(JsonObject json, String name) {
JsonElement e = json.get(name);
return e == null ? null : e.getAsString();
return e == null || e instanceof JsonNull ? null : e.getAsString();
}
public static String str(JsonObject json, String name1, String name2) {

View File

@ -487,6 +487,8 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
private String html;
private String locationLink;
private String txLink;
public String sliceHtml;
private boolean slicingHint;
/**
@ -746,5 +748,22 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
this.html = html;
}
public boolean isSlicingHint() {
return slicingHint;
}
public ValidationMessage setSlicingHint(boolean slicingHint) {
this.slicingHint = slicingHint;
return this;
}
public String getSliceHtml() {
return sliceHtml;
}
public void setSliceHtml(String sliceHtml) {
this.sliceHtml = sliceHtml;
}
}

View File

@ -0,0 +1,18 @@
package org.hl7.fhir.utilities.tests;
import java.io.IOException;
import org.hl7.fhir.utilities.Utilities;
import org.junit.Test;
import junit.framework.Assert;
public class UtilitiesTests {
@Test
public void testPath() throws IOException {
Assert.assertEquals(Utilities.path("[tmp]", "test.txt"), "c:\\temp\\test.txt");
Assert.assertEquals(Utilities.path("[user]", "test.txt"), System.getProperty("user.home")+"\\test.txt");
Assert.assertEquals(Utilities.path("[JAVA_HOME]", "test.txt"), System.getenv("JAVA_HOME")+"\\test.txt");
}
}

View File

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

View File

@ -145,8 +145,23 @@ public class BaseValidator {
*/
protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) {
if (!thePass) {
addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION);
}
addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION);
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails. And mark it as a slicing hint for later recovery if appropriate
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean slicingHint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, String html) {
if (!thePass) {
addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION).setSlicingHint(true).setSliceHtml(html);
}
return thePass;
}
@ -168,8 +183,7 @@ public class BaseValidator {
protected boolean txHint(List<ValidationMessage> errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String message = formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION, Source.TerminologyEngine)
.setTxLink(txLink);
addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION, Source.TerminologyEngine).setTxLink(txLink);
}
return thePass;
}
@ -338,9 +352,9 @@ public class BaseValidator {
}
protected void addValidationMessage(List<ValidationMessage> errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity) {
protected ValidationMessage addValidationMessage(List<ValidationMessage> errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity) {
Source source = this.source;
addValidationMessage(errors, type, line, col, path, msg, theSeverity, source);
return addValidationMessage(errors, type, line, col, path, msg, theSeverity, source);
}
protected ValidationMessage addValidationMessage(List<ValidationMessage> errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity, Source theSource) {

View File

@ -108,6 +108,7 @@ import org.hl7.fhir.r5.model.Range;
import org.hl7.fhir.r5.model.Ratio;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.SampledData;
import org.hl7.fhir.r5.model.SearchParameter;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.ExtensionContextType;
@ -156,6 +157,9 @@ import ca.uhn.fhir.util.ObjectUtil;
* Thinking of using this in a java program? Don't!
* You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine
*
* Validation todo:
* - support @default slices
*
* @author Grahame Grieve
*
*/
@ -207,6 +211,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private Element rootResource;
private StructureDefinition profile; // the profile that contains the content being validated
private boolean checkSpecials = true;
private Map<String, List<ValidationMessage>> sliceRecords;
public ValidatorHostContext(Object appContext) {
this.appContext = appContext;
@ -242,6 +247,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
res.rootResource = rootResource;
res.container = container;
res.profile = profile;
res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap<String, List<ValidationMessage>>();
return res;
}
@ -277,6 +283,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return resource;
}
public void sliceNotes(String url, List<ValidationMessage> record) {
sliceRecords.put(url, record);
}
public ValidatorHostContext forSlicing() {
ValidatorHostContext res = new ValidatorHostContext(appContext);
res.resource = resource;
res.rootResource = resource;
res.container = resource;
res.profile = profile;
res.checkSpecials = false;
res.sliceRecords = new HashMap<String, List<ValidationMessage>>();
return res;
}
}
@ -405,8 +426,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else
throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element");
boolean ok = true;
for (ValidationMessage v : valerrors)
List<ValidationMessage> record = new ArrayList<>();
for (ValidationMessage v : valerrors) {
ok = ok && !v.getLevel().isError();
if (v.getLevel().isError() || v.isSlicingHint()) {
record.add(v);
}
}
if (!ok && !record.isEmpty()) {
ctxt.sliceNotes(url, record);
}
return ok;
}
@ -1597,9 +1626,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
Set<String> allowedTypes = listExtensionTypes(ex);
String actualType = getExtensionType(element);
if (actualType == null)
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", allowedTypes.isEmpty(), "The Extension '" + url + "' definition is for a simple extension, so it must contain a value, not extensions");
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), "The Extension '" + url + "' definition is for a simple extension, so it must contain a value, not extensions");
else
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", allowedTypes.contains(actualType), "The Extension '" + url + "' definition allows for the types "+allowedTypes.toString()+" but found type "+actualType);
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType), "The Extension '" + url + "' definition allows for the types "+allowedTypes.toString()+" but found type "+actualType);
// 3. is the content of the extension valid?
validateElement(hostContext, errors, ex, ex.getSnapshot().getElement().get(0), null, null, resource, element, "Extension", stack, false, true, url);
@ -1691,7 +1720,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = true;
break;
}
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (sd.getBaseDefinition() != null) {
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
} else {
sd = null;
}
}
}
}
@ -1959,7 +1992,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (fetcher != null) {
boolean found;
try {
found = (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url);
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url);
} catch (IOException e1) {
found = false;
}
@ -2122,6 +2155,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// for nothing to check
}
private boolean isDefinitionURL(String url) {
return Utilities.existsInList(url, "http://hl7.org/fhirpath/System.Boolean", "http://hl7.org/fhirpath/System.String", "http://hl7.org/fhirpath/System.Integer",
"http://hl7.org/fhirpath/System.Decimal", "http://hl7.org/fhirpath/System.Date", "http://hl7.org/fhirpath/System.Time", "http://hl7.org/fhirpath/System.DateTime", "http://hl7.org/fhirpath/System.Quantity");
}
private void checkInnerNames(List<ValidationMessage> errors, Element e, String path, List<XhtmlNode> list) {
for (XhtmlNode node : list) {
if (node.getNodeType() == NodeType.Element) {
@ -2353,7 +2391,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!isShowMessagesFromReferences()) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), "Unable to find matching profile for "+ref+" among choices: " + asList(type.getTargetProfile()));
for (StructureDefinition sd : badProfiles.keySet()) {
hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Profile "+sd.getUrl()+" does not match for "+ref+" because of the following errors: "+errorSummary(badProfiles.get(sd)));
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for "+ref+" matching against Profile"+sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd)));
}
} else {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size()==1, "Unable to find matching profile for "+ref+" among choices: " + asList(type.getTargetProfile()));
@ -2369,7 +2407,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!isShowMessagesFromReferences()) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for "+ref+" among choices: " + asListByUrl(goodProfiles.keySet()));
for (StructureDefinition sd : badProfiles.keySet()) {
hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Profile "+sd.getUrl()+" does not match for "+ref+" because of the following errors: "+errorSummary(badProfiles.get(sd)));
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for "+ref+" matching against Profile"+sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd)));
}
} else {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for "+ref+" among choices: " + asListByUrl(goodProfiles.keySet()));
@ -2404,6 +2442,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED);
rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef, "Bundled or contained reference not found within the bundle/resource " + ref);
}
if (we == null && ft != null && assumeValidRestReferences) {
// if we == null, we inferred ft from the reference. if we are told to treat this as gospel
TypeRefComponent type = getReferenceTypeRef(container.getType());
Set<String> types = new HashSet<>();
for (CanonicalType tp : type.getTargetProfile()) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, tp.getValue());
if (sd != null) {
types.add(sd.getType());
}
}
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft), "The type '"+ft+"' implied by the reference URL "+ref+" is not a valid Target for this element (must be one of "+types+")");
}
if (pol == ReferenceValidationPolicy.CHECK_VALID) {
// todo....
}
@ -2434,16 +2485,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return true;
}
private String errorSummary(List<ValidationMessage> list) {
private String errorSummaryForSlicing(List<ValidationMessage> list) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (ValidationMessage vm : list) {
if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL) {
if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL || vm.isSlicingHint()) {
b.append(vm.getLocation()+": "+vm.getMessage());
}
}
return b.toString();
}
private String errorSummaryForSlicingAsHtml(List<ValidationMessage> list) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (ValidationMessage vm : list) {
if (vm.isSlicingHint()) {
b.append("<li>"+vm.getLocation()+": "+vm.getSliceHtml()+"</li>");
} else if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL ) {
b.append("<li>"+vm.getLocation()+": "+vm.getHtml()+"</li>");
}
}
return "<ul>"+b.toString()+"</ul>";
}
private TypeRefComponent getReferenceTypeRef(List<TypeRefComponent> types) {
for (TypeRefComponent tr : types) {
if ("Reference".equals(tr.getCode())) {
@ -3186,7 +3249,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
* @throws IOException
* @throws FHIRException
*/
private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, NodeStack stack) throws DefinitionException, FHIRException {
private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, NodeStack stack) throws DefinitionException, FHIRException {
if (!slicer.getSlicing().hasDiscriminator())
return false; // cannot validate in this case
@ -3221,19 +3284,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else
throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in "+profile.getUrl()+" has no types");
if (discriminator.isEmpty())
expression.append(" and this is " + type);
expression.append(" and $this is " + type);
else
expression.append(" and " + discriminator + " is " + type);
} else if (s.getType() == DiscriminatorType.PROFILE) {
if (criteriaElement.getType().size() == 0) {
throw new DefinitionException("Profile based discriminators must have a type ("+criteriaElement.getId()+")");
throw new DefinitionException("Profile based discriminators must have a type ("+criteriaElement.getId()+" in profile "+profile.getUrl()+")");
}
if (criteriaElement.getType().size() != 1) {
throw new DefinitionException("Profile based discriminators must have only one type ("+criteriaElement.getId()+")");
throw new DefinitionException("Profile based discriminators must have only one type ("+criteriaElement.getId()+" in profile "+profile.getUrl()+")");
}
List<CanonicalType> list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile();
if (list.size() == 0) {
throw new DefinitionException("Profile based discriminators must have a type with a profile ("+criteriaElement.getId()+")");
throw new DefinitionException("Profile based discriminators must have a type with a profile ("+criteriaElement.getId()+" in profile "+profile.getUrl()+")");
} else if (list.size() > 1) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or ");
for (CanonicalType c : list) {
@ -3281,7 +3344,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ed.setUserData("slice.expression.cache", n);
}
return evaluateSlicingExpression(hostContext, element, path, profile, n);
ValidatorHostContext shc = hostContext.forSlicing();
boolean pass = evaluateSlicingExpression(shc, element, path, profile, n);
if (!pass) {
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Does not match slice'"+ed.getSliceName(), "discriminator = "+Utilities.escapeXml(n.toString()));
for (String url : shc.sliceRecords.keySet()) {
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for "+stack.getLiteralPath()+" against profile "+url,
"Profile "+url+" does not match for "+stack.getLiteralPath()+" because of the following profile issues: "+errorSummaryForSlicingAsHtml(shc.sliceRecords.get(url)));
}
}
return pass;
}
public boolean evaluateSlicingExpression(ValidatorHostContext hostContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException {
@ -3518,22 +3590,30 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
if (checkSpecials) {
// specific known special validations
if (element.getType().equals("Bundle")) {
validateBundle(errors, element, stack);
} else if (element.getType().equals("Observation")) {
validateObservation(errors, element, stack);
} else if (element.getType().equals("Questionnaire")) {
validateQuestionannaire(errors, element, stack);
} else if (element.getType().equals("QuestionnaireResponse")) {
validateQuestionannaireResponse(hostContext, errors, element, stack);
} else if (element.getType().equals("CodeSystem")) {
validateCodeSystem(errors, element, stack);
}
checkSpecials(hostContext, errors, element, stack, checkSpecials);
validateResourceRules(errors, element, stack);
}
}
public void checkSpecials(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials) {
// specific known special validations
if (element.getType().equals("Bundle")) {
validateBundle(errors, element, stack, checkSpecials);
} else if (element.getType().equals("Observation")) {
validateObservation(errors, element, stack);
} else if (element.getType().equals("Questionnaire")) {
ArrayList<Element> parents = new ArrayList<>();
parents.add(element);
validateQuestionannaireItem(errors, element, element, stack, parents);
} else if (element.getType().equals("QuestionnaireResponse")) {
validateQuestionannaireResponse(hostContext, errors, element, stack);
} else if (element.getType().equals("CapabilityStatement")) {
validateCapabilityStatement(errors, element, stack);
} else if (element.getType().equals("CodeSystem")) {
validateCodeSystem(errors, element, stack);
}
}
private ResourceValidationTracker getResourceTracker(Element element) {
ResourceValidationTracker res = resourceTracker.get(element);
if (res == null) {
@ -3543,29 +3623,37 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return res;
}
private void validateQuestionannaire(List<ValidationMessage> errors, Element element, NodeStack stack) {
private void validateQuestionannaireItem(List<ValidationMessage> errors, Element element, Element questionnaire, NodeStack stack, List<Element> parents) {
List<Element> list = getItems(element);
for (int i = 0; i < list.size(); i++) {
Element e = list.get(i);
NodeStack ns = stack.push(element, i, e.getProperty().getDefinition(), e.getProperty().getDefinition());
validateQuestionnaireElement(errors, ns, element, e, new ArrayList<>());
NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition());
validateQuestionnaireElement(errors, ns, questionnaire, e, parents);
List<Element> np = new ArrayList<Element>();
np.add(e);
np.addAll(parents);
validateQuestionannaireItem(errors, e, questionnaire, ns, np);
}
}
private void validateQuestionnaireElement(List<ValidationMessage> errors, NodeStack ns, Element questionnaire, Element item, List<Element> parents) {
// R4+
if (FHIRVersion.isR4Plus(context.getVersion())) {
if (item.hasChild("enableWhen")) {
Element ew = item.getNamedChild("enableWhen");
String ql = ew.getNamedChildValue("question");
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null, "Questions with an enableWhen must have a value for the question link")) {
Element tgt = getQuestionById(item, ql);
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null, "Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition")) {
tgt = getQuestionById(questionnaire, ql);
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null, "Unable to find "+ql+" target for this question enableWhen")) {
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item, "Target for this question enableWhen can't reference itself")) {
warning(errors, IssueType.BUSINESSRULE, ns.literalPath, isBefore(item, tgt, parents), "The target of this enableWhen rule ("+ql+") comes after the question itself");
if (item.hasChildren("enableWhen")) {
List<Element> ewl = item.getChildren("enableWhen");
for (Element ew : ewl) {
// Element ew = item.getNamedChild("enableWhen");
String ql = ew.getNamedChildValue("question");
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null, "Questions with an enableWhen must have a value for the question link")) {
Element tgt = getQuestionById(item, ql);
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null, "Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition")) {
tgt = getQuestionById(questionnaire, ql);
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null, "Unable to find target '"+ql+"' for this question enableWhen")) {
if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item, "Target for this question enableWhen can't reference itself")) {
if (!isBefore(item, tgt, parents)) {
warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false, "The target of this enableWhen rule ("+ql+") comes after the question itself");
}
}
}
}
}
@ -3576,6 +3664,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean isBefore(Element item, Element tgt, List<Element> parents) {
// we work up the list, looking for tgt in the children of the parents
if (parents.contains(tgt)) {
// actually, if the target is a parent, that's automatically ok
return true;
}
for (Element p : parents) {
int i = findIndex(p, item);
int t = findIndex(p, tgt);
@ -3599,7 +3691,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
for (Element e : element.getChildren()) {
if (e == descendant)
return true;
if (isChild(element, descendant))
if (isChild(e, descendant))
return true;
}
return false;
@ -3638,11 +3730,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
Element div = text.getNamedChild("div");
if (lang != null && div != null) {
XhtmlNode xhtml = div.getXhtml();
String xl = xhtml.getAttribute("lang");
if (xl == null) {
warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have a language tag");
} else if (!xl.equals(lang)) {
warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language ("+lang+"), and the XHTML has a language ("+xl+"), but they differ ");
String l = xhtml.getAttribute("lang");
String xl = xhtml.getAttribute("xml:lang");
if (l == null && xl == null) {
warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)");
} else {
if (l == null) {
warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)");
} else if (!l.equals(lang)) {
warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language ("+lang+"), and the XHTML has a lang ("+l+"), but they differ ");
}
if (xl == null) {
warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)");
} else if (!xl.equals(lang)) {
warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language ("+lang+"), and the XHTML has an xml:lang ("+xl+"), but they differ ");
}
}
}
}
@ -3662,6 +3764,29 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
private void validateCapabilityStatement(List<ValidationMessage> errors, Element cs, NodeStack stack) {
int iRest = 0;
for (Element rest : cs.getChildrenByName("rest")) {
int iResource = 0;
for (Element resource : rest.getChildrenByName("resource")) {
int iSP = 0;
for (Element searchParam : resource.getChildrenByName("searchParam")) {
String ref = searchParam.getChildValue("definition");
String type = searchParam.getChildValue("type");
if (!Utilities.noString(ref)) {
SearchParameter sp = context.fetchResource(SearchParameter.class, ref);
if (sp != null) {
rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath+".rest["+iRest+"].resource["+iResource+"].searchParam["+iSP+"]",
sp.getType().toCode().equals(type), "Type mismatch - SearchParameter '"+sp.getUrl()+"' type is "+sp.getType().toCode()+", but type here is "+type);
}
}
iSP++;
}
iResource++;
}
iRest++;
}
}
private void validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) {
String url = cs.getNamedChildValue("url");
String vsu = cs.getNamedChildValue("valueSet");
@ -4247,7 +4372,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
return true;
}
private void validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack) {
private void validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials) {
List<Element> entries = new ArrayList<Element>();
bundle.getNamedChildren("entry", entries);
String type = bundle.getNamedChildValue("type");
@ -4291,6 +4416,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false, "The canonical URL ("+url+") cannot match the fullUrl ("+fullUrl+") unless the resource id ("+id+") also matches");
rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))), "The canonical URL ("+url+") cannot match the fullUrl ("+fullUrl+") unless on the canonical server itself");
}
// todo: check specials
}
}
@ -4617,9 +4743,6 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
// time = System.nanoTime();
// check type invariants
if (definition.getId().equals("Composition.section:sectionResults")) {
System.out.println("!");
}
checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false);
if (definition.getFixed() != null)
checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null);
@ -4630,16 +4753,14 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
if (childDefinitions.isEmpty()) {
if (actualType == null)
return; // there'll be an error elsewhere in this case, and we're going to stop.
StructureDefinition dt = null;
if (isAbsolute(actualType))
dt = this.context.fetchResource(StructureDefinition.class, actualType);
else
dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType);
if (dt == null)
throw new DefinitionException("Unable to resolve actual type " + actualType);
trackUsage(dt, hostContext, element);
childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0));
childDefinitions = getActualTypeChildren(hostContext, element, actualType);
} else if (definition.getType().size() > 1) {
// this only happens when the profile constrains the abstract children but leaves th choice open.
if (actualType == null)
return; // there'll be an error elsewhere in this case, and we're going to stop.
List<ElementDefinition> typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType);
// what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored)
mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType);
}
List<ElementInfo> children = listChildren(element, stack);
@ -4654,6 +4775,39 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
}
}
private void mergeChildLists(List<ElementDefinition> master, List<ElementDefinition> additional, String masterPath, String typePath) {
for (ElementDefinition ed : additional) {
boolean inMaster = false;
for (ElementDefinition t : master) {
String tp = masterPath + ed.getPath().substring(typePath.length());
if (t.getPath().equals(tp)) {
inMaster = true;
}
}
if (!inMaster) {
master.add(ed);
}
}
}
// todo: the element definition in context might assign a constrained profile for the type?
public List<ElementDefinition> getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) {
List<ElementDefinition> childDefinitions;
StructureDefinition dt = null;
if (isAbsolute(actualType))
dt = this.context.fetchResource(StructureDefinition.class, actualType);
else
dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType);
if (dt == null)
throw new DefinitionException("Unable to resolve actual type " + actualType);
trackUsage(dt, hostContext, element);
childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0));
return childDefinitions;
}
public void checkChild(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition,
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl)
throws FHIRException, DefinitionException {
@ -5018,7 +5172,10 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
// if (process) {
for (ElementInfo ei : children) {
unsupportedSlicing = matchSlice(hostContext, errors, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei);
if (ei.sliceInfo == null) {
ei.sliceInfo = new ArrayList<>();
}
unsupportedSlicing = matchSlice(hostContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei);
}
// }
}
@ -5032,14 +5189,16 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
if (ei.additionalSlice && ei.definition != null) {
if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) ||
ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) {
hint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.path, false, "This element does not match any known slice" + (profile == null ? "" : " for the profile " + profile.getUrl()));
slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.path, false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()),
"This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()+": "+errorSummaryForSlicingAsHtml(ei.sliceInfo)));
} else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) {
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, false, "This element does not match any known slice" + (profile == null ? "" : " for profile " + profile.getUrl() + " and slicing is CLOSED"));
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: "+errorSummaryForSlicing(ei.sliceInfo)),
"This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: "+errorSummaryForSlicingAsHtml(ei.sliceInfo)));
}
} else {
// Don't raise this if we're in an abstract profile, like Resource
if (!profile.getAbstract())
hint(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.path, (ei.definition != null), "Could not verify slice for profile " + profile.getUrl());
rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.path, (ei.definition != null), "This element is not allowed by the profile "+profile.getUrl());
}
// TODO: Should get the order of elements correct when parsing elements that are XML attributes vs. elements
boolean isXmlAttr = false;
@ -5081,7 +5240,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
checkInvariants(hostContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited);
}
public boolean matchSlice(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, NodeStack stack,
public boolean matchSlice(ValidatorHostContext hostContext, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, StructureDefinition profile, NodeStack stack,
ElementDefinition slicer, boolean unsupportedSlicing, List<String> problematicPaths, int sliceOffset, int i, ElementDefinition ed,
boolean childUnsupportedSlicing, ElementInfo ei) {
boolean match = false;
@ -5090,7 +5249,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
} else {
if (nameMatches(ei.name, tail(ed.getPath())))
try {
match = sliceMatches(hostContext, ei.element, ei.path, slicer, ed, profile, errors, stack);
match = sliceMatches(hostContext, ei.element, ei.path, slicer, ed, profile, errors, sliceInfo, stack);
if (match) {
ei.slice = slicer;
@ -5557,6 +5716,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
public class ElementInfo {
public List<ValidationMessage> sliceInfo;
public int index; // order of definition in overall order. all slices get the index of the slicing definition
public int sliceindex; // order of the definition in the slices (if slice != null)
public int count;

View File

@ -241,6 +241,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
private boolean debug;
private Set<String> loadedIgs = new HashSet<>();
private IValidatorResourceFetcher fetcher;
private boolean assumeValidRestReferences;
private class AsteriskFilter implements FilenameFilter {
String dir;
@ -432,7 +433,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return fetchFromUrl(src+(v == null ? "" : "|"+v), explore);
}
File f = new File(src);
File f = new File(Utilities.path(src));
if (f.exists()) {
if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists())
return loadPackage(new FileInputStream(Utilities.path(src, "package.tgz")), Utilities.path(src, "package.tgz"));
@ -760,8 +761,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
System.out.print("* load file: "+fn);
}
System.out.println(" - ignored due to error: "+(e.getMessage() == null ? " (null - NPE)" : e.getMessage()));
if (debug)
if (debug || ((e.getMessage() != null && e.getMessage().contains("cannot be cast")))) {
e.printStackTrace();
}
}
return r;
}
@ -1270,6 +1272,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setNoInvariantChecks(isNoInvariantChecks());
validator.setValidationLanguage(language);
validator.setAssumeValidRestReferences(assumeValidRestReferences);
validator.setFetcher(this);
return validator;
}
@ -1689,6 +1692,10 @@ public class ValidationEngine implements IValidatorResourceFetcher {
this.fetcher = fetcher;
}
public void setAssumeValidRestReferences(boolean assumeValidRestReferences) {
this.assumeValidRestReferences = assumeValidRestReferences;
}
}

View File

@ -217,6 +217,8 @@ public class Validator {
System.out.println(" referenced implementation guides or profiles as errors. (Default is to only raise information messages.)");
System.out.println("-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not");
System.out.println(" marked as mustSupport=true. Useful to identify elements included that may be ignored by recipients");
System.out.println("-assumeValidRestReferences: If present, assume that URLs that reference resources follow the RESTful URI pattern");
System.out.println(" and it is safe to infer the type from the URL");
System.out.println("");
System.out.println("The validator also supports the param -proxy=[address]:[port] for if you use a proxy");
System.out.println("");
@ -414,6 +416,7 @@ public class Validator {
String fhirpath = null;
String snomedCT = "900000000000207008";
boolean doDebug = false;
boolean assumeValidRestReferences = false;
// load the parameters - so order doesn't matter
for (int i = 0; i < args.length; i++) {
@ -447,6 +450,8 @@ public class Validator {
questionnaires.add(args[++i]);
} else if (args[i].equals("-native")) {
doNative = true;
} else if (args[i].equals("-assumeValidRestReferences")) {
assumeValidRestReferences = true;
} else if (args[i].equals("-debug")) {
doDebug = true;
} else if (args[i].equals("-sct")) {
@ -577,6 +582,7 @@ public class Validator {
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setLanguage(lang);
validator.setSnomedExtension(snomedCT);
validator.setAssumeValidRestReferences(assumeValidRestReferences);
IParser x;
if (output != null && output.endsWith(".json"))

View File

@ -177,6 +177,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
} else {
val.setAllowExamples(true);
}
val.setAssumeValidRestReferences(content.has("assumeValidRestReferences") ? content.get("assumeValidRestReferences").getAsBoolean() : false);
if (name.endsWith(".json"))
val.validate(null, errors, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON);
else
@ -200,6 +201,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION;
StructureDefinition sd = loadProfile(filename, contents, v, messages);
val.getContext().cacheResource(sd);
val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false);
List<ValidationMessage> errorsProfile = new ArrayList<ValidationMessage>();
if (name.endsWith(".json"))
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, asSdList(sd));

View File

@ -13,11 +13,11 @@
each other. It is fine to bump the point version of this POM without affecting
HAPI FHIR.
-->
<version>4.1.54-SNAPSHOT</version>
<version>4.1.63-SNAPSHOT</version>
<properties>
<hapi_fhir_version>4.1.0</hapi_fhir_version>
<validator_test_case_version>1.0.35-SNAPSHOT</validator_test_case_version>
<validator_test_case_version>1.0.42-SNAPSHOT</validator_test_case_version>
</properties>
<artifactId>org.hl7.fhir.core</artifactId>

View File

@ -1,7 +1,7 @@
@echo off
set oldver=4.1.53
set newver=4.1.54
set oldver=4.1.62
set newver=4.1.63
echo ..
echo =====================================================================
@ -11,7 +11,8 @@ echo ..
call mvn versions:set -DnewVersion=%newver%-SNAPSHOT
call git commit -a -m "Release new version"
call git commit -t v%newver% -a -m "Release new version %newver%"
call git push origin master
call "C:\tools\fnr.exe" -dir "C:\work\org.hl7.fhir\build" -fileMask "*.xml" -find "%oldver%-SNAPSHOT" -replace "%newver%-SNAPSHOT" -count 8
call "C:\tools\fnr.exe" -dir "C:\work\org.hl7.fhir\fhir-ig-publisher" -fileMask "*.xml" -find "%oldver%-SNAPSHOT" -replace "%newver%-SNAPSHOT" -count 2
@ -22,6 +23,9 @@ IF %ERRORLEVEL% NEQ 0 (
GOTO DONE
)
call "C:\tools\versionNotes.exe" -fileName C:\work\org.hl7.fhir\latest-ig-publisher\release-notes-validator.md -version %newver% -fileDest C:\temp\current-release-notes-validator.md -url https://fhir.github.io/latest-ig-publisher/org.hl7.fhir.validator.jar -maven https://oss.sonatype.org/service/local/artifact/maven/redirect?r=snapshots&g=ca.uhn.hapi.fhir&a=org.hl7.fhir.validation.cli&v=%newver%-SNAPSHOT&e=jar
copy org.hl7.fhir.validation.cli\target\org.hl7.fhir.validation.cli-%newver%-SNAPSHOT.jar ..\latest-ig-publisher\org.hl7.fhir.validator.jar
cd ..\latest-ig-publisher
call git commit -a -m "Release new version %newver%-SNAPSHOT"
@ -29,7 +33,9 @@ call git push origin master
cd ..\org.hl7.fhir.core
call python c:\tools\zulip-api\zulip\zulip\send.py --stream committers/notification --subject "java core" -m "New Java Core v%newver%-SNAPSHOT released. New Validator at https://oss.sonatype.org/service/local/artifact/maven/redirect?r=snapshots&g=ca.uhn.hapi.fhir&a=org.hl7.fhir.validation.cli&v=%newver%-SNAPSHOT&e=jar, and also deployed at https://fhir.github.io/latest-ig-publisher/org.hl7.fhir.validator.jar" --config-file zuliprc
call python c:\tools\zulip-api\zulip\zulip\send.py --stream tooling/releases --subject "Validator" -m "New Validator @ https://fhir.github.io/latest-ig-publisher/org.hl7.fhir.validator.jar (v%newver%)" --config-file zuliprc
call python c:\tools\zulip-api\zulip\zulip\send.py --stream tooling/releases --subject "Validator" --config-file zuliprc < C:\temp\current-release-notes-validator.md
del C:\temp\current-release-notes-validator.md
:DONE
echo ===============================================================