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}