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