diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index 53a8ac475..d57fb1f15 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_50.java index 3465ae370..d343c281d 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_50.java @@ -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); diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/OperationDefinition30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/OperationDefinition30_50.java index e9fbe4376..4dc0430cd 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/OperationDefinition30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/OperationDefinition30_50.java @@ -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()) { diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureDefinition30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureDefinition30_50.java index 2e1346654..738c8ed1a 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureDefinition30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureDefinition30_50.java @@ -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()) { diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/ValueSet30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/ValueSet30_50.java index 426e77c80..00e2ea731 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/ValueSet30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/ValueSet30_50.java @@ -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; } diff --git a/org.hl7.fhir.core.generator/configuration/Meta.java b/org.hl7.fhir.core.generator/configuration/Meta.java index 7137bf15b..e875d789c 100644 --- a/org.hl7.fhir.core.generator/configuration/Meta.java +++ b/org.hl7.fhir.core.generator/configuration/Meta.java @@ -29,7 +29,7 @@ * null 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; } diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index aa372157d..eb66cb271 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/model/Meta.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/model/Meta.java index bb0d97b8f..ae2b373e1 100644 --- a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/model/Meta.java +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/model/Meta.java @@ -277,7 +277,7 @@ public class Meta extends Type implements IBaseMetaType { * null 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; } diff --git a/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/MetaTest.java b/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/MetaTest.java new file mode 100644 index 000000000..6ded2d99c --- /dev/null +++ b/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/MetaTest.java @@ -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)); + } +} diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index 906fb23b3..2a22e1068 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index bb2834b61..d15a6299b 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Meta.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Meta.java index e98bef91f..90bfbf7fa 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Meta.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Meta.java @@ -421,7 +421,7 @@ public class Meta extends Type implements IBaseMetaType { * null 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; } diff --git a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/MetaTest.java b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/MetaTest.java new file mode 100644 index 000000000..2b6157fcc --- /dev/null +++ b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/MetaTest.java @@ -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)); + } +} + diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index 9768fbd06..b5d82f7ab 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Meta.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Meta.java index cf6835922..5a6440428 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Meta.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Meta.java @@ -476,7 +476,7 @@ public class Meta extends Type implements IBaseMetaType { * null 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; } diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/MetaTest.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/MetaTest.java new file mode 100644 index 000000000..215ab9f7e --- /dev/null +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/MetaTest.java @@ -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)); + } +} diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index de7db29bc..667c503f7 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index e2c3a3aaa..f2d8742ec 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -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 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 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 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 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 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)); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index 19d69ca45..11c4a94cf 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -539,7 +539,8 @@ public interface IWorkerContext { public void setOverrideVersionNs(String value); public StructureDefinition fetchTypeDefinition(String typeName); - + public StructureDefinition fetchRawProfile(String url); + public void setUcumService(UcumService ucumService); public String getLinkForUrl(String corePath, String s); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index a5838a0e4..455dc659f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -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); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Meta.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Meta.java index d6030976d..49fe64350 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Meta.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Meta.java @@ -1,685 +1,685 @@ -package org.hl7.fhir.r5.model; - - -/* - * #%L - * org.hl7.fhir.r5 - * %% - * Copyright (C) 2014 - 2019 Health Level 7 - * %% - * Licensed under the Apache License, Version 2.0 (the \"License\"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an \"AS IS\" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, \ - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this \ - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, \ - this list of conditions and the following disclaimer in the documentation \ - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND \ - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \ - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. \ - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \ - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \ - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR \ - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \ - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \ - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \ - POSSIBILITY OF SUCH DAMAGE. - */ - -// Generated on Tue, Dec 31, 2019 12:12+1100 for FHIR vcurrent - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.r5.model.Enumerations.*; -import org.hl7.fhir.instance.model.api.IBaseDatatypeElement; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.ICompositeType; -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.ChildOrder; -import ca.uhn.fhir.model.api.annotation.DatatypeDef; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.api.annotation.Block; - -import org.hl7.fhir.instance.model.api.IBaseMetaType; -/** - * Base StructureDefinition for Meta Type: The metadata about a resource. This is content in the resource that is maintained by the infrastructure. Changes to the content might not always be associated with version changes to the resource. - */ -@DatatypeDef(name="Meta") -public class Meta extends DataType implements IBaseMetaType { - - /** - * The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted. - */ - @Child(name = "versionId", type = {IdType.class}, order=0, min=0, max=1, modifier=false, summary=true) - @Description(shortDefinition="Version specific identifier", formalDefinition="The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted." ) - protected IdType versionId; - - /** - * When the resource last changed - e.g. when the version changed. - */ - @Child(name = "lastUpdated", type = {InstantType.class}, order=1, min=0, max=1, modifier=false, summary=true) - @Description(shortDefinition="When the resource version last changed", formalDefinition="When the resource last changed - e.g. when the version changed." ) - protected InstantType lastUpdated; - - /** - * A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc. - */ - @Child(name = "source", type = {UriType.class}, order=2, min=0, max=1, modifier=false, summary=true) - @Description(shortDefinition="Identifies where the resource comes from", formalDefinition="A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc." ) - protected UriType source; - - /** - * A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url). - */ - @Child(name = "profile", type = {CanonicalType.class}, order=3, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true) - @Description(shortDefinition="Profiles this resource claims to conform to", formalDefinition="A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url)." ) - protected List profile; - - /** - * Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure. - */ - @Child(name = "security", type = {Coding.class}, order=4, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true) - @Description(shortDefinition="Security Labels applied to this resource", formalDefinition="Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure." ) - @ca.uhn.fhir.model.api.annotation.Binding(valueSet="http://hl7.org/fhir/ValueSet/security-labels") - protected List security; - - /** - * Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource. - */ - @Child(name = "tag", type = {Coding.class}, order=5, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true) - @Description(shortDefinition="Tags applied to this resource", formalDefinition="Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource." ) - @ca.uhn.fhir.model.api.annotation.Binding(valueSet="http://hl7.org/fhir/ValueSet/common-tags") - protected List tag; - - private static final long serialVersionUID = -1386695622L; - - /** - * Constructor - */ - public Meta() { - super(); - } - - /** - * @return {@link #versionId} (The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.). This is the underlying object with id, value and extensions. The accessor "getVersionId" gives direct access to the value - */ - public IdType getVersionIdElement() { - if (this.versionId == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Meta.versionId"); - else if (Configuration.doAutoCreate()) - this.versionId = new IdType(); // bb - return this.versionId; - } - - public boolean hasVersionIdElement() { - return this.versionId != null && !this.versionId.isEmpty(); - } - - public boolean hasVersionId() { - return this.versionId != null && !this.versionId.isEmpty(); - } - - /** - * @param value {@link #versionId} (The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.). This is the underlying object with id, value and extensions. The accessor "getVersionId" gives direct access to the value - */ - public Meta setVersionIdElement(IdType value) { - this.versionId = value; - return this; - } - - /** - * @return The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted. - */ - public String getVersionId() { - return this.versionId == null ? null : this.versionId.getValue(); - } - - /** - * @param value The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted. - */ - public Meta setVersionId(String value) { - if (Utilities.noString(value)) - this.versionId = null; - else { - if (this.versionId == null) - this.versionId = new IdType(); - this.versionId.setValue(value); - } - return this; - } - - /** - * @return {@link #lastUpdated} (When the resource last changed - e.g. when the version changed.). This is the underlying object with id, value and extensions. The accessor "getLastUpdated" gives direct access to the value - */ - public InstantType getLastUpdatedElement() { - if (this.lastUpdated == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Meta.lastUpdated"); - else if (Configuration.doAutoCreate()) - this.lastUpdated = new InstantType(); // bb - return this.lastUpdated; - } - - public boolean hasLastUpdatedElement() { - return this.lastUpdated != null && !this.lastUpdated.isEmpty(); - } - - public boolean hasLastUpdated() { - return this.lastUpdated != null && !this.lastUpdated.isEmpty(); - } - - /** - * @param value {@link #lastUpdated} (When the resource last changed - e.g. when the version changed.). This is the underlying object with id, value and extensions. The accessor "getLastUpdated" gives direct access to the value - */ - public Meta setLastUpdatedElement(InstantType value) { - this.lastUpdated = value; - return this; - } - - /** - * @return When the resource last changed - e.g. when the version changed. - */ - public Date getLastUpdated() { - return this.lastUpdated == null ? null : this.lastUpdated.getValue(); - } - - /** - * @param value When the resource last changed - e.g. when the version changed. - */ - public Meta setLastUpdated(Date value) { - if (value == null) - this.lastUpdated = null; - else { - if (this.lastUpdated == null) - this.lastUpdated = new InstantType(); - this.lastUpdated.setValue(value); - } - return this; - } - - /** - * @return {@link #source} (A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.). This is the underlying object with id, value and extensions. The accessor "getSource" gives direct access to the value - */ - public UriType getSourceElement() { - if (this.source == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Meta.source"); - else if (Configuration.doAutoCreate()) - this.source = new UriType(); // bb - return this.source; - } - - public boolean hasSourceElement() { - return this.source != null && !this.source.isEmpty(); - } - - public boolean hasSource() { - return this.source != null && !this.source.isEmpty(); - } - - /** - * @param value {@link #source} (A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.). This is the underlying object with id, value and extensions. The accessor "getSource" gives direct access to the value - */ - public Meta setSourceElement(UriType value) { - this.source = value; - return this; - } - - /** - * @return A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc. - */ - public String getSource() { - return this.source == null ? null : this.source.getValue(); - } - - /** - * @param value A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc. - */ - public Meta setSource(String value) { - if (Utilities.noString(value)) - this.source = null; - else { - if (this.source == null) - this.source = new UriType(); - this.source.setValue(value); - } - return this; - } - - /** - * @return {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) - */ - public List getProfile() { - if (this.profile == null) - this.profile = new ArrayList(); - return this.profile; - } - - /** - * @return Returns a reference to this for easy method chaining - */ - public Meta setProfile(List theProfile) { - this.profile = theProfile; - return this; - } - - public boolean hasProfile() { - if (this.profile == null) - return false; - for (CanonicalType item : this.profile) - if (!item.isEmpty()) - return true; - return false; - } - - /** - * @return {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) - */ - public CanonicalType addProfileElement() {//2 - CanonicalType t = new CanonicalType(); - if (this.profile == null) - this.profile = new ArrayList(); - this.profile.add(t); - return t; - } - - /** - * @param value {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) - */ - public Meta addProfile(String value) { //1 - CanonicalType t = new CanonicalType(); - t.setValue(value); - if (this.profile == null) - this.profile = new ArrayList(); - this.profile.add(t); - return this; - } - - /** - * @param value {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) - */ - public boolean hasProfile(String value) { - if (this.profile == null) - return false; - for (CanonicalType v : this.profile) - if (v.getValue().equals(value)) // canonical - return true; - return false; - } - - /** - * @return {@link #security} (Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure.) - */ - public List getSecurity() { - if (this.security == null) - this.security = new ArrayList(); - return this.security; - } - - /** - * @return Returns a reference to this for easy method chaining - */ - public Meta setSecurity(List theSecurity) { - this.security = theSecurity; - return this; - } - - public boolean hasSecurity() { - if (this.security == null) - return false; - for (Coding item : this.security) - if (!item.isEmpty()) - return true; - return false; - } - - public Coding addSecurity() { //3 - Coding t = new Coding(); - if (this.security == null) - this.security = new ArrayList(); - this.security.add(t); - return t; - } - - public Meta addSecurity(Coding t) { //3 - if (t == null) - return this; - if (this.security == null) - this.security = new ArrayList(); - this.security.add(t); - return this; - } - - /** - * @return The first repetition of repeating field {@link #security}, creating it if it does not already exist {3} - */ - public Coding getSecurityFirstRep() { - if (getSecurity().isEmpty()) { - addSecurity(); - } - return getSecurity().get(0); - } - - /** - * @return {@link #tag} (Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource.) - */ - public List getTag() { - if (this.tag == null) - this.tag = new ArrayList(); - return this.tag; - } - - /** - * @return Returns a reference to this for easy method chaining - */ - public Meta setTag(List theTag) { - this.tag = theTag; - return this; - } - - public boolean hasTag() { - if (this.tag == null) - return false; - for (Coding item : this.tag) - if (!item.isEmpty()) - return true; - return false; - } - - public Coding addTag() { //3 - Coding t = new Coding(); - if (this.tag == null) - this.tag = new ArrayList(); - this.tag.add(t); - return t; - } - - public Meta addTag(Coding t) { //3 - if (t == null) - return this; - if (this.tag == null) - this.tag = new ArrayList(); - this.tag.add(t); - return this; - } - - /** - * @return The first repetition of repeating field {@link #tag}, creating it if it does not already exist {3} - */ - public Coding getTagFirstRep() { - if (getTag().isEmpty()) { - addTag(); - } - return getTag().get(0); - } - - protected void listChildren(List children) { - super.listChildren(children); - children.add(new Property("versionId", "id", "The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.", 0, 1, versionId)); - children.add(new Property("lastUpdated", "instant", "When the resource last changed - e.g. when the version changed.", 0, 1, lastUpdated)); - children.add(new Property("source", "uri", "A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.", 0, 1, source)); - children.add(new Property("profile", "canonical(StructureDefinition)", "A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).", 0, java.lang.Integer.MAX_VALUE, profile)); - children.add(new Property("security", "Coding", "Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure.", 0, java.lang.Integer.MAX_VALUE, security)); - children.add(new Property("tag", "Coding", "Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource.", 0, java.lang.Integer.MAX_VALUE, tag)); - } - - @Override - public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException { - switch (_hash) { - case -1407102957: /*versionId*/ return new Property("versionId", "id", "The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.", 0, 1, versionId); - case 1649733957: /*lastUpdated*/ return new Property("lastUpdated", "instant", "When the resource last changed - e.g. when the version changed.", 0, 1, lastUpdated); - case -896505829: /*source*/ return new Property("source", "uri", "A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.", 0, 1, source); - case -309425751: /*profile*/ return new Property("profile", "canonical(StructureDefinition)", "A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).", 0, java.lang.Integer.MAX_VALUE, profile); - case 949122880: /*security*/ return new Property("security", "Coding", "Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure.", 0, java.lang.Integer.MAX_VALUE, security); - case 114586: /*tag*/ return new Property("tag", "Coding", "Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource.", 0, java.lang.Integer.MAX_VALUE, tag); - default: return super.getNamedProperty(_hash, _name, _checkValid); - } - - } - - @Override - public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { - switch (hash) { - case -1407102957: /*versionId*/ return this.versionId == null ? new Base[0] : new Base[] {this.versionId}; // IdType - case 1649733957: /*lastUpdated*/ return this.lastUpdated == null ? new Base[0] : new Base[] {this.lastUpdated}; // InstantType - case -896505829: /*source*/ return this.source == null ? new Base[0] : new Base[] {this.source}; // UriType - case -309425751: /*profile*/ return this.profile == null ? new Base[0] : this.profile.toArray(new Base[this.profile.size()]); // CanonicalType - case 949122880: /*security*/ return this.security == null ? new Base[0] : this.security.toArray(new Base[this.security.size()]); // Coding - case 114586: /*tag*/ return this.tag == null ? new Base[0] : this.tag.toArray(new Base[this.tag.size()]); // Coding - default: return super.getProperty(hash, name, checkValid); - } - - } - - @Override - public Base setProperty(int hash, String name, Base value) throws FHIRException { - switch (hash) { - case -1407102957: // versionId - this.versionId = TypeConvertor.castToId(value); // IdType - return value; - case 1649733957: // lastUpdated - this.lastUpdated = TypeConvertor.castToInstant(value); // InstantType - return value; - case -896505829: // source - this.source = TypeConvertor.castToUri(value); // UriType - return value; - case -309425751: // profile - this.getProfile().add(TypeConvertor.castToCanonical(value)); // CanonicalType - return value; - case 949122880: // security - this.getSecurity().add(TypeConvertor.castToCoding(value)); // Coding - return value; - case 114586: // tag - this.getTag().add(TypeConvertor.castToCoding(value)); // Coding - return value; - default: return super.setProperty(hash, name, value); - } - - } - - @Override - public Base setProperty(String name, Base value) throws FHIRException { - if (name.equals("versionId")) { - this.versionId = TypeConvertor.castToId(value); // IdType - } else if (name.equals("lastUpdated")) { - this.lastUpdated = TypeConvertor.castToInstant(value); // InstantType - } else if (name.equals("source")) { - this.source = TypeConvertor.castToUri(value); // UriType - } else if (name.equals("profile")) { - this.getProfile().add(TypeConvertor.castToCanonical(value)); - } else if (name.equals("security")) { - this.getSecurity().add(TypeConvertor.castToCoding(value)); - } else if (name.equals("tag")) { - this.getTag().add(TypeConvertor.castToCoding(value)); - } else - return super.setProperty(name, value); - return value; - } - - @Override - public Base makeProperty(int hash, String name) throws FHIRException { - switch (hash) { - case -1407102957: return getVersionIdElement(); - case 1649733957: return getLastUpdatedElement(); - case -896505829: return getSourceElement(); - case -309425751: return addProfileElement(); - case 949122880: return addSecurity(); - case 114586: return addTag(); - default: return super.makeProperty(hash, name); - } - - } - - @Override - public String[] getTypesForProperty(int hash, String name) throws FHIRException { - switch (hash) { - case -1407102957: /*versionId*/ return new String[] {"id"}; - case 1649733957: /*lastUpdated*/ return new String[] {"instant"}; - case -896505829: /*source*/ return new String[] {"uri"}; - case -309425751: /*profile*/ return new String[] {"canonical"}; - case 949122880: /*security*/ return new String[] {"Coding"}; - case 114586: /*tag*/ return new String[] {"Coding"}; - default: return super.getTypesForProperty(hash, name); - } - - } - - @Override - public Base addChild(String name) throws FHIRException { - if (name.equals("versionId")) { - throw new FHIRException("Cannot call addChild on a primitive type Meta.versionId"); - } - else if (name.equals("lastUpdated")) { - throw new FHIRException("Cannot call addChild on a primitive type Meta.lastUpdated"); - } - else if (name.equals("source")) { - throw new FHIRException("Cannot call addChild on a primitive type Meta.source"); - } - else if (name.equals("profile")) { - throw new FHIRException("Cannot call addChild on a primitive type Meta.profile"); - } - else if (name.equals("security")) { - return addSecurity(); - } - else if (name.equals("tag")) { - return addTag(); - } - else - return super.addChild(name); - } - - public String fhirType() { - return "Meta"; - - } - - public Meta copy() { - Meta dst = new Meta(); - copyValues(dst); - return dst; - } - - public void copyValues(Meta dst) { - super.copyValues(dst); - dst.versionId = versionId == null ? null : versionId.copy(); - dst.lastUpdated = lastUpdated == null ? null : lastUpdated.copy(); - dst.source = source == null ? null : source.copy(); - if (profile != null) { - dst.profile = new ArrayList(); - for (CanonicalType i : profile) - dst.profile.add(i.copy()); - }; - if (security != null) { - dst.security = new ArrayList(); - for (Coding i : security) - dst.security.add(i.copy()); - }; - if (tag != null) { - dst.tag = new ArrayList(); - for (Coding i : tag) - dst.tag.add(i.copy()); - }; - } - - protected Meta typedCopy() { - return copy(); - } - - @Override - public boolean equalsDeep(Base other_) { - if (!super.equalsDeep(other_)) - return false; - if (!(other_ instanceof Meta)) - return false; - Meta o = (Meta) other_; - return compareDeep(versionId, o.versionId, true) && compareDeep(lastUpdated, o.lastUpdated, true) - && compareDeep(source, o.source, true) && compareDeep(profile, o.profile, true) && compareDeep(security, o.security, true) - && compareDeep(tag, o.tag, true); - } - - @Override - public boolean equalsShallow(Base other_) { - if (!super.equalsShallow(other_)) - return false; - if (!(other_ instanceof Meta)) - return false; - Meta o = (Meta) other_; - return compareValues(versionId, o.versionId, true) && compareValues(lastUpdated, o.lastUpdated, true) - && compareValues(source, o.source, true) && compareValues(profile, o.profile, true); - } - - public boolean isEmpty() { - return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(versionId, lastUpdated, source - , profile, security, tag); - } - -// Manual code (from Configuration.txt)t: - /** - * Convenience method which adds a tag - * - * @param theSystem The code system - * @param theCode The code - * @param theDisplay The display name - * @return Returns a reference to this for easy chaining - */ - public Meta addTag(String theSystem, String theCode, String theDisplay) { - addTag().setSystem(theSystem).setCode(theCode).setDisplay(theDisplay); - return this; - } - - /** - * Returns the first tag (if any) that has the given system and code, or returns - * null if none - */ - public Coding getTag(String theSystem, String theCode) { - for (Coding next : getTag()) { - if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) { - return next; - } - } - return null; - } - - /** - * Returns the first security label (if any) that has the given system and code, or returns - * null if none - */ - public Coding getSecurity(String theSystem, String theCode) { - for (Coding next : getTag()) { - if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) { - return next; - } - } - return null; - } - -// end addition - -} - +package org.hl7.fhir.r5.model; + + +/* + * #%L + * org.hl7.fhir.r5 + * %% + * Copyright (C) 2014 - 2019 Health Level 7 + * %% + * Licensed under the Apache License, Version 2.0 (the \"License\"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an \"AS IS\" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, \ + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this \ + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, \ + this list of conditions and the following disclaimer in the documentation \ + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND \ + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \ + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. \ + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \ + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \ + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR \ + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \ + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \ + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \ + POSSIBILITY OF SUCH DAMAGE. + */ + +// Generated on Tue, Dec 31, 2019 12:12+1100 for FHIR vcurrent + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.r5.model.Enumerations.*; +import org.hl7.fhir.instance.model.api.IBaseDatatypeElement; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.ICompositeType; +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.ChildOrder; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.api.annotation.Block; + +import org.hl7.fhir.instance.model.api.IBaseMetaType; +/** + * Base StructureDefinition for Meta Type: The metadata about a resource. This is content in the resource that is maintained by the infrastructure. Changes to the content might not always be associated with version changes to the resource. + */ +@DatatypeDef(name="Meta") +public class Meta extends DataType implements IBaseMetaType { + + /** + * The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted. + */ + @Child(name = "versionId", type = {IdType.class}, order=0, min=0, max=1, modifier=false, summary=true) + @Description(shortDefinition="Version specific identifier", formalDefinition="The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted." ) + protected IdType versionId; + + /** + * When the resource last changed - e.g. when the version changed. + */ + @Child(name = "lastUpdated", type = {InstantType.class}, order=1, min=0, max=1, modifier=false, summary=true) + @Description(shortDefinition="When the resource version last changed", formalDefinition="When the resource last changed - e.g. when the version changed." ) + protected InstantType lastUpdated; + + /** + * A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc. + */ + @Child(name = "source", type = {UriType.class}, order=2, min=0, max=1, modifier=false, summary=true) + @Description(shortDefinition="Identifies where the resource comes from", formalDefinition="A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc." ) + protected UriType source; + + /** + * A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url). + */ + @Child(name = "profile", type = {CanonicalType.class}, order=3, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true) + @Description(shortDefinition="Profiles this resource claims to conform to", formalDefinition="A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url)." ) + protected List profile; + + /** + * Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure. + */ + @Child(name = "security", type = {Coding.class}, order=4, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true) + @Description(shortDefinition="Security Labels applied to this resource", formalDefinition="Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure." ) + @ca.uhn.fhir.model.api.annotation.Binding(valueSet="http://hl7.org/fhir/ValueSet/security-labels") + protected List security; + + /** + * Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource. + */ + @Child(name = "tag", type = {Coding.class}, order=5, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true) + @Description(shortDefinition="Tags applied to this resource", formalDefinition="Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource." ) + @ca.uhn.fhir.model.api.annotation.Binding(valueSet="http://hl7.org/fhir/ValueSet/common-tags") + protected List tag; + + private static final long serialVersionUID = -1386695622L; + + /** + * Constructor + */ + public Meta() { + super(); + } + + /** + * @return {@link #versionId} (The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.). This is the underlying object with id, value and extensions. The accessor "getVersionId" gives direct access to the value + */ + public IdType getVersionIdElement() { + if (this.versionId == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Meta.versionId"); + else if (Configuration.doAutoCreate()) + this.versionId = new IdType(); // bb + return this.versionId; + } + + public boolean hasVersionIdElement() { + return this.versionId != null && !this.versionId.isEmpty(); + } + + public boolean hasVersionId() { + return this.versionId != null && !this.versionId.isEmpty(); + } + + /** + * @param value {@link #versionId} (The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.). This is the underlying object with id, value and extensions. The accessor "getVersionId" gives direct access to the value + */ + public Meta setVersionIdElement(IdType value) { + this.versionId = value; + return this; + } + + /** + * @return The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted. + */ + public String getVersionId() { + return this.versionId == null ? null : this.versionId.getValue(); + } + + /** + * @param value The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted. + */ + public Meta setVersionId(String value) { + if (Utilities.noString(value)) + this.versionId = null; + else { + if (this.versionId == null) + this.versionId = new IdType(); + this.versionId.setValue(value); + } + return this; + } + + /** + * @return {@link #lastUpdated} (When the resource last changed - e.g. when the version changed.). This is the underlying object with id, value and extensions. The accessor "getLastUpdated" gives direct access to the value + */ + public InstantType getLastUpdatedElement() { + if (this.lastUpdated == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Meta.lastUpdated"); + else if (Configuration.doAutoCreate()) + this.lastUpdated = new InstantType(); // bb + return this.lastUpdated; + } + + public boolean hasLastUpdatedElement() { + return this.lastUpdated != null && !this.lastUpdated.isEmpty(); + } + + public boolean hasLastUpdated() { + return this.lastUpdated != null && !this.lastUpdated.isEmpty(); + } + + /** + * @param value {@link #lastUpdated} (When the resource last changed - e.g. when the version changed.). This is the underlying object with id, value and extensions. The accessor "getLastUpdated" gives direct access to the value + */ + public Meta setLastUpdatedElement(InstantType value) { + this.lastUpdated = value; + return this; + } + + /** + * @return When the resource last changed - e.g. when the version changed. + */ + public Date getLastUpdated() { + return this.lastUpdated == null ? null : this.lastUpdated.getValue(); + } + + /** + * @param value When the resource last changed - e.g. when the version changed. + */ + public Meta setLastUpdated(Date value) { + if (value == null) + this.lastUpdated = null; + else { + if (this.lastUpdated == null) + this.lastUpdated = new InstantType(); + this.lastUpdated.setValue(value); + } + return this; + } + + /** + * @return {@link #source} (A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.). This is the underlying object with id, value and extensions. The accessor "getSource" gives direct access to the value + */ + public UriType getSourceElement() { + if (this.source == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Meta.source"); + else if (Configuration.doAutoCreate()) + this.source = new UriType(); // bb + return this.source; + } + + public boolean hasSourceElement() { + return this.source != null && !this.source.isEmpty(); + } + + public boolean hasSource() { + return this.source != null && !this.source.isEmpty(); + } + + /** + * @param value {@link #source} (A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.). This is the underlying object with id, value and extensions. The accessor "getSource" gives direct access to the value + */ + public Meta setSourceElement(UriType value) { + this.source = value; + return this; + } + + /** + * @return A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc. + */ + public String getSource() { + return this.source == null ? null : this.source.getValue(); + } + + /** + * @param value A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc. + */ + public Meta setSource(String value) { + if (Utilities.noString(value)) + this.source = null; + else { + if (this.source == null) + this.source = new UriType(); + this.source.setValue(value); + } + return this; + } + + /** + * @return {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) + */ + public List getProfile() { + if (this.profile == null) + this.profile = new ArrayList(); + return this.profile; + } + + /** + * @return Returns a reference to this for easy method chaining + */ + public Meta setProfile(List theProfile) { + this.profile = theProfile; + return this; + } + + public boolean hasProfile() { + if (this.profile == null) + return false; + for (CanonicalType item : this.profile) + if (!item.isEmpty()) + return true; + return false; + } + + /** + * @return {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) + */ + public CanonicalType addProfileElement() {//2 + CanonicalType t = new CanonicalType(); + if (this.profile == null) + this.profile = new ArrayList(); + this.profile.add(t); + return t; + } + + /** + * @param value {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) + */ + public Meta addProfile(String value) { //1 + CanonicalType t = new CanonicalType(); + t.setValue(value); + if (this.profile == null) + this.profile = new ArrayList(); + this.profile.add(t); + return this; + } + + /** + * @param value {@link #profile} (A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).) + */ + public boolean hasProfile(String value) { + if (this.profile == null) + return false; + for (CanonicalType v : this.profile) + if (v.getValue().equals(value)) // canonical + return true; + return false; + } + + /** + * @return {@link #security} (Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure.) + */ + public List getSecurity() { + if (this.security == null) + this.security = new ArrayList(); + return this.security; + } + + /** + * @return Returns a reference to this for easy method chaining + */ + public Meta setSecurity(List theSecurity) { + this.security = theSecurity; + return this; + } + + public boolean hasSecurity() { + if (this.security == null) + return false; + for (Coding item : this.security) + if (!item.isEmpty()) + return true; + return false; + } + + public Coding addSecurity() { //3 + Coding t = new Coding(); + if (this.security == null) + this.security = new ArrayList(); + this.security.add(t); + return t; + } + + public Meta addSecurity(Coding t) { //3 + if (t == null) + return this; + if (this.security == null) + this.security = new ArrayList(); + this.security.add(t); + return this; + } + + /** + * @return The first repetition of repeating field {@link #security}, creating it if it does not already exist {3} + */ + public Coding getSecurityFirstRep() { + if (getSecurity().isEmpty()) { + addSecurity(); + } + return getSecurity().get(0); + } + + /** + * @return {@link #tag} (Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource.) + */ + public List getTag() { + if (this.tag == null) + this.tag = new ArrayList(); + return this.tag; + } + + /** + * @return Returns a reference to this for easy method chaining + */ + public Meta setTag(List theTag) { + this.tag = theTag; + return this; + } + + public boolean hasTag() { + if (this.tag == null) + return false; + for (Coding item : this.tag) + if (!item.isEmpty()) + return true; + return false; + } + + public Coding addTag() { //3 + Coding t = new Coding(); + if (this.tag == null) + this.tag = new ArrayList(); + this.tag.add(t); + return t; + } + + public Meta addTag(Coding t) { //3 + if (t == null) + return this; + if (this.tag == null) + this.tag = new ArrayList(); + this.tag.add(t); + return this; + } + + /** + * @return The first repetition of repeating field {@link #tag}, creating it if it does not already exist {3} + */ + public Coding getTagFirstRep() { + if (getTag().isEmpty()) { + addTag(); + } + return getTag().get(0); + } + + protected void listChildren(List children) { + super.listChildren(children); + children.add(new Property("versionId", "id", "The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.", 0, 1, versionId)); + children.add(new Property("lastUpdated", "instant", "When the resource last changed - e.g. when the version changed.", 0, 1, lastUpdated)); + children.add(new Property("source", "uri", "A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.", 0, 1, source)); + children.add(new Property("profile", "canonical(StructureDefinition)", "A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).", 0, java.lang.Integer.MAX_VALUE, profile)); + children.add(new Property("security", "Coding", "Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure.", 0, java.lang.Integer.MAX_VALUE, security)); + children.add(new Property("tag", "Coding", "Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource.", 0, java.lang.Integer.MAX_VALUE, tag)); + } + + @Override + public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException { + switch (_hash) { + case -1407102957: /*versionId*/ return new Property("versionId", "id", "The version specific identifier, as it appears in the version portion of the URL. This value changes when the resource is created, updated, or deleted.", 0, 1, versionId); + case 1649733957: /*lastUpdated*/ return new Property("lastUpdated", "instant", "When the resource last changed - e.g. when the version changed.", 0, 1, lastUpdated); + case -896505829: /*source*/ return new Property("source", "uri", "A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.", 0, 1, source); + case -309425751: /*profile*/ return new Property("profile", "canonical(StructureDefinition)", "A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition-definitions.html#StructureDefinition.url).", 0, java.lang.Integer.MAX_VALUE, profile); + case 949122880: /*security*/ return new Property("security", "Coding", "Security labels applied to this resource. These tags connect specific resources to the overall security policy and infrastructure.", 0, java.lang.Integer.MAX_VALUE, security); + case 114586: /*tag*/ return new Property("tag", "Coding", "Tags applied to this resource. Tags are intended to be used to identify and relate resources to process and workflow, and applications are not required to consider the tags when interpreting the meaning of a resource.", 0, java.lang.Integer.MAX_VALUE, tag); + default: return super.getNamedProperty(_hash, _name, _checkValid); + } + + } + + @Override + public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { + switch (hash) { + case -1407102957: /*versionId*/ return this.versionId == null ? new Base[0] : new Base[] {this.versionId}; // IdType + case 1649733957: /*lastUpdated*/ return this.lastUpdated == null ? new Base[0] : new Base[] {this.lastUpdated}; // InstantType + case -896505829: /*source*/ return this.source == null ? new Base[0] : new Base[] {this.source}; // UriType + case -309425751: /*profile*/ return this.profile == null ? new Base[0] : this.profile.toArray(new Base[this.profile.size()]); // CanonicalType + case 949122880: /*security*/ return this.security == null ? new Base[0] : this.security.toArray(new Base[this.security.size()]); // Coding + case 114586: /*tag*/ return this.tag == null ? new Base[0] : this.tag.toArray(new Base[this.tag.size()]); // Coding + default: return super.getProperty(hash, name, checkValid); + } + + } + + @Override + public Base setProperty(int hash, String name, Base value) throws FHIRException { + switch (hash) { + case -1407102957: // versionId + this.versionId = TypeConvertor.castToId(value); // IdType + return value; + case 1649733957: // lastUpdated + this.lastUpdated = TypeConvertor.castToInstant(value); // InstantType + return value; + case -896505829: // source + this.source = TypeConvertor.castToUri(value); // UriType + return value; + case -309425751: // profile + this.getProfile().add(TypeConvertor.castToCanonical(value)); // CanonicalType + return value; + case 949122880: // security + this.getSecurity().add(TypeConvertor.castToCoding(value)); // Coding + return value; + case 114586: // tag + this.getTag().add(TypeConvertor.castToCoding(value)); // Coding + return value; + default: return super.setProperty(hash, name, value); + } + + } + + @Override + public Base setProperty(String name, Base value) throws FHIRException { + if (name.equals("versionId")) { + this.versionId = TypeConvertor.castToId(value); // IdType + } else if (name.equals("lastUpdated")) { + this.lastUpdated = TypeConvertor.castToInstant(value); // InstantType + } else if (name.equals("source")) { + this.source = TypeConvertor.castToUri(value); // UriType + } else if (name.equals("profile")) { + this.getProfile().add(TypeConvertor.castToCanonical(value)); + } else if (name.equals("security")) { + this.getSecurity().add(TypeConvertor.castToCoding(value)); + } else if (name.equals("tag")) { + this.getTag().add(TypeConvertor.castToCoding(value)); + } else + return super.setProperty(name, value); + return value; + } + + @Override + public Base makeProperty(int hash, String name) throws FHIRException { + switch (hash) { + case -1407102957: return getVersionIdElement(); + case 1649733957: return getLastUpdatedElement(); + case -896505829: return getSourceElement(); + case -309425751: return addProfileElement(); + case 949122880: return addSecurity(); + case 114586: return addTag(); + default: return super.makeProperty(hash, name); + } + + } + + @Override + public String[] getTypesForProperty(int hash, String name) throws FHIRException { + switch (hash) { + case -1407102957: /*versionId*/ return new String[] {"id"}; + case 1649733957: /*lastUpdated*/ return new String[] {"instant"}; + case -896505829: /*source*/ return new String[] {"uri"}; + case -309425751: /*profile*/ return new String[] {"canonical"}; + case 949122880: /*security*/ return new String[] {"Coding"}; + case 114586: /*tag*/ return new String[] {"Coding"}; + default: return super.getTypesForProperty(hash, name); + } + + } + + @Override + public Base addChild(String name) throws FHIRException { + if (name.equals("versionId")) { + throw new FHIRException("Cannot call addChild on a primitive type Meta.versionId"); + } + else if (name.equals("lastUpdated")) { + throw new FHIRException("Cannot call addChild on a primitive type Meta.lastUpdated"); + } + else if (name.equals("source")) { + throw new FHIRException("Cannot call addChild on a primitive type Meta.source"); + } + else if (name.equals("profile")) { + throw new FHIRException("Cannot call addChild on a primitive type Meta.profile"); + } + else if (name.equals("security")) { + return addSecurity(); + } + else if (name.equals("tag")) { + return addTag(); + } + else + return super.addChild(name); + } + + public String fhirType() { + return "Meta"; + + } + + public Meta copy() { + Meta dst = new Meta(); + copyValues(dst); + return dst; + } + + public void copyValues(Meta dst) { + super.copyValues(dst); + dst.versionId = versionId == null ? null : versionId.copy(); + dst.lastUpdated = lastUpdated == null ? null : lastUpdated.copy(); + dst.source = source == null ? null : source.copy(); + if (profile != null) { + dst.profile = new ArrayList(); + for (CanonicalType i : profile) + dst.profile.add(i.copy()); + }; + if (security != null) { + dst.security = new ArrayList(); + for (Coding i : security) + dst.security.add(i.copy()); + }; + if (tag != null) { + dst.tag = new ArrayList(); + for (Coding i : tag) + dst.tag.add(i.copy()); + }; + } + + protected Meta typedCopy() { + return copy(); + } + + @Override + public boolean equalsDeep(Base other_) { + if (!super.equalsDeep(other_)) + return false; + if (!(other_ instanceof Meta)) + return false; + Meta o = (Meta) other_; + return compareDeep(versionId, o.versionId, true) && compareDeep(lastUpdated, o.lastUpdated, true) + && compareDeep(source, o.source, true) && compareDeep(profile, o.profile, true) && compareDeep(security, o.security, true) + && compareDeep(tag, o.tag, true); + } + + @Override + public boolean equalsShallow(Base other_) { + if (!super.equalsShallow(other_)) + return false; + if (!(other_ instanceof Meta)) + return false; + Meta o = (Meta) other_; + return compareValues(versionId, o.versionId, true) && compareValues(lastUpdated, o.lastUpdated, true) + && compareValues(source, o.source, true) && compareValues(profile, o.profile, true); + } + + public boolean isEmpty() { + return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(versionId, lastUpdated, source + , profile, security, tag); + } + +// Manual code (from Configuration.txt)t: + /** + * Convenience method which adds a tag + * + * @param theSystem The code system + * @param theCode The code + * @param theDisplay The display name + * @return Returns a reference to this for easy chaining + */ + public Meta addTag(String theSystem, String theCode, String theDisplay) { + addTag().setSystem(theSystem).setCode(theCode).setDisplay(theDisplay); + return this; + } + + /** + * Returns the first tag (if any) that has the given system and code, or returns + * null if none + */ + public Coding getTag(String theSystem, String theCode) { + for (Coding next : getTag()) { + if (ca.uhn.fhir.util.ObjectUtil.equals(next.getSystem(), theSystem) && ca.uhn.fhir.util.ObjectUtil.equals(next.getCode(), theCode)) { + return next; + } + } + return null; + } + + /** + * Returns the first security label (if any) that has the given system and code, or returns + * null if none + */ + public Coding getSecurity(String theSystem, String theCode) { + 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; + } + } + return null; + } + +// end addition + +} + diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index 4515cc637..20053c157 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -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) { diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java index 5d10d4150..5bf6c714c 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java @@ -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 { diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java index 555630d2a..63885a005 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java @@ -99,7 +99,7 @@ public class FHIRPathTests { @Parameters(name = "{index}: file {0}") public static Iterable 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 list = new ArrayList(); List groups = new ArrayList(); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/MetaTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/MetaTest.java new file mode 100644 index 000000000..b311fca3a --- /dev/null +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/MetaTest.java @@ -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)); + } +} diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/PackageCacheTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/PackageCacheTests.java new file mode 100644 index 000000000..8b798a839 --- /dev/null +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/PackageCacheTests.java @@ -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); + + } + + +} + diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/PackageClientTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/PackageClientTests.java new file mode 100644 index 000000000..807d424e2 --- /dev/null +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/PackageClientTests.java @@ -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 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 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 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 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 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 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 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 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 matches = client.getVersions("Simplifier.Core.STU3X"); + Assert.assertTrue(matches.size() == 0); + } + + +} diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java index 9699e7b83..9c1bfd7e1 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java @@ -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; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/UtilitiesTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/UtilitiesTests.java new file mode 100644 index 000000000..b245d4afc --- /dev/null +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/UtilitiesTests.java @@ -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"); + } +} diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 59f79b22f..4bf772ba6 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index ce70b1bf4..514f571fc 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -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(); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java index e3ae236b8..6817c4c3e 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java @@ -186,7 +186,8 @@ 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()) { if (!Utilities.existsInList(f.getName(), ".git", ".svn") && !Utilities.existsInList(f.getName(), exemptions)) { @@ -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)); + } + } + } + } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java index 23c472ab9..c7c79d53f 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java @@ -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 * diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java new file mode 100644 index 000000000..47b7b023c --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java @@ -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 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 getVersions(String id) throws IOException { + List 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 sorted(Set keys) { + List res = new ArrayList<>(); + res.addAll(keys); + Collections.sort(res); + return res; + } + + public List 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 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 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); + } + + +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JSONUtil.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JSONUtil.java index 346f319e6..301574ea7 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JSONUtil.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JSONUtil.java @@ -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) { diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java index 85b94b619..fe28e6cb4 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java @@ -487,6 +487,8 @@ public class ValidationMessage implements Comparator, 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, 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; + } + } diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/UtilitiesTests.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/UtilitiesTests.java new file mode 100644 index 000000000..a7cc47ca1 --- /dev/null +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/UtilitiesTests.java @@ -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"); + } +} diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index a631cbfc4..91e1653f4 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index 7d926f1ae..dcadc3ebc 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/BaseValidator.java index 9dc56c767..81735d662 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/BaseValidator.java @@ -145,8 +145,23 @@ public class BaseValidator { */ protected boolean hint(List 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 false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + + protected boolean slicingHint(List 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 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 errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity) { + protected ValidationMessage addValidationMessage(List 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 errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity, Source theSource) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java index 81ed1244a..d23f65a01 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java @@ -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> 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>(); return res; } @@ -276,6 +282,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat public Base getResource() { return resource; } + + public void sliceNotes(String url, List 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>(); + 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 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 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 errors, Element e, String path, List 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 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 list) { + private String errorSummaryForSlicing(List 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 list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (ValidationMessage vm : list) { + if (vm.isSlicingHint()) { + b.append("
  • "+vm.getLocation()+": "+vm.getSliceHtml()+"
  • "); + } else if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL ) { + b.append("
  • "+vm.getLocation()+": "+vm.getHtml()+"
  • "); + } + } + return "
      "+b.toString()+"
    "; + } + private TypeRefComponent getReferenceTypeRef(List 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 errors, NodeStack stack) throws DefinitionException, FHIRException { + private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List errors, List 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 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 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 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,32 +3623,40 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return res; } - private void validateQuestionannaire(List errors, Element element, NodeStack stack) { + private void validateQuestionannaireItem(List errors, Element element, Element questionnaire, NodeStack stack, List parents) { List 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 np = new ArrayList(); + np.add(e); + np.addAll(parents); + validateQuestionannaireItem(errors, e, questionnaire, ns, np); } - } private void validateQuestionnaireElement(List errors, NodeStack ns, Element questionnaire, Element item, List 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 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 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 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 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 errors, Element bundle, NodeStack stack) { + private void validateBundle(List errors, Element bundle, NodeStack stack, boolean checkSpecials) { List entries = new ArrayList(); 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 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 children = listChildren(element, stack); @@ -4654,6 +4775,39 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L } } + private void mergeChildLists(List master, List 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 getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { + List 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 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,16 +5240,16 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L checkInvariants(hostContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited); } - public boolean matchSlice(ValidatorHostContext hostContext, List errors, StructureDefinition profile, NodeStack stack, + public boolean matchSlice(ValidatorHostContext hostContext, List errors, List sliceInfo, StructureDefinition profile, NodeStack stack, ElementDefinition slicer, boolean unsupportedSlicing, List problematicPaths, int sliceOffset, int i, ElementDefinition ed, boolean childUnsupportedSlicing, ElementInfo ei) { boolean match = false; if (slicer == null || slicer == ed) { - match = nameMatches(ei.name, tail(ed.getPath())); + match = nameMatches(ei.name, tail(ed.getPath())); } 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 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; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/ValidationEngine.java index e2cd2093d..89a003511 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/ValidationEngine.java @@ -241,6 +241,7 @@ public class ValidationEngine implements IValidatorResourceFetcher { private boolean debug; private Set 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; + } + } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/Validator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/Validator.java index bf4c3d31f..043f54ff1 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/Validator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/Validator.java @@ -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")) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java index 94f227698..66b5dd003 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java @@ -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 @@ -199,7 +200,8 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour System.out.println("Name: " + name+" - profile : "+profile.get("source").getAsString()); v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION; StructureDefinition sd = loadProfile(filename, contents, v, messages); - val.getContext().cacheResource(sd); + val.getContext().cacheResource(sd); + val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false); List errorsProfile = new ArrayList(); if (name.endsWith(".json")) val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, asSdList(sd)); diff --git a/pom.xml b/pom.xml index dc73cad7e..cf55740ef 100644 --- a/pom.xml +++ b/pom.xml @@ -13,11 +13,11 @@ each other. It is fine to bump the point version of this POM without affecting HAPI FHIR. --> - 4.1.54-SNAPSHOT + 4.1.63-SNAPSHOT 4.1.0 - 1.0.35-SNAPSHOT + 1.0.42-SNAPSHOT org.hl7.fhir.core diff --git a/release.bat b/release.bat index 34f1e3689..243f1a4b6 100644 --- a/release.bat +++ b/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 ===============================================================