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