Repository: FHIR/sushi (ts)

Issue:
Support elementdefinition-profile-element extension
The [elementdefinition-profile-element extension](http://hl7.org/fhir/R4/extension-elementdefinition-profile-element.html) refers to the:
> specific element to use in the referenced profile. This is used when a backbone element is being profiled, rather than an established type.

More specifically, it can be used to indicate that content reference element should take on the constraints of the thing that it targets.  See: https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Clarification.20on.20contentReference/near/225076962

Pull request:
Adding profile element extension support
Fixes #767 and addresses [CIMPL-678](https://standardhealthrecord.atlassian.net/browse/CIMPL-678), adding support for the [profile element extension](https://www.hl7.org/fhir/extension-elementdefinition-profile-element.html). When applied to an ElementDefinition's `type.profile` element, this extension allows for that ElementDefinition to take on the constraints of the ElementDefinition pointed to by this extension. Additionally, this extension allows for each recursive contentReference element to take on the constraints of the original element (e.g: when the extension is applied to `Questionnaire.item`, `Questionnaire.item.item` will pick up it's constraints as well). 

The effects of this can be seen by running [this example on FSHOnline](https://fshschool.org/FSHOnline/#/share/3v7riaz). This FSH demonstrates the profile-element extension in two ways:

1. By creating a new `Patient` profile and using the profile-element extension to apply the constraints on `USCorePatient.name` to the new profile's name element. US Core's Patient profile gives the `name` element a minimum cardinality of 1, so the following instance should generate an error since it does not set a `name` element at all.
2. By creating a new `Questionnaire` profile and using the profile-element extension to apply constraints on `Questionnaire.item` recursively. This profile gives the `item-ctl` extension a minimum cardinality of 1 on `Questionnaire.item`  and all of its children, so the following instance should generate an error since `Questionnaire.item.item` does not include this extension.

When run against master, this FSH will generate no errors since the constraints were not copied over, however when run against this branch there should be errors related to the missing elements. 

Patch:
diff --git a/src/fhirtypes/ElementDefinition.ts b/src/fhirtypes/ElementDefinition.ts
index c2a6c35..aef15a2 100644
--- a/src/fhirtypes/ElementDefinition.ts
+++ b/src/fhirtypes/ElementDefinition.ts
@@ -48,6 +48,9 @@ import { Fishable, Type, Metadata, logger } from '../utils';
 import { InstanceDefinition } from './InstanceDefinition';
 import { idRegex } from './primitiveTypes';
 
+const PROFILE_ELEMENT_EXTENSION =
+  'http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element';
+
 export class ElementDefinitionType {
   private _code: string;
   profile?: string[];
@@ -112,6 +115,7 @@ export class ElementDefinitionType {
     elDefType.aggregation = json.aggregation;
     elDefType.versioning = json.versioning;
     elDefType.extension = json.extension;
+
     return elDefType;
   }
 }
@@ -1819,24 +1823,29 @@ export class ElementDefinition {
     ) {
       let newElements: ElementDefinition[] = [];
       if (this.contentReference) {
-        // TODO: Implement recursive constraints once the approach is better clarified.
-        // See: https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Clarification.20on.20contentReference
-
         // Get the original resource JSON so we unfold unconstrained reference
         const type = this.structDef.type;
         const json = fisher.fishForFHIR(type, Type.Resource);
-        if (json) {
+        // contentReference elements will not contain a type field, so we must fish for the StructDef and
+        // check the differential
+        const profileJson = fisher.fishForFHIR(this.structDef.id, Type.Profile);
+        if (profileJson && this.hasProfileElementExtension(profileJson)) {
+          const def = this.structDef;
+          // Content references start with #, slice that off to get the id of referenced element
+          const contentRefId = this.getContentReferenceId();
+          const referencedElement = def.findElement(contentRefId);
+          newElements = this.cloneChildren(referencedElement);
+          if (newElements.length > 0) {
+            // If we successfully unfolded, this element is no longer a content reference
+            this.type = referencedElement.type;
+            delete this.contentReference;
+          }
+        } else if (json) {
           const def = StructureDefinition.fromJSON(json);
           // Content references start with #, slice that off to id of referenced element
           const contentRefId = this.getContentReferenceId();
           const referencedElement = def.findElement(contentRefId);
-          newElements = referencedElement?.children().map(e => {
-            const eClone = e.clone();
-            eClone.id = eClone.id.replace(referencedElement.id, this.id);
-            eClone.structDef = this.structDef;
-            eClone.captureOriginal();
-            return eClone;
-          });
+          newElements = this.cloneChildren(referencedElement);
           if (newElements.length > 0) {
             // If we successfully unfolded, this element is no longer a content reference
             this.type = referencedElement.type;
@@ -1846,13 +1855,7 @@ export class ElementDefinition {
       } else if (this.sliceName) {
         // If the element is sliced, we first try to unfold from the SD itself
         const slicedElement = this.slicedElement();
-        newElements = slicedElement.children().map(e => {
-          const eClone = e.clone();
-          eClone.id = eClone.id.replace(slicedElement.id, this.id);
-          eClone.structDef = this.structDef;
-          eClone.captureOriginal();
-          return eClone;
-        });
+        newElements = this.cloneChildren(slicedElement);
       }
       if (newElements.length === 0) {
         // If it has a profile, use that, otherwise use the code
@@ -1889,6 +1892,81 @@ export class ElementDefinition {
     return [];
   }
 
+  /**
+   * Returns an array of an ElementDefinition's unfolded children.
+   * @param {ElementDefinition} targetElement - The ElementDefinition being unfolded
+   * @returns {ElementDefinition[]} An array of the targetElement's children, with the IDs altered and
+   * the original property re-captured.
+   */
+
+  private cloneChildren(targetElement: ElementDefinition): ElementDefinition[] {
+    return targetElement?.children().map(e => {
+      const eClone = e.clone();
+      eClone.id = eClone.id.replace(targetElement.id, this.id);
+      eClone.structDef = this.structDef;
+      eClone.captureOriginal();
+      return eClone;
+    });
+  }
+
+  /**
+   * Checks a StructureDefinition's differential to determine if the profile element extension has been used on
+   * an element.
+   * @param {Object} profileJson - The json representation of this ElementDefinition's structDef
+   * @returns {boolean} True if the profile element extension is found on this elements profile property, false
+   * if the extension is not found
+   */
+  private hasProfileElementExtension(profileJson: any): boolean {
+    const elementName = this.getContentReferenceId();
+
+    const elementType = profileJson.differential.element.find(
+      (element: any) => element.id === elementName
+    )?.type?.[0];
+
+    // If the type property is not present in the differential, the profile-element extension is not present
+    if (!elementType) {
+      return false;
+    }
+
+    let profileCanonical, extensionUrl, targetElement: string;
+
+    // If the profile and _profile arrays are not present, the profile-element extension is not present
+    if (elementType.profile && elementType._profile) {
+      const profileIndex = elementType._profile.findIndex(
+        (profile: any) =>
+          profile &&
+          profile.extension.some(
+            (extension: any) =>
+              extension.hasOwnProperty('url') &&
+              extension.hasOwnProperty('valueString') &&
+              extension.url === PROFILE_ELEMENT_EXTENSION
+          )
+      );
+
+      if (profileIndex === -1) {
+        return false;
+      }
+
+      const extensionIndex = elementType._profile[profileIndex].extension?.findIndex(
+        (extensionObj: any) =>
+          extensionObj.hasOwnProperty('url') &&
+          extensionObj.hasOwnProperty('valueString') &&
+          extensionObj.url === PROFILE_ELEMENT_EXTENSION
+      );
+      extensionUrl = elementType._profile[profileIndex].extension[extensionIndex].url;
+      targetElement = elementType._profile[profileIndex].extension[extensionIndex].valueString;
+      profileCanonical = elementType.profile[profileIndex];
+    } else {
+      return false;
+    }
+
+    return (
+      profileCanonical === this.structDef.url &&
+      extensionUrl === PROFILE_ELEMENT_EXTENSION &&
+      targetElement === elementName
+    );
+  }
+
   private getContentReferenceId(): string | undefined {
     if (this.contentReference) {
       return this.contentReference.slice(this.contentReference.indexOf('#') + 1);


Test patch:
diff --git a/test/export/StructureDefinitionExporter.test.ts b/test/export/StructureDefinitionExporter.test.ts
index d7bf6c4..0b6a22c 100644
--- a/test/export/StructureDefinitionExporter.test.ts
+++ b/test/export/StructureDefinitionExporter.test.ts
@@ -245,6 +245,164 @@ describe('StructureDefinitionExporter', () => {
     );
   });
 
+  it('should apply constraints to all instances of contentReference elements when the profile-element extension is applied', () => {
+    const profile = new Profile('TestQuestionnaire');
+    profile.parent = 'Questionnaire';
+    profile.id = 'example-q';
+
+    const containsRule = new ContainsRule('item.extension');
+    containsRule.items = [
+      {
+        name: 'example-slice',
+        type: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl'
+      }
+    ];
+
+    const cardRule = new CardRule('item.extension[example-slice]');
+    cardRule.min = 1;
+    cardRule.max = '1';
+
+    const profileRule = new CaretValueRule('item');
+    profileRule.caretPath = 'type.profile';
+    profileRule.value = 'http://hl7.org/fhir/us/minimal/StructureDefinition/example-q';
+    const extensionRule = new CaretValueRule('item');
+    extensionRule.caretPath = 'type.profile.extension.url';
+    extensionRule.value =
+      'http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element';
+    const targetElementRule = new CaretValueRule('item');
+    targetElementRule.caretPath = 'type.profile.extension.valueString';
+    targetElementRule.value = 'Questionnaire.item';
+
+    const assignmentRule = new AssignmentRule('item.item.linkId');
+    assignmentRule.value = 'item-2';
+
+    profile.rules.push(
+      containsRule,
+      cardRule,
+      profileRule,
+      extensionRule,
+      targetElementRule,
+      assignmentRule
+    );
+    doc.profiles.set(profile.name, profile);
+
+    // The slice added to the parent element should be unfolded to the child
+    const exportedProfile = exporter.export().profiles[0];
+    expect(
+      exportedProfile.findElement('Questionnaire.item.item.extension:example-slice')
+    ).toBeDefined();
+  });
+
+  it('should apply the profile-element extension when there are several extensions in the type.profile array', () => {
+    const profile = new Profile('TestQuestionnaire');
+    profile.parent = 'Questionnaire';
+    profile.id = 'example-q';
+
+    const containsRule = new ContainsRule('item.extension');
+    containsRule.items = [
+      {
+        name: 'example-slice',
+        type: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl'
+      }
+    ];
+
+    const cardRule = new CardRule('item.extension[example-slice]');
+    cardRule.min = 1;
+    cardRule.max = '1';
+
+    const fakeProfileRule = new CaretValueRule('item');
+    fakeProfileRule.caretPath = 'type.profile';
+    fakeProfileRule.value = 'http://hl7.org/fhir/us/minimal/StructureDefinition/example-q';
+    const fakeExtensionRule = new CaretValueRule('item');
+    fakeExtensionRule.caretPath = 'type.profile.extension.url';
+    fakeExtensionRule.value = 'http://hl7.org/fhir/us/minimal/StructureDefinition/FakeExtension';
+    const fakeTargetElementRule = new CaretValueRule('item');
+    fakeTargetElementRule.caretPath = 'type.profile.extension.valueString';
+    fakeTargetElementRule.value = 'Foo';
+
+    const profileRule = new CaretValueRule('item');
+    profileRule.caretPath = 'type.profile[1]';
+    profileRule.value = 'http://hl7.org/fhir/us/minimal/StructureDefinition/example-q';
+    const extensionRule = new CaretValueRule('item');
+    extensionRule.caretPath = 'type.profile[1].extension.url';
+    extensionRule.value =
+      'http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element';
+    const targetElementRule = new CaretValueRule('item');
+    targetElementRule.caretPath = 'type.profile[1].extension.valueString';
+    targetElementRule.value = 'Questionnaire.item';
+
+    const assignmentRule = new AssignmentRule('item.item.linkId');
+    assignmentRule.value = 'item-2';
+
+    profile.rules.push(
+      containsRule,
+      cardRule,
+      fakeProfileRule,
+      fakeExtensionRule,
+      fakeTargetElementRule,
+      profileRule,
+      extensionRule,
+      targetElementRule,
+      assignmentRule
+    );
+    doc.profiles.set(profile.name, profile);
+
+    // The slice added to the parent element should be unfolded to the child
+    const exportedProfile = exporter.export().profiles[0];
+    expect(
+      exportedProfile.findElement('Questionnaire.item.item.extension:example-slice')
+    ).toBeDefined();
+  });
+
+  it('should not apply constraints to all instances of contentReference elements when the profile-element extension is misapplied', () => {
+    const profile = new Profile('TestQuestionnaire2');
+    profile.parent = 'Questionnaire';
+    profile.id = 'example-q';
+
+    const containsRule = new ContainsRule('item.extension');
+    containsRule.items = [
+      {
+        name: 'example-slice',
+        type: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl'
+      }
+    ];
+
+    const cardRule = new CardRule('item.extension[example-slice]');
+    cardRule.min = 1;
+    cardRule.max = '1';
+
+    // The extension is targeting another profile
+    const profileRule = new CaretValueRule('item');
+    profileRule.caretPath = 'type.profile';
+    profileRule.value = 'http://hl7.org/fhir/us/minimal/StructureDefinition/wrong-id';
+    const extensionRule = new CaretValueRule('item');
+    extensionRule.caretPath = 'type.profile.extension.url';
+    extensionRule.value =
+      'http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element';
+    const targetElementRule = new CaretValueRule('item');
+    targetElementRule.caretPath = 'type.profile.extension.valueString';
+    targetElementRule.value = 'Questionnaire.item';
+
+    const assignmentRule = new AssignmentRule('item.item.linkId');
+    assignmentRule.value = 'item-2';
+
+    profile.rules.push(
+      containsRule,
+      cardRule,
+      profileRule,
+      extensionRule,
+      targetElementRule,
+      assignmentRule
+    );
+    doc.profiles.set(profile.name, profile);
+
+    // The slice added to the parent element should be unfolded to the child
+    const exportedProfile = exporter.export().profiles[0];
+    expect(
+      exportedProfile.findElement('Questionnaire.item.item.extension:example-slice')
+    ).not.toBeDefined();
+  });
+
   // Extension
   it('should set all user-provided metadata for an extension', () => {
     const extension = new Extension('Foo');
diff --git a/test/testhelpers/testdefs/package/StructureDefinition-canonical.json b/test/testhelpers/testdefs/package/StructureDefinition-canonical.json
new file mode 100644
index 0000000..b94b27c
--- /dev/null
+++ b/test/testhelpers/testdefs/package/StructureDefinition-canonical.json
@@ -0,0 +1,1 @@
+{"resourceType":"StructureDefinition","id":"canonical","meta":{"lastUpdated":"2019-11-01T09:29:23.356+11:00"},"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status","valueCode":"normative"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version","valueCode":"4.0.0"}],"url":"http://hl7.org/fhir/StructureDefinition/canonical","version":"4.0.1","name":"canonical","status":"active","date":"2019-11-01T09:29:23+11:00","publisher":"HL7 FHIR Standard","contact":[{"telecom":[{"system":"url","value":"http://hl7.org/fhir"}]}],"description":"Base StructureDefinition for canonical type: A URI that is a reference to a canonical URL on a FHIR resource","fhirVersion":"4.0.1","kind":"primitive-type","abstract":false,"type":"canonical","baseDefinition":"http://hl7.org/fhir/StructureDefinition/uri","derivation":"specialization","snapshot":{"element":[{"id":"canonical","path":"canonical","short":"Primitive Type canonical","definition":"A URI that is a reference to a canonical URL on a FHIR resource","comment":"see [Canonical References](references.html#canonical)","min":0,"max":"*","base":{"path":"canonical","min":0,"max":"*"},"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":false},{"id":"canonical.id","path":"canonical.id","representation":["xmlAttr"],"short":"xml:id (or equivalent in JSON)","definition":"unique id for the element within a resource (for internal references)","min":0,"max":"1","base":{"path":"Element.id","min":0,"max":"1"},"type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"string"}],"code":"http://hl7.org/fhirpath/System.String"}],"isModifier":false,"isSummary":false},{"id":"canonical.extension","path":"canonical.extension","short":"Additional content defined by implementations","definition":"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.","comment":"There can be no stigma associated with the use of extensions by any application, project, or standard - regardless of the institution or jurisdiction that uses or defines the extensions.  The use of extensions is what allows the FHIR specification to retain a core level of simplicity for everyone.","alias":["extensions","user content"],"min":0,"max":"*","base":{"path":"Element.extension","min":0,"max":"*"},"type":[{"code":"Extension"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"},{"key":"ext-1","severity":"error","human":"Must have either extensions or value[x], not both","expression":"extension.exists() != value.exists()","xpath":"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])","source":"http://hl7.org/fhir/StructureDefinition/Extension"}],"isModifier":false,"isSummary":false},{"id":"canonical.value","path":"canonical.value","representation":["xmlAttr"],"short":"Primitive value for canonical","definition":"Primitive value for canonical","min":0,"max":"1","base":{"path":"uri.value","min":0,"max":"1"},"type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"string"},{"url":"http://hl7.org/fhir/StructureDefinition/regex","valueString":"\\S*"}],"code":"http://hl7.org/fhirpath/System.String"}],"isModifier":false,"isSummary":false}]},"differential":{"element":[{"id":"canonical","path":"canonical","short":"Primitive Type canonical","definition":"A URI that is a reference to a canonical URL on a FHIR resource","comment":"see [Canonical References](references.html#canonical)","min":0,"max":"*"},{"id":"canonical.value","path":"canonical.value","representation":["xmlAttr"],"short":"Primitive value for canonical","definition":"Primitive value for canonical","min":0,"max":"1","type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"canonical"},{"url":"http://hl7.org/fhir/StructureDefinition/regex","valueString":"\\S*"}],"code":"http://hl7.org/fhirpath/System.String"}]}]}}
\ No newline at end of file
diff --git a/test/testhelpers/testdefs/package/StructureDefinition-questionnaire-itemControl.json b/test/testhelpers/testdefs/package/StructureDefinition-questionnaire-itemControl.json
new file mode 100644
index 0000000..02dbf1c
--- /dev/null
+++ b/test/testhelpers/testdefs/package/StructureDefinition-questionnaire-itemControl.json
@@ -0,0 +1,1 @@
+{"resourceType":"StructureDefinition","id":"questionnaire-itemControl","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg","valueCode":"fhir"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm","valueInteger":1}],"url":"http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl","version":"4.0.1","name":"itemControl","status":"draft","date":"2013-07-04","publisher":"HL7","contact":[{"telecom":[{"system":"url","value":"http://www.hl7.org"}]}],"description":"The type of data entry control or structure that should be used to render the item.","fhirVersion":"4.0.1","mapping":[{"identity":"rim","uri":"http://hl7.org/v3","name":"RIM Mapping"}],"kind":"complex-type","abstract":false,"context":[{"type":"element","expression":"Questionnaire.item"},{"type":"element","expression":"Questionnaire.item.item"}],"type":"Extension","baseDefinition":"http://hl7.org/fhir/StructureDefinition/Extension","derivation":"constraint","snapshot":{"element":[{"id":"Extension","path":"Extension","short":"E.g. Fly-over, Table, Checkbox, Combo-box, Lookup, etc.","definition":"The type of data entry control or structure that should be used to render the item.","comment":"Different controls may be appropriate for different item types.  It is up to the system rendering a questionnaire as to what controls it will support and for which data types.","min":0,"max":"1","base":{"path":"Extension","min":0,"max":"*"},"condition":["ele-1"],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"},{"key":"ext-1","severity":"error","human":"Must have either extensions or value[x], not both","expression":"extension.exists() != value.exists()","xpath":"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])","source":"http://hl7.org/fhir/StructureDefinition/Extension"}],"isModifier":false},{"id":"Extension.id","path":"Extension.id","representation":["xmlAttr"],"short":"Unique id for inter-element referencing","definition":"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.","min":0,"max":"1","base":{"path":"Element.id","min":0,"max":"1"},"type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"string"}],"code":"http://hl7.org/fhirpath/System.String"}],"isModifier":false,"isSummary":false,"mapping":[{"identity":"rim","map":"n/a"}]},{"id":"Extension.extension","path":"Extension.extension","slicing":{"discriminator":[{"type":"value","path":"url"}],"description":"Extensions are always sliced by (at least) url","rules":"open"},"short":"Extension","definition":"An Extension","min":0,"max":"0","base":{"path":"Element.extension","min":0,"max":"*"},"type":[{"code":"Extension"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"},{"key":"ext-1","severity":"error","human":"Must have either extensions or value[x], not both","expression":"extension.exists() != value.exists()","xpath":"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \"value\")])","source":"http://hl7.org/fhir/StructureDefinition/Extension"}],"isModifier":false,"isSummary":false},{"id":"Extension.url","path":"Extension.url","representation":["xmlAttr"],"short":"identifies the meaning of the extension","definition":"Source of the definition for the extension code - a logical name or a URL.","comment":"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.","min":1,"max":"1","base":{"path":"Extension.url","min":1,"max":"1"},"type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"uri"}],"code":"http://hl7.org/fhirpath/System.String"}],"fixedUri":"http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl","isModifier":false,"isSummary":false,"mapping":[{"identity":"rim","map":"N/A"}]},{"id":"Extension.value[x]","path":"Extension.value[x]","short":"Value of extension","definition":"Value of extension - must be one of a constrained set of the data types (see [Extensibility](http://hl7.org/fhir/extensibility.html) for a list).","min":1,"max":"1","base":{"path":"Extension.value[x]","min":0,"max":"1"},"type":[{"code":"CodeableConcept"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":false,"binding":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName","valueString":"ItemControl"}],"strength":"extensible","description":"User interface controls or widgets used for questionnaire items.","valueSet":"http://hl7.org/fhir/ValueSet/questionnaire-item-control"},"mapping":[{"identity":"rim","map":"N/A"}]}]},"differential":{"element":[{"id":"Extension","path":"Extension","short":"E.g. Fly-over, Table, Checkbox, Combo-box, Lookup, etc.","definition":"The type of data entry control or structure that should be used to render the item.","comment":"Different controls may be appropriate for different item types.  It is up to the system rendering a questionnaire as to what controls it will support and for which data types.","min":0,"max":"1"},{"id":"Extension.extension","path":"Extension.extension","max":"0"},{"id":"Extension.url","path":"Extension.url","fixedUri":"http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl"},{"id":"Extension.value[x]","path":"Extension.value[x]","min":1,"type":[{"code":"CodeableConcept"}],"binding":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName","valueString":"ItemControl"}],"strength":"extensible","description":"User interface controls or widgets used for questionnaire items.","valueSet":"http://hl7.org/fhir/ValueSet/questionnaire-item-control"}}]}}
\ No newline at end of file