001package org.hl7.fhir.r4.utils;
002
003
004
005
006import java.util.ArrayList;
007import java.util.HashMap;
008import java.util.List;
009import java.util.Map;
010
011import org.apache.commons.lang3.NotImplementedException;
012import org.hl7.fhir.exceptions.DefinitionException;
013import org.hl7.fhir.exceptions.FHIRException;
014import org.hl7.fhir.exceptions.FHIRFormatError;
015import org.hl7.fhir.r4.conformance.ProfileUtilities;
016import org.hl7.fhir.r4.context.IWorkerContext;
017import org.hl7.fhir.r4.model.Base;
018import org.hl7.fhir.r4.model.BooleanType;
019import org.hl7.fhir.r4.model.CanonicalType;
020import org.hl7.fhir.r4.model.Coding;
021import org.hl7.fhir.r4.model.DateTimeType;
022import org.hl7.fhir.r4.model.DateType;
023import org.hl7.fhir.r4.model.DecimalType;
024import org.hl7.fhir.r4.model.Element;
025import org.hl7.fhir.r4.model.ElementDefinition;
026import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;
027import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
028import org.hl7.fhir.r4.model.Enumeration;
029import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
030import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
031import org.hl7.fhir.r4.model.Factory;
032import org.hl7.fhir.r4.model.IntegerType;
033import org.hl7.fhir.r4.model.Quantity;
034import org.hl7.fhir.r4.model.Questionnaire;
035import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
036import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
037import org.hl7.fhir.r4.model.QuestionnaireResponse;
038import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
039import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
040import org.hl7.fhir.r4.model.Reference;
041import org.hl7.fhir.r4.model.Resource;
042import org.hl7.fhir.r4.model.StringType;
043import org.hl7.fhir.r4.model.StructureDefinition;
044import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
045import org.hl7.fhir.r4.model.TimeType;
046import org.hl7.fhir.r4.model.Type;
047import org.hl7.fhir.r4.model.UriType;
048import org.hl7.fhir.r4.model.ValueSet;
049import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
050import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
051import org.hl7.fhir.r4.terminologies.ValueSetExpander;
052import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
053import org.hl7.fhir.utilities.Utilities;
054
055
056
057/*
058  Copyright (c) 2011+, HL7, Inc.
059  All rights reserved.
060
061  Redistribution and use in source and binary forms, with or without modification, 
062  are permitted provided that the following conditions are met:
063
064 * Redistributions of source code must retain the above copyright notice, this 
065     list of conditions and the following disclaimer.
066 * Redistributions in binary form must reproduce the above copyright notice, 
067     this list of conditions and the following disclaimer in the documentation 
068     and/or other materials provided with the distribution.
069 * Neither the name of HL7 nor the names of its contributors may be used to 
070     endorse or promote products derived from this software without specific 
071     prior written permission.
072
073  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
074  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
075  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
076  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
077  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
078  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
079  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
080  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
081  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
082  POSSIBILITY OF SUCH DAMAGE.
083
084 */
085
086
087/**
088 * This class takes a profile, and builds a questionnaire from it
089 * 
090 * If you then convert this questionnaire to a form using the 
091 * XMLTools form builder, and then take the QuestionnaireResponse 
092 * this creates, you can use QuestionnaireInstanceConvert to 
093 * build an instance the conforms to the profile
094 *  
095 * FHIR context: 
096 *   conceptLocator, codeSystems, valueSets, maps, client, profiles
097 * You don"t have to provide any of these, but 
098 * the more you provide, the better the conversion will be
099 * 
100 * @author Grahame
101 *
102 */
103public class QuestionnaireBuilder {
104
105  private static final int MaxListboxCodings = 20;
106  private IWorkerContext context;
107  private int lastid = 0;
108  private Resource resource;
109  private StructureDefinition profile;
110  private Questionnaire questionnaire;
111  private QuestionnaireResponse response;
112  private String questionnaireId;
113  private Factory factory = new Factory();
114  private Map<String, String> vsCache = new HashMap<String, String>();
115  private ValueSetExpander expander;
116
117  // sometimes, when this is used, the questionnaire is already build and cached, and we are
118  // processing the response. for technical reasons, we still go through the process, but
119  // we don't do the intensive parts of the work (save time)
120  private Questionnaire prebuiltQuestionnaire;
121
122  public QuestionnaireBuilder(IWorkerContext context) {
123    super();
124    this.context = context;
125  }
126
127  public Resource getReference() {
128    return resource;
129  }
130
131  public void setReference(Resource resource) {
132    this.resource = resource;
133  }
134
135  public StructureDefinition getProfile() {
136    return profile;
137  }
138
139  public void setProfile(StructureDefinition profile) {
140    this.profile = profile;
141  }
142
143  public Questionnaire getQuestionnaire() {
144    return questionnaire;
145  }
146
147  public void setQuestionnaire(Questionnaire questionnaire) {
148    this.questionnaire = questionnaire;
149  }
150
151  public QuestionnaireResponse getResponse() {
152    return response;
153  }
154
155  public void setResponse(QuestionnaireResponse response) {
156    this.response = response;
157  }
158
159  public String getQuestionnaireId() {
160    return questionnaireId;
161  }
162
163  public void setQuestionnaireId(String questionnaireId) {
164    this.questionnaireId = questionnaireId;
165  }
166
167  public Questionnaire getPrebuiltQuestionnaire() {
168    return prebuiltQuestionnaire;
169  }
170
171  public void setPrebuiltQuestionnaire(Questionnaire prebuiltQuestionnaire) {
172    this.prebuiltQuestionnaire = prebuiltQuestionnaire;
173  }
174
175  public ValueSetExpander getExpander() {
176    return expander;
177  }
178
179  public void setExpander(ValueSetExpander expander) {
180    this.expander = expander;
181  }
182
183  public void build() throws FHIRException {
184                if (profile == null)
185      throw new DefinitionException("QuestionnaireBuilder.build: no profile found");
186
187    if (resource != null)
188      if (!profile.getType().equals(resource.getResourceType().toString()))
189        throw new DefinitionException("Wrong Type");
190
191    if (prebuiltQuestionnaire != null)
192      questionnaire = prebuiltQuestionnaire;
193    else
194      questionnaire = new Questionnaire();
195    if (resource != null) 
196      response = new QuestionnaireResponse();
197    processMetadata();
198
199
200    List<ElementDefinition> list = new ArrayList<ElementDefinition>();
201    List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>();
202
203    if (resource != null)
204      answerGroups.addAll(response.getItem());
205    if (prebuiltQuestionnaire != null) {
206      // give it a fake group to build
207      Questionnaire.QuestionnaireItemComponent group = new Questionnaire.QuestionnaireItemComponent();
208      group.setType(QuestionnaireItemType.GROUP);
209      buildGroup(group, profile, profile.getSnapshot().getElement().get(0), list, answerGroups);
210    } else
211      buildGroup(questionnaire.getItem().get(0), profile, profile.getSnapshot().getElement().get(0), list, answerGroups);
212    //
213    //     NarrativeGenerator ngen = new NarrativeGenerator(context);
214    //     ngen.generate(result);
215    //
216    //    if FResponse <> nil then
217    //      FResponse.collapseAllContained;
218  }
219
220  private void processMetadata() {
221    // todo: can we derive a more informative identifier from the questionnaire if we have a profile
222    if (prebuiltQuestionnaire == null) {
223      questionnaire.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(questionnaireId);
224      questionnaire.setVersion(profile.getVersion());
225      questionnaire.setStatus(profile.getStatus());
226      questionnaire.setDate(profile.getDate());
227      questionnaire.setPublisher(profile.getPublisher());
228      Questionnaire.QuestionnaireItemComponent item = new Questionnaire.QuestionnaireItemComponent();
229      questionnaire.addItem(item);
230      item.getCode().addAll(profile.getKeyword());
231      questionnaire.setId(nextId("qs"));
232    }
233
234    if (response != null) {
235      // no identifier - this is transient
236      response.setQuestionnaire("#"+questionnaire.getId());
237      response.getContained().add(questionnaire);
238      response.setStatus(QuestionnaireResponseStatus.INPROGRESS);
239      QuestionnaireResponse.QuestionnaireResponseItemComponent item = new QuestionnaireResponse.QuestionnaireResponseItemComponent();
240      response.addItem(item);
241      item.setUserData("object", resource);
242    }
243
244  }
245
246  private String nextId(String prefix) {
247    lastid++;
248    return prefix+Integer.toString(lastid);
249  }
250
251  private void buildGroup(QuestionnaireItemComponent group, StructureDefinition profile, ElementDefinition element,
252      List<ElementDefinition> parents, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
253          group.setLinkId(element.getPath()); // todo: this will be wrong when we start slicing
254          group.setText(element.getShort()); // todo - may need to prepend the name tail... 
255          if (element.getComment() != null) {
256                Questionnaire.QuestionnaireItemComponent display = new Questionnaire.QuestionnaireItemComponent();
257                display.setType(QuestionnaireItemType.DISPLAY);
258                display.setText(element.getComment());
259                group.addItem(display);
260          }
261          group.setType(QuestionnaireItemType.GROUP);
262          ToolingExtensions.addFlyOver(group, element.getDefinition());
263    group.setRequired(element.getMin() > 0);
264    if (element.getMin() > 0)
265        ToolingExtensions.addMin(group, element.getMin());
266    group.setRepeats(!element.getMax().equals("1"));
267    if (!element.getMax().equals("*"))
268        ToolingExtensions.addMax(group, Integer.parseInt(element.getMax()));
269
270    for (org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) {
271      ag.setLinkId(group.getLinkId());
272      ag.setText(group.getText());
273    }
274
275    // now, we iterate the children
276    List<ElementDefinition> list = ProfileUtilities.getChildList(profile, element);
277    for (ElementDefinition child : list) {
278
279      if (!isExempt(element, child) && !parents.contains(child)) {
280                                List<ElementDefinition> nparents = new ArrayList<ElementDefinition>();
281        nparents.addAll(parents);
282        nparents.add(child);
283        QuestionnaireItemComponent childGroup = group.addItem();
284        childGroup.setType(QuestionnaireItemType.GROUP);
285
286        List<QuestionnaireResponse.QuestionnaireResponseItemComponent> nResponse = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>();
287        processExisting(child.getPath(), answerGroups, nResponse);
288        // if the element has a type, we add a question. else we add a group on the basis that
289        // it will have children of its own
290        if (child.getType().isEmpty() || isAbstractType(child.getType())) 
291          buildGroup(childGroup, profile, child, nparents, nResponse);
292        else if (isInlineDataType(child.getType()))
293          buildGroup(childGroup, profile, child, nparents, nResponse); // todo: get the right children for this one...
294        else
295          buildQuestion(childGroup, profile, child, child.getPath(), nResponse, parents);
296      }
297    }
298  }
299
300  private boolean isAbstractType(List<TypeRefComponent> type) {
301    return type.size() == 1 && (type.get(0).getWorkingCode().equals("Element") || type.get(0).getWorkingCode().equals("BackboneElement"));
302  }
303
304  private boolean isInlineDataType(List<TypeRefComponent> type) {
305    return type.size() == 1 && !Utilities.existsInList(type.get(0).getWorkingCode(), "code", "string", "id", "oid", "markdown", "uri", "boolean", "decimal", "dateTime", "date", "instant", "time", "CodeableConcept", "Period", "Ratio",
306        "HumanName", "Address", "ContactPoint", "Identifier", "integer", "positiveInt", "unsignedInt", "Coding", "Quantity",  "Count",  "Age",  "Duration", 
307        "Distance",  "Money", "Money", "Reference", "Duration", "base64Binary", "Attachment", "Age", "Range", "Timing", "Annotation", "SampledData", "Extension",
308        "SampledData", "Narrative", "Resource", "Meta", "url", "canonical");
309  }
310
311  private boolean isExempt(ElementDefinition element, ElementDefinition child) {
312    String n = tail(child.getPath());
313    String t = "";
314    if (!element.getType().isEmpty())
315      t =  element.getType().get(0).getWorkingCode();
316
317    // we don't generate questions for the base stuff in every element
318    if (t.equals("Resource")  && (n.equals("text") || n.equals("language") || n.equals("contained")))
319      return true;
320      // we don't generate questions for extensions
321    else if (n.equals("extension") || n.equals("modifierExtension")) {
322      if (child.getType().size() > 0 && !child.getType().get(0).hasProfile()) 
323      return false;
324      else
325        return true;
326    } else
327      return false;
328  }
329
330  private String tail(String path) {
331    return path.substring(path.lastIndexOf('.')+1);
332  }
333
334  private void processExisting(String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> nResponse) throws FHIRException {
335    // processing existing data
336    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) {
337      List<Base> children = ((Element) ag.getUserData("object")).listChildrenByName(tail(path));
338      for (Base child : children) {
339        if (child != null) {
340          QuestionnaireResponse.QuestionnaireResponseItemComponent ans = ag.addItem();
341          ans.setUserData("object", child);
342          nResponse.add(ans);
343        }
344      }
345    }
346  }
347
348  private void buildQuestion(QuestionnaireItemComponent group, StructureDefinition profile, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, List<ElementDefinition> parents) throws FHIRException {
349      group.setLinkId(path);
350
351      // in this context, we don't have any concepts to mark...
352      group.setText(element.getShort()); // prefix with name?
353      group.setRequired(element.getMin() > 0);
354            if (element.getMin() > 0)
355                ToolingExtensions.addMin(group, element.getMin());
356      group.setRepeats(!element.getMax().equals('1'));
357            if (!element.getMax().equals("*"))
358                ToolingExtensions.addMax(group, Integer.parseInt(element.getMax()));
359
360      for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) {
361        ag.setLinkId(group.getLinkId());
362        ag.setText(group.getText());
363      }
364
365      if (!Utilities.noString(element.getComment())) 
366        ToolingExtensions.addFlyOver(group, element.getDefinition()+" "+element.getComment());
367      else
368        ToolingExtensions.addFlyOver(group, element.getDefinition());
369
370      if (element.getType().size() > 1 || element.getType().get(0).getWorkingCode().equals("*")) {
371        List<TypeRefComponent> types = expandTypeList(element.getType());
372        Questionnaire.QuestionnaireItemComponent q = addQuestion(group, QuestionnaireItemType.CHOICE, element.getPath(), "_type", "type", null, makeTypeList(profile, types, element.getPath()));
373          for (TypeRefComponent t : types) {
374            Questionnaire.QuestionnaireItemComponent sub = q.addItem();
375            sub.setType(QuestionnaireItemType.GROUP);
376            sub.setLinkId(element.getPath()+"._"+t.getUserData("text"));
377            sub.setText((String) t.getUserData("text"));
378            // always optional, never repeats
379
380            List<QuestionnaireResponse.QuestionnaireResponseItemComponent> selected = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>();
381            selectTypes(profile, sub, t, answerGroups, selected);
382            processDataType(profile, sub, element, element.getPath()+"._"+t.getUserData("text"), t, selected, parents);
383          }
384      } else
385        // now we have to build the question panel for each different data type
386        processDataType(profile, group, element, element.getPath(), element.getType().get(0), answerGroups, parents);
387
388  }
389
390  private List<TypeRefComponent> expandTypeList(List<TypeRefComponent> types) {
391          List<TypeRefComponent> result = new ArrayList<TypeRefComponent>();
392    for (TypeRefComponent t : types) {
393            if (t.hasProfile())
394        result.add(t);
395            else if (t.getWorkingCode().equals("*")) {
396              result.add(new TypeRefComponent().setCode("integer"));
397              result.add(new TypeRefComponent().setCode("decimal"));
398              result.add(new TypeRefComponent().setCode("dateTime"));
399              result.add(new TypeRefComponent().setCode("date"));
400              result.add(new TypeRefComponent().setCode("instant"));
401              result.add(new TypeRefComponent().setCode("time"));
402              result.add(new TypeRefComponent().setCode("string"));
403              result.add(new TypeRefComponent().setCode("uri"));
404              result.add(new TypeRefComponent().setCode("boolean"));
405              result.add(new TypeRefComponent().setCode("Coding"));
406              result.add(new TypeRefComponent().setCode("CodeableConcept"));
407              result.add(new TypeRefComponent().setCode("Attachment"));
408              result.add(new TypeRefComponent().setCode("Identifier"));
409              result.add(new TypeRefComponent().setCode("Quantity"));
410              result.add(new TypeRefComponent().setCode("Range"));
411              result.add(new TypeRefComponent().setCode("Period"));
412              result.add(new TypeRefComponent().setCode("Ratio"));
413              result.add(new TypeRefComponent().setCode("HumanName"));
414              result.add(new TypeRefComponent().setCode("Address"));
415        result.add(new TypeRefComponent().setCode("ContactPoint"));
416        result.add(new TypeRefComponent().setCode("Timing"));
417              result.add(new TypeRefComponent().setCode("Reference"));
418      } else
419        result.add(t);
420    }
421    return result;
422  }
423
424  private ValueSet makeTypeList(StructureDefinition profile, List<TypeRefComponent> types, String path) {
425    ValueSet vs = new ValueSet();
426    vs.setName("Type options for "+path);
427    vs.setDescription(vs.present());
428          vs.setStatus(PublicationStatus.ACTIVE);
429    vs.setExpansion(new ValueSetExpansionComponent());
430    vs.getExpansion().setIdentifier(Factory.createUUID());
431    vs.getExpansion().setTimestampElement(DateTimeType.now());
432    for (TypeRefComponent t : types) {
433      if (t.hasTarget()) {
434        for (UriType u : t.getTargetProfile()) {
435                if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) { 
436                  ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains();
437              cc.setCode(u.getValue().substring(40));
438            cc.setSystem("http://hl7.org/fhir/resource-types");
439              cc.setDisplay(cc.getCode());
440                }
441        }
442      } else if (!t.hasProfile()) {
443        ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains();
444        cc.setCode(t.getWorkingCode());
445        cc.setDisplay(t.getWorkingCode());
446        cc.setSystem("http://hl7.org/fhir/data-types");
447      } else for (UriType u : t.getProfile()) {
448        ProfileUtilities pu = new ProfileUtilities(context, null, null);
449        StructureDefinition ps = pu.getProfile(profile, u.getValue());
450        if (ps != null) {
451          ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains();
452                cc.setCode(u.getValue());
453          cc.setDisplay(ps.getType());
454          cc.setSystem("http://hl7.org/fhir/resource-types");
455        }
456      }
457    }
458
459    return vs;
460  }
461
462  private void selectTypes(StructureDefinition profile, QuestionnaireItemComponent sub, TypeRefComponent t, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> source, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> dest) throws FHIRFormatError {
463    List<QuestionnaireResponse.QuestionnaireResponseItemComponent> temp = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>();
464
465    for (QuestionnaireResponse.QuestionnaireResponseItemComponent g : source)
466      if (instanceOf(t, (Element) g.getUserData("object"))) 
467        temp.add(g);
468    for (QuestionnaireResponse.QuestionnaireResponseItemComponent g : temp)
469      source.remove(g);
470    for (QuestionnaireResponse.QuestionnaireResponseItemComponent g : temp) {
471      // 1st the answer:
472      assert(g.getItem().size() == 0); // it should be empty
473      QuestionnaireResponse.QuestionnaireResponseItemComponent q = g.addItem();
474      q.setLinkId(g.getLinkId()+"._type");
475      q.setText("type");
476
477      QuestionnaireResponseItemAnswerComponent a = q.addAnswer();
478      if (t.hasTarget()) {
479        for (UriType u : t.getTargetProfile()) {
480          if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) {
481            Coding cc = new Coding();
482            a.setValue(cc);
483            cc.setCode(u.getValue().substring(40));
484            cc.setSystem("http://hl7.org/fhir/resource-types");
485          }
486        }
487      } else {
488        Coding cc = new Coding();
489        a.setValue(cc);
490        ProfileUtilities pu = new ProfileUtilities(context, null, null);
491        StructureDefinition ps = null;
492        if (t.hasProfile())
493          ps = pu.getProfile(profile, t.getProfile().get(0).getValue());
494
495        if (ps != null) {
496          cc.setCode(t.getProfile().get(0).getValue());
497          cc.setSystem("http://hl7.org/fhir/resource-types");
498        } else {
499          cc.setCode(t.getWorkingCode());
500          cc.setSystem("http://hl7.org/fhir/data-types");
501        }
502      }
503
504      // 1st: create the subgroup
505      QuestionnaireResponse.QuestionnaireResponseItemComponent subg = a.addItem();
506      dest.add(subg);
507      subg.setLinkId(sub.getLinkId());
508      subg.setText(sub.getText());
509      subg.setUserData("object", g.getUserData("object"));
510    }
511  }
512
513  private boolean instanceOf(TypeRefComponent t, Element obj) {
514    if (t.getWorkingCode().equals("Reference")) {
515      if (!(obj instanceof Reference)) {
516        return false;
517      } else {
518        String url = ((Reference) obj).getReference();
519        // there are several problems here around profile matching. This process is degenerative, and there's probably nothing we can do to solve it
520        if (url.startsWith("http:") || url.startsWith("https:"))
521            return true;
522        else if (t.hasProfile() && t.getProfile().get(0).getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) 
523          return url.startsWith(t.getProfile().get(0).getValue().substring(40)+'/');
524        else
525          return true;
526      }
527    } else if (t.getWorkingCode().equals("Quantity")) {
528      return obj instanceof Quantity;
529    } else
530      throw new NotImplementedException("Not Done Yet");
531  }
532
533  private QuestionnaireItemComponent addQuestion(QuestionnaireItemComponent group, QuestionnaireItemType af, String path, String id, String name, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
534    return addQuestion(group, af, path, id, name, answerGroups, null);
535  }
536  
537  private QuestionnaireItemComponent addQuestion(QuestionnaireItemComponent group, QuestionnaireItemType af, String path, String id, String name, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, ValueSet vs) throws FHIRException {
538    QuestionnaireItemComponent result = group.addItem();
539    if (vs != null) {
540      if (vs.getExpansion() == null) {
541        result.setAnswerValueSet(vs.getUrl());
542        ToolingExtensions.addControl(result, "lookup"); 
543      } else {
544        if (Utilities.noString(vs.getId())) {
545          vs.setId(nextId("vs"));
546          questionnaire.getContained().add(vs);
547          vsCache.put(vs.getUrl(), vs.getId());
548          vs.setText(null);
549          vs.setCompose(null);
550          vs.getContact().clear();
551          vs.setPublisherElement(null);
552          vs.setCopyrightElement(null);
553        }
554        result.setAnswerValueSet("#"+vs.getId());
555      }
556    }
557  
558    result.setLinkId(path+'.'+id);
559    result.setText(name);
560    result.setType(af);
561    result.setRequired(false);
562    result.setRepeats(false);
563    if (id.endsWith("/1")) 
564      id = id.substring(0, id.length()-2);
565
566    if (answerGroups != null) {
567
568      for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) {
569        List<Base> children = new ArrayList<Base>(); 
570
571        QuestionnaireResponse.QuestionnaireResponseItemComponent aq = null;
572        Element obj = (Element) ag.getUserData("object");
573        if (isPrimitive((TypeRefComponent) obj))
574          children.add(obj);
575        else if (obj instanceof Enumeration) {
576          String value = ((Enumeration) obj).toString();
577          children.add(new StringType(value));
578        } else
579          children = obj.listChildrenByName(id);
580
581        for (Base child: children) {
582          if (child != null) {
583            if (aq == null) {
584              aq = ag.addItem();
585              aq.setLinkId(result.getLinkId());
586              aq.setText(result.getText());
587            }
588            aq.addAnswer().setValue(convertType(child, af, vs, result.getLinkId()));
589          }
590        }
591      }
592    }
593    return result;
594  }
595
596  @SuppressWarnings("unchecked")
597  private Type convertType(Base value, QuestionnaireItemType af, ValueSet vs, String path) throws FHIRException {
598    switch (af) {
599      // simple cases
600    case BOOLEAN: if (value instanceof BooleanType) return (Type) value; break;
601    case DECIMAL: if (value instanceof DecimalType) return (Type) value; break;
602    case INTEGER: if (value instanceof IntegerType) return (Type) value; break;
603    case DATE: if (value instanceof DateType) return (Type) value; break;
604    case DATETIME: if (value instanceof DateTimeType) return (Type) value; break;
605    case TIME: if (value instanceof TimeType) return (Type) value; break;
606    case STRING:
607      if (value instanceof StringType) 
608        return (Type) value;
609      else if (value instanceof UriType) 
610        return new StringType(((UriType) value).asStringValue());
611                break;
612    case TEXT: if (value instanceof StringType) return (Type) value; break;
613    case QUANTITY: if (value instanceof  Quantity) return (Type) value; break;
614
615    // complex cases:
616    // ? QuestionnaireItemTypeAttachment: ...?
617    case CHOICE:
618    case OPENCHOICE :
619      if (value instanceof Coding)
620        return (Type) value;
621      else if (value instanceof Enumeration) { 
622        Coding cc = new Coding();
623        cc.setCode(((Enumeration<Enum<?>>) value).asStringValue());
624        cc.setSystem(getSystemForCode(vs, cc.getCode(), path));
625        return cc;
626      }  else if (value instanceof StringType) {
627        Coding cc = new Coding();
628        cc.setCode(((StringType) value).asStringValue());
629        cc.setSystem(getSystemForCode(vs, cc.getCode(), path));
630        return cc;
631      }
632                break;
633
634    case REFERENCE:
635      if (value instanceof Reference)
636        return (Type) value;
637      else if (value instanceof StringType) {
638        Reference r = new Reference();
639        r.setReference(((StringType) value).asStringValue());
640      }
641                break;
642         default:
643                break;
644    }
645
646    throw new FHIRException("Unable to convert from '"+value.getClass().toString()+"' for Answer Format "+af.toCode()+", path = "+path);
647  }
648
649  private String getSystemForCode(ValueSet vs, String code, String path) throws FHIRException {
650//    var
651//    i, q : integer;
652//  begin
653    String result = null;
654    if (vs == null) {
655      if (prebuiltQuestionnaire == null) 
656        throw new FHIRException("Logic error at path = "+path);
657      for (Resource r : prebuiltQuestionnaire.getContained()) {
658        if (r instanceof ValueSet) {
659          vs = (ValueSet) r;
660          if (vs.hasExpansion()) {
661            for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
662              if (c.getCode().equals(code)) {
663                  if (result == null)
664                    result = c.getSystem();
665                  else
666                    throw new FHIRException("Multiple matches in "+vs.getUrl()+" for code "+code+" at path = "+path);
667              }
668            }
669          }
670        }
671      }
672    }
673    
674    for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
675      if (c.getCode().equals(code)) {
676        if (result == null)
677          result = c.getSystem();
678        else
679          throw new FHIRException("Multiple matches in "+vs.getUrl()+" for code "+code+" at path = "+path);
680      }
681    }
682    if (result != null)
683      return result;
684    throw new FHIRException("Unable to resolve code "+code+" at path = "+path);
685  }
686
687  private boolean isPrimitive(TypeRefComponent t) {
688    String code = t.getWorkingCode();
689    StructureDefinition sd = context.fetchTypeDefinition(code);
690    return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
691  }
692
693  private void processDataType(StructureDefinition profile, QuestionnaireItemComponent group, ElementDefinition element, String path, TypeRefComponent t, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, List<ElementDefinition> parents) throws FHIRException {
694    String tc = t.getWorkingCode();
695    if (tc.equals("code"))
696      addCodeQuestions(group, element, path, answerGroups);
697    else if (Utilities.existsInList(tc, "string", "id", "oid", "uuid", "markdown"))
698      addStringQuestions(group, element, path, answerGroups);
699    else if (Utilities.existsInList(tc, "uri", "url", "canonical"))
700      addUriQuestions(group, element, path, answerGroups);
701    else if (tc.equals("boolean"))
702      addBooleanQuestions(group, element, path, answerGroups);
703    else if (tc.equals("decimal"))
704      addDecimalQuestions(group, element, path, answerGroups);
705    else if (tc.equals("dateTime") || tc.equals("date"))
706        addDateTimeQuestions(group, element, path, answerGroups);
707    else if (tc.equals("instant"))
708      addInstantQuestions(group, element, path, answerGroups);
709    else if (tc.equals("time"))
710      addTimeQuestions(group, element, path, answerGroups);
711    else if (tc.equals("CodeableConcept"))
712      addCodeableConceptQuestions(group, element, path, answerGroups);
713    else if (tc.equals("Period"))
714      addPeriodQuestions(group, element, path, answerGroups);
715    else if (tc.equals("Ratio"))
716      addRatioQuestions(group, element, path, answerGroups);
717    else if (tc.equals("HumanName"))
718      addHumanNameQuestions(group, element, path, answerGroups);
719    else if (tc.equals("Address"))
720      addAddressQuestions(group, element, path, answerGroups);
721    else if (tc.equals("ContactPoint"))
722      addContactPointQuestions(group, element, path, answerGroups);
723    else if (tc.equals("Identifier"))
724      addIdentifierQuestions(group, element, path, answerGroups);
725    else if (tc.equals("integer") || tc.equals("positiveInt") || tc.equals("unsignedInt") )
726      addIntegerQuestions(group, element, path, answerGroups);
727    else if (tc.equals("Coding"))
728      addCodingQuestions(group, element, path, answerGroups);
729    else if (Utilities.existsInList(tc, "Quantity", "Count", "Age", "Duration", "Distance", "Money"))
730      addQuantityQuestions(group, element, path, answerGroups);
731    else if (tc.equals("Money"))
732      addMoneyQuestions(group, element, path, answerGroups);
733    else if (tc.equals("Reference"))
734      addReferenceQuestions(group, element, path, t.getTargetProfile(), answerGroups);
735    else if (tc.equals("Duration"))
736      addDurationQuestions(group, element, path, answerGroups);
737    else if (tc.equals("base64Binary"))
738      addBinaryQuestions(group, element, path, answerGroups);
739    else if (tc.equals("Attachment"))
740      addAttachmentQuestions(group, element, path, answerGroups);
741    else if (tc.equals("Age"))
742      addAgeQuestions(group, element, path, answerGroups);
743    else if (tc.equals("Range"))
744      addRangeQuestions(group, element, path, answerGroups);
745    else if (tc.equals("Timing"))
746      addTimingQuestions(group, element, path, answerGroups);
747    else if (tc.equals("Annotation"))
748      addAnnotationQuestions(group, element, path, answerGroups);
749    else if (tc.equals("SampledData"))
750      addSampledDataQuestions(group, element, path, answerGroups);
751    else if (tc.equals("Extension")) {
752      if (t.hasProfile())
753        addExtensionQuestions(profile, group, element, path, t.getProfile().get(0).getValue(), answerGroups, parents);
754    } else if (tc.equals("SampledData"))
755      addSampledDataQuestions(group, element, path, answerGroups);
756    else if (!tc.equals("Narrative") && !tc.equals("Resource") && !tc.equals("Meta") && !tc.equals("Signature")) {
757      StructureDefinition sd = context.fetchTypeDefinition(tc);
758      if (sd == null)
759        throw new NotImplementedException("Unhandled Data Type: "+tc+" on element "+element.getPath());
760      buildGroup(group, sd, sd.getSnapshot().getElementFirstRep(), parents, answerGroups);
761    }
762  }
763
764  private void addCodeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
765    ToolingExtensions.addFhirType(group, "code");
766    ValueSet vs = resolveValueSet(null, element.hasBinding() ? element.getBinding() : null);
767    addQuestion(group, QuestionnaireItemType.CHOICE, path, "value", unCamelCase(tail(element.getPath())), answerGroups, vs);
768    group.setText(null);
769    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
770      ag.setText(null);
771  }
772
773  private String unCamelCase(String s) {
774    StringBuilder result = new StringBuilder();
775    
776      for (int i = 0; i < s.length(); i++) {
777        if (Character.isUpperCase(s.charAt(i))) 
778          result.append(' ');
779        result.append(s.charAt(i));
780      }
781      return result.toString().toLowerCase();
782  }
783
784  private void addStringQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
785    ToolingExtensions.addFhirType(group, "string");
786    addQuestion(group, QuestionnaireItemType.STRING, path, "value", group.getText(), answerGroups);
787          group.setText(null);
788    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
789      ag.setText(null);
790  }
791
792  private void addTimeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
793    ToolingExtensions.addFhirType(group, "time");
794    addQuestion(group, QuestionnaireItemType.TIME, path, "value", group.getText(), answerGroups);
795          group.setText(null);
796    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
797      ag.setText(null);
798  }
799
800  private void addUriQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
801    ToolingExtensions.addFhirType(group, "uri");
802    addQuestion(group, QuestionnaireItemType.STRING, path, "value", group.getText(), answerGroups);
803          group.setText(null);
804    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
805      ag.setText(null);
806  }
807
808  private void addBooleanQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
809    ToolingExtensions.addFhirType(group, "boolean");
810    addQuestion(group, QuestionnaireItemType.BOOLEAN, path, "value", group.getText(), answerGroups);
811          group.setText(null);
812    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
813      ag.setText(null);
814  }
815
816  private void addDecimalQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
817    ToolingExtensions.addFhirType(group, "decimal");
818    addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", group.getText(), answerGroups);
819          group.setText(null);
820    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
821      ag.setText(null);
822  }
823
824  private void addIntegerQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
825    ToolingExtensions.addFhirType(group, "integer");
826    addQuestion(group, QuestionnaireItemType.INTEGER, path, "value", group.getText(), answerGroups);
827          group.setText(null);
828    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
829      ag.setText(null);
830  }
831
832  private void addDateTimeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
833    ToolingExtensions.addFhirType(group, "datetime");
834    addQuestion(group, QuestionnaireItemType.DATETIME, path, "value", group.getText(), answerGroups);
835          group.setText(null);
836    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
837      ag.setText(null);
838  }
839
840  private void addInstantQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
841    ToolingExtensions.addFhirType(group, "instant");
842    addQuestion(group, QuestionnaireItemType.DATETIME, path, "value", group.getText(), answerGroups);
843          group.setText(null);
844    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
845      ag.setText(null);
846  }
847
848  private void addBinaryQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) {
849    ToolingExtensions.addFhirType(group, "binary");
850    // ? Lloyd: how to support binary content
851  }
852  
853  // Complex Types ---------------------------------------------------------------
854
855  private QuestionnaireItemType answerTypeForBinding(ElementDefinitionBindingComponent binding) {
856    if (binding == null) 
857      return QuestionnaireItemType.OPENCHOICE;
858    else if (binding.getStrength() != BindingStrength.REQUIRED) 
859      return QuestionnaireItemType.OPENCHOICE;
860    else
861      return QuestionnaireItemType.CHOICE;
862  }
863
864  private void addCodingQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
865    ToolingExtensions.addFhirType(group, "Coding");
866    addQuestion(group, answerTypeForBinding(element.hasBinding() ? element.getBinding() : null), path, "value", group.getText(), answerGroups, resolveValueSet(null, element.hasBinding() ? element.getBinding() : null));
867    group.setText(null);
868    for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
869      ag.setText(null);
870  }
871
872  private void addCodeableConceptQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
873    ToolingExtensions.addFhirType(group, "CodeableConcept");
874    addQuestion(group, answerTypeForBinding(element.hasBinding() ? element.getBinding() : null), path, "coding", "code:", answerGroups, resolveValueSet(null, element.hasBinding() ? element.getBinding() : null));
875    addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups);
876  }
877
878  private ValueSet makeAnyValueSet() {
879    // TODO Auto-generated method stub
880    return null;
881  }
882
883  private void addPeriodQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
884    ToolingExtensions.addFhirType(group, "Period");
885    addQuestion(group, QuestionnaireItemType.DATETIME, path, "low", "start:", answerGroups);
886    addQuestion(group, QuestionnaireItemType.DATETIME, path, "end", "end:", answerGroups);
887  }
888
889  private void addRatioQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
890    ToolingExtensions.addFhirType(group, "Ratio");
891    addQuestion(group, QuestionnaireItemType.DECIMAL, path, "numerator", "numerator:", answerGroups);
892    addQuestion(group, QuestionnaireItemType.DECIMAL, path, "denominator", "denominator:", answerGroups);
893    addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups);
894  }
895
896  private void addHumanNameQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
897    ToolingExtensions.addFhirType(group, "Name");
898    addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups);
899    addQuestion(group, QuestionnaireItemType.STRING, path, "family", "family:", answerGroups).setRepeats(true);
900    addQuestion(group, QuestionnaireItemType.STRING, path, "given", "given:", answerGroups).setRepeats(true);
901  }
902
903  private void addAddressQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
904    ToolingExtensions.addFhirType(group, "Address");
905    addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups);
906    addQuestion(group, QuestionnaireItemType.STRING, path, "line", "line:", answerGroups).setRepeats(true);
907    addQuestion(group, QuestionnaireItemType.STRING, path, "city", "city:", answerGroups);
908    addQuestion(group, QuestionnaireItemType.STRING, path, "state", "state:", answerGroups);
909    addQuestion(group, QuestionnaireItemType.STRING, path, "postalCode", "post code:", answerGroups);
910    addQuestion(group, QuestionnaireItemType.STRING, path, "country", "country:", answerGroups);
911    addQuestion(group, QuestionnaireItemType.CHOICE, path, "use", "use:", answerGroups, resolveValueSet("http://hl7.org/fhir/vs/address-use"));
912  }
913
914    private void addContactPointQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
915    ToolingExtensions.addFhirType(group, "ContactPoint");
916    addQuestion(group, QuestionnaireItemType.CHOICE, path, "system", "type:", answerGroups, resolveValueSet("http://hl7.org/fhir/vs/contact-point-system"));
917    addQuestion(group, QuestionnaireItemType.STRING, path, "value", "value:", answerGroups);
918    addQuestion(group, QuestionnaireItemType.CHOICE, path, "use", "use:", answerGroups, resolveValueSet("http://hl7.org/fhir/vs/contact-point-use"));
919    }
920    
921    private void addIdentifierQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
922      ToolingExtensions.addFhirType(group, "Identifier");
923      addQuestion(group, QuestionnaireItemType.STRING, path, "label", "label:", answerGroups);
924      addQuestion(group, QuestionnaireItemType.STRING, path, "system", "system:", answerGroups);
925      addQuestion(group, QuestionnaireItemType.STRING, path, "value", "value:", answerGroups);
926    }
927
928    private void addSimpleQuantityQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
929      ToolingExtensions.addFhirType(group, "Quantity");
930      addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups);
931      addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups);
932      addQuestion(group, QuestionnaireItemType.STRING, path, "code", "coded units:", answerGroups);
933      addQuestion(group, QuestionnaireItemType.STRING, path, "system", "units system:", answerGroups);
934    }
935
936    private void addQuantityQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
937      ToolingExtensions.addFhirType(group, "Quantity");
938      addQuestion(group, QuestionnaireItemType.CHOICE, path, "comparator", "comp:", answerGroups, resolveValueSet("http://hl7.org/fhir/vs/quantity-comparator"));
939      addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups);
940      addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups);
941      addQuestion(group, QuestionnaireItemType.STRING, path, "code", "coded units:", answerGroups);
942      addQuestion(group, QuestionnaireItemType.STRING, path, "system", "units system:", answerGroups);
943    }
944
945    private void addMoneyQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
946      ToolingExtensions.addFhirType(group, "Money");
947      addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups);
948      addQuestion(group, QuestionnaireItemType.STRING, path, "currency", "currency:", answerGroups);
949  }
950
951    private void addAgeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
952      ToolingExtensions.addFhirType(group, "Age");
953      addQuestion(group, QuestionnaireItemType.CHOICE, path, "comparator", "comp:", answerGroups, resolveValueSet("http://hl7.org/fhir/vs/quantity-comparator"));
954      addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups);
955      addQuestion(group, QuestionnaireItemType.CHOICE, path, "units", "units:", answerGroups, resolveValueSet("http://hl7.org/fhir/vs/duration-units"));
956    }
957
958    private void addDurationQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
959      ToolingExtensions.addFhirType(group, "Duration");
960      addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups);
961      addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups);
962    }
963
964    private void addAttachmentQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) {
965      ToolingExtensions.addFhirType(group, "Attachment");
966      //    raise Exception.Create("addAttachmentQuestions not Done Yet");
967    }
968
969    private void addRangeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
970      ToolingExtensions.addFhirType(group, "Range");
971      addQuestion(group, QuestionnaireItemType.DECIMAL, path, "low", "low:", answerGroups);
972      addQuestion(group, QuestionnaireItemType.DECIMAL, path, "high", "high:", answerGroups);
973      addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups);
974    }
975    
976    private void addSampledDataQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) {
977      ToolingExtensions.addFhirType(group, "SampledData");
978    }
979    
980    private void addTimingQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
981      ToolingExtensions.addFhirType(group, "Schedule");
982      addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups);
983      addQuestion(group, QuestionnaireItemType.DATETIME, path, "date", "date:", answerGroups);
984      QuestionnaireItemComponent q = addQuestion(group, QuestionnaireItemType.REFERENCE, path, "author", "author:", answerGroups);
985      ToolingExtensions.addAllowedResource(q, "Patient");
986      ToolingExtensions.addAllowedResource(q, "Practitioner");
987      ToolingExtensions.addAllowedResource(q, "RelatedPerson");
988    }
989    
990    private void addAnnotationQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) {
991      ToolingExtensions.addFhirType(group, "Annotation");
992    }
993  // Special Types ---------------------------------------------------------------
994
995    private void addReferenceQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, List<CanonicalType> profileURL, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException {
996      //  var
997      //    rn : String;
998      //    i : integer;
999      //    q : TFhirQuestionnaireGroupQuestion;
1000      ToolingExtensions.addFhirType(group, "Reference");
1001
1002      QuestionnaireItemComponent q = addQuestion(group, QuestionnaireItemType.REFERENCE, path, "value", group.getText(), answerGroups);
1003      group.setText(null);
1004      CommaSeparatedStringBuilder rn = new CommaSeparatedStringBuilder();
1005      for (UriType u : profileURL)
1006      if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/"))
1007        rn.append(u.getValue().substring(40));
1008      if (rn.length() == 0)
1009        ToolingExtensions.addReferenceFilter(q, "subject=$subj&patient=$subj&encounter=$encounter");
1010      else {
1011        ToolingExtensions.addAllowedResource(q, rn.toString());
1012        ToolingExtensions.addReferenceFilter(q, "subject=$subj&patient=$subj&encounter=$encounter");
1013      }
1014      for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups)
1015        ag.setText(null);
1016    }
1017
1018
1019    private void addExtensionQuestions(StructureDefinition profile, QuestionnaireItemComponent group, ElementDefinition element, String path, String url, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, List<ElementDefinition> parents) throws FHIRException { 
1020      // if this a  profiled extension, then we add it
1021        if (!Utilities.noString(url)) {
1022                StructureDefinition ed =  context.fetchResource(StructureDefinition.class, url);
1023                if (ed != null) {
1024          if (answerGroups.size() > 0)
1025            throw new NotImplementedException("Debug this");
1026                        buildQuestion(group, profile, ed.getSnapshot().getElement().get(0), path+".extension["+url+"]", answerGroups, parents);
1027        }
1028      }
1029    }
1030
1031    private ValueSet resolveValueSet(String url) {
1032//      if (prebuiltQuestionnaire != null)
1033        return null; // we don't do anything with value sets in this case
1034
1035//      if (vsCache.containsKey(url))
1036//        return (ValueSet) questionnaire.getContained(vsCache.get(url));
1037//      else {
1038//        ValueSet vs = context.findValueSet(url);
1039//        if (vs != null)
1040//          return expander.expand(vs, MaxListboxCodings, false);
1041//      }
1042//       
1043//       /*     on e: ETooCostly do
1044//            begin
1045//              result := TFhirValueSet.Create;
1046//              try
1047//                result.identifierST := ref.referenceST;
1048//                result.link;
1049//              finally
1050//                result.Free;
1051//              end;
1052//            end;
1053//            on e : Exception do
1054//              raise;
1055//          end;*/
1056//      }
1057    }
1058
1059    private ValueSet resolveValueSet(Object object, ElementDefinitionBindingComponent binding) {
1060      return null;
1061    }
1062
1063}