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