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