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