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