Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir.core into main_convertor_files
This commit is contained in:
commit
a4a6f852bd
|
@ -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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
165
org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java
vendored
Normal file
165
org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java
vendored
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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));
|
||||
|
|
4
pom.xml
4
pom.xml
|
@ -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>
|
||||
|
|
14
release.bat
14
release.bat
|
@ -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 ===============================================================
|
||||
|
|
Loading…
Reference in New Issue