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