diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEElement.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEElement.java index a302265c7..9a0c0711f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEElement.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEElement.java @@ -25,7 +25,7 @@ public class PEElement extends ProfiledElement { for (CanonicalType u : t.getProfile()) { res.add(t.getWorkingCode()+"["+u.getValue()+"]"); } - } else { + } else if (!t.getCode().startsWith("http://hl7.org/fhirpath/")) { res.add(t.getWorkingCode()); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElement.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElement.java index 14de92dfd..51bcad80a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElement.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElement.java @@ -74,7 +74,9 @@ public abstract class ProfiledElement { } /** - * @return a list of types. There is always at least one type; it might be Element, Type, BackboneElement or BackboneType + * @return a list of types. There is usually at least one type; it might be Element, Type, BackboneElement or BackboneType + * + * The following elements don't have types (true primitives): Element.id. Extension.url, PrimitiveType.value */ public abstract List types(); @@ -206,7 +208,7 @@ public abstract class ProfiledElement { @Override public String toString() { - return name+"("+schemaName()+"):"+types().toString()+" ["+min()+":"+max()+"] \""+shortDocumentation()+"\""; + return name+"("+schemaName()+"):"+types().toString()+" ["+min()+":"+(max() == Integer.MAX_VALUE ? "*" : max() )+"] \""+shortDocumentation()+"\""; } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElementBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElementBuilder.java index 0c3b95316..0a7df45d7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElementBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/ProfiledElementBuilder.java @@ -9,6 +9,7 @@ import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.ResourceFactory; @@ -38,8 +39,8 @@ public class ProfiledElementBuilder { * * When built with this method, the profile element can't have instance data * - * Warning: profiles and resources can be recursive; you can't iterate this tree until it you get - * to the leaves because you will never get to a child that doesn't have children + * Warning: profiles and resources are recursive; you can't iterate this tree until it you get + * to the leaves because there are nodes that don't terminate (extensions have extensions) * */ public ProfiledElement buildProfileElement(String url) { @@ -154,14 +155,27 @@ public class ProfiledElementBuilder { protected List listChildren(StructureDefinition baseStructure, ElementDefinition baseDefinition, StructureDefinition profileStructure, ElementDefinition profileDefinition, TypeRefComponent t) { if (profileDefinition.getType().size() == 1 || (!profileDefinition.getPath().contains("."))) { assert profileDefinition.getType().size() != 1 || profileDefinition.getType().contains(t); - List list = pu.getChildList(profileStructure, profileDefinition); - if (list != null && list.size() > 0) { + List res = new ArrayList<>(); + StructureDefinition profile = profileStructure; + List list = pu.getChildList(profile, profileDefinition); + if (list.size() == 0) { + profile = t.hasProfile() ? context.fetchResource(StructureDefinition.class, t.getProfile().get(0).getValue()) : context.fetchTypeDefinition(t.getWorkingCode()); + list = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep()); + } + if (list.size() > 0) { + StructureDefinition base = baseStructure; List blist = pu.getChildList(baseStructure, baseDefinition); - List res = new ArrayList<>(); + if (blist.size() == 0) { + base = context.fetchTypeDefinition(t.getWorkingCode()); + blist = pu.getChildList(base, base.getSnapshot().getElementFirstRep()); + } int i = 0; while (i < list.size()) { ElementDefinition defn = list.get(i); if (defn.hasSlicing()) { + if (defn.getSlicing().getRules() != SlicingRules.CLOSED) { + res.add(new PEElement(this, base, getByName(blist, defn), profileStructure, defn)); + } i++; while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) { StructureDefinition ext = getExtensionDefinition(list.get(i)); @@ -173,14 +187,12 @@ public class ProfiledElementBuilder { i++; } } else { - res.add(new PEElement(this, baseStructure, getByName(blist, defn), profileStructure, defn)); + res.add(new PEElement(this, base, getByName(blist, defn), profileStructure, defn)); i++; } } - return res; - } else { - throw new DefinitionException("not done yet"); } + return res; } else { throw new DefinitionException("not done yet"); } @@ -198,11 +210,12 @@ public class ProfiledElementBuilder { private ElementDefinition getByName(List blist, ElementDefinition defn) { for (ElementDefinition ed : blist) { - if (ed.getPath().equals(defn.getPath())) { + if (ed.getName().equals(defn.getName())) { return ed; } } return null; } + } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/ProfiledElementTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/ProfiledElementTests.java index 69d05ac9f..3270d40da 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/ProfiledElementTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/ProfiledElementTests.java @@ -36,9 +36,9 @@ public class ProfiledElementTests { Assertions.assertEquals("\\-", pe.documentation()); List children = pe.children("Patient"); - Assertions.assertEquals(27, children.size()); + Assertions.assertEquals(28, children.size()); - pe = children.get(8); + pe = children.get(9); Assertions.assertEquals("birthsex", pe.name()); Assertions.assertEquals("extension", pe.schemaName()); Assertions.assertEquals(0, pe.min()); @@ -49,7 +49,7 @@ public class ProfiledElementTests { Assertions.assertEquals("Extension", pe.shortDocumentation()); Assertions.assertEquals("A code classifying the person's sex assigned at birth as specified by the [Office of the National Coordinator for Health IT (ONC)](https://www.healthit.gov/newsroom/about-onc).", pe.documentation()); - pe = children.get(7); + pe = children.get(8); Assertions.assertEquals("ethnicity", pe.name()); Assertions.assertEquals("extension", pe.schemaName()); @@ -61,6 +61,98 @@ public class ProfiledElementTests { Assertions.assertEquals("US Core ethnicity Extension", pe.shortDocumentation()); Assertions.assertEquals("Concepts classifying the person into a named category of humans sharing common history, traits, geographical origin or nationality. The ethnicity codes used to represent these concepts are based upon the [CDC ethnicity and Ethnicity Code Set Version 1.0](http://www.cdc.gov/phin/resources/vocabulary/index.html) which includes over 900 concepts for representing race and ethnicity of which 43 reference ethnicity. The ethnicity concepts are grouped by and pre-mapped to the 2 OMB ethnicity categories: - Hispanic or Latino - Not Hispanic or Latino.", pe.documentation()); + pe = children.get(12); + Assertions.assertEquals("identifier", pe.name()); + Assertions.assertEquals("identifier", pe.schemaName()); + Assertions.assertEquals(1, pe.min()); + Assertions.assertEquals(Integer.MAX_VALUE, pe.max()); + Assertions.assertEquals("Identifier", pe.types().get(0)); + Assertions.assertNotNull(pe.definition()); + Assertions.assertNotNull(pe.baseDefinition()); + Assertions.assertEquals("An identifier for this patient", pe.shortDocumentation()); + Assertions.assertEquals("An identifier for this patient.", pe.documentation()); + + + List iChildren = pe.children("Identifier"); + Assertions.assertEquals(8, iChildren.size()); + + + pe = iChildren.get(2); + + Assertions.assertEquals("use", pe.name()); + Assertions.assertEquals("use", pe.schemaName()); + Assertions.assertEquals(0, pe.min()); + Assertions.assertEquals(1, pe.max()); + Assertions.assertEquals("code", pe.types().get(0)); + Assertions.assertNotNull(pe.definition()); + Assertions.assertNotNull(pe.baseDefinition()); + Assertions.assertEquals("usual | official | temp | secondary | old (If known)", pe.shortDocumentation()); + Assertions.assertEquals("The purpose of this identifier.", pe.documentation()); + + iChildren = pe.children("code"); + Assertions.assertEquals(3, iChildren.size()); + + pe = iChildren.get(2); + Assertions.assertEquals("value", pe.name()); + Assertions.assertEquals("value", pe.schemaName()); + Assertions.assertEquals(0, pe.min()); + Assertions.assertEquals(1, pe.max()); + Assertions.assertEquals(0, pe.types().size()); + Assertions.assertNotNull(pe.definition()); + Assertions.assertNotNull(pe.baseDefinition()); + Assertions.assertEquals("Primitive value for code", pe.shortDocumentation()); + Assertions.assertEquals("Primitive value for code", pe.documentation()); + + pe = iChildren.get(0); + Assertions.assertEquals("id", pe.name()); + Assertions.assertEquals("id", pe.schemaName()); + Assertions.assertEquals(0, pe.min()); + Assertions.assertEquals(1, pe.max()); + Assertions.assertEquals(0, pe.types().size()); + Assertions.assertNotNull(pe.definition()); + Assertions.assertNotNull(pe.baseDefinition()); + Assertions.assertEquals("xml:id (or equivalent in JSON)", pe.shortDocumentation()); + Assertions.assertEquals("unique id for the element within a resource (for internal references)", pe.documentation()); + + // let's go down the rabbit hole + pe = iChildren.get(1); + Assertions.assertEquals("extension", pe.name()); + Assertions.assertEquals("extension", pe.schemaName()); + Assertions.assertEquals(0, pe.min()); + Assertions.assertEquals(Integer.MAX_VALUE, pe.max()); + Assertions.assertEquals("Extension", pe.types().get(0)); + Assertions.assertNotNull(pe.definition()); + Assertions.assertNotNull(pe.baseDefinition()); + Assertions.assertEquals("Additional content defined by implementations", pe.shortDocumentation()); + Assertions.assertEquals("May be used to represent additional information that is not part of the basic definition of the resource. To make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", pe.documentation()); + + iChildren = pe.children("Extension"); + Assertions.assertEquals(4, iChildren.size()); + pe = iChildren.get(1); + Assertions.assertEquals("extension", pe.name()); + Assertions.assertEquals("extension", pe.schemaName()); + Assertions.assertEquals(0, pe.min()); + Assertions.assertEquals(Integer.MAX_VALUE, pe.max()); + Assertions.assertEquals("Extension", pe.types().get(0)); + Assertions.assertNotNull(pe.definition()); + Assertions.assertNotNull(pe.baseDefinition()); + Assertions.assertEquals("Additional content defined by implementations", pe.shortDocumentation()); + Assertions.assertEquals("May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", pe.documentation()); + + + iChildren = pe.children("Extension"); + Assertions.assertEquals(4, iChildren.size()); + pe = iChildren.get(1); + Assertions.assertEquals("extension", pe.name()); + Assertions.assertEquals("extension", pe.schemaName()); + Assertions.assertEquals(0, pe.min()); + Assertions.assertEquals(Integer.MAX_VALUE, pe.max()); + Assertions.assertEquals("Extension", pe.types().get(0)); + Assertions.assertNotNull(pe.definition()); + Assertions.assertNotNull(pe.baseDefinition()); + Assertions.assertEquals("Additional content defined by implementations", pe.shortDocumentation()); + Assertions.assertEquals("May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", pe.documentation()); + } } diff --git a/pom.xml b/pom.xml index 5d280c917..706235642 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 6.2.1 - 1.2.4 + 1.2.5-SNAPSHOT 5.7.1 1.8.2 3.0.0-M5