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