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